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) for N TACs sampled at T time points
  • tacs: shape (T,) is also accepted for a single TAC
  • aif: 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): use tacs = tacs.T
  • You have one TAC only: pass tacs as (T,) or tacs[:, None]
  • Your time values are in seconds: set time_unit="s"
  • You want a fixed blood-volume fraction: use model="3k" or model="4k" and pass vB=...
  • You loaded lists instead of arrays: fit_compartment_model(...) will accept them, but converting to np.float32 first 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.