HepatoXu#

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.HepatoXu)
class HepatoXu(LinearReferenceClock):
    pass

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

Define clock metadata#

[4]:
model.metadata["clock_name"] = "hepatoxu"
model.metadata["data_type"] = "DNA methylation"  # Paper: The study developed circulating tumour-DNA methylation markers.
model.metadata["species"] = "Homo sapiens"  # Paper: Plasma samples came from human HCC patients and normal controls.
model.metadata["year"] = 2017
model.metadata["approved_by_author"] = "⌛"
model.metadata["citation"] = "Xu, R.-H., et al. “Circulating tumour DNA methylation markers for diagnosis and prognosis of hepatocellular carcinoma.” Nature Materials 16: 1155–1161 (2017)."
model.metadata["doi"] = "https://doi.org/10.1038/nmat4997"
model.metadata["notes"] = "Ten-marker plasma cfDNA methylation logistic model producing the combined HCC diagnosis score (cd-score); this packaged model does not implement the separate eight-marker prognosis score."
model.metadata["research_only"] = None
model.metadata["tissue"] = ["plasma cell-free DNA"]  # Paper: The diagnostic dataset comprised plasma cfDNA from HCC patients and normal controls.
model.metadata["predicts"] = ["hepatocellular carcinoma"]  # Paper: The ten-marker model produced a combined diagnosis score designated cd-score.
model.metadata["training_target"] = ["hepatocellular carcinoma"]  # Paper: The logistic regression was fitted as a binary prediction of HCC versus normal plasma samples.
model.metadata["unit"] = ["unitless"]  # Paper: The packaged LinearReferenceClock returns the weighted linear score without a sigmoid transformation.
model.metadata["model_type"] = "feature-selected logistic regression"  # Paper: Ten overlapping markers from LASSO and random forest were used as covariates in logistic regression.
model.metadata["platform"] = ["targeted bisulfite sequencing"]  # Paper: Chinese plasma methylation values were obtained by targeted bisulfite sequencing using molecular inversion probes.
model.metadata["population"] = "adults"  # Paper: The 1,933-sample dataset was split 2:1; the training set had 1,275 samples from 715 HCC and 560 normal samples.
model.metadata["journal"] = "Nature Materials"
model.metadata["last_author"] = "Kang Zhang"
model.metadata["n_features"] = 10
model.metadata["citations"] = 884
model.metadata["citations_date"] = "2026-07-05"

Download clock dependencies#

[5]:
os.system(f"curl -sL -o coefficients.csv https://raw.githubusercontent.com/bio-learn/biolearn/180852e2bab473303cb85da627178b1695ee9d86/biolearn/data/HepatoXu.csv")
[5]:
0

Load features#

[6]:
df = pd.read_csv('coefficients.csv')
mask = df['CpGmarker'].astype(str).str.lower().isin(['intercept', '(intercept)'])
intercept_value = float(df.loc[mask, 'CoefficientTraining'].iloc[0]) if mask.any() else 0.0
coef_df = df.loc[~mask].reset_index(drop=True)
model.features = coef_df['CpGmarker'].tolist()

Load weights into base model#

[7]:
weights = torch.tensor(coef_df['CoefficientTraining'].tolist()).unsqueeze(0).float()
intercept = torch.tensor([intercept_value]).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': 'Xu, Ruo-Han, et al. "Circulating tumour DNA methylation markers '
             'for diagnosis and prognosis of hepatocellular carcinoma." Nature '
             'Materials 16.11 (2017): 1155-1161.',
 'clock_name': 'hepatoxu',
 'data_type': 'methylation',
 'doi': 'https://doi.org/10.1038/nmat4997',
 'notes': None,
 'research_only': None,
 'species': 'Homo sapiens',
 'version': None,
 'year': 2017}
reference_values: None
preprocess_name: None
preprocess_dependencies: None
postprocess_name: None
postprocess_dependencies: None
features: ['cg10428836',
 'cg26668608',
 'cg25754195',
 'cg05205842',
 'cg11606215',
 'cg24067911',
 'cg18196829',
 'cg23211949',
 'cg17213048',
 'cg25459300']
base_model_features: None

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

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

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

base_model.linear.weight: tensor([[11.5430,  4.5570,  2.5190, -3.6120,  6.8650, -5.4390, -9.0780, -5.2090,
          6.6600,  1.9940]])
base_model.linear.bias: tensor([15.5950])

%==================================== 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([[49.1148],
        [40.0752],
        [23.5150],
        [ 1.2405],
        [ 2.2419],
        [-6.4443],
        [ 1.1513],
        [33.1075],
        [61.1116],
        [22.9915]], 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: coefficients.csv