{ "cells": [ { "cell_type": "markdown", "id": "18c4d6a7", "metadata": {}, "source": [ "# RepliTaliNorm" ] }, { "cell_type": "markdown", "id": "e659d200", "metadata": {}, "source": [ "## Index\n", "1. [Instantiate model class](#Instantiate-model-class)\n", "2. [Define clock metadata](#Define-clock-metadata)\n", "3. [Download clock dependencies](#Download-clock-dependencies)\n", "5. [Load features](#Load-features)\n", "6. [Load weights into base model](#Load-weights-into-base-model)\n", "7. [Load reference values](#Load-reference-values)\n", "8. [Load preprocess and postprocess objects](#Load-preprocess-and-postprocess-objects)\n", "10. [Check all clock parameters](#Check-all-clock-parameters)\n", "10. [Basic test](#Basic-test)\n", "11. [Save torch model](#Save-torch-model)\n", "12. [Clear directory](#Clear-directory)" ] }, { "cell_type": "markdown", "id": "e7d50618", "metadata": {}, "source": [ "Let's first import some packages:" ] }, { "cell_type": "code", "execution_count": 1, "id": "5e426b95", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:02.585274Z", "iopub.status.busy": "2026-07-04T19:52:02.585131Z", "iopub.status.idle": "2026-07-04T19:52:04.618526Z", "shell.execute_reply": "2026-07-04T19:52:04.617995Z" } }, "outputs": [], "source": [ "import os\n", "import inspect\n", "import shutil\n", "import json\n", "import torch\n", "import pandas as pd\n", "import pyaging as pya" ] }, { "cell_type": "markdown", "id": "cd74f2a1", "metadata": {}, "source": [ "## Instantiate model class" ] }, { "cell_type": "code", "execution_count": 2, "id": "1a817cfe", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:04.620450Z", "iopub.status.busy": "2026-07-04T19:52:04.620244Z", "iopub.status.idle": "2026-07-04T19:52:04.623172Z", "shell.execute_reply": "2026-07-04T19:52:04.622699Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "class RepliTaliNorm(LinearReferenceClock):\n", " pass\n", "\n" ] } ], "source": [ "def print_entire_class(cls):\n", " source = inspect.getsource(cls)\n", " print(source)\n", "\n", "print_entire_class(pya.models.RepliTaliNorm)" ] }, { "cell_type": "code", "execution_count": 3, "id": "1ded1023", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:04.624273Z", "iopub.status.busy": "2026-07-04T19:52:04.624191Z", "iopub.status.idle": "2026-07-04T19:52:04.625794Z", "shell.execute_reply": "2026-07-04T19:52:04.625482Z" } }, "outputs": [], "source": [ "model = pya.models.RepliTaliNorm()" ] }, { "cell_type": "markdown", "id": "12ec3578", "metadata": {}, "source": [ "## Define clock metadata" ] }, { "cell_type": "code", "execution_count": 4, "id": "b3f11541", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:04.627112Z", "iopub.status.busy": "2026-07-04T19:52:04.627020Z", "iopub.status.idle": "2026-07-04T19:52:04.629204Z", "shell.execute_reply": "2026-07-04T19:52:04.628820Z" } }, "outputs": [], "source": [ "model.metadata[\"clock_name\"] = \"replitalinorm\"\n", "model.metadata[\"data_type\"] = \"DNA methylation\" # Paper: Both models use DNA methylation at CpGs in common partially methylated domains.\n", "model.metadata[\"species\"] = \"Homo sapiens\" # Paper: The models were developed in primary human cells.\n", "model.metadata[\"year\"] = 2022\n", "model.metadata[\"approved_by_author\"] = \"⌛\"\n", "model.metadata[\"citation\"] = \"Endicott, J.L., Nolte, P.A., Shen, H. & Laird, P.W. Cell division drives DNA methylation loss in late-replicating domains in primary human cells. Nature Communications 13, 6659 (2022).\"\n", "model.metadata[\"doi\"] = \"https://doi.org/10.1038/s41467-022-34268-8\"\n", "model.metadata[\"notes\"] = \"Upstream starting-PD normalization model used during RepliTali construction. It was trained only in the chronologically youngest fetal skin fibroblast line (AG06561) to estimate the unobserved pre-culture replicative-history offset; it is not the final 87-CpG RepliTali model.\"\n", "model.metadata[\"research_only\"] = None\n", "model.metadata[\"tissue\"] = [\"cultured fibroblasts\"] # Paper: The normalizer was trained on the chronologically youngest primary cell, fetal skin fibroblast AG06561.\n", "model.metadata[\"predicts\"] = [\"replicative history\"] # Paper: The model estimates the starting-passage PD used to correct unknown pre-culture cell divisions.\n", "model.metadata[\"training_target\"] = [\"population doublings\"] # Paper: The normalizer was fitted to the measured PD trajectory of AG06561.\n", "model.metadata[\"unit\"] = [\"population doublings\"] # Paper: The calibration outcome is cumulative population doublings.\n", "model.metadata[\"model_type\"] = \"elastic net regression\" # Paper: Both the starting-PD normalizer and final RepliTali use elastic-net regression with alpha 0.5.\n", "model.metadata[\"platform\"] = [\"Illumina EPIC\"] # Paper: Training methylation was measured using the Infinium MethylationEPIC array.\n", "model.metadata[\"population\"] = \"human cell cultures\" # Paper: Only the chronologically youngest fetal fibroblast line was used to train the starting-PD normalizer.\n", "model.metadata[\"journal\"] = \"Nature Communications\"\n", "model.metadata[\"last_author\"] = \"Peter W. Laird\"\n", "model.metadata[\"n_features\"] = 218\n", "model.metadata[\"citations\"] = 86\n", "model.metadata[\"citations_date\"] = \"2026-07-05\"\n" ] }, { "cell_type": "markdown", "id": "ca60fea5", "metadata": {}, "source": [ "## Download clock dependencies" ] }, { "cell_type": "code", "execution_count": 5, "id": "fbcda558", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:04.630292Z", "iopub.status.busy": "2026-07-04T19:52:04.630221Z", "iopub.status.idle": "2026-07-04T19:52:05.266315Z", "shell.execute_reply": "2026-07-04T19:52:05.265464Z" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "os.system(f\"curl -sL -o RepliTaliNorm_CpGs.rda https://raw.githubusercontent.com/HigginsChenLab/methylCIPHER/19b12296b0d7eb7055a97d068064df635f44ce3e/data/RepliTaliNorm_CpGs.rda\")\n", "os.system(f\"curl -sL -o RepliTaliNorm_ref.rda https://raw.githubusercontent.com/HigginsChenLab/methylCIPHER/19b12296b0d7eb7055a97d068064df635f44ce3e/data/RepliTaliNorm_ref.rda\")" ] }, { "cell_type": "code", "execution_count": 6, "id": "67ac3d2e", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.268512Z", "iopub.status.busy": "2026-07-04T19:52:05.268336Z", "iopub.status.idle": "2026-07-04T19:52:05.272288Z", "shell.execute_reply": "2026-07-04T19:52:05.271717Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing download.r\n" ] } ], "source": [ "%%writefile download.r\n", "\n", "library(jsonlite)\n", "load(\"RepliTaliNorm_CpGs.rda\")\n", "load(\"RepliTaliNorm_ref.rda\")\n", "write_json(RepliTaliNorm_CpGs, \"coefficients.json\", digits = 10)\n", "write_json(RepliTaliNorm_ref$intercept, \"intercept.json\", digits = 10)" ] }, { "cell_type": "code", "execution_count": 7, "id": "053ad203", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.274226Z", "iopub.status.busy": "2026-07-04T19:52:05.274098Z", "iopub.status.idle": "2026-07-04T19:52:05.733935Z", "shell.execute_reply": "2026-07-04T19:52:05.733448Z" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "os.system(\"Rscript download.r\")" ] }, { "cell_type": "markdown", "id": "04e7dcd1", "metadata": {}, "source": [ "## Load features" ] }, { "cell_type": "code", "execution_count": 8, "id": "198f3104", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.735345Z", "iopub.status.busy": "2026-07-04T19:52:05.735236Z", "iopub.status.idle": "2026-07-04T19:52:05.738232Z", "shell.execute_reply": "2026-07-04T19:52:05.737779Z" } }, "outputs": [], "source": [ "coef_df = pd.DataFrame(json.load(open('coefficients.json')))\n", "model.features = coef_df['CpG'].tolist()\n", "iv = json.load(open('intercept.json'))\n", "intercept_value = iv[0] if isinstance(iv, list) else iv" ] }, { "cell_type": "markdown", "id": "f74b2a4d", "metadata": {}, "source": [ "## Load weights into base model" ] }, { "cell_type": "code", "execution_count": 9, "id": "3425943e", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.739414Z", "iopub.status.busy": "2026-07-04T19:52:05.739330Z", "iopub.status.idle": "2026-07-04T19:52:05.741105Z", "shell.execute_reply": "2026-07-04T19:52:05.740755Z" } }, "outputs": [], "source": [ "weights = torch.tensor(coef_df['coefficient'].tolist()).unsqueeze(0).float()\n", "intercept = torch.tensor([intercept_value]).float()" ] }, { "cell_type": "code", "execution_count": 10, "id": "9981bd78", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.742216Z", "iopub.status.busy": "2026-07-04T19:52:05.742137Z", "iopub.status.idle": "2026-07-04T19:52:05.744169Z", "shell.execute_reply": "2026-07-04T19:52:05.743851Z" } }, "outputs": [], "source": [ "base_model = pya.models.LinearModel(input_dim=len(model.features))\n", "\n", "base_model.linear.weight.data = weights.float()\n", "base_model.linear.bias.data = intercept.float()\n", "\n", "model.base_model = base_model" ] }, { "cell_type": "markdown", "id": "837c0c69", "metadata": {}, "source": [ "## Load reference values" ] }, { "cell_type": "code", "execution_count": 11, "id": "db8dbe0d", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.745363Z", "iopub.status.busy": "2026-07-04T19:52:05.745295Z", "iopub.status.idle": "2026-07-04T19:52:05.746796Z", "shell.execute_reply": "2026-07-04T19:52:05.746439Z" } }, "outputs": [], "source": [ "model.reference_values = None" ] }, { "cell_type": "markdown", "id": "210e66bb", "metadata": {}, "source": [ "## Load preprocess and postprocess objects" ] }, { "cell_type": "code", "execution_count": 12, "id": "f4b41ee0", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.747831Z", "iopub.status.busy": "2026-07-04T19:52:05.747770Z", "iopub.status.idle": "2026-07-04T19:52:05.749135Z", "shell.execute_reply": "2026-07-04T19:52:05.748815Z" } }, "outputs": [], "source": [ "model.preprocess_name = None\n", "model.preprocess_dependencies = None" ] }, { "cell_type": "code", "execution_count": 13, "id": "4f9de32e", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.750369Z", "iopub.status.busy": "2026-07-04T19:52:05.750283Z", "iopub.status.idle": "2026-07-04T19:52:05.751747Z", "shell.execute_reply": "2026-07-04T19:52:05.751453Z" } }, "outputs": [], "source": [ "model.postprocess_name = None\n", "model.postprocess_dependencies = None" ] }, { "cell_type": "markdown", "id": "064cbe98", "metadata": {}, "source": [ "## Check all clock parameters" ] }, { "cell_type": "code", "execution_count": 14, "id": "93273584", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.752995Z", "iopub.status.busy": "2026-07-04T19:52:05.752920Z", "iopub.status.idle": "2026-07-04T19:52:05.756529Z", "shell.execute_reply": "2026-07-04T19:52:05.756089Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "%==================================== Model Details ====================================%\n", "Model Attributes:\n", "\n", "training: True\n", "metadata: {'approved_by_author': '⌛',\n", " 'citation': 'Endicott, Jamie L., et al. \"Cell division drives DNA methylation '\n", " 'loss in late-replicating domains in primary human cells.\" Nature '\n", " 'Communications 13.1 (2022): 6659.',\n", " 'clock_name': 'replitalinorm',\n", " 'data_type': 'methylation',\n", " 'doi': 'https://doi.org/10.1038/s41467-022-34268-8',\n", " 'notes': None,\n", " 'research_only': None,\n", " 'species': 'Homo sapiens',\n", " 'version': None,\n", " 'year': 2022}\n", "reference_values: None\n", "preprocess_name: None\n", "preprocess_dependencies: None\n", "postprocess_name: None\n", "postprocess_dependencies: None\n", "features: ['cg00065944', 'cg00077044', 'cg00156723', 'cg00278655', 'cg00434151', 'cg00564996', 'cg00824087', 'cg00832327', 'cg01052456', 'cg01129509', 'cg01249202', 'cg01463644', 'cg01922998', 'cg02002355', 'cg02110836', 'cg02148017', 'cg02576202', 'cg02607176', 'cg02653473', 'cg03041877', 'cg03137548', 'cg03257387', 'cg03312661', 'cg03364201', 'cg03445914', 'cg03464371', 'cg03490300', 'cg03644817', 'cg04246882', 'cg04395911']... [Total elements: 218]\n", "base_model_features: None\n", "\n", "%==================================== Model Details ====================================%\n", "Model Structure:\n", "\n", "base_model: LinearModel(\n", " (linear): Linear(in_features=218, out_features=1, bias=True)\n", ")\n", "\n", "%==================================== Model Details ====================================%\n", "Model Parameters and Weights:\n", "\n", "base_model.linear.weight: [-0.6055963635444641, -1.0204650163650513, -0.39693695306777954, -0.13793666660785675, -0.19817344844341278, -0.4360240697860718, -0.005339149385690689, -0.6537990570068359, -0.4812646210193634, -1.2297855615615845, -0.5315495133399963, -0.41737160086631775, -0.3746994137763977, -1.131536602973938, -0.010423482395708561, -0.3394857943058014, -0.2915678024291992, -2.6015284061431885, -0.0052614822052419186, -0.27036532759666443, -2.179006814956665, 0.6976109147071838, -1.055829405784607, -0.5952957272529602, -0.0049955337308347225, 0.4774562418460846, -1.847993016242981, -0.5958481431007385, -0.1950979232788086, -0.007305147126317024]... [Tensor of shape torch.Size([1, 218])]\n", "base_model.linear.bias: tensor([75.8801])\n", "\n", "%==================================== Model Details ====================================%\n", "\n" ] } ], "source": [ "pya.utils.print_model_details(model)" ] }, { "cell_type": "markdown", "id": "5d4c1bc6", "metadata": {}, "source": [ "## Basic test" ] }, { "cell_type": "code", "execution_count": 15, "id": "59b48fb9", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.757781Z", "iopub.status.busy": "2026-07-04T19:52:05.757703Z", "iopub.status.idle": "2026-07-04T19:52:05.761720Z", "shell.execute_reply": "2026-07-04T19:52:05.761361Z" } }, "outputs": [ { "data": { "text/plain": [ "tensor([[76.1917],\n", " [62.6222],\n", " [78.3400],\n", " [75.3361],\n", " [71.4866],\n", " [67.6907],\n", " [74.4185],\n", " [71.8606],\n", " [72.5646],\n", " [88.5451]], dtype=torch.float64, grad_fn=)" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "torch.manual_seed(42)\n", "input = torch.randn(10, len(model.features), dtype=float)\n", "model.eval()\n", "model.to(float)\n", "pred = model(input)\n", "pred" ] }, { "cell_type": "markdown", "id": "28becd60", "metadata": {}, "source": [ "## Save torch model" ] }, { "cell_type": "code", "execution_count": 16, "id": "486e7036", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.762987Z", "iopub.status.busy": "2026-07-04T19:52:05.762917Z", "iopub.status.idle": "2026-07-04T19:52:05.766022Z", "shell.execute_reply": "2026-07-04T19:52:05.765567Z" } }, "outputs": [], "source": [ "torch.save(model, f\"../weights/{model.metadata['clock_name']}.pt\")" ] }, { "cell_type": "markdown", "id": "890b9045", "metadata": {}, "source": [ "## Clear directory\n", "" ] }, { "cell_type": "code", "execution_count": 17, "id": "0ece2c83", "metadata": { "execution": { "iopub.execute_input": "2026-07-04T19:52:05.767268Z", "iopub.status.busy": "2026-07-04T19:52:05.767195Z", "iopub.status.idle": "2026-07-04T19:52:05.770711Z", "shell.execute_reply": "2026-07-04T19:52:05.770336Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Deleted file: intercept.json\n", "Deleted file: download.r\n", "Deleted file: RepliTaliNorm_CpGs.rda\n", "Deleted file: RepliTaliNorm_ref.rda\n", "Deleted file: coefficients.json\n" ] } ], "source": [ "# Function to remove a folder and all its contents\n", "def remove_folder(path):\n", " try:\n", " shutil.rmtree(path)\n", " print(f\"Deleted folder: {path}\")\n", " except Exception as e:\n", " print(f\"Error deleting folder {path}: {e}\")\n", "\n", "# Get a list of all files and folders in the current directory\n", "all_items = os.listdir('.')\n", "\n", "# Loop through the items\n", "for item in all_items:\n", " # Check if it's a file and does not end with .ipynb\n", " if os.path.isfile(item) and not item.endswith('.ipynb'):\n", " os.remove(item)\n", " print(f\"Deleted file: {item}\")\n", " # Check if it's a folder\n", " elif os.path.isdir(item):\n", " remove_folder(item)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.11" } }, "nbformat": 4, "nbformat_minor": 5 }