Note
Go to the end to download the full example code.
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:
Ingest procedure events from MIMIC-IV into a TanaT
EventSequencePool.Restrict to the most frequent procedure codes to keep the tensor tractable.
Call
to_tensor()withohe=Trueto obtain a dense(N, M, K)array alongside patient IDs and feature names, all in a single call.Feed the tensor to SWoTTeD, a dictionary-learning model that decomposes the population into R temporal patterns, each with a characteristic temporal signature.
Interpret the result using the
feature_namesreturned byto_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:
EventSequencePoolfrom a SQL sourceFeature frequency filtering with
temporal_data()to_tensor()with OHETemporal pattern interpretation using
idsandfeature_names
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)