Learning clinical temporal patterns with SWoTTeD#

Scenario: You want to discover latent clinical temporal patterns, i.e. recurrent temporal patterns of medical procedures, directly from raw MIMIC-IV data, without any supervision.

This tutorial shows a complete end-to-end pipeline:

  1. Ingest procedure events from MIMIC-IV into a TanaT EventSequencePool.

  2. Restrict to the most frequent procedure codes to keep the tensor tractable.

  3. Call to_tensor() with ohe=True to obtain a dense (N, M, K) array alongside patient IDs and feature names, all in a single call.

  4. Feed the tensor to SWoTTeD, a dictionary-learning model that decomposes the population into R temporal patterns, each with a characteristic temporal signature.

  5. Interpret the result using the feature_names returned by to_tensor().

Note

SWoTTeD is not bundled with TanaT. Install it separately:

pip install swotted

SWoTTeD can be seen as a deep machine learning model. It is based on a a PyTorch module. Thus, it illustrates how TanaT can help you in applying machine learning models on temporal sequences datasets.

Attention

SQL ingestion also requires connectorx:

pip install 'tanat[sql]'

TanaT concepts covered:

Imports#

import numpy as np
import polars as pl
import torch
from omegaconf import OmegaConf
from torch.utils.data import DataLoader

from swotted import fastSWoTTeDDataset, fastSWoTTeDModule, fastSWoTTeDTrainer

from tanat.criterion import EntityCriterion, LengthCriterion
from tanat.dataset import access
from tanat.sequence.type.event.pool import EventSequencePool

Step 1: Discover the top procedure codes#

We build a single pool over all procedure codes, count code frequencies, then restrict to the top 30 most frequent codes. Keeping only frequent codes avoids an extremely sparse OHE tensor (352 codes x 92 patients would be ~99% zeros).

DB = f"sqlite:///{access('mimic4')}"

pool = EventSequencePool(
    store=(
        EventSequencePool.builder()
        .add_sql(
            DB,
            'SELECT subject_id, chartdate, icd_code FROM "hosp/procedures_icd"',
            id_column="subject_id",
            time_column="chartdate",
            features=["icd_code"],
        )
        .build("procedures_store", exist_ok=True)
    )
)
mimic4.db:   0%|          | 0.00/147M [00:00<?, ?B/s]
mimic4.db:   0%|          | 139k/147M [00:00<01:45, 1.39MB/s]
mimic4.db:   0%|          | 279k/147M [00:00<02:43, 897kB/s]
mimic4.db:   0%|          | 459k/147M [00:00<02:40, 912kB/s]
mimic4.db:   0%|          | 639k/147M [00:00<02:39, 916kB/s]
mimic4.db:   1%|          | 819k/147M [00:00<02:39, 920kB/s]
mimic4.db:   1%|          | 999k/147M [00:01<02:38, 922kB/s]
mimic4.db:   1%|          | 1.20M/147M [00:01<02:33, 951kB/s]
mimic4.db:   1%|          | 1.43M/147M [00:01<02:22, 1.02MB/s]
mimic4.db:   1%|          | 1.64M/147M [00:01<02:19, 1.05MB/s]
mimic4.db:   1%|▏         | 1.87M/147M [00:01<02:13, 1.09MB/s]
mimic4.db:   1%|▏         | 2.05M/147M [00:02<02:20, 1.03MB/s]
mimic4.db:   2%|▏         | 2.24M/147M [00:02<02:23, 1.01MB/s]
mimic4.db:   2%|▏         | 2.42M/147M [00:02<02:27, 980kB/s]
mimic4.db:   2%|▏         | 2.53M/147M [00:02<02:23, 1.01MB/s]
mimic4.db:   2%|▏         | 2.64M/147M [00:02<02:22, 1.01MB/s]
mimic4.db:   2%|▏         | 2.83M/147M [00:02<02:00, 1.20MB/s]
mimic4.db:   2%|▏         | 3.10M/147M [00:02<01:31, 1.57MB/s]
mimic4.db:   2%|▏         | 3.52M/147M [00:02<01:04, 2.22MB/s]
mimic4.db:   3%|▎         | 4.06M/147M [00:03<00:46, 3.05MB/s]
mimic4.db:   3%|▎         | 4.79M/147M [00:03<00:33, 4.20MB/s]
mimic4.db:   4%|▍         | 5.82M/147M [00:03<00:24, 5.88MB/s]
mimic4.db:   5%|▍         | 7.25M/147M [00:03<00:16, 8.24MB/s]
mimic4.db:   6%|▌         | 9.06M/147M [00:03<00:12, 10.9MB/s]
mimic4.db:   7%|▋         | 10.9M/147M [00:03<00:10, 13.0MB/s]
mimic4.db:   9%|▊         | 12.7M/147M [00:03<00:09, 14.3MB/s]
mimic4.db:  10%|▉         | 14.1M/147M [00:03<00:09, 14.2MB/s]
mimic4.db:  11%|█         | 15.9M/147M [00:03<00:08, 15.2MB/s]
mimic4.db:  12%|█▏        | 17.4M/147M [00:03<00:08, 15.2MB/s]
mimic4.db:  13%|█▎        | 19.0M/147M [00:04<00:08, 15.2MB/s]
mimic4.db:  14%|█▍        | 20.6M/147M [00:04<00:08, 15.3MB/s]
mimic4.db:  15%|█▌        | 22.1M/147M [00:04<00:08, 15.2MB/s]
mimic4.db:  16%|█▌        | 23.7M/147M [00:04<00:08, 15.2MB/s]
mimic4.db:  17%|█▋        | 25.3M/147M [00:04<00:07, 15.4MB/s]
mimic4.db:  18%|█▊        | 27.0M/147M [00:04<00:07, 15.8MB/s]
mimic4.db:  20%|█▉        | 28.7M/147M [00:04<00:07, 16.1MB/s]
mimic4.db:  21%|██        | 30.4M/147M [00:04<00:07, 15.7MB/s]
mimic4.db:  22%|██▏       | 31.9M/147M [00:04<00:07, 15.1MB/s]
mimic4.db:  23%|██▎       | 33.5M/147M [00:05<00:07, 14.9MB/s]
mimic4.db:  24%|██▍       | 35.0M/147M [00:05<00:07, 14.3MB/s]
mimic4.db:  25%|██▍       | 36.5M/147M [00:05<00:07, 14.6MB/s]
mimic4.db:  26%|██▌       | 38.0M/147M [00:05<00:07, 14.4MB/s]
mimic4.db:  27%|██▋       | 39.4M/147M [00:05<00:07, 14.3MB/s]
mimic4.db:  28%|██▊       | 40.9M/147M [00:05<00:08, 12.3MB/s]
mimic4.db:  29%|██▊       | 42.1M/147M [00:05<00:13, 8.06MB/s]
mimic4.db:  29%|██▉       | 43.2M/147M [00:06<00:16, 6.28MB/s]
mimic4.db:  30%|██▉       | 44.0M/147M [00:06<00:17, 6.06MB/s]
mimic4.db:  30%|███       | 44.8M/147M [00:06<00:16, 6.36MB/s]
mimic4.db:  31%|███       | 45.8M/147M [00:06<00:13, 7.25MB/s]
mimic4.db:  32%|███▏      | 46.7M/147M [00:06<00:13, 7.40MB/s]
mimic4.db:  32%|███▏      | 47.7M/147M [00:06<00:12, 8.12MB/s]
mimic4.db:  33%|███▎      | 49.0M/147M [00:06<00:10, 9.40MB/s]
mimic4.db:  34%|███▍      | 50.2M/147M [00:06<00:09, 9.94MB/s]
mimic4.db:  35%|███▍      | 51.4M/147M [00:07<00:09, 10.6MB/s]
mimic4.db:  36%|███▌      | 52.5M/147M [00:07<00:10, 8.90MB/s]
mimic4.db:  36%|███▋      | 53.6M/147M [00:07<00:10, 9.13MB/s]
mimic4.db:  37%|███▋      | 54.8M/147M [00:07<00:09, 9.98MB/s]
mimic4.db:  38%|███▊      | 55.9M/147M [00:07<00:09, 10.1MB/s]
mimic4.db:  39%|███▊      | 56.9M/147M [00:07<00:09, 9.49MB/s]
mimic4.db:  39%|███▉      | 57.9M/147M [00:07<00:13, 6.79MB/s]
mimic4.db:  40%|███▉      | 58.7M/147M [00:08<00:12, 7.10MB/s]
mimic4.db:  41%|████      | 59.8M/147M [00:08<00:11, 7.88MB/s]
mimic4.db:  41%|████▏     | 60.7M/147M [00:08<00:10, 8.32MB/s]
mimic4.db:  42%|████▏     | 61.9M/147M [00:08<00:09, 9.32MB/s]
mimic4.db:  43%|████▎     | 63.4M/147M [00:08<00:07, 10.7MB/s]
mimic4.db:  44%|████▍     | 64.6M/147M [00:08<00:07, 11.0MB/s]
mimic4.db:  45%|████▍     | 66.0M/147M [00:08<00:07, 11.4MB/s]
mimic4.db:  46%|████▌     | 67.2M/147M [00:08<00:06, 11.6MB/s]
mimic4.db:  47%|████▋     | 69.0M/147M [00:08<00:05, 13.3MB/s]
mimic4.db:  48%|████▊     | 70.6M/147M [00:08<00:05, 14.1MB/s]
mimic4.db:  49%|████▉     | 72.2M/147M [00:09<00:05, 14.2MB/s]
mimic4.db:  50%|█████     | 73.9M/147M [00:09<00:04, 15.2MB/s]
mimic4.db:  51%|█████▏    | 75.5M/147M [00:09<00:04, 15.2MB/s]
mimic4.db:  52%|█████▏    | 77.0M/147M [00:09<00:04, 15.2MB/s]
mimic4.db:  54%|█████▎    | 78.8M/147M [00:09<00:04, 15.8MB/s]
mimic4.db:  55%|█████▍    | 80.4M/147M [00:09<00:04, 15.8MB/s]
mimic4.db:  56%|█████▌    | 82.0M/147M [00:09<00:04, 15.8MB/s]
mimic4.db:  57%|█████▋    | 83.7M/147M [00:09<00:03, 16.1MB/s]
mimic4.db:  58%|█████▊    | 85.3M/147M [00:09<00:03, 15.9MB/s]
mimic4.db:  59%|█████▉    | 87.0M/147M [00:09<00:03, 16.0MB/s]
mimic4.db:  60%|██████    | 88.6M/147M [00:10<00:03, 16.0MB/s]
mimic4.db:  61%|██████▏   | 90.2M/147M [00:10<00:03, 15.9MB/s]
mimic4.db:  62%|██████▏   | 91.9M/147M [00:10<00:03, 15.9MB/s]
mimic4.db:  64%|██████▎   | 93.5M/147M [00:10<00:03, 15.8MB/s]
mimic4.db:  65%|██████▍   | 95.1M/147M [00:10<00:03, 15.5MB/s]
mimic4.db:  66%|██████▌   | 96.6M/147M [00:10<00:03, 15.4MB/s]
mimic4.db:  67%|██████▋   | 98.2M/147M [00:10<00:03, 15.4MB/s]
mimic4.db:  68%|██████▊   | 99.7M/147M [00:10<00:03, 15.2MB/s]
mimic4.db:  69%|██████▉   | 101M/147M [00:10<00:03, 11.9MB/s]
mimic4.db:  70%|██████▉   | 103M/147M [00:11<00:04, 9.86MB/s]
mimic4.db:  70%|███████   | 104M/147M [00:11<00:07, 6.08MB/s]
mimic4.db:  71%|███████   | 105M/147M [00:11<00:07, 5.61MB/s]
mimic4.db:  72%|███████▏  | 105M/147M [00:11<00:08, 5.12MB/s]
mimic4.db:  72%|███████▏  | 106M/147M [00:12<00:08, 4.89MB/s]
mimic4.db:  73%|███████▎  | 107M/147M [00:12<00:08, 4.92MB/s]
mimic4.db:  73%|███████▎  | 108M/147M [00:12<00:07, 5.30MB/s]
mimic4.db:  74%|███████▎  | 108M/147M [00:12<00:07, 5.40MB/s]
mimic4.db:  74%|███████▍  | 109M/147M [00:12<00:06, 5.42MB/s]
mimic4.db:  75%|███████▍  | 110M/147M [00:12<00:06, 5.39MB/s]
mimic4.db:  75%|███████▌  | 110M/147M [00:12<00:06, 5.45MB/s]
mimic4.db:  75%|███████▌  | 111M/147M [00:13<00:08, 4.43MB/s]
mimic4.db:  76%|███████▌  | 112M/147M [00:13<00:07, 4.87MB/s]
mimic4.db:  76%|███████▋  | 112M/147M [00:13<00:07, 4.86MB/s]
mimic4.db:  77%|███████▋  | 113M/147M [00:13<00:07, 4.45MB/s]
mimic4.db:  77%|███████▋  | 114M/147M [00:13<00:06, 5.26MB/s]
mimic4.db:  78%|███████▊  | 114M/147M [00:13<00:07, 4.56MB/s]
mimic4.db:  78%|███████▊  | 115M/147M [00:13<00:06, 5.01MB/s]
mimic4.db:  79%|███████▊  | 116M/147M [00:14<00:06, 4.61MB/s]
mimic4.db:  79%|███████▉  | 116M/147M [00:14<00:06, 4.90MB/s]
mimic4.db:  80%|███████▉  | 117M/147M [00:14<00:06, 4.42MB/s]
mimic4.db:  80%|███████▉  | 118M/147M [00:14<00:06, 4.54MB/s]
mimic4.db:  80%|████████  | 118M/147M [00:14<00:05, 5.37MB/s]
mimic4.db:  81%|████████  | 119M/147M [00:14<00:06, 4.51MB/s]
mimic4.db:  81%|████████▏ | 120M/147M [00:14<00:05, 4.93MB/s]
mimic4.db:  82%|████████▏ | 120M/147M [00:15<00:05, 5.01MB/s]
mimic4.db:  82%|████████▏ | 121M/147M [00:15<00:05, 4.65MB/s]
mimic4.db:  83%|████████▎ | 122M/147M [00:15<00:05, 4.56MB/s]
mimic4.db:  83%|████████▎ | 122M/147M [00:15<00:05, 4.70MB/s]
mimic4.db:  84%|████████▎ | 123M/147M [00:15<00:04, 4.99MB/s]
mimic4.db:  84%|████████▍ | 123M/147M [00:15<00:05, 4.69MB/s]
mimic4.db:  84%|████████▍ | 124M/147M [00:15<00:04, 4.88MB/s]
mimic4.db:  85%|████████▍ | 125M/147M [00:15<00:04, 4.95MB/s]
mimic4.db:  85%|████████▌ | 125M/147M [00:16<00:04, 4.93MB/s]
mimic4.db:  86%|████████▌ | 126M/147M [00:16<00:04, 5.20MB/s]
mimic4.db:  86%|████████▌ | 127M/147M [00:16<00:03, 5.35MB/s]
mimic4.db:  87%|████████▋ | 127M/147M [00:16<00:03, 5.37MB/s]
mimic4.db:  87%|████████▋ | 128M/147M [00:16<00:03, 5.63MB/s]
mimic4.db:  87%|████████▋ | 129M/147M [00:16<00:03, 5.53MB/s]
mimic4.db:  88%|████████▊ | 129M/147M [00:16<00:03, 4.63MB/s]
mimic4.db:  88%|████████▊ | 130M/147M [00:16<00:03, 5.06MB/s]
mimic4.db:  91%|█████████ | 134M/147M [00:17<00:01, 12.5MB/s]
mimic4.db:  92%|█████████▏| 136M/147M [00:17<00:00, 14.9MB/s]
mimic4.db:  93%|█████████▎| 137M/147M [00:17<00:00, 11.5MB/s]
mimic4.db:  94%|█████████▍| 139M/147M [00:17<00:00, 10.8MB/s]
mimic4.db:  95%|█████████▌| 140M/147M [00:17<00:00, 11.6MB/s]
mimic4.db:  96%|█████████▌| 142M/147M [00:17<00:00, 11.6MB/s]
mimic4.db:  97%|█████████▋| 143M/147M [00:17<00:00, 12.6MB/s]
mimic4.db:  98%|█████████▊| 145M/147M [00:17<00:00, 13.4MB/s]
mimic4.db:  99%|█████████▉| 146M/147M [00:18<00:00, 13.7MB/s]
mimic4.db: 100%|██████████| 147M/147M [00:18<00:00, 8.11MB/s]
┌─ Event SequenceStore
│
│ Step 1/4: Sorting & preparing data
│
│ Step 2/4: Building sequence index
│
│ Step 3/4: Writing entity & time index features
│
│ Step 4/4: Computing & writing metadata
│
└─ Done (92 sequences · 722 entities · 0.00s)
TOP_K = 30

top_codes = (
    pool.temporal_data()
    .groupby("icd_code")
    .size()
    .sort_values(ascending=False)
    .head(TOP_K)
    .index.tolist()
)
print(f"Retaining {TOP_K} codes out of {pool.temporal_data()['icd_code'].nunique()}")
print("Top codes:", top_codes[:10], "...")
Retaining 30 codes out of 352
Top codes: ['02HV33Z', '3897', '966', '9671', '3961', '3893', '5491', '9604', '3E0G76Z', '3891'] ...

Step 2: Restrict the pool to the top codes and drop empty sequences#

filter_entities() with an EntityCriterion keeps only the most frequent procedure codes.

Because some patients may have no remaining procedure events after this filtering step, we also remove every empty sequence with LengthCriterion before we build the tensor. This keeps the downstream SWoTTeD input focused on patients with at least one usable pathways.

pool.filter_entities(
    EntityCriterion(query=pl.col("icd_code").is_in(top_codes)),
    inplace=True,
)

n_before = len(pool)
non_empty_seq = pool.which(LengthCriterion(gt=0))
pool = pool.subset(non_empty_seq)
print("Pool size:", len(pool))
[filter_entities] EntityCriterion → 284 / 722 entities (39.3%) · 23 IDs affected
[which]           LengthCriterion → 69 / 92 IDs (75.0%)
Pool size: 69

Step 3: Encode as a dense 3-D tensor#

We now have a cohort of patients with at least one retained procedure event. The next step is to project these histories onto a shared daily time axis.

to_tensor() returns a 3-tuple (arr, ids, feature_names):

  • arr: shape (N, M, K), i.e. N patients x M daily bins x K OHE codes.

  • ids: the N patient identifiers aligned with axis 0.

  • feature_names: the K column labels aligned with axis 2.

ohe=True one-hot encodes icd_code in-place; fill_value=0 replaces empty bins with zeros (no procedure recorded that day).

Hint

to_tensor() is a function that bridges the world of TanaT’s sequences with the worlds of deep machine learning. Tensors are the basic data structure for Keras, PyTorch or JAX machine learning engines.

BIN_SIZE = "365D"  # one bin = 365 days (1 years)

# Cast ``icd_code`` to a categorical type
pool.cast_features({"icd_code": pl.Categorical})
arr, ids, feature_names = pool.to_tensor(
    features="icd_code",
    bin_size=BIN_SIZE,
    fill_value=0,
    ohe=True,
)

print(f"Tensor shape : {arr.shape}")  # (N, M, K)
print(f"Patients     : {len(ids)}")
print(f"Features     : {len(feature_names)}")
print(f"Sparsity     : {(arr == 0).mean():.4%} empty bins")
print(f"Non-zero cells : {(arr != 0).sum()}")
print(f"Patients with events : {(arr.sum(axis=(1, 2)) != 0).sum()} / {arr.shape[0]}")
Tensor shape : (69, 91, 30)
Patients     : 69
Features     : 30
Sparsity     : 99.9517% empty bins
Non-zero cells : 91
Patients with events : 69 / 69

Step 4: Prepare the tensor for SWoTTeD#

SWoTTeD’s fastSWoTTeDModule expects a tensor of shape (N, K, M) The to_tensor() returns (N, M, K). A single transpose aligns the axes.

# (N, M, K)  →  (N, K, M)
X = torch.from_numpy(arr.transpose(0, 2, 1).astype(np.float32))
print(f"SWoTTeD input shape : {X.shape}")  # (N, K, M)
SWoTTeD input shape : torch.Size([69, 30, 91])

Step 5: Train SWoTTeD#

We search for R = 5 temporal patterns, each described by a temporal window of Tw = 7 days. Training runs for 50 epochs on CPU, fast enough on this small cohort.

R = 5  # number of temporal patterns to discover
Tw = 7  # temporal window width (days)

N_patients, K_codes, T_days = X.shape

swotted_cfg = OmegaConf.create(
    {
        "model": {
            "non_succession": True,
            "sparsity": 0.1,
            "rank": R,
            "twl": Tw,
            "N": K_codes,
            "metric": "Bernoulli",  # binary OHE data → Bernoulli loss
        },
        "training": {
            "batch_size": N_patients,
            "nepochs": 50,
            "lr": 1e-2,
        },
        "predict": {
            "nepochs": 20,
            "lr": 1e-2,
        },
    }
)

device = torch.device("cpu")
model = fastSWoTTeDModule(swotted_cfg).to(device)

loader = DataLoader(
    fastSWoTTeDDataset(X.to(device)),
    batch_size=N_patients,
    shuffle=False,
    collate_fn=lambda x: x,
)

trainer = fastSWoTTeDTrainer(
    fast_dev_run=False,
    max_epochs=swotted_cfg.training.nepochs,
    accelerator="cpu",
)
trainer.fit(model=model, train_dataloaders=loader)
GPU available: False, used: False
TPU available: False, using: 0 TPU cores
/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/lightning/pytorch/trainer/connectors/logger_connector/logger_connector.py:76: Starting from v1.9.0, `tensorboardX` has been removed as a dependency of the `lightning.pytorch` package, due to potential conflicts with other packages in the ML ecosystem. For this reason, `logger=True` will use `CSVLogger` as the default logger, unless the `tensorboard` or `tensorboardX` packages are found. Please `pip install lightning[extra]` or one of them to enable TensorBoard support by default
💡 Tip: For seamless cloud logging and experiment tracking, try installing [litlogger](https://pypi.org/project/litlogger/) to enable LitLogger, which logs metrics and artifacts automatically to the Lightning Experiments platform.
💡 Tip: For seamless cloud uploads and versioning, try installing [litmodels](https://pypi.org/project/litmodels/) to enable LitModelCheckpoint, which syncs automatically with the Lightning model registry.
/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/lightning/pytorch/trainer/configuration_validator.py:70: You defined a `validation_step` but have no `val_dataloader`. Skipping val loop.

  | Name         | Type              | Params | Mode  | FLOPs
-------------------------------------------------------------------
0 | model        | SlidingWindowConv | 0      | train | 0
  | other params | n/a               | 1.1 K  | n/a   | n/a
-------------------------------------------------------------------
1.1 K     Trainable params
0         Non-trainable params
1.1 K     Total params
0.004     Total estimated model params size (MB)
1         Modules in train mode
0         Modules in eval mode
0         Total Flops
/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/lightning/pytorch/utilities/_pytree.py:21: `isinstance(treespec, LeafSpec)` is deprecated, use `isinstance(treespec, TreeSpec) and treespec.is_leaf()` instead.
/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/lightning/pytorch/trainer/connectors/data_connector.py:434: The 'train_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=3` in the `DataLoader` to improve performance.
/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/lightning/pytorch/loops/fit_loop.py:321: The number of training batches (1) is smaller than the logging interval Trainer(log_every_n_steps=50). Set a lower value for log_every_n_steps if you want to see logs for the training epoch.

Training: |          | 0/? [00:00<?, ?it/s]
Training: |          | 0/? [00:00<?, ?it/s]
Epoch 0:   0%|          | 0/1 [00:00<?, ?it/s]
Epoch 0: 100%|██████████| 1/1 [00:00<00:00, 23.18it/s]
Epoch 0: 100%|██████████| 1/1 [00:00<00:00, 23.02it/s, v_num=0]
Epoch 0: 100%|██████████| 1/1 [00:00<00:00, 22.87it/s, v_num=0]
Epoch 0:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 1:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 1: 100%|██████████| 1/1 [00:00<00:00, 104.54it/s, v_num=0]
Epoch 1: 100%|██████████| 1/1 [00:00<00:00, 102.56it/s, v_num=0]
Epoch 1: 100%|██████████| 1/1 [00:00<00:00, 99.93it/s, v_num=0]
Epoch 1:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 2:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 2: 100%|██████████| 1/1 [00:00<00:00, 107.36it/s, v_num=0]
Epoch 2: 100%|██████████| 1/1 [00:00<00:00, 104.95it/s, v_num=0]
Epoch 2: 100%|██████████| 1/1 [00:00<00:00, 102.28it/s, v_num=0]
Epoch 2:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 3:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 3: 100%|██████████| 1/1 [00:00<00:00, 105.59it/s, v_num=0]
Epoch 3: 100%|██████████| 1/1 [00:00<00:00, 103.52it/s, v_num=0]
Epoch 3: 100%|██████████| 1/1 [00:00<00:00, 100.78it/s, v_num=0]
Epoch 3:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 4:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 4: 100%|██████████| 1/1 [00:00<00:00, 109.45it/s, v_num=0]
Epoch 4: 100%|██████████| 1/1 [00:00<00:00, 106.96it/s, v_num=0]
Epoch 4: 100%|██████████| 1/1 [00:00<00:00, 104.24it/s, v_num=0]
Epoch 4:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 5:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 5: 100%|██████████| 1/1 [00:00<00:00, 108.78it/s, v_num=0]
Epoch 5: 100%|██████████| 1/1 [00:00<00:00, 106.62it/s, v_num=0]
Epoch 5: 100%|██████████| 1/1 [00:00<00:00, 103.92it/s, v_num=0]
Epoch 5:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 6:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 6: 100%|██████████| 1/1 [00:00<00:00, 103.74it/s, v_num=0]
Epoch 6: 100%|██████████| 1/1 [00:00<00:00, 101.81it/s, v_num=0]
Epoch 6: 100%|██████████| 1/1 [00:00<00:00, 99.31it/s, v_num=0]
Epoch 6:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 7:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 7: 100%|██████████| 1/1 [00:00<00:00, 108.69it/s, v_num=0]
Epoch 7: 100%|██████████| 1/1 [00:00<00:00, 106.44it/s, v_num=0]
Epoch 7: 100%|██████████| 1/1 [00:00<00:00, 103.78it/s, v_num=0]
Epoch 7:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 8:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 8: 100%|██████████| 1/1 [00:00<00:00, 109.32it/s, v_num=0]
Epoch 8: 100%|██████████| 1/1 [00:00<00:00, 107.17it/s, v_num=0]
Epoch 8: 100%|██████████| 1/1 [00:00<00:00, 104.21it/s, v_num=0]
Epoch 8:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 9:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 9: 100%|██████████| 1/1 [00:00<00:00, 107.65it/s, v_num=0]
Epoch 9: 100%|██████████| 1/1 [00:00<00:00, 105.53it/s, v_num=0]
Epoch 9: 100%|██████████| 1/1 [00:00<00:00, 102.92it/s, v_num=0]
Epoch 9:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 10:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 10: 100%|██████████| 1/1 [00:00<00:00, 107.13it/s, v_num=0]
Epoch 10: 100%|██████████| 1/1 [00:00<00:00, 105.04it/s, v_num=0]
Epoch 10: 100%|██████████| 1/1 [00:00<00:00, 102.18it/s, v_num=0]
Epoch 10:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 11:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 11: 100%|██████████| 1/1 [00:00<00:00, 104.12it/s, v_num=0]
Epoch 11: 100%|██████████| 1/1 [00:00<00:00, 102.15it/s, v_num=0]
Epoch 11: 100%|██████████| 1/1 [00:00<00:00, 99.72it/s, v_num=0]
Epoch 11:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 12:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 12: 100%|██████████| 1/1 [00:00<00:00, 108.04it/s, v_num=0]
Epoch 12: 100%|██████████| 1/1 [00:00<00:00, 105.66it/s, v_num=0]
Epoch 12: 100%|██████████| 1/1 [00:00<00:00, 103.00it/s, v_num=0]
Epoch 12:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 13:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 13: 100%|██████████| 1/1 [00:00<00:00, 108.16it/s, v_num=0]
Epoch 13: 100%|██████████| 1/1 [00:00<00:00, 106.03it/s, v_num=0]
Epoch 13: 100%|██████████| 1/1 [00:00<00:00, 103.36it/s, v_num=0]
Epoch 13:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 14:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 14: 100%|██████████| 1/1 [00:00<00:00, 103.65it/s, v_num=0]
Epoch 14: 100%|██████████| 1/1 [00:00<00:00, 101.60it/s, v_num=0]
Epoch 14: 100%|██████████| 1/1 [00:00<00:00, 99.13it/s, v_num=0]
Epoch 14:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 15:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 15: 100%|██████████| 1/1 [00:00<00:00, 105.99it/s, v_num=0]
Epoch 15: 100%|██████████| 1/1 [00:00<00:00, 103.90it/s, v_num=0]
Epoch 15: 100%|██████████| 1/1 [00:00<00:00, 101.11it/s, v_num=0]
Epoch 15:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 16:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 16: 100%|██████████| 1/1 [00:00<00:00, 107.08it/s, v_num=0]
Epoch 16: 100%|██████████| 1/1 [00:00<00:00, 104.96it/s, v_num=0]
Epoch 16: 100%|██████████| 1/1 [00:00<00:00, 101.66it/s, v_num=0]
Epoch 16:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 17:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 17: 100%|██████████| 1/1 [00:00<00:00, 105.85it/s, v_num=0]
Epoch 17: 100%|██████████| 1/1 [00:00<00:00, 103.76it/s, v_num=0]
Epoch 17: 100%|██████████| 1/1 [00:00<00:00, 101.17it/s, v_num=0]
Epoch 17:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 18:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 18: 100%|██████████| 1/1 [00:00<00:00, 107.90it/s, v_num=0]
Epoch 18: 100%|██████████| 1/1 [00:00<00:00, 105.76it/s, v_num=0]
Epoch 18: 100%|██████████| 1/1 [00:00<00:00, 103.17it/s, v_num=0]
Epoch 18:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 19:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 19: 100%|██████████| 1/1 [00:00<00:00, 102.77it/s, v_num=0]
Epoch 19: 100%|██████████| 1/1 [00:00<00:00, 100.75it/s, v_num=0]
Epoch 19: 100%|██████████| 1/1 [00:00<00:00, 98.34it/s, v_num=0]
Epoch 19:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 20:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 20: 100%|██████████| 1/1 [00:00<00:00, 103.58it/s, v_num=0]
Epoch 20: 100%|██████████| 1/1 [00:00<00:00, 101.60it/s, v_num=0]
Epoch 20: 100%|██████████| 1/1 [00:00<00:00, 99.08it/s, v_num=0]
Epoch 20:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 21:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 21: 100%|██████████| 1/1 [00:00<00:00, 107.02it/s, v_num=0]
Epoch 21: 100%|██████████| 1/1 [00:00<00:00, 104.88it/s, v_num=0]
Epoch 21: 100%|██████████| 1/1 [00:00<00:00, 101.93it/s, v_num=0]
Epoch 21:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 22:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 22: 100%|██████████| 1/1 [00:00<00:00, 106.48it/s, v_num=0]
Epoch 22: 100%|██████████| 1/1 [00:00<00:00, 104.39it/s, v_num=0]
Epoch 22: 100%|██████████| 1/1 [00:00<00:00, 101.79it/s, v_num=0]
Epoch 22:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 23:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 23: 100%|██████████| 1/1 [00:00<00:00, 100.22it/s, v_num=0]
Epoch 23: 100%|██████████| 1/1 [00:00<00:00, 98.34it/s, v_num=0]
Epoch 23: 100%|██████████| 1/1 [00:00<00:00, 96.08it/s, v_num=0]
Epoch 23:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 24:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 24: 100%|██████████| 1/1 [00:00<00:00, 105.58it/s, v_num=0]
Epoch 24: 100%|██████████| 1/1 [00:00<00:00, 103.46it/s, v_num=0]
Epoch 24: 100%|██████████| 1/1 [00:00<00:00, 100.92it/s, v_num=0]
Epoch 24:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 25:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 25: 100%|██████████| 1/1 [00:00<00:00, 106.61it/s, v_num=0]
Epoch 25: 100%|██████████| 1/1 [00:00<00:00, 104.49it/s, v_num=0]
Epoch 25: 100%|██████████| 1/1 [00:00<00:00, 101.73it/s, v_num=0]
Epoch 25:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 26:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 26: 100%|██████████| 1/1 [00:00<00:00, 102.53it/s, v_num=0]
Epoch 26: 100%|██████████| 1/1 [00:00<00:00, 100.52it/s, v_num=0]
Epoch 26: 100%|██████████| 1/1 [00:00<00:00, 97.84it/s, v_num=0]
Epoch 26:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 27:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 27: 100%|██████████| 1/1 [00:00<00:00, 106.05it/s, v_num=0]
Epoch 27: 100%|██████████| 1/1 [00:00<00:00, 104.02it/s, v_num=0]
Epoch 27: 100%|██████████| 1/1 [00:00<00:00, 101.44it/s, v_num=0]
Epoch 27:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 28:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 28: 100%|██████████| 1/1 [00:00<00:00, 102.88it/s, v_num=0]
Epoch 28: 100%|██████████| 1/1 [00:00<00:00, 100.94it/s, v_num=0]
Epoch 28: 100%|██████████| 1/1 [00:00<00:00, 98.49it/s, v_num=0]
Epoch 28:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 29:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 29: 100%|██████████| 1/1 [00:00<00:00, 105.36it/s, v_num=0]
Epoch 29: 100%|██████████| 1/1 [00:00<00:00, 103.01it/s, v_num=0]
Epoch 29: 100%|██████████| 1/1 [00:00<00:00, 100.42it/s, v_num=0]
Epoch 29:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 30:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 30: 100%|██████████| 1/1 [00:00<00:00, 106.40it/s, v_num=0]
Epoch 30: 100%|██████████| 1/1 [00:00<00:00, 104.32it/s, v_num=0]
Epoch 30: 100%|██████████| 1/1 [00:00<00:00, 101.75it/s, v_num=0]
Epoch 30:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 31:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 31: 100%|██████████| 1/1 [00:00<00:00, 106.11it/s, v_num=0]
Epoch 31: 100%|██████████| 1/1 [00:00<00:00, 104.00it/s, v_num=0]
Epoch 31: 100%|██████████| 1/1 [00:00<00:00, 101.45it/s, v_num=0]
Epoch 31:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 32:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 32: 100%|██████████| 1/1 [00:00<00:00, 105.46it/s, v_num=0]
Epoch 32: 100%|██████████| 1/1 [00:00<00:00, 103.40it/s, v_num=0]
Epoch 32: 100%|██████████| 1/1 [00:00<00:00, 100.66it/s, v_num=0]
Epoch 32:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 33:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 33: 100%|██████████| 1/1 [00:00<00:00, 105.03it/s, v_num=0]
Epoch 33: 100%|██████████| 1/1 [00:00<00:00, 102.99it/s, v_num=0]
Epoch 33: 100%|██████████| 1/1 [00:00<00:00, 100.44it/s, v_num=0]
Epoch 33:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 34:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 34: 100%|██████████| 1/1 [00:00<00:00, 104.71it/s, v_num=0]
Epoch 34: 100%|██████████| 1/1 [00:00<00:00, 102.71it/s, v_num=0]
Epoch 34: 100%|██████████| 1/1 [00:00<00:00, 100.19it/s, v_num=0]
Epoch 34:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 35:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 35: 100%|██████████| 1/1 [00:00<00:00, 105.21it/s, v_num=0]
Epoch 35: 100%|██████████| 1/1 [00:00<00:00, 102.99it/s, v_num=0]
Epoch 35: 100%|██████████| 1/1 [00:00<00:00, 100.42it/s, v_num=0]
Epoch 35:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 36:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 36: 100%|██████████| 1/1 [00:00<00:00, 103.66it/s, v_num=0]
Epoch 36: 100%|██████████| 1/1 [00:00<00:00, 101.60it/s, v_num=0]
Epoch 36: 100%|██████████| 1/1 [00:00<00:00, 98.93it/s, v_num=0]
Epoch 36:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 37:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 37: 100%|██████████| 1/1 [00:00<00:00, 104.98it/s, v_num=0]
Epoch 37: 100%|██████████| 1/1 [00:00<00:00, 102.98it/s, v_num=0]
Epoch 37: 100%|██████████| 1/1 [00:00<00:00, 100.51it/s, v_num=0]
Epoch 37:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 38:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 38: 100%|██████████| 1/1 [00:00<00:00, 103.51it/s, v_num=0]
Epoch 38: 100%|██████████| 1/1 [00:00<00:00, 101.53it/s, v_num=0]
Epoch 38: 100%|██████████| 1/1 [00:00<00:00, 99.08it/s, v_num=0]
Epoch 38:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 39:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 39: 100%|██████████| 1/1 [00:00<00:00, 105.80it/s, v_num=0]
Epoch 39: 100%|██████████| 1/1 [00:00<00:00, 103.55it/s, v_num=0]
Epoch 39: 100%|██████████| 1/1 [00:00<00:00, 100.98it/s, v_num=0]
Epoch 39:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 40:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 40: 100%|██████████| 1/1 [00:00<00:00, 105.55it/s, v_num=0]
Epoch 40: 100%|██████████| 1/1 [00:00<00:00, 103.50it/s, v_num=0]
Epoch 40: 100%|██████████| 1/1 [00:00<00:00, 100.96it/s, v_num=0]
Epoch 40:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 41:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 41: 100%|██████████| 1/1 [00:00<00:00, 105.15it/s, v_num=0]
Epoch 41: 100%|██████████| 1/1 [00:00<00:00, 103.14it/s, v_num=0]
Epoch 41: 100%|██████████| 1/1 [00:00<00:00, 100.64it/s, v_num=0]
Epoch 41:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 42:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 42: 100%|██████████| 1/1 [00:00<00:00, 103.99it/s, v_num=0]
Epoch 42: 100%|██████████| 1/1 [00:00<00:00, 101.72it/s, v_num=0]
Epoch 42: 100%|██████████| 1/1 [00:00<00:00, 99.22it/s, v_num=0]
Epoch 42:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 43:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 43: 100%|██████████| 1/1 [00:00<00:00, 104.24it/s, v_num=0]
Epoch 43: 100%|██████████| 1/1 [00:00<00:00, 102.11it/s, v_num=0]
Epoch 43: 100%|██████████| 1/1 [00:00<00:00, 99.40it/s, v_num=0]
Epoch 43:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 44:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 44: 100%|██████████| 1/1 [00:00<00:00, 102.79it/s, v_num=0]
Epoch 44: 100%|██████████| 1/1 [00:00<00:00, 100.84it/s, v_num=0]
Epoch 44: 100%|██████████| 1/1 [00:00<00:00, 98.23it/s, v_num=0]
Epoch 44:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 45:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 45: 100%|██████████| 1/1 [00:00<00:00, 103.81it/s, v_num=0]
Epoch 45: 100%|██████████| 1/1 [00:00<00:00, 101.77it/s, v_num=0]
Epoch 45: 100%|██████████| 1/1 [00:00<00:00, 99.34it/s, v_num=0]
Epoch 45:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 46:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 46: 100%|██████████| 1/1 [00:00<00:00, 103.76it/s, v_num=0]
Epoch 46: 100%|██████████| 1/1 [00:00<00:00, 101.75it/s, v_num=0]
Epoch 46: 100%|██████████| 1/1 [00:00<00:00, 99.25it/s, v_num=0]
Epoch 46:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 47:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 47: 100%|██████████| 1/1 [00:00<00:00, 105.00it/s, v_num=0]
Epoch 47: 100%|██████████| 1/1 [00:00<00:00, 102.93it/s, v_num=0]
Epoch 47: 100%|██████████| 1/1 [00:00<00:00, 100.42it/s, v_num=0]
Epoch 47:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 48:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 48: 100%|██████████| 1/1 [00:00<00:00, 104.25it/s, v_num=0]
Epoch 48: 100%|██████████| 1/1 [00:00<00:00, 102.25it/s, v_num=0]
Epoch 48: 100%|██████████| 1/1 [00:00<00:00, 99.47it/s, v_num=0]
Epoch 48:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 49:   0%|          | 0/1 [00:00<?, ?it/s, v_num=0]
Epoch 49: 100%|██████████| 1/1 [00:00<00:00, 105.09it/s, v_num=0]
Epoch 49: 100%|██████████| 1/1 [00:00<00:00, 103.10it/s, v_num=0]
Epoch 49: 100%|██████████| 1/1 [00:00<00:00, 96.99it/s, v_num=0] `Trainer.fit` stopped: `max_epochs=50` reached.

Epoch 49: 100%|██████████| 1/1 [00:00<00:00, 82.11it/s, v_num=0]

Step 6: Extract and interpret the learned temporal patterns#

reorderPhenotypes() reorders the R temporal patterns by their activation strength. Each temporal pattern is a (K, Tw) matrix, i.e. a temporal signature over the K procedure codes.

feature_names returned by to_tensor() gives us the code label for each row, so we can read off which procedures drive each temporal pattern and when during the window they tend to occur.

patterns, pathways = model.reorderPhenotypes(model.Ph.detach().cpu(), tw=Tw)
patterns = (
    patterns.detach().numpy()
)  # (R, K, Tw): temporal signature per temporal pattern
pathways = (
    pathways.detach().numpy()
)  # (N, R, T'): activation of each temporal pattern per patient

print(f"Phenotypes shape : {patterns.shape}")
print(f"Pathways shape   : {pathways.shape}")
Phenotypes shape : (5, 30, 7)
Pathways shape   : (69, 5, 85)

For each temporal pattern, print the top-3 most active procedure codes. phenotypes has shape (R, K, Tw): sum over the time axis to get the overall “weight” of each code in each temporal pattern.

Note

Here, we simplified the analysis of the patterns and we invite the reader to dig more the SWoTTeD model for a deeper analysis of the temporal patterns that are extracted.

More specifically, SWoTTeD discover temporal patterns that describes typical behaviors as small sequences of events.

code_weights = patterns.sum(axis=-1)  # (R, K)

print("\nTemporal pattern overview:")
for r in range(R):
    top_idx = np.argsort(code_weights[r])[::-1][:3]
    # Strip the "icd_code_" prefix added by OHE for readability.
    top_codes_r = [feature_names[i].removeprefix("icd_code_") for i in top_idx]
    print(f"  Top procedures for pattern {r + 1}: {top_codes_r}")
Temporal pattern overview:
  Top procedures for pattern 1: ['8847', '0W9G3ZX', '3615']
  Top procedures for pattern 2: ['5A1221Z', '02HV33Z', 'B548ZZA']
  Top procedures for pattern 3: ['9672', '8847', '02H633Z']
  Top procedures for pattern 4: ['4513', '3615', '9672']
  Top procedures for pattern 5: ['02HV33Z', '9671', '0DJ08ZZ']

Step 7: Assign temporal patterns back to patient IDs#

pathways tensor contains information about how much similar is a patient to each pattern, at a given time. By assigning a patient to the most similar pattern, we cluster the set of patients into set of patients sharing temporal patterns.

Inpractice, pathways shape is (N, R, T’); take argmax over R (axis=1) to get the dominant temporal pattern index for each patient × time bin, then keep the most frequent dominant temporal pattern across time bins (majority vote).

from scipy.stats import mode

dominant_per_patient = mode(pathways.argmax(axis=1), axis=1).mode  # shape: (N,)

patient_patterns = dict(zip(ids, dominant_per_patient.tolist()))
print("\nPatient -> dominant temporal pattern (first 10):")
for pid, ph in list(patient_patterns.items())[:10]:
    print(f"  {pid} -> temporal pattern {ph + 1}")
Patient -> dominant temporal pattern (first 10):
  10000032 -> temporal pattern 2
  10001217 -> temporal pattern 1
  10002428 -> temporal pattern 3
  10002495 -> temporal pattern 1
  10003046 -> temporal pattern 5
  10003400 -> temporal pattern 4
  10004235 -> temporal pattern 1
  10004422 -> temporal pattern 1
  10004457 -> temporal pattern 2
  10004720 -> temporal pattern 1

Total running time of the script: (0 minutes 23.937 seconds)

Gallery generated by Sphinx-Gallery