YingDamAge#

Index#

  1. Instantiate model class

  2. Define clock metadata

  3. Download clock dependencies

  4. Load features

  5. Load weights into base model

  6. Load reference values

  7. Load preprocess and postprocess objects

  8. Check all clock parameters

  9. Basic test

  10. Save torch model

  11. Clear directory

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.YingDamAge)
class YingDamAge(pyagingModel):
    def __init__(self):
        super().__init__()

    def preprocess(self, x):
        return x

    def postprocess(self, x):
        return x

[3]:
model = pya.models.YingDamAge()

Define clock metadata#

[4]:
model.metadata["clock_name"] = "yingdamage"
model.metadata["data_type"] = "DNA methylation"  # Paper: All three clocks use whole-blood DNA-methylation beta values.
model.metadata["species"] = "Homo sapiens"  # Paper: The models were trained in human Generation Scotland blood samples.
model.metadata["year"] = 2024
model.metadata["approved_by_author"] = "⌛"
model.metadata["citation"] = "Ying, K., Liu, H., Tarkhov, A.E. et al. Causality-enriched epigenetic age uncouples damage and adaptation. Nature Aging 4, 231–246 (2024)."
model.metadata["doi"] = "https://doi.org/10.1038/s43587-023-00557-0"
model.metadata["notes"] = "Causality-enriched age predictor restricted to damaging age-related CpGs, with feature penalties weighted by EWMR causality scores."
model.metadata["research_only"] = None
model.metadata["tissue"] = ["whole blood"]  # Paper: The model was trained in whole-blood methylation.
model.metadata["predicts"] = ["damaging epigenetic age"]  # Paper: DamAge is designed as a biomarker of age-related damaging methylation changes.
model.metadata["training_target"] = ["chronological age"]  # Paper: All three causality-enriched elastic-net models were trained to predict chronological age.
model.metadata["unit"] = ["years"]  # Paper: Model selection used mean absolute error in years against chronological age.
model.metadata["model_type"] = "causality-weighted elastic net regression"  # Paper: Feature-specific elastic-net penalty factors were assigned from each CpG's causality score.
model.metadata["platform"] = ["Illumina 450K"]  # Paper: Training used Generation Scotland whole-blood methylation at CpGs available to the 450K-based analysis.
model.metadata["population"] = "adults"  # Paper: Age-related methylation was estimated in 7,036 Generation Scotland participants aged 18–93, and 2,664 blood samples were used for clock training.
model.metadata["journal"] = "Nature Aging"
model.metadata["last_author"] = "Vadim N. Gladyshev"
model.metadata["n_features"] = 1089
model.metadata["citations"] = 183
model.metadata["citations_date"] = "2026-07-05"

Download clock dependencies#

Download directly with curl#

[5]:
supplementary_url = "https://static-content.springer.com/esm/art%3A10.1038%2Fs43587-023-00557-0/MediaObjects/43587_2023_557_MOESM6_ESM.zip"
supplementary_file_name = "43587_2023_557_MOESM6_ESM.zip"
os.system(f"curl -o {supplementary_file_name} {supplementary_url}")
os.system(f'unzip {supplementary_file_name}')
[5]:
0

Load features#

From CSV file#

[6]:
df = pd.read_csv('YingDamAge.csv')
df['feature'] = df['term']
df['coefficient'] = df['estimate']
model.features = df['feature'][1:].tolist()

Load weights into base model#

[7]:
weights = torch.tensor(df['coefficient'][1:].tolist()).unsqueeze(0)
intercept = torch.tensor([df['coefficient'][0]])

Linear model#

[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': ('Ying, Kejun, et al. "Causality-enriched epigenetic age '
              'uncouples damage and adaptation." Nature Aging (2024): 1-16.',),
 'clock_name': 'yingdamage',
 'data_type': 'methylation',
 'doi': 'https://doi.org/10.1038/s43587-023-00557-0',
 'notes': None,
 'research_only': None,
 'species': 'Homo sapiens',
 'version': None,
 'year': 2024}
reference_values: None
preprocess_name: None
preprocess_dependencies: None
postprocess_name: None
postprocess_dependencies: None
features: ['cg00003994', 'cg00023464', 'cg00049440', 'cg00052482', 'cg00073543', 'cg00084338', 'cg00115654', 'cg00117599', 'cg00192773', 'cg00228017', 'cg00296038', 'cg00300637', 'cg00310410', 'cg00330279', 'cg00332802', 'cg00346985', 'cg00423487', 'cg00462168', 'cg00488692', 'cg00512563', 'cg00523379', 'cg00534318', 'cg00554993', 'cg00563845', 'cg00603274', 'cg00612299', 'cg00614360', 'cg00645579', 'cg00655552', 'cg00697033']... [Total elements: 1089]
base_model_features: None

%==================================== Model Details ====================================%
Model Structure:

base_model: LinearModel(
  (linear): Linear(in_features=1089, out_features=1, bias=True)
)

%==================================== Model Details ====================================%
Model Parameters and Weights:

base_model.linear.weight: [0.1111111119389534, 0.1420900523662567, 0.8157786130905151, -4.8587541580200195, 0.47707557678222656, 0.5002065300941467, 0.39611729979515076, 0.9855431914329529, 0.2023957073688507, 0.35894259810447693, 0.05865344777703285, -1.7170811891555786, 0.6014043688774109, -2.180171012878418, 0.9566295146942139, 0.4613795876502991, 0.8703015446662903, 0.1672862470149994, 0.06815365701913834, 0.15401731431484222, -3.003317356109619, 0.026848409324884415, -7.108214378356934, -5.615413665771484, 0.0425444021821022, 0.48533663153648376, 0.15448161959648132, 0.4560099244117737, -4.907559871673584, 0.8859975337982178]... [Tensor of shape torch.Size([1, 1089])]
base_model.linear.bias: tensor([543.4316])

%==================================== 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([[584.9497],
        [565.7351],
        [780.7145],
        [383.1862],
        [373.4299],
        [484.9948],
        [714.8078],
        [755.3972],
        [452.7687],
        [641.0902]], 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: 43587_2023_557_MOESM6_ESM.zip
Deleted file: YingCausAge.csv
Deleted file: YingDamAge.csv
Deleted file: YingAdaptAge.csv