Use your own TAC, AIF, and time data¶
If you only read one page, read this one.
lls is format-agnostic. It does not load CSV, Excel, parquet, MAT, NPZ, HDF5, or imaging files for you. Instead, you load your data with the tools you already use, convert it to arrays, and pass those arrays to fit_compartment_model(...).
What the function expects¶
tacs: shape(T, N)forNTACs sampled atTtime pointstacs: shape(T,)is also accepted for a single TACaif: shape(T_aif,)time: shape(T,)aif_time: optional shape(T_aif,)
In practice:
- the first axis is always time
- if your TAC array is stored as
(N, T), transpose it to(T, N) - if TAC and AIF share the same time grid, you do not need
aif_time - if time is in seconds, set
time_unit="s"
Minimal recipe¶
from lls import fit_compartment_model
params, fitted, mse = fit_compartment_model(
tacs=tacs,
aif=aif,
time=time,
model="4k_vB",
time_unit="min",
)
Use model="3k" or model="4k" only if you also provide vB=....
Example: columns in a CSV file¶
import numpy as np
import pandas as pd
from lls import fit_compartment_model
df = pd.read_csv("subject01_curves.csv")
time_s = df["time_s"].to_numpy(dtype=np.float32)
aif = df["aif"].to_numpy(dtype=np.float32)
tacs = df[["roi_1", "roi_2", "roi_3"]].to_numpy(dtype=np.float32)
params, fitted, mse = fit_compartment_model(
tacs=tacs,
aif=aif,
time=time_s,
model="4k_vB",
time_unit="s",
)
This example assumes one row per frame and one TAC column per ROI. The dataframe slice already has shape (T, N), so no transpose is needed.
Example: arrays in an NPZ file¶
import numpy as np
from lls import fit_compartment_model
data = np.load("subject01_curves.npz")
time_min = np.asarray(data["time"], dtype=np.float32)
aif = np.asarray(data["aif"], dtype=np.float32)
tacs = np.asarray(data["tacs"], dtype=np.float32)
if tacs.ndim == 2 and tacs.shape[0] != time_min.size and tacs.shape[1] == time_min.size:
tacs = tacs.T
params, fitted, mse = fit_compartment_model(
tacs=tacs,
aif=aif,
time=time_min,
model="4k_vB",
)
The important part is not the file type. The important part is ending up with:
- one one-dimensional time vector
- one one-dimensional AIF vector
- one TAC array whose first dimension matches the number of time points
Example: TAC and AIF on different time grids¶
Leave interpolate=True and pass both time vectors:
params, fitted, mse = fit_compartment_model(
tacs=tacs,
aif=aif,
time=tac_time_s,
aif_time=aif_time_s,
model="3k_vB",
time_unit="s",
interpolate=True,
)
Use interpolate=False only when TAC and AIF already share the same sampling times.
Common fixes¶
- Your TAC array is
(N, T): usetacs = tacs.T - You have one TAC only: pass
tacsas(T,)ortacs[:, None] - Your time values are in seconds: set
time_unit="s" - You want a fixed blood-volume fraction: use
model="3k"ormodel="4k"and passvB=... - You loaded lists instead of arrays:
fit_compartment_model(...)will accept them, but converting tonp.float32first is clearer
If your collaborators only need one recommendation¶
Start with fit_compartment_model(...), keep interpolate=True, and focus on getting tacs, aif, and time into the right shapes. That is enough for most uses of this package.