SenChronoAge#
Index#
Let’s first import some packages:
[1]:
import os
import inspect
import shutil
import json
import torch
import pandas as pd
import pyaging as pya
Instantiate model class#
[2]:
def print_entire_class(cls):
source = inspect.getsource(cls)
print(source)
print_entire_class(pya.models.SenChronoAge)
class SenChronoAge(LinearReferenceClock):
pass
[3]:
model = pya.models.SenChronoAge()
Define clock metadata#
[4]:
model.metadata["clock_name"] = "senchronoage"
model.metadata["data_type"] = "DNA methylation" # Paper: The predictors use CpG DNA-methylation beta values.
model.metadata["species"] = "Homo sapiens" # Paper: All three assigned predictors were developed from human cell or human whole-blood datasets.
model.metadata["year"] = 2026
model.metadata["approved_by_author"] = "⌛"
model.metadata["citation"] = "Kasamoto, K., Gibson, J., Moqri, M., Smith, R. & Higgins-Chen, A.T. DNA methylation signatures of cellular senescence are not reversed by senolytic treatment. Aging Cell 25, e70430 (2026)."
model.metadata["doi"] = "https://doi.org/10.1111/acel.70430"
model.metadata["notes"] = "Senescence-enriched chronological-age predictor restricted to CpGs whose directions were concordant across in-vitro senescence, age and mortality analyses."
model.metadata["research_only"] = None
model.metadata["tissue"] = ["whole blood"] # Paper: SenChronoAge was trained in GSE40279 whole blood.
model.metadata["predicts"] = ["chronological age"] # Paper: SenChronoAge is the chronological-age predictor among the three clocks.
model.metadata["training_target"] = ["chronological age"] # Paper: The model was trained against chronological age.
model.metadata["unit"] = ["years"] # Paper: The chronological-age regression output is on the age scale in years.
model.metadata["model_type"] = "elastic net regression" # Paper: SenChronoAge used glmnet elastic-net regression with ten-fold cross-validation.
model.metadata["platform"] = ["Illumina 450K", "Illumina EPIC"] # Paper: Feature discovery and predictor training used human 450K and EPIC methylation datasets.
model.metadata["population"] = "adults" # Paper: GSE40279 contains 656 participants aged 19–101.
model.metadata["journal"] = "Aging Cell"
model.metadata["last_author"] = "Albert T. Higgins-Chen"
model.metadata["n_features"] = 187
model.metadata["citations"] = 0
model.metadata["citations_date"] = "2026-07-05"
Download clock dependencies#
[5]:
os.system(f"curl -sL -o SenChronoAge_CpGs.csv https://raw.githubusercontent.com/HigginsChenLab/methylCIPHER/19b12296b0d7eb7055a97d068064df635f44ce3e/data-raw/SenescenceAge/SenChronoAge_CpGs.csv")
[5]:
0
Load features#
[6]:
coef_df = pd.read_csv('SenChronoAge_CpGs.csv')
model.features = coef_df['CpG'].tolist()
Load weights into base model#
[7]:
weights = torch.tensor(coef_df['Coefficient'].tolist()).unsqueeze(0).float()
intercept = torch.tensor([-80.0532]).float()
[8]:
base_model = pya.models.LinearModel(input_dim=len(model.features))
base_model.linear.weight.data = weights.float()
base_model.linear.bias.data = intercept.float()
model.base_model = base_model
Load reference values#
[9]:
model.reference_values = None
Load preprocess and postprocess objects#
[10]:
model.preprocess_name = None
model.preprocess_dependencies = None
[11]:
model.postprocess_name = None
model.postprocess_dependencies = None
Check all clock parameters#
[12]:
pya.utils.print_model_details(model)
%==================================== Model Details ====================================%
Model Attributes:
training: True
metadata: {'approved_by_author': '⌛',
'citation': 'Kasamoto, Kotaro, et al. "DNA methylation clocks for estimating '
'replicative senescence in human cells." Aging Cell (2026): '
'e70430.',
'clock_name': 'senchronoage',
'data_type': 'methylation',
'doi': 'https://doi.org/10.1111/acel.70430',
'notes': None,
'research_only': None,
'species': 'Homo sapiens',
'version': None,
'year': 2026}
reference_values: None
preprocess_name: None
preprocess_dependencies: None
postprocess_name: None
postprocess_dependencies: None
features: ['cg00210473', 'cg00254017', 'cg00296652', 'cg00602326', 'cg00922748', 'cg00964103', 'cg01013171', 'cg01079407', 'cg01141812', 'cg01196788', 'cg01249187', 'cg01263575', 'cg01302656', 'cg01654770', 'cg01675158', 'cg01769968', 'cg01987776', 'cg02716556', 'cg02796545', 'cg03649284', 'cg03660134', 'cg04094193', 'cg04270048', 'cg04432128', 'cg04503600', 'cg04597312', 'cg04840739', 'cg04912316', 'cg05530568', 'cg05818515']... [Total elements: 187]
base_model_features: None
%==================================== Model Details ====================================%
Model Structure:
base_model: LinearModel(
(linear): Linear(in_features=187, out_features=1, bias=True)
)
%==================================== Model Details ====================================%
Model Parameters and Weights:
base_model.linear.weight: [9.502220153808594, -10.09235954284668, -7.13555383682251, 7.0440497398376465, 0.07385352998971939, 4.94988489151001, 3.2296602725982666, -0.23233868181705475, 13.087000846862793, 6.111741065979004, 3.240180492401123, -5.130921840667725, 5.639341354370117, 4.501803398132324, 0.6832999587059021, -0.25918036699295044, 4.322950839996338, 18.638635635375977, 3.3074159622192383, 5.428537845611572, -0.6449773907661438, 5.176876068115234, 1.967892050743103, 5.64846658706665, -0.2769940495491028, 3.7448530197143555, 7.980226039886475, 3.951530694961548, 12.726526260375977, -9.475204467773438]... [Tensor of shape torch.Size([1, 187])]
base_model.linear.bias: tensor([-80.0532])
%==================================== Model Details ====================================%
Basic test#
[13]:
torch.manual_seed(42)
input = torch.randn(10, len(model.features), dtype=float)
model.eval()
model.to(float)
pred = model(input)
pred
[13]:
tensor([[ -54.5464],
[-218.2588],
[ 143.5234],
[-101.8496],
[-296.5361],
[-180.5059],
[-193.5961],
[-180.5310],
[ -16.9680],
[ 65.8968]], dtype=torch.float64, grad_fn=<AddmmBackward0>)
Save torch model#
[14]:
torch.save(model, f"../weights/{model.metadata['clock_name']}.pt")
Clear directory#
[15]:
# Function to remove a folder and all its contents
def remove_folder(path):
try:
shutil.rmtree(path)
print(f"Deleted folder: {path}")
except Exception as e:
print(f"Error deleting folder {path}: {e}")
# Get a list of all files and folders in the current directory
all_items = os.listdir('.')
# Loop through the items
for item in all_items:
# Check if it's a file and does not end with .ipynb
if os.path.isfile(item) and not item.endswith('.ipynb'):
os.remove(item)
print(f"Deleted file: {item}")
# Check if it's a folder
elif os.path.isdir(item):
remove_folder(item)
Deleted file: SenChronoAge_CpGs.csv