Analysis Toolbox#

Analysis Toolbox Demo

This notebook demonstrates post-fit analysis helpers: ROI statistics, ROI timecourses, and group-level comparisons. The example synthesizes a small batch study, so it runs without bundled datasets.

[1]:
from pathlib import Path
import json
import shutil

import numpy as np
import matplotlib.pyplot as plt

from mrax.analysis import ROI, collect_roi_stats, extract_timecourse, analyze_groups
from mrax.core.batch import run_batch_studies
from mrax.core.synthetic import make_example_suite
from mrax.data_struct import Quantity, Study, load_study_results

Run a small batch study

Create four synthetic subjects, fit a T1 inversion-recovery model for each subject, and persist the generated results under tmp/analysis_toolbox_demo.

[2]:
demo_root = Path.cwd() / "tmp" / "analysis_toolbox_demo"
inputs_root = demo_root / "inputs"
outputs_root = demo_root / "outputs"

for path in (inputs_root, outputs_root):
    if path.exists():
        shutil.rmtree(path)
    path.mkdir(parents=True, exist_ok=True)

subjects = [
    ("control_01", 0, "control"),
    ("control_02", 1, "control"),
    ("treated_01", 2, "treated"),
    ("treated_02", 3, "treated"),
]

for name, seed, group in subjects:
    study_dir = inputs_root / name
    study_dir.mkdir(parents=True, exist_ok=True)
    (study_dir / "config.json").write_text(json.dumps({"seed": seed, "group": group}))

def loader(study_dir: Path):
    cfg = json.loads((study_dir / "config.json").read_text())
    examples = make_example_suite(noise_level=0.03, seed=cfg["seed"])[:1]
    study_globals = {"field_strength_mhz": Quantity(400.0, "MHz")}
    return Study.from_examples(examples, study_globals=study_globals), {"config": cfg}

summaries = run_batch_studies(
    studies_root=inputs_root,
    study_loader=loader,
    output_root=outputs_root,
    analysis_kwargs={"num_steps": 3, "step_size": 0.02},
)
summaries

[2]:
[{'study_name': 'control_01',
  'manifest_path': PosixPath('/tmp/mrax-notebook-smoke/tmp/analysis_toolbox_demo/outputs/control_01/study_manifest.json'),
  'results': [{'name': 't1_ir',
    'model': 't1_ir',
    'fields': {'amplitude': Quantity(values=Array([[0.68209094, 0.68224174, 0.6835417 , 0.6861043 , 0.68232936,
             0.6828738 , 0.6820199 , 0.6835348 , 0.6820076 , 0.6838535 ,
             0.6821099 , 0.68606627, 0.6823191 , 0.68227303, 0.6821362 ,
             0.68248117],
            [0.7430966 , 0.7430551 , 0.7426947 , 0.715351  , 0.68526715,
             0.68195516, 0.68180376, 0.68175155, 0.6817051 , 0.6817301 ,
             0.68183374, 0.6818913 , 0.68316513, 0.68289655, 0.72453564,
             0.6853528 ],
            [0.7431159 , 0.74310654, 0.7431274 , 0.7429968 , 0.6829904 ,
             0.6817659 , 0.6816841 , 0.6816497 , 0.681646  , 0.6816479 ,
             0.6816673 , 0.681732  , 0.6820277 , 0.6840507 , 0.7336869 ,
             0.7423251 ],
            [0.743139  , 0.74313307, 0.7431121 , 0.7431135 , 0.7401124 ,
             0.6817257 , 0.6816459 , 0.6816228 , 0.6816217 , 0.681634  ,
             0.6816503 , 0.6817084 , 0.68205976, 0.69966215, 0.74276143,
             0.7425461 ],
            [0.7431496 , 0.7431441 , 0.74312794, 0.74303895, 0.74270695,
             0.6818746 , 0.6816437 , 0.6816312 , 0.681623  , 0.6816238 ,
             0.68164027, 0.68169117, 0.6819515 , 0.68580484, 0.73335654,
             0.7408031 ],
            [0.7431452 , 0.74314225, 0.74313277, 0.743066  , 0.73993075,
             0.68217987, 0.6816532 , 0.6816411 , 0.68162644, 0.6816286 ,
             0.6816521 , 0.68168825, 0.6817677 , 0.68207175, 0.68308324,
             0.7064828 ],
            [0.7431378 , 0.74313027, 0.74311626, 0.74308586, 0.7430133 ,
             0.6839724 , 0.68179494, 0.68167686, 0.6816574 , 0.6816509 ,
             0.6816626 , 0.68169695, 0.6817174 , 0.6816542 , 0.6817295 ,
             0.6820693 ],
            [0.74309283, 0.7430971 , 0.74307835, 0.7430046 , 0.74300927,
             0.7428877 , 0.6827399 , 0.68199426, 0.6818232 , 0.68170184,
             0.681732  , 0.68166983, 0.6816904 , 0.6816737 , 0.68164635,
             0.6816524 ],
            [0.74268305, 0.74285424, 0.7428627 , 0.7430068 , 0.74308497,
             0.7430924 , 0.7430648 , 0.7429047 , 0.68234295, 0.6820088 ,
             0.68174464, 0.68170226, 0.6816719 , 0.68165505, 0.6816522 ,
             0.6816001 ],
            [0.6820593 , 0.6857469 , 0.7412452 , 0.74295425, 0.7430819 ,
             0.74309504, 0.7431188 , 0.74310315, 0.7430316 , 0.7120217 ,
             0.6819575 , 0.6816913 , 0.68166226, 0.6816227 , 0.68162066,
             0.6816158 ],
            [0.6816944 , 0.6817793 , 0.68198496, 0.7413331 , 0.7430636 ,
             0.7430702 , 0.7431282 , 0.74314004, 0.7431251 , 0.7429289 ,
             0.68243366, 0.6817075 , 0.6816401 , 0.68162125, 0.6816098 ,
             0.6816091 ],
            [0.6816782 , 0.6816629 , 0.68178725, 0.68317   , 0.74303055,
             0.74313307, 0.7431388 , 0.7431503 , 0.7431256 , 0.7430916 ,
             0.6987384 , 0.6817765 , 0.6816399 , 0.68162584, 0.68161064,
             0.6816029 ],
            [0.6816719 , 0.68165964, 0.6818252 , 0.68242776, 0.74303496,
             0.743112  , 0.7431446 , 0.7431435 , 0.7431302 , 0.7430827 ,
             0.7396205 , 0.68181455, 0.6816685 , 0.68162394, 0.68161243,
             0.68160504],
            [0.68166906, 0.6816749 , 0.6817141 , 0.6863515 , 0.74027157,
             0.7430819 , 0.7431213 , 0.74312514, 0.74311996, 0.7430243 ,
             0.68383425, 0.6817812 , 0.6817007 , 0.6816423 , 0.6816262 ,
             0.6816244 ],
            [0.68184483, 0.681838  , 0.68178993, 0.68456984, 0.68959475,
             0.7418566 , 0.7428324 , 0.7430257 , 0.74152803, 0.74035823,
             0.68271613, 0.6820309 , 0.6817781 , 0.68173194, 0.68167466,
             0.6816559 ],
            [0.6827251 , 0.68281454, 0.6824246 , 0.68618023, 0.6822656 ,
             0.68244934, 0.68219686, 0.6829403 , 0.6820001 , 0.68501204,
             0.6825542 , 0.68416804, 0.68326324, 0.6830336 , 0.6824102 ,
             0.6824026 ]], dtype=float32), si_unit='1'),
     't1': Quantity(values=Array([[2451.0042, 2450.9924, 2451.019 , 2451.067 , 2450.9758, 2451.0269,
             2450.9536, 2451.019 , 2450.967 , 2451.    , 2450.9907, 2451.0186,
             2450.9875, 2451.0293, 2450.9902, 2450.9543],
            [2450.6682, 2450.6782, 2450.6865, 2450.9756, 2451.117 , 2450.959 ,
             2450.9565, 2450.9448, 2450.941 , 2450.937 , 2450.977 , 2450.9465,
             2451.001 , 2450.982 , 2450.7292, 2451.0247],
            [2597.483 , 2557.1414, 2450.6584, 2450.6804, 2451.0735, 2450.9758,
             2450.927 , 2450.9053, 2450.8936, 2450.9014, 2450.903 , 2450.9592,
             2450.958 , 2451.067 , 2450.6934, 2450.6833],
            [2598.8752, 2598.8071, 2596.972 , 2450.6743, 2450.6846, 2450.9487,
             2450.907 , 2450.876 , 2450.8782, 2450.8914, 2450.911 , 2450.9556,
             2451.181 , 2451.4946, 2450.6675, 2450.66  ],
            [2599.0725, 2599.0088, 2598.7795, 2586.176 , 2450.6772, 2450.989 ,
             2450.8948, 2450.8818, 2450.8774, 2450.8809, 2450.91  , 2450.9478,
             2451.3464, 2474.6184, 2597.622 , 2596.6504],
            [2599.0647, 2599.026 , 2598.9653, 2597.409 , 2450.676 , 2451.117 ,
             2450.927 , 2450.896 , 2450.8928, 2450.8853, 2450.9182, 2451.0535,
             2451.7002, 2599.3315, 2599.2295, 2598.9407],
            [2599.025 , 2598.994 , 2598.8833, 2598.3318, 2450.6565, 2451.1584,
             2450.9744, 2450.9192, 2450.947 , 2450.9158, 2450.947 , 2451.038 ,
             2452.4092, 2599.3352, 2599.2078, 2599.2068],
            [2598.74  , 2598.7803, 2598.531 , 2595.6448, 2450.6572, 2450.6738,
             2451.0337, 2450.982 , 2450.974 , 2450.9292, 2451.005 , 2451.0403,
             2452.1953, 2490.0293, 2599.2485, 2599.2266],
            [2593.291 , 2593.9888, 2596.794 , 2585.4006, 2450.7188, 2450.666 ,
             2450.6763, 2450.6865, 2450.9702, 2450.97  , 2450.9998, 2451.1274,
             2452.213 , 2458.0876, 2460.9697, 2594.822 ],
            [2452.127 , 2453.8057, 2450.7751, 2450.6821, 2450.67  , 2450.6587,
             2450.6584, 2450.68  , 2450.6816, 2450.948 , 2451.0796, 2451.1028,
             2451.181 , 2451.0474, 2451.1313, 2451.228 ],
            [2451.0676, 2451.1035, 2451.64  , 2450.676 , 2450.6567, 2544.4973,
             2450.665 , 2450.673 , 2450.6794, 2450.6855, 2451.0452, 2451.0022,
             2450.9883, 2450.9631, 2450.968 , 2450.9502],
            [2450.9895, 2451.0176, 2451.144 , 2451.2048, 2450.664 , 2450.7876,
             2450.661 , 2450.6614, 2450.6804, 2450.676 , 2451.0825, 2451.032 ,
             2450.9453, 2450.9583, 2450.9197, 2450.892 ],
            [2450.9844, 2450.965 , 2451.1003, 2451.157 , 2450.665 , 2450.6738,
             2450.6838, 2450.66  , 2450.666 , 2450.6812, 2450.6907, 2450.9827,
             2450.9578, 2450.904 , 2450.893 , 2450.8838],
            [2450.943 , 2450.9353, 2450.993 , 2451.0217, 2450.6921, 2450.6777,
             2450.6792, 2450.6748, 2450.674 , 2450.686 , 2451.046 , 2450.964 ,
             2450.9248, 2450.913 , 2450.8914, 2450.8958],
            [2450.9949, 2450.9749, 2450.9807, 2451.0176, 2451.0493, 2450.6926,
             2450.6877, 2450.6858, 2450.69  , 2450.6912, 2450.9695, 2450.9937,
             2450.9436, 2450.9377, 2450.9043, 2450.9092],
            [2450.9827, 2450.983 , 2451.0076, 2451.0732, 2450.9792, 2451.0076,
             2450.9675, 2450.99  , 2450.9287, 2450.9658, 2451.0012, 2451.0393,
             2451.0032, 2450.983 , 2450.9822, 2450.962 ]], dtype=float32), si_unit='ms')},
    'loss_history': [0.15132774412631989,
     0.14381380379199982,
     0.1369260996580124],
    'cached': False}],
  'metadata': {'config': {'seed': 0, 'group': 'control'}},
  'study_dir': PosixPath('/tmp/mrax-notebook-smoke/tmp/analysis_toolbox_demo/inputs/control_01')},
 {'study_name': 'control_02',
  'manifest_path': PosixPath('/tmp/mrax-notebook-smoke/tmp/analysis_toolbox_demo/outputs/control_02/study_manifest.json'),
  'results': [{'name': 't1_ir',
    'model': 't1_ir',
    'fields': {'amplitude': Quantity(values=Array([[0.6822252 , 0.6823839 , 0.6839408 , 0.68228877, 0.6820798 ,
             0.6831264 , 0.6822702 , 0.6823908 , 0.6822631 , 0.6846213 ,
             0.6821853 , 0.68461674, 0.68264514, 0.682621  , 0.6822236 ,
             0.6826585 ],
            [0.7431072 , 0.74310666, 0.74300545, 0.6925658 , 0.6845981 ,
             0.68209374, 0.6818051 , 0.6817074 , 0.68170273, 0.68179005,
             0.6817736 , 0.681787  , 0.6820992 , 0.6866397 , 0.71368307,
             0.7423841 ],
            [0.743111  , 0.7431143 , 0.74312735, 0.7430483 , 0.6832909 ,
             0.6818545 , 0.6816737 , 0.6816567 , 0.68164337, 0.6816542 ,
             0.6816691 , 0.6819223 , 0.68235606, 0.68482894, 0.7428077 ,
             0.7424368 ],
            [0.74314046, 0.74313474, 0.743111  , 0.743049  , 0.72677654,
             0.6817443 , 0.6816455 , 0.6816309 , 0.68162054, 0.68163717,
             0.6816423 , 0.68170744, 0.68193036, 0.69467705, 0.74138117,
             0.74220234],
            [0.74314505, 0.74314094, 0.7431274 , 0.7430745 , 0.7427708 ,
             0.68173534, 0.6816522 , 0.6816261 , 0.68162555, 0.6816292 ,
             0.6816449 , 0.6816919 , 0.6818061 , 0.6859464 , 0.7378271 ,
             0.7373897 ],
            [0.74314433, 0.7431437 , 0.74312896, 0.74307674, 0.74279183,
             0.68227786, 0.68167865, 0.6816443 , 0.6816226 , 0.6816236 ,
             0.68163186, 0.6816626 , 0.6818772 , 0.6827852 , 0.6863788 ,
             0.7057801 ],
            [0.743133  , 0.7431266 , 0.7431236 , 0.7430445 , 0.7430071 ,
             0.71171623, 0.68173295, 0.6816723 , 0.68167   , 0.6816453 ,
             0.6816655 , 0.6816939 , 0.6817345 , 0.68169075, 0.6817215 ,
             0.68185526],
            [0.7431084 , 0.74308354, 0.74309754, 0.7430374 , 0.7430007 ,
             0.74277085, 0.682819  , 0.68199825, 0.681797  , 0.68167454,
             0.6816888 , 0.6816733 , 0.68171257, 0.6816315 , 0.6816421 ,
             0.6816557 ],
            [0.7424498 , 0.74273425, 0.74295163, 0.74290705, 0.7430694 ,
             0.7430976 , 0.7429578 , 0.7424428 , 0.70971715, 0.6821044 ,
             0.68170065, 0.68169785, 0.6816567 , 0.68165684, 0.6816389 ,
             0.6816362 ],
            [0.6819688 , 0.6824943 , 0.7384075 , 0.7429611 , 0.74307823,
             0.7431027 , 0.74311346, 0.74312115, 0.7430394 , 0.74240345,
             0.68195426, 0.6817086 , 0.68164223, 0.68164104, 0.68161803,
             0.6816171 ],
            [0.68169826, 0.68172234, 0.68227935, 0.692978  , 0.743104  ,
             0.7430871 , 0.74313617, 0.74313915, 0.743124  , 0.74280775,
             0.6827494 , 0.6817308 , 0.6816592 , 0.68161684, 0.6816176 ,
             0.68160653],
            [0.6816644 , 0.68169355, 0.68178177, 0.699271  , 0.74304515,
             0.74313074, 0.743145  , 0.7431379 , 0.743137  , 0.7430318 ,
             0.6849173 , 0.6817845 , 0.6816407 , 0.6816165 , 0.68161255,
             0.6816023 ],
            [0.6816688 , 0.68167526, 0.68175566, 0.6870008 , 0.7430245 ,
             0.74312943, 0.7431429 , 0.74314153, 0.7431396 , 0.7430812 ,
             0.71757215, 0.68178266, 0.6816769 , 0.68162894, 0.6816141 ,
             0.68160814],
            [0.6816895 , 0.68169343, 0.6817601 , 0.68219596, 0.74291927,
             0.74308765, 0.74310994, 0.7431202 , 0.743093  , 0.7429749 ,
             0.7072856 , 0.6819312 , 0.6816661 , 0.6816423 , 0.68162847,
             0.68162113],
            [0.68181974, 0.6819553 , 0.6819781 , 0.6821021 , 0.6834799 ,
             0.7196619 , 0.74258935, 0.74301046, 0.7428989 , 0.73720545,
             0.6833024 , 0.6820794 , 0.6817877 , 0.6817106 , 0.6816779 ,
             0.68166465],
            [0.6823212 , 0.68455577, 0.68242085, 0.6825844 , 0.6928078 ,
             0.6820986 , 0.6825319 , 0.68278486, 0.68217915, 0.68345153,
             0.6837263 , 0.6830403 , 0.6821558 , 0.68209946, 0.68222785,
             0.68371826]], dtype=float32), si_unit='1'),
     't1': Quantity(values=Array([[2450.965 , 2450.9946, 2450.9976, 2450.9749, 2450.9722, 2451.0132,
             2450.9897, 2451.0005, 2450.9792, 2451.0005, 2450.974 , 2451.0134,
             2450.9675, 2450.9976, 2450.9963, 2451.0002],
            [2450.6736, 2450.6743, 2450.6821, 2451.0254, 2451.0364, 2451.03  ,
             2450.931 , 2450.9287, 2450.91  , 2450.9375, 2450.9575, 2450.926 ,
             2450.9497, 2451.0083, 2450.9514, 2450.688 ],
            [2596.9404, 2595.1887, 2450.66  , 2450.666 , 2451.0134, 2450.9907,
             2450.9136, 2450.9065, 2450.898 , 2450.8923, 2450.9148, 2450.983 ,
             2451.0825, 2451.0261, 2450.6777, 2450.6816],
            [2598.955 , 2598.8325, 2597.8748, 2529.9014, 2450.7131, 2450.9536,
             2450.907 , 2450.8887, 2450.8794, 2450.8977, 2450.892 , 2450.9546,
             2451.0713, 2451.4368, 2450.6567, 2450.664 ],
            [2599.041 , 2598.999 , 2598.7866, 2596.2937, 2450.6716, 2450.9885,
             2450.9175, 2450.8816, 2450.89  , 2450.8796, 2450.9048, 2450.9663,
             2451.148 , 2506.513 , 2596.9832, 2597.5386],
            [2599.0457, 2599.0496, 2598.9314, 2598.4412, 2450.6606, 2451.1008,
             2450.9282, 2450.898 , 2450.883 , 2450.8826, 2450.893 , 2450.9773,
             2451.7725, 2599.2678, 2599.212 , 2599.149 ],
            [2598.9727, 2598.9292, 2598.8762, 2596.9866, 2450.6594, 2451.1597,
             2450.9358, 2450.9077, 2450.8967, 2450.921 , 2450.9392, 2451.0974,
             2452.5295, 2599.3088, 2599.2422, 2599.208 ],
            [2598.819 , 2598.4116, 2598.8376, 2598.1492, 2450.6565, 2450.6702,
             2451.0315, 2450.9539, 2450.9583, 2450.9307, 2450.997 , 2451.067 ,
             2451.8914, 2599.3115, 2599.267 , 2599.2358],
            [2572.8503, 2582.2822, 2595.3599, 2595.1042, 2450.682 , 2452.3845,
             2450.6848, 2450.6858, 2450.9575, 2450.9756, 2451.0015, 2451.2502,
             2451.2312, 2453.3955, 2452.8457, 2503.9285],
            [2451.2607, 2451.6152, 2450.6865, 2451.014 , 2451.772 , 2450.6572,
             2450.6704, 2450.6787, 2450.6863, 2450.6848, 2451.0657, 2451.1816,
             2451.0886, 2451.4436, 2451.301 , 2451.2334],
            [2451.084 , 2451.1143, 2451.3718, 2451.4612, 2450.6584, 2536.6719,
             2450.6587, 2450.6643, 2450.68  , 2450.6865, 2451.0356, 2451.0405,
             2451.1025, 2450.9531, 2451.0242, 2450.9397],
            [2450.9758, 2450.9998, 2451.0679, 2452.1614, 2450.6667, 2450.659 ,
             2450.6777, 2450.6577, 2450.67  , 2450.6882, 2451.0198, 2451.0417,
             2450.9426, 2450.9226, 2450.9197, 2450.8906],
            [2450.9878, 2450.997 , 2451.0293, 2451.2158, 2450.6755, 2450.661 ,
             2450.6572, 2450.663 , 2450.6777, 2450.6836, 2450.7717, 2450.9983,
             2450.9614, 2450.8992, 2450.8953, 2450.892 ],
            [2450.9546, 2450.971 , 2450.971 , 2451.0198, 2450.686 , 2450.6821,
             2450.6738, 2450.6765, 2450.6838, 2450.684 , 2450.943 , 2450.9836,
             2450.9133, 2450.9058, 2450.8904, 2450.8882],
            [2450.974 , 2451.0117, 2451.0042, 2451.0037, 2450.9958, 2450.7583,
             2450.689 , 2450.6892, 2450.689 , 2450.6936, 2450.9797, 2450.9675,
             2450.9697, 2450.934 , 2450.9277, 2450.9214],
            [2450.9946, 2450.9814, 2451.0076, 2450.9702, 2451.0415, 2450.9692,
             2450.9797, 2450.9941, 2450.9702, 2451.0032, 2450.9805, 2451.028 ,
             2450.9587, 2450.964 , 2450.9739, 2451.0242]], dtype=float32), si_unit='ms')},
    'loss_history': [0.150870680809021,
     0.14339159429073334,
     0.13653187453746796],
    'cached': False}],
  'metadata': {'config': {'seed': 1, 'group': 'control'}},
  'study_dir': PosixPath('/tmp/mrax-notebook-smoke/tmp/analysis_toolbox_demo/inputs/control_02')},
 {'study_name': 'treated_01',
  'manifest_path': PosixPath('/tmp/mrax-notebook-smoke/tmp/analysis_toolbox_demo/outputs/treated_01/study_manifest.json'),
  'results': [{'name': 't1_ir',
    'model': 't1_ir',
    'fields': {'amplitude': Quantity(values=Array([[0.6840635 , 0.68246615, 0.6824025 , 0.6823969 , 0.68219155,
             0.6859856 , 0.68218327, 0.68274003, 0.68375576, 0.68296057,
             0.68260694, 0.6940091 , 0.6822574 , 0.6836015 , 0.6823665 ,
             0.68184596],
            [0.74310863, 0.74307024, 0.74283046, 0.7427845 , 0.6832082 ,
             0.68192667, 0.6817856 , 0.68172425, 0.68167526, 0.68175465,
             0.6817727 , 0.68201536, 0.6819208 , 0.68260914, 0.69655573,
             0.68653774],
            [0.7431198 , 0.7431408 , 0.74308574, 0.74308497, 0.7163158 ,
             0.68178153, 0.68166167, 0.68164635, 0.68164593, 0.68165547,
             0.6817036 , 0.6817828 , 0.6820144 , 0.695111  , 0.74243337,
             0.74258995],
            [0.74313664, 0.74313664, 0.74311763, 0.743121  , 0.69347614,
             0.6817588 , 0.6816627 , 0.68162507, 0.681626  , 0.6816272 ,
             0.68165946, 0.681688  , 0.6825288 , 0.6866009 , 0.74284095,
             0.71099097],
            [0.7431459 , 0.743142  , 0.7431288 , 0.74306506, 0.74271953,
             0.6817773 , 0.68164766, 0.6816255 , 0.6816238 , 0.6816261 ,
             0.6816576 , 0.6817054 , 0.6818784 , 0.6836108 , 0.7415086 ,
             0.7423605 ],
            [0.7431446 , 0.74314034, 0.74313146, 0.7430735 , 0.74300826,
             0.68218046, 0.6816794 , 0.6816329 , 0.6816322 , 0.68163204,
             0.6816404 , 0.681662  , 0.68176955, 0.6816962 , 0.68202084,
             0.7130929 ],
            [0.743135  , 0.74313396, 0.74311066, 0.7430717 , 0.743012  ,
             0.6835617 , 0.681735  , 0.68167424, 0.68166286, 0.68166465,
             0.6816571 , 0.68166894, 0.6817153 , 0.6816774 , 0.68187755,
             0.68243366],
            [0.7431015 , 0.7431064 , 0.7430737 , 0.743019  , 0.74304515,
             0.74279356, 0.6842172 , 0.68182147, 0.68175715, 0.6817733 ,
             0.6817072 , 0.68166715, 0.6816939 , 0.68163335, 0.6816291 ,
             0.6816552 ],
            [0.74287397, 0.7424762 , 0.74284005, 0.74287313, 0.7430483 ,
             0.7430072 , 0.7428921 , 0.70976615, 0.6828131 , 0.68191016,
             0.6817669 , 0.6816976 , 0.6816705 , 0.6816583 , 0.6816383 ,
             0.6816017 ],
            [0.6828026 , 0.6848309 , 0.7176072 , 0.7427187 , 0.74308777,
             0.74305534, 0.7431299 , 0.7431133 , 0.7429568 , 0.7180792 ,
             0.68228   , 0.6817193 , 0.68164736, 0.68162495, 0.68161565,
             0.6816161 ],
            [0.68169355, 0.68174034, 0.6819078 , 0.7165696 , 0.7430512 ,
             0.743127  , 0.7431362 , 0.7431384 , 0.7431311 , 0.74306774,
             0.68236923, 0.68173504, 0.6816384 , 0.6816253 , 0.6816088 ,
             0.6816075 ],
            [0.6816504 , 0.6816575 , 0.6817962 , 0.6823734 , 0.7430882 ,
             0.74312824, 0.743113  , 0.7431478 , 0.74314284, 0.7430648 ,
             0.74110776, 0.68176514, 0.681641  , 0.6816179 , 0.68161225,
             0.6816039 ],
            [0.68165284, 0.6816668 , 0.68172306, 0.69065   , 0.74162036,
             0.74312806, 0.7431376 , 0.74313945, 0.74312645, 0.7430848 ,
             0.72388333, 0.68177485, 0.6816497 , 0.68162644, 0.6816115 ,
             0.6816093 ],
            [0.68168014, 0.68168473, 0.681896  , 0.6824043 , 0.74072087,
             0.74307877, 0.743118  , 0.7431275 , 0.74310255, 0.7430742 ,
             0.6913082 , 0.6818626 , 0.6816873 , 0.6816535 , 0.6816231 ,
             0.68162876],
            [0.6817374 , 0.6817978 , 0.68193597, 0.68378305, 0.69661707,
             0.7419625 , 0.74300665, 0.7428335 , 0.74252176, 0.7294907 ,
             0.6839797 , 0.6819715 , 0.68173784, 0.68179655, 0.681662  ,
             0.68167084],
            [0.68310577, 0.68206555, 0.68204725, 0.68195146, 0.68435055,
             0.6821535 , 0.6819756 , 0.68235797, 0.6820901 , 0.68230015,
             0.6828375 , 0.6821874 , 0.68194205, 0.68281436, 0.68305475,
             0.68334603]], dtype=float32), si_unit='1'),
     't1': Quantity(values=Array([[2450.9875, 2451.0042, 2451.0015, 2450.982 , 2451.0002, 2451.0015,
             2450.9775, 2451.0142, 2451.037 , 2451.0002, 2451.0125, 2451.0293,
             2450.9858, 2450.9958, 2450.9783, 2450.9546],
            [2450.6687, 2450.6836, 2450.684 , 2450.6873, 2451.0164, 2450.955 ,
             2450.9475, 2450.9287, 2450.9092, 2450.9363, 2450.945 , 2450.9639,
             2450.9646, 2450.9993, 2450.9956, 2451.0237],
            [2597.7512, 2450.7615, 2568.751 , 2450.665 , 2450.7717, 2450.9426,
             2450.9058, 2450.904 , 2450.8923, 2450.8933, 2450.9336, 2450.974 ,
             2450.988 , 2451.0657, 2450.686 , 2450.6833],
            [2598.7734, 2598.8787, 2598.5012, 2450.864 , 2451.1174, 2450.964 ,
             2450.9128, 2450.889 , 2450.883 , 2450.883 , 2450.9375, 2450.955 ,
             2451.185 , 2451.4702, 2450.658 , 2451.5806],
            [2599.023 , 2598.9834, 2598.8347, 2596.9023, 2450.678 , 2450.982 ,
             2450.9048, 2450.8765, 2450.8901, 2450.8875, 2450.9182, 2450.9675,
             2451.2063, 2478.931 , 2596.5989, 2598.4702],
            [2599.0432, 2599.028 , 2598.952 , 2598.0007, 2450.6594, 2451.0605,
             2450.9304, 2450.8904, 2450.8926, 2450.8906, 2450.911 , 2450.9875,
             2451.4753, 2594.1616, 2599.2627, 2598.9248],
            [2599.0007, 2598.9897, 2598.7158, 2597.9233, 2450.6567, 2451.2097,
             2450.9468, 2450.9163, 2450.9084, 2450.9297, 2450.949 , 2451.073 ,
             2451.7476, 2599.2961, 2599.2158, 2599.1978],
            [2598.8777, 2598.863 , 2598.616 , 2594.4778, 2450.6572, 2450.6577,
             2451.0874, 2450.9521, 2450.9297, 2450.946 , 2451.0156, 2451.0452,
             2452.2515, 2599.2903, 2599.2732, 2599.2168],
            [2588.8704, 2515.1702, 2590.081 , 2591.7634, 2450.6978, 2450.6687,
             2450.6848, 2450.947 , 2451.0076, 2450.9902, 2451.0684, 2451.1404,
             2451.5   , 2470.1252, 2454.25  , 2599.171 ],
            [2451.6245, 2452.4426, 2450.927 , 2450.7412, 2456.2043, 2590.2302,
             2450.6587, 2450.6758, 2450.6882, 2450.7615, 2451.1575, 2451.145 ,
             2451.1362, 2451.1404, 2451.1775, 2451.2708],
            [2451.079 , 2451.0786, 2451.3218, 2451.2986, 2450.661 , 2451.279 ,
             2450.6577, 2450.6675, 2450.666 , 2450.6755, 2451.0735, 2451.085 ,
             2451.0322, 2451.0396, 2450.9458, 2450.9792],
            [2450.967 , 2450.9766, 2451.1392, 2451.1453, 2450.6575, 2450.6572,
             2563.1907, 2450.67  , 2450.665 , 2450.678 , 2450.685 , 2451.0293,
             2451.0002, 2450.9248, 2450.9197, 2450.8987],
            [2450.9521, 2450.9602, 2451.0422, 2451.2039, 2450.6865, 2450.6575,
             2450.665 , 2450.6687, 2450.6804, 2450.6877, 2450.7332, 2450.9912,
             2450.9175, 2450.91  , 2450.89  , 2450.8914],
            [2450.9482, 2450.9424, 2451.0024, 2451.0425, 2450.6877, 2450.6826,
             2450.6787, 2450.6807, 2450.6821, 2450.6833, 2451.0068, 2450.9587,
             2450.9219, 2450.932 , 2450.8923, 2450.8987],
            [2450.955 , 2450.9524, 2450.997 , 2451.0276, 2450.9934, 2450.6882,
             2450.688 , 2450.6914, 2450.6912, 2450.7053, 2450.9998, 2450.9478,
             2450.923 , 2450.9414, 2450.9053, 2450.9192],
            [2451.0237, 2450.9744, 2451.0024, 2450.9663, 2451.043 , 2451.0002,
             2450.9307, 2450.9863, 2450.9807, 2450.962 , 2451.0088, 2450.9568,
             2450.9631, 2450.982 , 2450.9983, 2451.0154]], dtype=float32), si_unit='ms')},
    'loss_history': [0.1519847810268402,
     0.14443373680114746,
     0.1375051885843277],
    'cached': False}],
  'metadata': {'config': {'seed': 2, 'group': 'treated'}},
  'study_dir': PosixPath('/tmp/mrax-notebook-smoke/tmp/analysis_toolbox_demo/inputs/treated_01')},
 {'study_name': 'treated_02',
  'manifest_path': PosixPath('/tmp/mrax-notebook-smoke/tmp/analysis_toolbox_demo/outputs/treated_02/study_manifest.json'),
  'results': [{'name': 't1_ir',
    'model': 't1_ir',
    'fields': {'amplitude': Quantity(values=Array([[0.6819282 , 0.6821133 , 0.6820945 , 0.68258524, 0.6831481 ,
             0.68211824, 0.6823683 , 0.6821233 , 0.6837261 , 0.6821822 ,
             0.6822014 , 0.6822707 , 0.6873775 , 0.6822371 , 0.68197894,
             0.68229187],
            [0.74310976, 0.7430717 , 0.74296534, 0.7413105 , 0.68234795,
             0.68237644, 0.681757  , 0.6817129 , 0.68170804, 0.68171924,
             0.68175286, 0.6820982 , 0.68249285, 0.7367408 , 0.7167938 ,
             0.74241537],
            [0.7431094 , 0.7431012 , 0.7431211 , 0.74307096, 0.7100313 ,
             0.68181795, 0.6816567 , 0.681635  , 0.6816503 , 0.681659  ,
             0.68169   , 0.6817444 , 0.6822094 , 0.68695635, 0.6949966 ,
             0.7429936 ],
            [0.74314034, 0.74313235, 0.7431151 , 0.7431215 , 0.6959359 ,
             0.68174803, 0.68165904, 0.68163085, 0.6816202 , 0.6816269 ,
             0.6816541 , 0.68168986, 0.6822395 , 0.6993946 , 0.73918563,
             0.7428811 ],
            [0.743148  , 0.74314237, 0.7431336 , 0.7430738 , 0.74189067,
             0.6817358 , 0.6816418 , 0.68162155, 0.68163085, 0.68162715,
             0.68164676, 0.6816921 , 0.68189776, 0.683156  , 0.74166566,
             0.74158204],
            [0.7431435 , 0.74314195, 0.7431219 , 0.7430674 , 0.74290943,
             0.6820586 , 0.68168426, 0.68164724, 0.6816238 , 0.68163896,
             0.6816453 , 0.6816638 , 0.68175536, 0.68174475, 0.6847813 ,
             0.74007535],
            [0.74313605, 0.74313074, 0.74312514, 0.74303955, 0.74306446,
             0.68289053, 0.68175703, 0.6816816 , 0.6816431 , 0.6816524 ,
             0.6816525 , 0.68167853, 0.6817513 , 0.68168956, 0.68185526,
             0.6824597 ],
            [0.7430965 , 0.7430842 , 0.7430801 , 0.7430206 , 0.74301827,
             0.7425623 , 0.6901343 , 0.68189347, 0.6819251 , 0.68175477,
             0.6817431 , 0.681685  , 0.6816719 , 0.6816359 , 0.68162847,
             0.68164366],
            [0.7428843 , 0.7427033 , 0.74290264, 0.7429037 , 0.7430729 ,
             0.7430755 , 0.74300987, 0.73655576, 0.69262946, 0.6820481 ,
             0.68176603, 0.68166953, 0.68166345, 0.6816533 , 0.6816446 ,
             0.6816443 ],
            [0.69634414, 0.6822409 , 0.7011271 , 0.7427899 , 0.74292254,
             0.74310994, 0.74312896, 0.7431046 , 0.7429813 , 0.6878972 ,
             0.6820877 , 0.68173265, 0.6816383 , 0.68163884, 0.68161905,
             0.6816243 ],
            [0.68175626, 0.6817678 , 0.68194526, 0.7428911 , 0.7430945 ,
             0.7430793 , 0.74313956, 0.7431377 , 0.7431169 , 0.74297774,
             0.682119  , 0.6816966 , 0.6816555 , 0.68162006, 0.68160754,
             0.6816114 ],
            [0.6816671 , 0.6816841 , 0.6817586 , 0.68763775, 0.7430784 ,
             0.74313253, 0.7431427 , 0.7431448 , 0.74313587, 0.7430706 ,
             0.7144781 , 0.68174994, 0.6816478 , 0.6816194 , 0.6816096 ,
             0.6816074 ],
            [0.68164545, 0.6816629 , 0.68178254, 0.6931603 , 0.7429813 ,
             0.74311984, 0.7431188 , 0.7431396 , 0.74313194, 0.7430612 ,
             0.7355741 , 0.6817853 , 0.6816387 , 0.681627  , 0.68161345,
             0.6816081 ],
            [0.6816836 , 0.6817001 , 0.6818009 , 0.6836951 , 0.7429817 ,
             0.7430736 , 0.7431284 , 0.7431197 , 0.7431174 , 0.742898  ,
             0.70832884, 0.68181854, 0.6817004 , 0.6816316 , 0.68163025,
             0.6816242 ],
            [0.6817742 , 0.68176675, 0.68192565, 0.68202   , 0.71891445,
             0.74299794, 0.74269587, 0.74302965, 0.74305636, 0.73991597,
             0.68362325, 0.6820194 , 0.6819398 , 0.6817013 , 0.6816793 ,
             0.6816616 ],
            [0.68232626, 0.68237925, 0.6822307 , 0.68230146, 0.6822261 ,
             0.68212116, 0.68302226, 0.68253684, 0.6832022 , 0.6824918 ,
             0.6825006 , 0.6820824 , 0.68236876, 0.6829198 , 0.6821655 ,
             0.68327224]], dtype=float32), si_unit='1'),
     't1': Quantity(values=Array([[2450.9714, 2450.9382, 2450.9402, 2450.983 , 2451.049 , 2450.9731,
             2451.0266, 2450.9758, 2451.0283, 2450.9512, 2450.9646, 2450.9897,
             2451.001 , 2450.947 , 2450.944 , 2450.9856],
            [2450.68  , 2450.6812, 2450.6848, 2450.6882, 2450.9822, 2450.9722,
             2450.9387, 2450.921 , 2450.971 , 2450.9233, 2450.942 , 2451.0002,
             2450.992 , 2450.6934, 2450.7732, 2450.6877],
            [2588.0947, 2596.4175, 2450.658 , 2450.671 , 2450.9539, 2450.9553,
             2450.9175, 2450.8894, 2450.8936, 2450.9185, 2450.919 , 2450.9373,
             2450.9963, 2451.0642, 2451.0725, 2450.6663],
            [2598.9458, 2598.7795, 2598.1145, 2456.092 , 2451.0186, 2450.9785,
             2450.915 , 2450.8882, 2450.8752, 2450.8794, 2450.9048, 2450.959 ,
             2451.1548, 2451.318 , 2450.6716, 2450.6755],
            [2599.0413, 2598.9988, 2598.8645, 2592.0054, 2450.6843, 2450.9724,
             2450.8984, 2450.8818, 2450.902 , 2450.8787, 2450.909 , 2451.012 ,
             2451.3728, 2486.6982, 2596.3757, 2598.3196],
            [2599.0127, 2599.041 , 2598.8738, 2596.4758, 2450.6653, 2451.099 ,
             2450.9414, 2450.895 , 2450.873 , 2450.9104, 2450.9326, 2450.9602,
             2451.4463, 2598.8376, 2599.2224, 2598.765 ],
            [2599.0127, 2598.9824, 2598.9304, 2596.341 , 2450.9502, 2451.158 ,
             2450.9587, 2450.9197, 2450.9026, 2450.9026, 2450.9336, 2451.089 ,
             2451.811 , 2599.3115, 2599.2168, 2599.2021],
            [2598.7896, 2598.5784, 2598.617 , 2575.9834, 2450.6577, 2450.6782,
             2451.0479, 2450.9773, 2450.9636, 2450.9656, 2451.0042, 2451.1394,
             2451.6816, 2599.2522, 2599.2686, 2599.2375],
            [2595.6826, 2593.777 , 2592.9644, 2555.3313, 2450.9253, 2450.6616,
             2450.6765, 2450.6963, 2451.0198, 2450.9941, 2451.022 , 2451.0525,
             2451.388 , 2452.827 , 2487.021 , 2479.443 ],
            [2455.5752, 2451.7288, 2452.2415, 2559.8303, 2555.9146, 2450.6914,
             2450.663 , 2450.678 , 2450.689 , 2450.9958, 2451.2095, 2451.1497,
             2451.0784, 2451.3125, 2451.4062, 2451.2473],
            [2451.2664, 2451.14  , 2451.2551, 2450.66  , 2450.7732, 2577.148 ,
             2450.665 , 2450.6638, 2450.6816, 2450.6848, 2451.046 , 2451.0542,
             2451.0293, 2450.9736, 2450.9417, 2451.008 ],
            [2451.0078, 2451.0298, 2451.2124, 2451.6052, 2450.6665, 2450.6975,
             2450.6606, 2450.6665, 2450.676 , 2450.6807, 2450.7998, 2451.0024,
             2450.949 , 2450.9336, 2450.9087, 2450.893 ],
            [2450.945 , 2450.9631, 2451.0513, 2451.1985, 2450.674 , 2450.6687,
             2537.5068, 2450.6716, 2450.6816, 2450.685 , 2450.6921, 2450.968 ,
             2450.9197, 2450.9187, 2450.8818, 2450.893 ],
            [2450.9492, 2450.9446, 2450.9868, 2451.1123, 2450.6843, 2450.6846,
             2450.6694, 2450.6838, 2450.6804, 2450.689 , 2450.932 , 2450.9758,
             2450.931 , 2450.911 , 2450.8904, 2450.8882],
            [2450.9658, 2450.9355, 2450.9958, 2450.9653, 2450.7627, 2450.686 ,
             2450.689 , 2450.689 , 2450.6824, 2450.6914, 2451.0054, 2450.9502,
             2450.9612, 2450.9336, 2450.9136, 2450.9077],
            [2450.982 , 2450.988 , 2450.9907, 2450.9985, 2450.999 , 2450.9514,
             2450.9734, 2450.9438, 2450.9673, 2451.0188, 2451.0056, 2450.9568,
             2450.982 , 2450.9858, 2450.9749, 2450.9958]], dtype=float32), si_unit='ms')},
    'loss_history': [0.15260642766952515,
     0.1450989693403244,
     0.13821451365947723],
    'cached': False}],
  'metadata': {'config': {'seed': 3, 'group': 'treated'}},
  'study_dir': PosixPath('/tmp/mrax-notebook-smoke/tmp/analysis_toolbox_demo/inputs/treated_02')}]

ROI statistics for parameter maps

Load one subject's manifest and extract ROI statistics from the fitted maps.

[3]:
manifest = summaries[0]["manifest_path"]
data = load_study_results(manifest)
exp = data["experiments"]["t1_ir"]
maps = exp["fitted_maps"]
mr_image = exp["mr_image"]

mask = np.zeros(mr_image.shape[-2:], dtype=bool)
mask[4:12, 4:12] = True
roi = ROI("center", mask)

rows = collect_roi_stats(maps, [roi], stats=("mean", "variance"))
rows

[3]:
[{'map': 'amplitude',
  'roi': 'center',
  'stat': 'mean',
  'value': 0.7074053883552551,
  'unit': '1'},
 {'map': 'amplitude',
  'roi': 'center',
  'stat': 'variance',
  'value': 0.0008956128731369972,
  'unit': '1'},
 {'map': 't1',
  'roi': 'center',
  'stat': 'mean',
  'value': 2452.32373046875,
  'unit': 'ms'},
 {'map': 't1',
  'roi': 'center',
  'stat': 'variance',
  'value': 137.02313232421875,
  'unit': 'ms'}]

ROI timecourse

Reduce the ROI in the raw image stack to a timecourse.

[4]:
timecourse = extract_timecourse(mr_image, roi, stat="mean")
ti = exp["observables"].get("TI")
ti_vals = np.asarray(ti.values) if ti is not None else np.arange(timecourse.shape[0])

fig, ax = plt.subplots(figsize=(4, 3))
ax.plot(ti_vals, timecourse, marker="o")
ax.set_xlabel("TI (ms)")
ax.set_ylabel("ROI mean signal")
ax.set_title("ROI timecourse")
plt.show()

../_images/notebooks_analysis_toolbox_7_0.png

Timecourse plotting and animation

The arrays returned by MRax analysis helpers can be plotted directly with matplotlib.

[5]:
from matplotlib.animation import FuncAnimation, PillowWriter
from mrax.analysis.timecourse_processing import mean_timecourses_by_label, nan_gaussian_filter, apply_baseline


def plot_timecourses(image_series, label_mask, times, *, dilution=None, baseline_frames=50, smooth_sigma=2.0, labels=None, infusion_times=None, time_scale=60.0, time_unit="min", save_path=None):
    series = np.asarray(image_series, dtype=float)
    times_arr = np.asarray(times, dtype=float)
    raw_timecourses, label_values = mean_timecourses_by_label(
        series,
        label_mask,
        baseline_frames=0,
        smooth_sigma=smooth_sigma,
        dilution=dilution,
    )
    timecourses = raw_timecourses.copy()
    if baseline_frames > 0:
        for idx in range(timecourses.shape[1]):
            timecourses[:, idx] -= np.nanmean(timecourses[:baseline_frames, idx])
    fig, ax = plt.subplots(figsize=(7.2, 4.2))
    for infusion_time in infusion_times or []:
        ax.axvline(x=infusion_time, color="gray", linestyle="--", linewidth=1)
    labels = labels or {label: str(label) for label in label_values}
    for idx, label in enumerate(label_values):
        ax.plot(times_arr / time_scale, raw_timecourses[:, idx], label=labels.get(label, str(label)), linewidth=2)
    ax.set_xlabel(f"Time ({time_unit})")
    ax.set_ylabel("Parameter Value")
    ax.legend(loc="upper right")
    if save_path:
        fig.savefig(save_path, dpi=300, bbox_inches="tight")
        plt.close(fig)
    return timecourses


def plot_mean_timecourses(timecourses, label, times, *, groups=None, infusion_times=None, vmin=None, vmax=None, label_index=0, time_scale=60.0, time_unit="min"):
    data = np.asarray(timecourses, dtype=float)
    times_arr = np.asarray(times, dtype=float)
    if data.ndim == 3 and data.shape[1] == times_arr.size:
        data = data[..., label_index][:, None, :]
    elif data.ndim == 2 and data.shape[0] == times_arr.size:
        data = data[:, label_index][None, None, :]
    group_names = list(groups) if groups is not None else [f"Group {i+1}" for i in range(data.shape[0])]
    fig, ax = plt.subplots(figsize=(7.2, 4.2))
    for infusion_time in infusion_times or []:
        ax.axvline(x=infusion_time, color="gray", linestyle="--", linewidth=1)
    for group_idx, group_name in enumerate(group_names):
        group_data = data[group_idx]
        mean_tc = np.nanmean(group_data, axis=0)
        sem_tc = np.nanstd(group_data, axis=0) / np.sqrt(max(1, group_data.shape[0]))
        line, = ax.plot(times_arr / time_scale, mean_tc, lw=2, label=group_name)
        ax.fill_between(times_arr / time_scale, mean_tc - sem_tc, mean_tc + sem_tc, color=line.get_color(), alpha=0.25)
    ax.set_xlabel(f"Time ({time_unit})")
    ax.set_ylabel(label)
    if vmin is not None or vmax is not None:
        ax.set_ylim(vmin, vmax)
    ax.legend(frameon=False, loc="upper right")
    return fig


def map_animation(image_series, reference, times, save_path, *, dilution=None, vmin=0.0, vmax=0.0005, infusion_times=None, baseline_frames=5, smooth_sigma=1.0, fps=30, dpi=300):
    series = np.asarray(image_series, dtype=float)
    if smooth_sigma and smooth_sigma > 0:
        series = nan_gaussian_filter(series, sigma=smooth_sigma)
    if dilution is not None:
        dil = apply_baseline(np.asarray(dilution, dtype=float), baseline_frames=baseline_frames, mode="divide", axis=0)
        with np.errstate(invalid="ignore", divide="ignore"):
            series = series / np.clip(dil, 1e-2, None)
    times_arr = np.asarray(times, dtype=float)
    baseline = np.nanmean(series[:baseline_frames], axis=0)
    fig, ax = plt.subplots(figsize=(5, 5))
    ax.imshow(reference, cmap="gray")
    im = ax.imshow(series[0] - baseline, vmin=vmin, vmax=vmax, cmap="jet")
    ax.axis("off")
    plt.colorbar(im, ax=ax)
    title = ax.set_title(f"t={times_arr[0]:.2f}")

    def update(frame):
        im.set_array(series[frame] - baseline)
        title.set_text(f"t={times_arr[frame]:.2f}")
        return [im, title]

    anim = FuncAnimation(fig, update, frames=series.shape[0], blit=True)
    anim.save(save_path, writer=PillowWriter(fps=fps), dpi=dpi)
    plt.close(fig)


label_mask = np.zeros(mr_image.shape[-2:], dtype=int)
label_mask[4:12, 4:12] = 1

timecourses = plot_timecourses(
    mr_image,
    label_mask,
    times=ti_vals * 1e-3,
    infusion_times=[5],
)

plot_mean_timecourses(
    timecourses[None, ...],
    label="Signal",
    times=ti_vals * 1e-3,
    groups=["control"],
    label_index=0,
)

[5]:
../_images/notebooks_analysis_toolbox_9_0.png
../_images/notebooks_analysis_toolbox_9_1.png
../_images/notebooks_analysis_toolbox_9_2.png
[6]:
animation_path = outputs_root / "roi_animation.gif"
map_animation(
    mr_image,
    reference=mr_image[0],
    times=ti_vals * 1e-3,
    save_path=str(animation_path),
)
animation_path

[6]:
PosixPath('/tmp/mrax-notebook-smoke/tmp/analysis_toolbox_demo/outputs/roi_animation.gif')

Group summaries and comparisons

Use the batch output root to compare groups. Members can be study folder names (inside outputs_root) or explicit manifest paths.

[7]:
groups = {
    "control": ["control_01", "control_02"],
    "treated": ["treated_01", "treated_02"],
}

results = analyze_groups(
    groups,
    output_root=outputs_root,
    experiment_name="t1_ir",
    rois=[roi],
    map_names=["t1"],
    stats=("mean",),
    p_adjust="fdr_bh",
)

results["group_summary"]

[7]:
[{'group': 'control',
  'experiment': 't1_ir',
  'map': 't1',
  'roi': 'center',
  'stat': 'mean',
  'count': 2.0,
  'nan_count': 0.0,
  'mean': 2452.281494140625,
  'variance': 0.003567814826965332,
  'std': 0.05973118805921519,
  'sem': 0.04223632812499999,
  'median': 2452.281494140625,
  'min': 2452.2392578125,
  'max': 2452.32373046875,
  'p25': 2452.2603759765625,
  'p75': 2452.3026123046875,
  'unit': 'ms'},
 {'group': 'treated',
  'experiment': 't1_ir',
  'map': 't1',
  'roi': 'center',
  'stat': 'mean',
  'count': 2.0,
  'nan_count': 0.0,
  'mean': 2454.6890869140625,
  'variance': 0.08044984936714172,
  'std': 0.28363682653552186,
  'sem': 0.20056152343749997,
  'median': 2454.6890869140625,
  'min': 2454.488525390625,
  'max': 2454.8896484375,
  'p25': 2454.5888061523438,
  'p75': 2454.7893676757812,
  'unit': 'ms'}]
[8]:
results["comparisons"]

[8]:
[{'experiment': 't1_ir',
  'map': 't1',
  'roi': 'center',
  'stat': 'mean',
  'group_a': 'control',
  'group_b': 'treated',
  'n_a': 2.0,
  'n_b': 2.0,
  'mean_a': 2452.281494140625,
  'mean_b': 2454.68896484375,
  'mean_diff': -2.407470703125,
  't_stat': -11.746614794850906,
  't_p': 0.04431433472445087,
  'effect_size_d': -11.746614794850906,
  'effect_size_g': -6.712351311343374,
  'np_test': 'mannwhitney',
  'np_stat': 0.0,
  'np_p': 0.3333333333333333,
  'unit': 'ms',
  't_p_fdr_bh': 0.04431433472445087}]

Plot group distributions and significance

Visualize per-subject distributions and group summaries with significance markers.

[9]:
def _grouped_values(rows, *, experiment, map_name, roi, stat):
    grouped = {}
    for row in rows:
        if row.get("experiment") == experiment and row.get("map") == map_name and row.get("roi") == roi and row.get("stat") == stat:
            grouped.setdefault(str(row.get("group")), []).append(float(row.get("value", np.nan)))
    return grouped


def _significance_label(p_value):
    if p_value is None or not np.isfinite(p_value):
        return "n/a"
    if p_value <= 0.001:
        return "***"
    if p_value <= 0.01:
        return "**"
    if p_value <= 0.05:
        return "*"
    return "n.s."


def plot_group_distributions(subject_rows, *, experiment, map_name, roi, stat, comparisons=None, kind="box"):
    grouped = _grouped_values(subject_rows, experiment=experiment, map_name=map_name, roi=roi, stat=stat)
    order = sorted(grouped)
    values = [grouped[name] for name in order]
    fig, ax = plt.subplots(figsize=(5.2, 3.6))
    positions = np.arange(len(order))
    if kind == "violin":
        ax.violinplot(values, positions=positions, showmedians=True)
    else:
        ax.boxplot(values, positions=positions, widths=0.6, patch_artist=True)
    rng = np.random.default_rng(0)
    for idx, group_vals in enumerate(values):
        ax.scatter(np.full(len(group_vals), positions[idx]) + rng.normal(0, 0.04, len(group_vals)), group_vals, s=16, color="#2f2f2f", alpha=0.65)
    ax.set_xticks(positions)
    ax.set_xticklabels(order)
    ax.set_ylabel(stat)
    ax.set_title(f"{experiment} - {map_name} - {roi}")
    return fig


def plot_group_summary(summary_rows, *, experiment, map_name, roi, stat, comparisons=None, value_key="mean", error_key="sem"):
    rows = [row for row in summary_rows if row.get("experiment") == experiment and row.get("map") == map_name and row.get("roi") == roi and row.get("stat") == stat]
    order = sorted(str(row.get("group")) for row in rows)
    by_group = {str(row.get("group")): row for row in rows}
    means = [float(by_group[group].get(value_key, np.nan)) for group in order]
    errors = [float(by_group[group].get(error_key, np.nan)) for group in order]
    fig, ax = plt.subplots(figsize=(5.2, 3.6))
    positions = np.arange(len(order))
    ax.bar(positions, means, yerr=errors, capsize=4, color="#88aadd", alpha=0.8)
    ax.set_xticks(positions)
    ax.set_xticklabels(order)
    ax.set_ylabel(f"{value_key} ({stat})")
    ax.set_title(f"{experiment} - {map_name} - {roi}")
    return fig


fig = plot_group_distributions(
    results["subject_stats"],
    comparisons=results["comparisons"],
    experiment="t1_ir",
    map_name="t1",
    roi="center",
    stat="mean",
    kind="box",
)
plt.show()

../_images/notebooks_analysis_toolbox_15_0.png
[10]:
fig = plot_group_summary(
    results["group_summary"],
    comparisons=results["comparisons"],
    experiment="t1_ir",
    map_name="t1",
    roi="center",
    stat="mean",
)
plt.show()

../_images/notebooks_analysis_toolbox_16_0.png