diff --git a/virtual-staining/README.md b/virtual-staining/README.md new file mode 100644 index 0000000..7760335 --- /dev/null +++ b/virtual-staining/README.md @@ -0,0 +1,51 @@ +# Virtual staining evaluation + +Reproduce paper metrics for five models on IMC virtual-staining task. + +## Quick start (paper reproduction) + +```bash +python3 evaluate_all.py --config configs/paper.yaml --table +python3 evaluate_all.py plot --results results/per_point_results.csv +``` + +## Input data format + +### `immuvis_npz` — used by ImmuVis models + +Each file is named `recn-{image_id}.npz` and contains: + +| Key | Shape | Content | +|---|---|---| +| `recon` | `(C, H, W)` | Model predictions, one channel per marker | +| `target` | `(C, H, W)` | Ground truth, same channel ordering | +| `marker_names` | `(C,)` | Array of marker name strings | +| `metadata` | scalar (JSON string) | `{"image_path": "...", "dataset_name": "hn"}` | + +Image ID is extracted from `metadata["image_path"]` by taking the filename stem (last `/`-separated component, minus extension). + +Predictions/ground truth are indexed by `[channel, :, :]` — the `marker_names` array is searched for the target marker name to find the channel index. + +### `virtues_npy` — used by Virtues and Eva models + +Directory of single-channel `.npy` files named `{image_id}_{marker}_recon.npy`, each shape `(H, W)`. + +Marker names in filenames use underscores and short forms; they are normalised on load: + +| Filename marker | Canonical name | +|---|---| +| `Carbonic` | `Carbonic Anhydrase` | +| `PARP` | `cl.PARP` | +| `H3` or `Histone` | `Histone H3` | +| `Carbonic_Anhydrase` | `Carbonic Anhydrase` (underscores → spaces) | + +No ground truth is stored in these files. Ground truth is always sourced from a separate `immuvis_npz` model (configured via `ground_truth.source_model`). + +## Output + +All CSVs in `output_dir/` (default `results/`): + +- `per_point_results.csv` — per (model, image, marker) MSE + Pearson +- `per_marker_pearson.csv` — per-marker aggregated Pearson (sufficient statistics) +- `summary.csv` — per-(model, marker) mean ± std +- `boxplot.pdf` — grouped box plot via `plot` subcommand diff --git a/virtual-staining/configs/paper.yaml b/virtual-staining/configs/paper.yaml new file mode 100644 index 0000000..6e9d47d --- /dev/null +++ b/virtual-staining/configs/paper.yaml @@ -0,0 +1,58 @@ +marker_stats_csv: "data/marker_ds_stats.csv" +crop_size: [128, 128] +output_dir: "results" + +models: + + - name: "virtues_our_norm" + path: "/raid_encrypted/immucan/recons/virtues_our_norm/" + format: "virtues_npy" + display_name: "Virtues_{IMC17M}_{OP}" + prediction_transform: + type: "immu_offset" + params: { scale: 3.4 } + ground_truth: + source_model: "immuvis_612" + transform: + type: "immu_offset" + params: { scale: 3.4 } + + - name: "eva_hn_recon_crop" + path: "/raid_encrypted/immucan/recons/eva_hn_recon_crop/" + format: "virtues_npy" + display_name: "EVA_{OP}" + prediction_transform: + type: "immu_offset" + params: { scale: 3.4 } + ground_truth: + source_model: "immuvis_612" + transform: + type: "immu_offset" + params: { scale: 3.4 } + + - name: "immuvis_609" + path: "/raid_encrypted/immucan/results/with_reconstructs/recons/immuvis_609_loo/" + format: "immuvis_npz" + display_name: "ImmuVis_{ViT}" + filter_pairs_to: "virtues_our_norm" + prediction_transform: + type: "immu_offset" + params: { scale: 3.4 } + + - name: "immuvis_612" + path: "/raid_encrypted/immucan/results/with_reconstructs/recons/immuvis_612_loo/" + format: "immuvis_npz" + display_name: "ImmuVis_{Conv}" + filter_pairs_to: "virtues_our_norm" + prediction_transform: + type: "immu_offset" + params: { scale: 3.4 } + + - name: "immuvis_622" + path: "/raid_encrypted/immucan/results/with_reconstructs/recons/immuvis_622_loo/" + format: "immuvis_npz" + display_name: "ImmuVis_{Swin}" + filter_pairs_to: "virtues_our_norm" + prediction_transform: + type: "immu_offset" + params: { scale: 3.4 } diff --git a/virtual-staining/configs/swin.yaml b/virtual-staining/configs/swin.yaml new file mode 100644 index 0000000..5e13a82 --- /dev/null +++ b/virtual-staining/configs/swin.yaml @@ -0,0 +1,31 @@ +marker_stats_csv: "data/marker_ds_stats.csv" +crop_size: [128, 128] +output_dir: "results_swin" + +models: + - name: "virtues_our_norm" + path: "/raid_encrypted/immucan/recons/virtues_our_norm/" + format: "virtues_npy" + display_name: "Virtues_{IMC17M}_{OP}" + prediction_transform: + type: "immu_offset" + params: { scale: 3.4 } + ground_truth: + source_model: "immuvis_612" + transform: + type: "immu_offset" + params: { scale: 3.4 } + + - name: "immuvis_612" + path: "/raid_encrypted/immucan/results/with_reconstructs/recons/immuvis_612_loo/" + format: "immuvis_npz" + evaluate: false + + - name: "immuvis_622" + path: "/raid_encrypted/immucan/results/with_reconstructs/recons/immuvis_622_loo/" + format: "immuvis_npz" + display_name: "ImmuVis_{Swin}" + filter_pairs_to: "virtues_our_norm" + prediction_transform: + type: "immu_offset" + params: { scale: 3.4 } diff --git a/virtual-staining/data/marker_ds_stats.csv b/virtual-staining/data/marker_ds_stats.csv new file mode 100644 index 0000000..60d3860 --- /dev/null +++ b/virtual-staining/data/marker_ds_stats.csv @@ -0,0 +1,1104 @@ +dataset,marker,mean,std +ajaib,ANXA1,1.8867999,1.4325083 +ajaib,Annexin A2,2.5906734,1.8487732 +ajaib,BCAN,0.35533065,0.49032253 +ajaib,CD3,0.32282087,0.4849442 +ajaib,CD31,0.34290355,0.5333133 +ajaib,CD45,0.9035476,0.84936285 +ajaib,CD56,0.38731748,0.55113417 +ajaib,CD8a,0.10363408,0.2797177 +ajaib,CHI3L1,1.2517227,1.0664754 +ajaib,DLL3,0.61922073,0.72237915 +ajaib,DNA1,1.8383244,1.5781451 +ajaib,DNA2,2.1791697,1.8029904 +ajaib,EZH2,0.15917648,0.3444679 +ajaib,GFAP,3.0861979,2.3386188 +ajaib,HIF1a,0.19504488,0.38124502 +ajaib,HOPX,1.0162091,0.84970856 +ajaib,IBA1,0.7077021,0.863007 +ajaib,JARID2-C,0.65877837,0.6773189 +ajaib,JARID2-N,1.3351457,1.0416846 +ajaib,Ki67,0.39875317,0.630466 +ajaib,MOG,0.6731499,0.7067643 +ajaib,NKp46,0.3261348,0.4982631 +ajaib,NeuN,0.4025644,0.55103904 +ajaib,Olig1,0.6364985,0.6482717 +ajaib,P2RY12,0.07329095,0.23646952 +ajaib,SCD5,0.51204824,0.61683494 +ajaib,SLC1A3/EAAT1,1.2096884,1.0621824 +ajaib,SMA,1.1120795,1.0155596 +ajaib,SNAI1,0.6277812,0.6454764 +ajaib,SOD2,1.52386,1.4239427 +ajaib,Sox2,1.2062689,1.0686481 +ajaib,TGFB,1.2774839,1.2160736 +ajaib,TMEM119,0.14108454,0.32875964 +ajaib,TNC,1.0776216,0.9862629 +bc2,B2M,1.0913177,0.9570048 +bc2,CD11c,0.5179493,0.7614665 +bc2,CD14,1.1768866,1.0730242 +bc2,CD15,0.50928736,0.74698687 +bc2,CD16,0.4124157,0.6614149 +bc2,CD163,0.22383115,0.5353088 +bc2,CD20,0.33072546,0.5818898 +bc2,CD206,0.22325876,0.49325815 +bc2,CD27,0.37352687,0.57275426 +bc2,CD3,0.45524663,0.6790324 +bc2,CD303,0.42375585,0.6198073 +bc2,CD33,0.7138953,0.80524004 +bc2,CD38,0.24351272,0.46662462 +bc2,CD4,0.66190803,0.7849515 +bc2,CD40,0.42986953,0.6159899 +bc2,CD45RA,0.2969163,0.5210544 +bc2,CD45RO,1.0931832,1.0120113 +bc2,CD68,0.46652392,0.7293658 +bc2,CD7,0.46236905,0.7088825 +bc2,CD8a,0.3814504,0.68250793 +bc2,Carbonic Anhydrase,0.5721002,0.7541853 +bc2,DNA1,2.3502564,1.7378116 +bc2,DNA2,2.7249131,1.9426922 +bc2,Ecad,1.3985403,1.3515 +bc2,FOXP3,0.262787,0.4985953 +bc2,GrzB,0.5648242,0.7124527 +bc2,HLADR,1.1214923,1.2104586 +bc2,Histone H3,1.4360414,1.235262 +bc2,ICOS,0.34913686,0.5580114 +bc2,Ido1,0.43130183,0.6174654 +bc2,Ki67,0.8044486,1.1611943 +bc2,LAG3,0.12424834,0.31599718 +bc2,MPO,0.107310906,0.2820297 +bc2,PD1,0.17723635,0.39315665 +bc2,PDGFRb,0.33714542,0.60485864 +bc2,PDL1,0.31302354,0.53508323 +bc2,SMA,0.25577575,0.5342981 +bc2,TCF7,0.4746825,0.6585966 +bc2,VISTA,0.34632054,0.56615317 +bc2,cl.PARP,0.06662108,0.24967027 +bengsch,CD103,3.7330017e-05,0.0034692893 +bengsch,CD15,0.0037948906,0.040700745 +bengsch,CD163,0.01900454,0.09415517 +bengsch,CD20,0.0026825126,0.028004544 +bengsch,CD204,0.021889782,0.09313599 +bengsch,CD3,0.0049988357,0.045708977 +bengsch,CD33,0.0018449894,0.025173351 +bengsch,CD34,0.005310685,0.0488997 +bengsch,CD38,0.0011198582,0.017967174 +bengsch,CD39,0.00071273313,0.016530132 +bengsch,CD4,0.022521764,0.09574238 +bengsch,CD45,0.02413267,0.10022804 +bengsch,CD45RO,0.016335169,0.083177246 +bengsch,CD68,0.013230273,0.07946098 +bengsch,CD69,0.00231411,0.026598277 +bengsch,CD8a,0.008587888,0.06371553 +bengsch,CK7,0.00059143535,0.012074825 +bengsch,CXCR5,0.0004501789,0.008812984 +bengsch,CXCR6,0.00012576376,0.006600696 +bengsch,Collagen Type I,0.015234645,0.07259197 +bengsch,DNA1,0.060100943,0.141262 +bengsch,DNA2,0.10120985,0.16706881 +bengsch,EOMES,0.00052248954,0.014563367 +bengsch,Ecad,0.04409742,0.12472093 +bengsch,FOXP3,0.00052574556,0.012941681 +bengsch,GrzB,0.0018886265,0.02834759 +bengsch,HLADR,0.0242808,0.10116339 +bengsch,Histone H3,0.07370914,0.15936896 +bengsch,Ki67,0.0024637163,0.02544675 +bengsch,LAG3,0.0009763784,0.018108528 +bengsch,LYVE1,0.0018886307,0.023818469 +bengsch,PD1,0.0012661072,0.02036363 +bengsch,PDL1,0.00048629133,0.012137138 +bengsch,S6,0.013561702,0.06365383 +bengsch,SMA,0.024369989,0.091955535 +bengsch,TBET,0.0013670654,0.02266656 +bengsch,TCF1,1.0986769e-05,0.0013513775 +bengsch,TIGIT,2.838154e-06,0.0009998336 +bengsch,TOX,0.00090717827,0.011628287 +bengsch,Tim3,0.0034945167,0.036230754 +bengsch,beta-Catenin,0.023489663,0.09720118 +bengsch,pERK,0.0012568109,0.017341407 +bowen-lynch,B2M,0.21626018,0.42402196 +bowen-lynch,CCR6,0.07376027,0.20510063 +bowen-lynch,CD11b,0.17752409,0.41361564 +bowen-lynch,CD11c,0.027302545,0.14731531 +bowen-lynch,CD134,0.07370221,0.23691022 +bowen-lynch,CD137,0.035486117,0.1636433 +bowen-lynch,CD14,0.17828393,0.41920757 +bowen-lynch,CD163,0.08009961,0.27560306 +bowen-lynch,CD19,0.058258984,0.21847123 +bowen-lynch,CD276,0.14297223,0.34122828 +bowen-lynch,CD3,0.10536787,0.2948848 +bowen-lynch,CD33,0.030214556,0.15145837 +bowen-lynch,CD39,0.013218546,0.09989492 +bowen-lynch,CD4,0.11240313,0.31690732 +bowen-lynch,CD45,0.088474,0.26494607 +bowen-lynch,CD45RO,0.06268383,0.21785466 +bowen-lynch,CD66b,0.0187602,0.11927008 +bowen-lynch,CD68,0.061229136,0.24055478 +bowen-lynch,CD73,0.021146055,0.12564924 +bowen-lynch,CD8a,0.076002784,0.27571616 +bowen-lynch,CD94,0.030314485,0.15017089 +bowen-lynch,DNA1,1.1448427,1.5693245 +bowen-lynch,DNA2,1.3261478,1.7747663 +bowen-lynch,FOXP3,0.20539354,0.40719825 +bowen-lynch,GAPDH,0.6229827,0.9025271 +bowen-lynch,GrzB,0.01881336,0.11822124 +bowen-lynch,HLADR,0.1020177,0.2970339 +bowen-lynch,ICOS,0.041762657,0.174797 +bowen-lynch,Ido1,0.7490025,0.99027115 +bowen-lynch,Ki67,0.15366082,0.4209811 +bowen-lynch,LAG3,0.010476761,0.08291539 +bowen-lynch,MPO,0.035111774,0.15828711 +bowen-lynch,NaK-ATPase,0.5978036,0.9831284 +bowen-lynch,PD1,0.024240637,0.13402615 +bowen-lynch,PDL1,0.04592221,0.18345222 +bowen-lynch,Tim3,0.23587063,0.4784572 +bowen-lynch,VISTA,0.12521937,0.30563152 +bowen-lynch,panCK,0.8440805,1.4750981 +cho_2025,Arg1,0.092814215,0.27222216 +cho_2025,CD134,0.034660064,0.16837859 +cho_2025,CD137,0.03633909,0.17175835 +cho_2025,CD15,0.15203136,0.38976008 +cho_2025,CD163,0.12612903,0.3956172 +cho_2025,CD20,0.060410816,0.19831724 +cho_2025,CD206,0.13380606,0.37707037 +cho_2025,CD3,0.14783956,0.3510605 +cho_2025,CD4,0.1608946,0.3735177 +cho_2025,CD45RA,0.13459198,0.33430287 +cho_2025,CD45RO,0.04129955,0.18365794 +cho_2025,CD47,0.04462624,0.19176377 +cho_2025,CD57,0.043490592,0.19253533 +cho_2025,CD68,0.35974735,0.6761287 +cho_2025,CD73,0.034239788,0.17174989 +cho_2025,CD74,0.13685913,0.3411162 +cho_2025,CD86,0.12675838,0.3296943 +cho_2025,CD8a,0.124633946,0.33353373 +cho_2025,Collagen Type I,0.21955755,0.5088377 +cho_2025,DCSIGN,0.17581442,0.39205888 +cho_2025,DNA1,1.596309,2.1141746 +cho_2025,DNA2,1.8120601,2.36593 +cho_2025,FOXP3,0.07407881,0.25046542 +cho_2025,GrzB,0.020200191,0.12900779 +cho_2025,HLADR,0.16868316,0.40364426 +cho_2025,ICOS,0.22173944,0.4736584 +cho_2025,Ki67,0.050554566,0.2312214 +cho_2025,LAG3,0.05071001,0.20268312 +cho_2025,PD1,0.043400604,0.18716471 +cho_2025,PDL1,0.031513978,0.15767549 +cho_2025,PM2,0.50477713,0.7622422 +cho_2025,PM3,0.53484803,0.82660055 +cho_2025,PM4,0.4312915,0.68170786 +cho_2025,PTPN22,0.06553011,0.23765641 +cho_2025,Podoplanin,0.0601018,0.22241539 +cho_2025,TIGIT,0.045276128,0.19701996 +cho_2025,TOX,0.010099826,0.08566869 +cho_2025,Tim3,0.15397945,0.37814403 +cho_2025,VISTA,0.03678945,0.1736282 +cho_2025,pSTAT3,0.03171404,0.16259086 +cho_2025,panCK,0.46243203,0.82662576 +cords,CCL21,0.027515192,0.1440939 +cords,CD10,0.25160038,0.49875516 +cords,CD146,0.13504347,0.34547797 +cords,CD15,0.39745766,0.7199061 +cords,CD20,0.061521653,0.21049464 +cords,CD248,0.28448284,0.52641857 +cords,CD3,0.08525508,0.26762044 +cords,CD31-vWF,0.13094555,0.35103855 +cords,CD34,0.13739571,0.352273 +cords,CD4,0.08658451,0.2647516 +cords,CD45,0.5066642,0.7495717 +cords,CD68,0.20484802,0.47428107 +cords,CD73,0.12159153,0.31774747 +cords,CD8a,0.08938545,0.29124352 +cords,CXCL12,0.07300765,0.24431796 +cords,Cadherin-11,0.10952852,0.3095969 +cords,Carbonic Anhydrase,0.11948717,0.3133043 +cords,Caveolin-1,0.26229498,0.5037378 +cords,Collagen Type I,0.2200518,0.50563645 +cords,DNA1,1.623275,2.1225126 +cords,DNA2,1.830106,2.3664238 +cords,FAP,0.18219574,0.40266895 +cords,FOXP3,0.13166796,0.35958406 +cords,FSP1,0.08923293,0.2740813 +cords,HLADR,0.31722412,0.6376437 +cords,Histone H3,0.4543488,0.7733628 +cords,Ido1,0.10576001,0.29597497 +cords,K-Cadherin,0.04648372,0.18759371 +cords,Ki67,0.19737992,0.4286624 +cords,LYVE1,0.12871273,0.32758275 +cords,MMP11,0.18863378,0.4080249 +cords,MMP9,0.19345942,0.416162 +cords,MPO,0.04197681,0.18808496 +cords,PD1,0.057877246,0.21127884 +cords,PDGFRb,0.20508778,0.4251456 +cords,PNAd,0.07942408,0.2660159 +cords,Podoplanin,0.09226401,0.275438 +cords,SMA,0.17340332,0.46856642 +cords,TCF1/7,0.09027275,0.27277395 +cords,VCAM1,0.14445025,0.3503442 +cords,Vimentin,0.5666927,1.0726424 +cords,p75,0.027219046,0.14387853 +cords,panCK,0.66697586,1.1512239 +cords-fibro,CCL21,0.035703182,0.16233718 +cords-fibro,CD10,0.22001015,0.4360651 +cords-fibro,CD11b,0.07614734,0.25557268 +cords-fibro,CD11c,0.051892184,0.21443242 +cords-fibro,CD146,0.0903795,0.27675173 +cords-fibro,CD20,0.14853853,0.37533486 +cords-fibro,CD3,0.07208185,0.25192755 +cords-fibro,CD31-vWF,0.11590988,0.33288628 +cords-fibro,CD34,0.08744889,0.26280358 +cords-fibro,CD4,0.20368826,0.42880854 +cords-fibro,CD45,0.042757053,0.18700477 +cords-fibro,CD68,0.12100415,0.35165256 +cords-fibro,CD73,0.06367905,0.22637297 +cords-fibro,CD8a,0.124753766,0.34866288 +cords-fibro,CXCL13,0.09967509,0.2939518 +cords-fibro,Cadherin-11,0.21190202,0.4534189 +cords-fibro,Carbonic Anhydrase,0.14732026,0.325939 +cords-fibro,DNA1,1.0129967,1.2590905 +cords-fibro,DNA2,1.2403554,1.4655879 +cords-fibro,FAP,0.15176477,0.36578825 +cords-fibro,FOXP3,0.14326218,0.3667941 +cords-fibro,FSP1,0.102165624,0.28749505 +cords-fibro,Fibronectin,0.5297275,0.8809759 +cords-fibro,HLADR,0.2551976,0.59652853 +cords-fibro,Histone H3,0.44401896,0.7242539 +cords-fibro,Ido1,0.07663875,0.24922676 +cords-fibro,Ki67,0.093852155,0.2778135 +cords-fibro,LYVE1,0.14481786,0.346302 +cords-fibro,MMP9,0.22874312,0.43342048 +cords-fibro,MPO,0.018928336,0.11634359 +cords-fibro,PDGFRb,0.18420164,0.403208 +cords-fibro,PNAd,0.082314484,0.24362123 +cords-fibro,Podoplanin,0.10380777,0.2953313 +cords-fibro,SMA,0.22765186,0.51880753 +cords-fibro,VCAM1,0.055949386,0.20417963 +cords-fibro,Vimentin,0.626536,1.0799131 +cords-fibro,anti-Hu,0.15526015,0.36413786 +cords-fibro,p75,0.04067545,0.17966683 +cords-fibro,panCK,0.6832903,1.0595571 +damond-lung,AMY2A,0.4457657,0.58622587 +damond-lung,CD20,0.025154674,0.13951355 +damond-lung,CD3,0.091951944,0.2699594 +damond-lung,CD31,0.2095147,0.41785723 +damond-lung,CD38,0.14884599,0.34319088 +damond-lung,CD4,0.5768262,0.60822266 +damond-lung,CD44,0.30612114,0.5408354 +damond-lung,CD45,0.07672432,0.26390922 +damond-lung,CD45RA,0.37356365,0.52203405 +damond-lung,CD68,0.16717657,0.39006197 +damond-lung,CD8a,0.113403216,0.32097137 +damond-lung,CD99,0.62682533,0.85132796 +damond-lung,CK19,0.20914774,0.4335009 +damond-lung,Carbonic Anhydrase,0.27428827,0.4772369 +damond-lung,DNA1,1.316282,1.0655293 +damond-lung,DNA2,1.7440245,1.239166 +damond-lung,Ecad,1.2703111,1.1320945 +damond-lung,FOXP3,0.040984392,0.17910936 +damond-lung,GCG,0.0751224,0.29642016 +damond-lung,GLUT1,0.40509403,0.57650757 +damond-lung,Histone H3,1.6889312,1.0867926 +damond-lung,Histone H3 - p,0.06755266,0.21776405 +damond-lung,IAPP,0.48557788,0.6553999 +damond-lung,Insulin,0.06722856,0.2276653 +damond-lung,Ki67,0.055412896,0.23324357 +damond-lung,MPO,0.10231398,0.31984562 +damond-lung,NKX6-1,0.20487078,0.42450416 +damond-lung,PCSK2,0.5446405,0.6862541 +damond-lung,PDX1,1.0470935,0.96556157 +damond-lung,PIN,0.14978546,0.44013527 +damond-lung,PPY,0.056119427,0.19140762 +damond-lung,PTPRN,0.5200336,0.65975475 +damond-lung,SMA,0.39762458,0.5436523 +damond-lung,SYP,0.5398996,0.77921987 +damond-lung,c-Caspase3c-PARP,0.17144747,0.36154944 +damond-lung,pRB,0.12457307,0.31658614 +danenberg,B2M,0.17958032,0.35913667 +danenberg,CD11c,0.050208792,0.18838847 +danenberg,CD134,0.011426412,0.08642977 +danenberg,CD15,0.06866715,0.21168871 +danenberg,CD16,0.046053454,0.18268943 +danenberg,CD163,0.02202402,0.12582015 +danenberg,CD20,0.09204524,0.26236832 +danenberg,CD3,0.05045595,0.18724558 +danenberg,CD31-vWF,0.11439938,0.30254441 +danenberg,CD38,0.08275593,0.24264656 +danenberg,CD4,0.15552373,0.3442372 +danenberg,CD45RA,0.27783057,0.47691837 +danenberg,CD45RO,0.3027268,0.5204612 +danenberg,CD57,0.21579035,0.41548863 +danenberg,CD68,0.048540477,0.1983564 +danenberg,CD8a,0.13647845,0.33159673 +danenberg,CK5,0.04638619,0.17908761 +danenberg,CK8-18,0.15723132,0.36321574 +danenberg,CXCL12,0.1355003,0.3390217 +danenberg,Caveolin-1,0.15278618,0.4005967 +danenberg,DNA1,1.0323505,1.3993403 +danenberg,DNA2,1.2530365,1.6436793 +danenberg,ER,0.7934884,1.04868 +danenberg,FOXP3,0.043540668,0.1737492 +danenberg,FSP1,0.068251185,0.23035833 +danenberg,GITR,0.035173085,0.1554169 +danenberg,HER2 (3B5),0.29334486,0.52584225 +danenberg,HER2 (D8F12),0.13628,0.31364965 +danenberg,HLA-ABC,0.31064257,0.51127636 +danenberg,HLADR,0.18613653,0.4379039 +danenberg,Histone H3,0.95648104,1.3326107 +danenberg,ICOS,0.020652337,0.12159414 +danenberg,Ki67,0.04812058,0.17866538 +danenberg,PD1,0.03040663,0.14447668 +danenberg,PDGFRb,0.09700017,0.2866977 +danenberg,Podoplanin,0.08352669,0.2507823 +danenberg,SMA,0.41770002,0.685172 +danenberg,c-Caspase3c-PARP,0.08610985,0.24962573 +danenberg,panCK,0.60424227,1.0009164 +ehret_2025_p1,Arg1,0.07037628,0.19108346 +ehret_2025_p1,CD15,0.069045916,0.2254954 +ehret_2025_p1,CD163,0.046761665,0.21512192 +ehret_2025_p1,CD20,0.031280793,0.16828395 +ehret_2025_p1,CD3,0.0384365,0.1860424 +ehret_2025_p1,CD31,0.064771295,0.20578918 +ehret_2025_p1,CD4,0.08925824,0.27801758 +ehret_2025_p1,CD45,0.11549515,0.33622825 +ehret_2025_p1,CD56,0.02075052,0.104803994 +ehret_2025_p1,CD68,0.080207646,0.29989478 +ehret_2025_p1,CD8a,0.023834718,0.15345046 +ehret_2025_p1,CDX2,0.2619903,0.5080097 +ehret_2025_p1,CEACAM5,0.12293241,0.28100047 +ehret_2025_p1,CK20,0.10731737,0.31903854 +ehret_2025_p1,CK8-18,0.11589095,0.3195476 +ehret_2025_p1,Carbonic Anhydrase,0.057664458,0.15495667 +ehret_2025_p1,Caveolin-1,0.14626084,0.3468051 +ehret_2025_p1,DNA1,0.8884958,1.3617188 +ehret_2025_p1,DNA2,1.0385852,1.5604346 +ehret_2025_p1,EGFR,0.06623271,0.21848133 +ehret_2025_p1,FOXP3,0.026518948,0.1295962 +ehret_2025_p1,FSP1,0.05473035,0.2027233 +ehret_2025_p1,GATA3,0.025934968,0.118241996 +ehret_2025_p1,GPA33,0.15945324,0.34030676 +ehret_2025_p1,GrzB,0.07313954,0.25182495 +ehret_2025_p1,HER2 (3B5),0.02100289,0.10868768 +ehret_2025_p1,HER3,0.29342377,0.5953109 +ehret_2025_p1,HLA-ABC,0.46175784,0.7063016 +ehret_2025_p1,HLADR,0.13163312,0.39807683 +ehret_2025_p1,Ki67,0.2614532,0.6961091 +ehret_2025_p1,LAG3,0.02885649,0.13490902 +ehret_2025_p1,PD1,0.01349535,0.085975036 +ehret_2025_p1,PDL1,0.06572497,0.23439707 +ehret_2025_p1,PTEN,0.19962887,0.41206387 +ehret_2025_p1,Podoplanin,0.20620708,0.42931387 +ehret_2025_p1,SMA,0.15839843,0.36972186 +ehret_2025_p1,TBET,0.09732738,0.3037256 +ehret_2025_p1,Vimentin,0.4711598,1.0114207 +ehret_2025_p1,beta-Catenin,0.3822168,0.6756034 +ehret_2025_p1,c-Caspase3c-PARP,0.117381044,0.29027885 +ehret_2025_p1,p53,0.12467121,0.30795434 +ehret_2025_p1,panCK,0.39639312,0.89340174 +ehret_2025_p2,Arg1,0.123137325,0.31382924 +ehret_2025_p2,CD11c,0.035553634,0.17119572 +ehret_2025_p2,CD137,0.021260113,0.11781593 +ehret_2025_p2,CD15,0.12839702,0.34337214 +ehret_2025_p2,CD163,0.014385548,0.105268635 +ehret_2025_p2,CD20,0.011140515,0.092995666 +ehret_2025_p2,CD3,0.029466629,0.16078126 +ehret_2025_p2,CD31,0.08098116,0.28784463 +ehret_2025_p2,CD38,0.04969145,0.1891954 +ehret_2025_p2,CD4,0.06844379,0.22905795 +ehret_2025_p2,CD40,0.033963602,0.15682563 +ehret_2025_p2,CD45,0.08511434,0.29515797 +ehret_2025_p2,CD56,0.024543447,0.1350456 +ehret_2025_p2,CD68,0.038309284,0.1913009 +ehret_2025_p2,CD7,0.028858278,0.1578329 +ehret_2025_p2,CD73,0.040758222,0.17131673 +ehret_2025_p2,CD79A,0.028080128,0.1426543 +ehret_2025_p2,CD8a,0.025865348,0.16958788 +ehret_2025_p2,CTLA-4,0.014548732,0.10131066 +ehret_2025_p2,Carbonic Anhydrase,0.11176929,0.27878088 +ehret_2025_p2,Caveolin-1,0.11336519,0.3027744 +ehret_2025_p2,DNA1,1.1350152,1.3994559 +ehret_2025_p2,DNA2,1.4003409,1.661258 +ehret_2025_p2,FOXP3,0.019976223,0.11498854 +ehret_2025_p2,FSP1,0.05481966,0.19863024 +ehret_2025_p2,GrzB,0.02206797,0.12885782 +ehret_2025_p2,HLA-ABC,0.21304135,0.40987292 +ehret_2025_p2,HLADR,0.1156424,0.33790055 +ehret_2025_p2,Ido1,0.029890878,0.13226254 +ehret_2025_p2,Ki67,0.33723038,0.7215448 +ehret_2025_p2,LAG3,0.011081462,0.08820556 +ehret_2025_p2,PD1,0.012185095,0.09230355 +ehret_2025_p2,PDL1,0.037566926,0.15509321 +ehret_2025_p2,Podoplanin,0.14797577,0.34463376 +ehret_2025_p2,SMA,0.17614776,0.4679049 +ehret_2025_p2,TBET,0.037884425,0.16236232 +ehret_2025_p2,TCF1/7,0.025077071,0.12837645 +ehret_2025_p2,TMEM173,0.19727589,0.41627413 +ehret_2025_p2,TOX2,0.03995441,0.16795972 +ehret_2025_p2,Tim3,0.045926705,0.18110146 +ehret_2025_p2,VISTA,0.026965348,0.1388307 +ehret_2025_p2,Vimentin,0.1453613,0.44047573 +ehret_2025_p2,c-Caspase3c-PARP,0.15657905,0.33445317 +ehret_2025_p2,panCK,0.15641391,0.3868733 +einhaus_2025,CD11b,0.087288015,0.26355144 +einhaus_2025,CD11c,0.05051722,0.2078926 +einhaus_2025,CD14,0.12545145,0.3174227 +einhaus_2025,CD15,0.0082876375,0.08529091 +einhaus_2025,CD16,0.081461035,0.24964602 +einhaus_2025,CD163,0.073930584,0.24724239 +einhaus_2025,CD20,0.031878162,0.15633114 +einhaus_2025,CD206,0.12332642,0.31397146 +einhaus_2025,CD209,0.11467376,0.30049375 +einhaus_2025,CD3,0.071259394,0.24444604 +einhaus_2025,CD31,0.12936287,0.37786326 +einhaus_2025,CD36,0.07791061,0.25911057 +einhaus_2025,CD4,0.07654491,0.24374655 +einhaus_2025,CD44,0.17549214,0.40990603 +einhaus_2025,CD45,0.3391297,0.5712611 +einhaus_2025,CD45RA,0.0872133,0.2785476 +einhaus_2025,CD56,0.16626684,0.37613666 +einhaus_2025,CD68,0.1704134,0.38763544 +einhaus_2025,CD8a,0.065625906,0.24669188 +einhaus_2025,Collagen Type I,0.17909463,0.4398937 +einhaus_2025,DNA1,1.1103865,1.3068688 +einhaus_2025,DNA2,1.3944235,1.5433066 +einhaus_2025,Ecad,0.11539788,0.312092 +einhaus_2025,FOXP3,0.06259502,0.22023316 +einhaus_2025,GrzB,0.118518576,0.35398954 +einhaus_2025,HLADR,0.13599335,0.34491098 +einhaus_2025,Histone H3,1.336593,1.4304906 +einhaus_2025,Ki67,0.048610497,0.2102037 +einhaus_2025,SMA,0.25611123,0.5371329 +einhaus_2025,VEGF,0.10640374,0.29537392 +einhaus_2025,Vimentin,0.26851925,0.48771462 +einhaus_2025,pCREB,0.2629931,0.47841227 +einhaus_2025,pERK,0.028303644,0.14944418 +einhaus_2025,pMAPKAPK2,0.13591437,0.3351153 +einhaus_2025,pNFkB,0.1831116,0.40431267 +einhaus_2025,pS6,0.44894278,0.70839894 +einhaus_2025,pSTAT1,0.049103465,0.19880874 +einhaus_2025,pSTAT3,0.056556802,0.2107591 +einhaus_2025,panCK,0.40095595,0.73292756 +einhaus_2025,pp38,0.057254527,0.20780146 +haley_glio,B2M,0.92697287,0.6550664 +haley_glio,CD109,0.6531552,0.46943122 +haley_glio,CD11b,0.45448232,0.3795595 +haley_glio,CD11c,0.36551487,0.32941794 +haley_glio,CD14,0.6142885,0.46943548 +haley_glio,CD16,0.206376,0.22892255 +haley_glio,CD163,0.22141556,0.34261933 +haley_glio,CD206,0.26835832,0.21378914 +haley_glio,CD235ab,0.1023185,0.14426063 +haley_glio,CD3,0.32721892,0.2559408 +haley_glio,CD31,0.10457045,0.13707419 +haley_glio,CD4,0.28380924,0.21066174 +haley_glio,CD44,0.6812569,0.54745346 +haley_glio,CD45,0.19187082,0.15376261 +haley_glio,CD66b,0.15228339,0.11666006 +haley_glio,CD68,0.26704773,0.2090153 +haley_glio,CD74,0.40166172,0.46251303 +haley_glio,CD8a,0.37095413,0.2893752 +haley_glio,DNA1,0.8736806,0.9562651 +haley_glio,DNA2,1.0916985,1.1183983 +haley_glio,Fibrinogen,0.30990574,0.3001865 +haley_glio,GFAP,1.4034036,1.1108476 +haley_glio,GLUT1,1.6371323,1.239787 +haley_glio,GrzB,0.2684667,0.23488255 +haley_glio,HIF1a,0.43968868,0.35095584 +haley_glio,HLADR,0.74422413,0.6815611 +haley_glio,IBA1,0.6290147,0.6897317 +haley_glio,Ki67,0.22830053,0.27351648 +haley_glio,Met,0.7135092,0.521702 +haley_glio,Nestin,1.574611,1.1430076 +haley_glio,Olig2,0.37411746,0.3317144 +haley_glio,P2RY12,0.33924577,0.33733085 +haley_glio,S100B,1.562471,1.1637048 +haley_glio,SMA,0.13167362,0.22972375 +haley_glio,Sox2,0.39371654,0.38217887 +haley_glio,TMEM119,0.33236462,0.36040717 +haley_glio,VISTA,0.21730721,0.22415528 +haley_glio,Vimentin,1.2814283,0.9676178 +haley_glio,pERK,0.48461476,0.4437556 +haley_glio,panCK,0.5504522,0.40743992 +hn,B2M,0.84232295,0.8626283 +hn,CD11c,0.29415178,0.55668247 +hn,CD14,0.8273755,0.9277423 +hn,CD15,0.31846204,0.6963053 +hn,CD16,0.24774912,0.5189994 +hn,CD163,0.14831343,0.44202396 +hn,CD20,0.2152261,0.44185543 +hn,CD206,0.19798046,0.49671945 +hn,CD27,0.24632728,0.46096867 +hn,CD3,0.28445065,0.5287083 +hn,CD303,0.29346558,0.5087772 +hn,CD33,0.5667491,0.7376138 +hn,CD38,0.15964596,0.37374017 +hn,CD4,0.4551276,0.64426214 +hn,CD40,0.36598626,0.5668136 +hn,CD45RA,0.2318593,0.44001997 +hn,CD45RO,0.7319288,0.83537716 +hn,CD68,0.3920593,0.64434975 +hn,CD7,0.26858342,0.53778714 +hn,CD8a,0.24620691,0.537587 +hn,Carbonic Anhydrase,0.43578085,0.66625893 +hn,DNA1,2.0309799,1.6299753 +hn,DNA2,2.3921769,1.8398364 +hn,Ecad,1.1058418,1.189054 +hn,FOXP3,0.18265896,0.41564202 +hn,GrzB,0.3793961,0.5727175 +hn,HLADR,0.7151413,0.9919897 +hn,Histone H3,1.0435237,1.0599073 +hn,ICOS,0.23863749,0.46029037 +hn,Ido1,0.27403584,0.488564 +hn,Ki67,0.58166176,1.0173593 +hn,LAG3,0.07987878,0.25373393 +hn,MPO,0.10601735,0.29269382 +hn,PD1,0.11674425,0.31026167 +hn,PDGFRb,0.21141292,0.45952335 +hn,PDL1,0.26068738,0.4757169 +hn,SMA,0.13819826,0.3883654 +hn,TCF7,0.27986005,0.49055004 +hn,VISTA,0.31455418,0.55212784 +hn,cl.PARP,0.044622574,0.1959962 +hoch-protein,CD11b,0.48032123,0.6124523 +hoch-protein,CD11c,0.48245633,0.64354455 +hoch-protein,CD15,0.1445535,0.331025 +hoch-protein,CD19,0.31890634,0.49992546 +hoch-protein,CD20,0.16015913,0.37605846 +hoch-protein,CD206,0.14166333,0.39853376 +hoch-protein,CD3,0.20452939,0.41544065 +hoch-protein,CD303,0.08512552,0.25405937 +hoch-protein,CD36,0.4984883,0.7064436 +hoch-protein,CD4,0.3282304,0.5245233 +hoch-protein,CD45,0.33152172,0.49435186 +hoch-protein,CD45RA,0.57023525,0.65712094 +hoch-protein,CD45RO,0.7412932,0.7984477 +hoch-protein,CD68,0.31027862,0.54064393 +hoch-protein,CD7,0.18917115,0.39514956 +hoch-protein,CD8a,0.2230382,0.4706146 +hoch-protein,CXCR2,0.29180795,0.45909187 +hoch-protein,Caveolin-1,0.5115437,0.636152 +hoch-protein,Collagen Type I,0.31689104,0.5180855 +hoch-protein,DNA1,2.1763346,1.9480253 +hoch-protein,DNA2,2.472821,2.1729627 +hoch-protein,FOXP3,0.2535219,0.4304799 +hoch-protein,GrzB,0.21390867,0.4176933 +hoch-protein,HLADR,0.58032393,0.8747887 +hoch-protein,Histone H3,1.4342092,1.392438 +hoch-protein,Histone H3 k27me3,0.7890759,0.9693851 +hoch-protein,ICOS,0.099636056,0.2785001 +hoch-protein,Ido1,0.38060564,0.5327327 +hoch-protein,Ki67,0.18690768,0.4286361 +hoch-protein,Ki67-pt198,0.28789958,0.4862421 +hoch-protein,MITF,0.14519678,0.32187843 +hoch-protein,MPO,0.053907562,0.20213746 +hoch-protein,PD1,0.22447273,0.4112935 +hoch-protein,PDL1,0.4386594,0.58380324 +hoch-protein,S100B,0.61417437,0.69978553 +hoch-protein,SMA,0.57744026,0.77209216 +hoch-protein,Sox10,0.5320484,0.6347482 +hoch-protein,Sox9,0.11302565,0.2963805 +hoch-protein,TCF7,0.27125275,0.45433864 +hoch-protein,TOX,0.50968486,0.6157346 +hoch-protein,Vimentin,0.98303187,0.94362146 +hoch-protein,beta-Catenin,0.89047426,0.88480866 +hoch-protein,cl.PARP,0.06326303,0.21938078 +hoch-protein,p75,0.21527258,0.39915976 +hoch-protein,pERK,0.12477478,0.32107598 +hoch-protein,pS6,0.5484183,0.7623343 +hoch-rna,B2M,0.657206,0.72837055 +hoch-rna,CCL18,0.13130663,0.31720147 +hoch-rna,CCL19,0.2086938,0.40313828 +hoch-rna,CCL2,0.1416232,0.33463973 +hoch-rna,CCL22,0.1451178,0.33182687 +hoch-rna,CCL4,0.10303637,0.27924427 +hoch-rna,CCL8,0.1773423,0.365722 +hoch-rna,CCR2,0.87861055,0.8242187 +hoch-rna,CD134,0.051215608,0.19133836 +hoch-rna,CD15,0.08239481,0.26590034 +hoch-rna,CD163,0.13969886,0.3925066 +hoch-rna,CD3,0.35777363,0.5582537 +hoch-rna,CD31,0.18416291,0.38352042 +hoch-rna,CD38,0.29445985,0.48016512 +hoch-rna,CD68,0.23943149,0.48043376 +hoch-rna,CD8a,0.47183698,0.5863741 +hoch-rna,CK5,0.07340767,0.24590987 +hoch-rna,CXCL10,0.123755015,0.3190362 +hoch-rna,CXCL12,0.12536815,0.31275907 +hoch-rna,CXCL13,0.1531146,0.3462503 +hoch-rna,CXCL8,0.14517297,0.3320234 +hoch-rna,CXCL9,0.20606224,0.40542 +hoch-rna,Cadherin-11,0.31395018,0.5224563 +hoch-rna,DNA1,1.9753599,1.7071172 +hoch-rna,DNA2,2.3034444,1.92087 +hoch-rna,DapB,0.11631884,0.29451776 +hoch-rna,FAP,0.14143534,0.34367278 +hoch-rna,GLUT1,0.44381118,0.60647184 +hoch-rna,HLADR,1.492162,1.2621177 +hoch-rna,Histone H3,1.8100928,1.4877335 +hoch-rna,LAG3,0.048884638,0.18738705 +hoch-rna,MART1,0.34517848,0.50006855 +hoch-rna,MPO,0.12505335,0.29908934 +hoch-rna,PD1,0.032132387,0.15125652 +hoch-rna,PDL1,1.0875529,0.95767075 +hoch-rna,S100B,0.8305788,0.8150838 +hoch-rna,SMA,0.7890759,0.7861015 +hoch-rna,Sox10,0.5581483,0.64554393 +hoch-rna,Vimentin,1.1667637,0.9366643 +hoch-rna,cl.PARP,0.10731677,0.2790905 +hoch-rna,pRB,1.0735041,1.1260431 +jackson-basel,CD20,0.03203395,0.14933643 +jackson-basel,CD3,0.027598437,0.14689226 +jackson-basel,CD44,0.86953,1.171038 +jackson-basel,CD45,0.048948787,0.18873025 +jackson-basel,CD68,0.076832354,0.26685086 +jackson-basel,CK14,0.042617332,0.17732495 +jackson-basel,CK19,0.12429212,0.33071128 +jackson-basel,CK5,0.034137744,0.15741834 +jackson-basel,CK7,0.0542035,0.19988796 +jackson-basel,CK8-18,0.17952853,0.42191496 +jackson-basel,Carbonic Anhydrase,0.14815807,0.33228475 +jackson-basel,DNA1,0.4230957,0.7579245 +jackson-basel,DNA2,0.57175696,0.9439096 +jackson-basel,EGFR,0.08582615,0.24749403 +jackson-basel,EPCAM,0.10600772,0.2724353 +jackson-basel,ER,0.06443376,0.21332705 +jackson-basel,Ecad,0.33937725,0.5935064 +jackson-basel,Fibronectin,0.3897048,0.7017485 +jackson-basel,GATA3,0.12273324,0.31182933 +jackson-basel,HER2 (3B5),0.22015314,0.45070943 +jackson-basel,Histone H3,0.6345758,0.92756003 +jackson-basel,Histone H3 - p,0.06676734,0.22568597 +jackson-basel,Histone H3 - t,0.12476008,0.3167687 +jackson-basel,Ki67,0.06160536,0.22028401 +jackson-basel,PR,0.17445083,0.37605816 +jackson-basel,SMA,0.17109677,0.49829653 +jackson-basel,Slug,0.06379098,0.20816438 +jackson-basel,Sox9,0.13331561,0.35090557 +jackson-basel,Twist,0.058084533,0.20326768 +jackson-basel,Vimentin,0.23540013,0.5863996 +jackson-basel,beta-Catenin,0.038956158,0.16450284 +jackson-basel,c-Caspase3c-PARP,0.17124808,0.36308768 +jackson-basel,c-Myc,0.09970683,0.26847738 +jackson-basel,mTOR,0.18904926,0.38787487 +jackson-basel,p53,0.022884382,0.122156166 +jackson-basel,pERK,0.142012,0.42854887 +jackson-basel,pS6,0.39575028,0.6113909 +jackson-basel,panCK,0.47522625,0.86510015 +jackson-basel,vWF,0.033390164,0.15723994 +jackson-zurich,CD20,0.06742663,0.21103334 +jackson-zurich,CD3,0.063035734,0.20587625 +jackson-zurich,CD44,1.1005716,1.296108 +jackson-zurich,CD45,0.107611604,0.27295992 +jackson-zurich,CD68,0.12507191,0.33600506 +jackson-zurich,CK14,0.064850025,0.22144268 +jackson-zurich,CK19,0.21415964,0.4603852 +jackson-zurich,CK5,0.06381801,0.22435206 +jackson-zurich,CK7,0.13346453,0.34443536 +jackson-zurich,CK8-18,0.27780867,0.54311395 +jackson-zurich,Carbonic Anhydrase,0.2101069,0.3834356 +jackson-zurich,DNA1,0.40174338,0.67576516 +jackson-zurich,DNA2,0.55926126,0.8541494 +jackson-zurich,EGFR,0.15769807,0.33088854 +jackson-zurich,EPCAM,0.21452503,0.383127 +jackson-zurich,ER,0.14054503,0.3277669 +jackson-zurich,Ecad,0.43711343,0.675547 +jackson-zurich,Fibronectin,0.64111793,0.8924298 +jackson-zurich,GATA3,0.24283415,0.47011447 +jackson-zurich,HER2 (3B5),0.27282006,0.47958097 +jackson-zurich,Histone H3,0.65744543,0.85328627 +jackson-zurich,Histone H3 - p,0.103325896,0.27540728 +jackson-zurich,Histone H3 - t,0.17936589,0.36450547 +jackson-zurich,Ki67,0.10616658,0.29554367 +jackson-zurich,PR,0.23401423,0.43674585 +jackson-zurich,SMA,0.24559195,0.60272473 +jackson-zurich,Slug,0.1567912,0.33068448 +jackson-zurich,Sox9,0.22129233,0.42832905 +jackson-zurich,Twist,0.13945185,0.3160941 +jackson-zurich,Vimentin,0.35288045,0.7288304 +jackson-zurich,beta-Catenin,0.05589225,0.19327919 +jackson-zurich,c-Caspase3c-PARP,0.17611195,0.3568934 +jackson-zurich,c-Myc,0.23448497,0.41030332 +jackson-zurich,mTOR,0.31732926,0.5116558 +jackson-zurich,p53,0.04829538,0.17332897 +jackson-zurich,pERK,0.10852048,0.33636647 +jackson-zurich,pS6,0.45661455,0.6246253 +jackson-zurich,panCK,0.5324664,0.8858225 +jackson-zurich,vWF,0.05716719,0.2044837 +kucukkose,AKT,0.5406417,0.59409994 +kucukkose,CD14,0.51643807,0.62406856 +kucukkose,CD16,0.19683911,0.4406164 +kucukkose,CD20,0.020983033,0.12997398 +kucukkose,CD3,0.17109354,0.3764187 +kucukkose,CD4,0.121349856,0.30781016 +kucukkose,CD45,0.20182687,0.410621 +kucukkose,CD45RO,0.17097677,0.39552298 +kucukkose,CD68,0.20748164,0.44517642 +kucukkose,CD8a,0.12364023,0.3398465 +kucukkose,CTLA-4,0.20955203,0.3797736 +kucukkose,ERK,0.73226035,0.7329029 +kucukkose,Ecad,0.4196799,0.5616302 +kucukkose,FOXP3,0.12342062,0.30863655 +kucukkose,GrzB,0.22184049,0.4348303 +kucukkose,Histone H3,1.9820175,1.6808537 +kucukkose,ICOS,0.084775895,0.25857994 +kucukkose,IFNy,0.20056495,0.39192006 +kucukkose,IL10,0.545947,0.60119194 +kucukkose,IL17a,0.16153744,0.36276942 +kucukkose,Ki67,0.30809894,0.51263434 +kucukkose,LAG3,0.052056003,0.1991259 +kucukkose,NFkb,1.0996958,0.94006217 +kucukkose,PD1,0.029820235,0.14612722 +kucukkose,PDL1,0.08561221,0.2526536 +kucukkose,SMA,0.42264655,0.7875097 +kucukkose,TBET,0.2250907,0.41891968 +kucukkose,beta-Catenin,0.38257152,0.5094172 +kucukkose,c-Caspase3c-PARP,0.12597534,0.31868201 +kucukkose,pAKT,0.14123294,0.34507883 +kucukkose,pERK,0.078859575,0.25340283 +kucukkose,pS6,0.39237127,0.61021906 +kucukkose,pSTAT3,0.18463387,0.38784656 +kucukkose,panCK,0.48672578,0.64366484 +meyer,AR,0.054222994,0.20626669 +meyer,Bcl2,0.048298605,0.19106753 +meyer,CD11c,0.07291931,0.25288567 +meyer,CD15,0.086885296,0.27568892 +meyer,CD20,0.10001265,0.2905801 +meyer,CD3,0.082382455,0.26173955 +meyer,CD31-vWF,0.25552812,0.49416506 +meyer,CD4,0.14456613,0.35501274 +meyer,CD44,0.6587275,0.97144 +meyer,CD45,0.34360218,0.56890625 +meyer,CD68,0.08531452,0.28206316 +meyer,CD8a,0.06740681,0.24462941 +meyer,CK14,0.05630856,0.20155674 +meyer,CK5,0.10877768,0.324737 +meyer,CK7,0.10808251,0.33063087 +meyer,CK8-18,0.092834614,0.2893786 +meyer,Carbonic Anhydrase,0.19241624,0.41006908 +meyer,DNA1,1.0898186,1.4979576 +meyer,DNA2,1.2892791,1.7228844 +meyer,EGFR,0.08431662,0.25901082 +meyer,ER,0.1243908,0.3267336 +meyer,Ecad,0.25765702,0.5401659 +meyer,Fibronectin,0.56438076,0.8629648 +meyer,GATA3,0.06920134,0.23417774 +meyer,HER2 (3B5),0.13928486,0.3571338 +meyer,HLADR,0.27462792,0.5534639 +meyer,Histone H3,0.55197257,0.8254397 +meyer,Histone H3 - p,0.09735103,0.2904158 +meyer,Histone H3 k27me3,0.16570967,0.37566218 +meyer,Ki67,0.14518753,0.4159391 +meyer,PR,0.06000295,0.21746655 +meyer,SMA,0.15336725,0.46837118 +meyer,Vimentin,0.39781976,0.80247283 +meyer,c-Caspase3c-PARP,0.084522285,0.2637129 +meyer,c-Myc,0.13067928,0.32533884 +meyer,mTOR,0.085744426,0.27034894 +meyer,p53,0.091126,0.2706601 +meyer,pS6,0.121924534,0.3304209 +meyer,panCK,0.40120825,0.80839676 +nsclc2-panel1,B2M,0.7810954,0.85236186 +nsclc2-panel1,CD11c,0.31339654,0.6127733 +nsclc2-panel1,CD14,0.95180315,0.98369426 +nsclc2-panel1,CD15,0.4404363,0.814682 +nsclc2-panel1,CD16,0.2176149,0.48026332 +nsclc2-panel1,CD163,0.18413824,0.5116423 +nsclc2-panel1,CD20,0.311165,0.58254784 +nsclc2-panel1,CD206,0.30699313,0.6659219 +nsclc2-panel1,CD27,0.21828239,0.44896442 +nsclc2-panel1,CD3,0.2863068,0.5645606 +nsclc2-panel1,CD303,0.2657507,0.49265897 +nsclc2-panel1,CD33,0.6679901,0.80807513 +nsclc2-panel1,CD38,0.124623634,0.35337576 +nsclc2-panel1,CD4,0.50054073,0.6966742 +nsclc2-panel1,CD40,0.31754366,0.5544668 +nsclc2-panel1,CD45RA,0.2767068,0.5268935 +nsclc2-panel1,CD45RO,0.76309454,0.944331 +nsclc2-panel1,CD68,0.49630076,0.80595607 +nsclc2-panel1,CD7,0.3218065,0.6133559 +nsclc2-panel1,CD8a,0.24563155,0.6096131 +nsclc2-panel1,Carbonic Anhydrase,0.38552853,0.6055642 +nsclc2-panel1,DNA1,1.9040612,1.5781254 +nsclc2-panel1,DNA2,2.2623062,1.7927098 +nsclc2-panel1,Ecad,0.95261467,1.1005775 +nsclc2-panel1,FOXP3,0.14228173,0.3579211 +nsclc2-panel1,GrzB,0.3700204,0.5915228 +nsclc2-panel1,HLADR,0.92045254,1.1350536 +nsclc2-panel1,Histone H3,0.77395874,0.87552017 +nsclc2-panel1,ICOS,0.17509118,0.40246168 +nsclc2-panel1,Ido1,0.3415125,0.5772894 +nsclc2-panel1,Ki67,0.20989831,0.53940207 +nsclc2-panel1,LAG3,0.044297215,0.19248961 +nsclc2-panel1,MPO,0.067843586,0.23118055 +nsclc2-panel1,PD1,0.11582511,0.3279922 +nsclc2-panel1,PDGFRb,0.27691993,0.53994364 +nsclc2-panel1,PDL1,0.24370576,0.48283783 +nsclc2-panel1,SMA,0.12203869,0.3523208 +nsclc2-panel1,TCF7,0.3137136,0.5517466 +nsclc2-panel1,VISTA,0.21459009,0.46645528 +nsclc2-panel1,cl.PARP,0.04692895,0.20356512 +nsclc2-panel2,Arg1,0.07067018,0.25940028 +nsclc2-panel2,Bcl2,0.07781985,0.2491396 +nsclc2-panel2,CD10,0.13155553,0.3474276 +nsclc2-panel2,CD11b,0.04951313,0.23592916 +nsclc2-panel2,CD11c,0.08367373,0.3041375 +nsclc2-panel2,CD134,0.005468183,0.070878975 +nsclc2-panel2,CD137,0.01553541,0.11584364 +nsclc2-panel2,CD146,0.057433523,0.23755515 +nsclc2-panel2,CD15,0.152682,0.36335734 +nsclc2-panel2,CD155,0.18503053,0.4149914 +nsclc2-panel2,CD20,0.032192007,0.16848364 +nsclc2-panel2,CD204,0.083115146,0.33963442 +nsclc2-panel2,CD209,0.02358083,0.1575658 +nsclc2-panel2,CD3,0.06658651,0.2698827 +nsclc2-panel2,CD31,0.031461317,0.19771808 +nsclc2-panel2,CD45,0.13515571,0.39480114 +nsclc2-panel2,CD56,0.0824923,0.20748103 +nsclc2-panel2,CD66b,0.012980784,0.10986266 +nsclc2-panel2,CD7,0.065033816,0.25072232 +nsclc2-panel2,CD73,0.10585461,0.30911115 +nsclc2-panel2,CD8a,0.067598745,0.319429 +nsclc2-panel2,CXCL13,0.049893837,0.22198339 +nsclc2-panel2,CXCL8,0.012470393,0.0999899 +nsclc2-panel2,DNA1,1.4918557,1.4659138 +nsclc2-panel2,DNA2,1.8061417,1.6850433 +nsclc2-panel2,EGFR,0.1143295,0.30985463 +nsclc2-panel2,EOMES,0.009812394,0.092255786 +nsclc2-panel2,Ecad,0.4620419,0.81078625 +nsclc2-panel2,FOXP3,0.012975078,0.10792715 +nsclc2-panel2,GATA3,0.016913267,0.11925633 +nsclc2-panel2,GITR,0.02621667,0.15520662 +nsclc2-panel2,Histone H3,0.76720965,0.94140655 +nsclc2-panel2,IgG,0.109489456,0.30949327 +nsclc2-panel2,Ki67,0.16893591,0.39324766 +nsclc2-panel2,LAMP3,0.015521408,0.12160726 +nsclc2-panel2,MMP9,0.022233434,0.15655819 +nsclc2-panel2,NKG2A,0.016775634,0.12241124 +nsclc2-panel2,PD1,0.035952095,0.178487 +nsclc2-panel2,Podoplanin,0.085206375,0.27634192 +nsclc2-panel2,SMA,0.07423703,0.2670923 +nsclc2-panel2,TBET,0.015829764,0.11669018 +nsclc2-panel2,Tim3,0.07510293,0.26936418 +nsclc2-panel2,p53,0.039940666,0.16555272 +nsclc2-panel2,panCK,0.35380155,0.59986806 +ohara,CD11b,0.09640341,0.26498458 +ohara,CD11c,0.103659794,0.29280588 +ohara,CD16,0.08427084,0.29110724 +ohara,CD163,0.06488678,0.2825008 +ohara,CD20,0.016548252,0.10487571 +ohara,CD206,0.10378433,0.31239635 +ohara,CD3,0.074894354,0.24857679 +ohara,CD31,0.123983726,0.35111457 +ohara,CD4,0.08840991,0.2604653 +ohara,CD45,0.09232381,0.24018808 +ohara,CD45RO,0.18284701,0.41327244 +ohara,CD68,0.31570816,0.60224503 +ohara,CD8a,0.13943957,0.33601263 +ohara,CK5,0.4232941,0.7315434 +ohara,Collagen Type I,0.3048947,0.613758 +ohara,DNA1,1.3965206,1.8005612 +ohara,DNA2,1.6310626,2.0469162 +ohara,Ecad,0.44403327,0.7102655 +ohara,FOXP3,0.08084431,0.23551649 +ohara,GATA3,0.14696649,0.34613726 +ohara,GrzB,0.11242513,0.3396354 +ohara,Histone H3,0.6531263,0.99876386 +ohara,Ki67,0.09760929,0.33955792 +ohara,PD1,0.04092618,0.16700076 +ohara,PDL1,0.087445445,0.2535075 +ohara,SMA,0.21534686,0.6053147 +ohara,Vimentin,0.5275408,0.9165643 +ohara,panCK,0.24299325,0.49522635 +rigamonti_2025,Arg1,0.11728295,0.31465045 +rigamonti_2025,C1Qa,0.097527154,0.28487724 +rigamonti_2025,CCR4,0.13804103,0.33985034 +rigamonti_2025,CD14,0.4162776,0.660328 +rigamonti_2025,CD16,0.13035887,0.34767735 +rigamonti_2025,CD163,0.048909463,0.21762551 +rigamonti_2025,CD20,0.011438127,0.09174012 +rigamonti_2025,CD3,0.13508403,0.34906995 +rigamonti_2025,CD31,0.07573254,0.26389122 +rigamonti_2025,CD4,0.0669469,0.23869197 +rigamonti_2025,CD45,0.14904097,0.4038707 +rigamonti_2025,CD66a,0.3089502,0.6508433 +rigamonti_2025,CD68,0.27729777,0.5747501 +rigamonti_2025,CD8a,0.06793486,0.25691277 +rigamonti_2025,CXCL4,0.11797726,0.31197354 +rigamonti_2025,Collagen Type I,0.17212562,0.44952735 +rigamonti_2025,DNA1,0.78137094,1.1066957 +rigamonti_2025,DNA2,0.97940445,1.3211085 +rigamonti_2025,FOXP3,0.10535822,0.29375616 +rigamonti_2025,GrzB,0.13760675,0.38313195 +rigamonti_2025,HLADR,0.3222919,0.62378436 +rigamonti_2025,Ki67,0.20184828,0.47635204 +rigamonti_2025,LAMP3,0.305711,0.5575512 +rigamonti_2025,MS4A4A,0.010809938,0.08895681 +rigamonti_2025,S100a8,0.6983759,1.1356529 +rigamonti_2025,SMA,0.22188078,0.5284744 +rigamonti_2025,Tim3,0.32362366,0.54791903 +rigamonti_2025,Vimentin,0.30745956,0.6594157 +rigamonti_2025,panCK,0.6668426,1.0704694 +steenbuck-immune,ATP5A,0.6524907,0.8112574 +steenbuck-immune,Arg1,0.1380058,0.3424694 +steenbuck-immune,CD103,0.21180491,0.40343857 +steenbuck-immune,CD11b,0.10754653,0.32465577 +steenbuck-immune,CD11c,0.07676834,0.2559664 +steenbuck-immune,CD15,0.2896705,0.5631189 +steenbuck-immune,CD16,0.06589634,0.24238992 +steenbuck-immune,CD163,0.072528,0.30117857 +steenbuck-immune,CD20,0.14043476,0.34830397 +steenbuck-immune,CD204,0.08486313,0.33418158 +steenbuck-immune,CD206,0.0632894,0.26465958 +steenbuck-immune,CD27,0.14236833,0.3336346 +steenbuck-immune,CD3,0.032374207,0.18393187 +steenbuck-immune,CD303,0.3070556,0.47595018 +steenbuck-immune,CD4,0.16826054,0.36395934 +steenbuck-immune,CD45RA,0.030360492,0.15870608 +steenbuck-immune,CD45RO,0.07731427,0.26576805 +steenbuck-immune,CD54,0.20303853,0.42077065 +steenbuck-immune,CD56,0.16931482,0.42863223 +steenbuck-immune,CD57,0.06777329,0.26706624 +steenbuck-immune,CD66b,0.038701374,0.18749312 +steenbuck-immune,CD7,0.081477396,0.26476973 +steenbuck-immune,CD73,0.18442677,0.38163581 +steenbuck-immune,CD8a,0.05231621,0.2411446 +steenbuck-immune,Caveolin-1,0.29503933,0.5533571 +steenbuck-immune,Cirate Synthase,1.0071194,0.8716036 +steenbuck-immune,DNA1,1.2869184,1.1173843 +steenbuck-immune,DNA2,1.6353647,1.2954105 +steenbuck-immune,FOXP3,0.11255594,0.2973802 +steenbuck-immune,GCG,0.33757806,0.54157627 +steenbuck-immune,GrzB,0.2413658,0.42682886 +steenbuck-immune,HLADR,0.2867266,0.56705433 +steenbuck-immune,Hexokinase I,0.19204974,0.44929564 +steenbuck-immune,Histone H3,1.3067907,1.0698206 +steenbuck-immune,Insulin,0.13628651,0.41808918 +steenbuck-immune,Ki67,0.14200844,0.34119737 +steenbuck-immune,LDHA,0.43481922,0.55353063 +steenbuck-immune,MPO,0.055947974,0.21420135 +steenbuck-immune,NKX6-1,0.14379834,0.3574058 +steenbuck-immune,PD1,0.21877924,0.42569256 +steenbuck-immune,PDX1,0.39954188,0.6128653 +steenbuck-immune,SMA,0.43214118,0.6287976 +steenbuck-immune,SYP,0.2662728,0.6069506 +steenbuck-immune,Somatostatin,0.061113223,0.2905201 +steenbuck-immune,TMEM173,0.18721844,0.4227247 +steenbuck-immune,Tim3,0.2737617,0.46118218 +steenbuck-immune,Vimentin,0.70045364,0.88632536 +steenbuck-islets,ADAR1,0.9897236,0.9647835 +steenbuck-islets,AMY1A,1.278372,1.078825 +steenbuck-islets,ARX,0.41866055,0.5903109 +steenbuck-islets,ATPIF1,1.3359278,1.0748515 +steenbuck-islets,B2M,0.633718,0.684801 +steenbuck-islets,Biotin,2.0687437,0.9529763 +steenbuck-islets,C-peptide,0.3138822,0.6255017 +steenbuck-islets,C3,0.7752663,0.7412992 +steenbuck-islets,CD155,1.0854656,0.79373294 +steenbuck-islets,CD163,0.11157688,0.36787367 +steenbuck-islets,CD3,0.2448785,0.42879033 +steenbuck-islets,CD9,0.96994305,0.85932434 +steenbuck-islets,CFTR,0.16589153,0.44596893 +steenbuck-islets,CHGA,0.43340066,0.70031637 +steenbuck-islets,CK19,0.6473748,1.0923265 +steenbuck-islets,Carbonic Anhydrase,0.63422734,0.69451624 +steenbuck-islets,Caveolin-1,0.4059943,0.61959153 +steenbuck-islets,DNA1,1.4257544,1.058985 +steenbuck-islets,DNA2,1.8288996,1.1923661 +steenbuck-islets,ERN1,0.96580637,0.7312952 +steenbuck-islets,Ecad,2.1964207,1.2713307 +steenbuck-islets,FOXA2,0.79827577,0.77559274 +steenbuck-islets,GCG,0.54068035,0.7007971 +steenbuck-islets,GHRL,0.12693901,0.2930767 +steenbuck-islets,HLA-ABC,1.2476661,0.94359934 +steenbuck-islets,Histone H3,1.5863191,1.0567737 +steenbuck-islets,IAPP,0.5157257,0.67089224 +steenbuck-islets,Insulin,0.2834838,0.5892914 +steenbuck-islets,Ki67,0.20501803,0.4035962 +steenbuck-islets,MX1,1.2727709,0.7737261 +steenbuck-islets,NFkb,1.3268938,0.78660375 +steenbuck-islets,NKX2.2,0.280245,0.5064175 +steenbuck-islets,NKX6-1,0.26736784,0.4721437 +steenbuck-islets,PCSK1,0.4396896,0.5796396 +steenbuck-islets,PCSK2,0.53607565,0.85621 +steenbuck-islets,PDL1,2.1908095,0.9269259 +steenbuck-islets,PDX1,0.5278312,0.6865653 +steenbuck-islets,PPY,0.080596335,0.22793074 +steenbuck-islets,Pax6,0.43280455,0.6017129 +steenbuck-islets,Proglucagon,0.33333763,0.71617013 +steenbuck-islets,Proinsulin,0.42730525,0.80652195 +steenbuck-islets,SYP,0.5249277,0.81799906 +steenbuck-islets,Somatostatin,0.12752123,0.42021853 +steenbuck-islets,TXNIP,1.37238,1.243522 +steenbuck-islets,Vimentin,0.922467,0.91279674 +steenbuck-islets,WFS1,0.7017302,0.738711 +steenbuck-islets,XBP1,0.51704067,0.5800997 +susman,C-peptide,0.012967767,0.06392856 +susman,CA2,0.046226878,0.121627845 +susman,CD11b,0.07243174,0.1651669 +susman,CD14,0.102658875,0.20120491 +susman,CD20,0.017125582,0.07171462 +susman,CD3,0.046449725,0.12597142 +susman,CD31,0.018659001,0.0834581 +susman,CD4,0.048599623,0.12804729 +susman,CD44,0.029075304,0.10096722 +susman,CD45,0.101293266,0.22142425 +susman,CD45RO,0.056900192,0.1504067 +susman,CD56,0.055910263,0.14458628 +susman,CD57,0.017242052,0.07911182 +susman,CD68,0.12054198,0.2427502 +susman,CD8a,0.03623705,0.12189221 +susman,CD99,0.25866202,0.40203145 +susman,Collagen Type I,0.29695314,0.4647867 +susman,DNA1,1.5454178,2.1549945 +susman,DNA2,1.750642,2.40498 +susman,FOXP3,0.063177295,0.14678557 +susman,GCG,0.03694078,0.121559225 +susman,GHRL,0.027673235,0.08805629 +susman,GrzB,0.054134034,0.16160758 +susman,HLA-ABC,0.16711514,0.27534583 +susman,HLADR,0.17167859,0.3270983 +susman,Ki67,0.03272954,0.12849817 +susman,NFkb,0.03331632,0.095005974 +susman,NKX6-1,0.01892813,0.077172 +susman,Nestin,0.02473834,0.09562882 +susman,PDX1,0.06629033,0.15666915 +susman,PP,0.03318053,0.09945216 +susman,Somatostatin,0.04032324,0.12777847 +susman,beta-Actin,0.2210029,0.34227118 +susman,pS6,0.032024186,0.10711287 +susman,panCK,0.073345974,0.1925852 +xu,AXL,0.04473145,0.18763654 +xu,CCR4,0.08625021,0.24664788 +xu,CD11b,0.14384082,0.3527957 +xu,CD11c,0.16975717,0.3544171 +xu,CD134,0.07263973,0.23355304 +xu,CD14,0.49253345,0.63786 +xu,CD16,0.14938277,0.35328054 +xu,CD20,0.06138105,0.2167492 +xu,CD27,0.13398534,0.32341895 +xu,CD3,0.14696132,0.34624025 +xu,CD38,0.11250904,0.3024561 +xu,CD4,0.14279748,0.33713832 +xu,CD45,0.110961094,0.29632786 +xu,CD68,0.7733078,0.87289953 +xu,CD74,0.23070115,0.45051393 +xu,CD8a,0.14931786,0.37043622 +xu,Carbonic Anhydrase,0.3484972,0.5033844 +xu,Collagen Type I,0.543953,0.77889085 +xu,DNA1,1.218239,1.1974738 +xu,DNA2,1.5178053,1.3849472 +xu,EPCAM,0.07147028,0.23401673 +xu,Ecad,0.22415285,0.40465388 +xu,FAP,0.14911307,0.3421408 +xu,FOXP3,0.059444107,0.21288553 +xu,HLADR,0.42386323,0.68982595 +xu,ICOS,0.05580406,0.2227041 +xu,IL7a,0.29379424,0.48671332 +xu,Ido1,0.044292036,0.18858711 +xu,Ki67,0.11286357,0.33303505 +xu,LAG3,0.051427845,0.19440484 +xu,PD1,0.040555727,0.17648494 +xu,PDGFRb,0.114485644,0.3021476 +xu,PDL1,0.07726144,0.2378758 +xu,SMA,0.38910252,0.6200218 +xu,Tim3,0.08646494,0.2640964 +xu,VEGF,0.4389078,0.56927276 +xu,Vimentin,1.1780053,1.1402973 diff --git a/virtual-staining/evaluate_all.py b/virtual-staining/evaluate_all.py new file mode 100644 index 0000000..6081f7f --- /dev/null +++ b/virtual-staining/evaluate_all.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 +"""Entry point. Delegates to src.main.""" + +from src.main import main + +if __name__ == "__main__": + main() diff --git a/virtual-staining/src/__init__.py b/virtual-staining/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/virtual-staining/src/cli.py b/virtual-staining/src/cli.py new file mode 100644 index 0000000..9c6050f --- /dev/null +++ b/virtual-staining/src/cli.py @@ -0,0 +1,84 @@ +"""Command-line interface.""" + +from __future__ import annotations + +import argparse + + +def build_parser() -> argparse.ArgumentParser: + p = argparse.ArgumentParser( + description="Evaluate virtual-staining models against ground truth.", + ) + p.add_argument( + "--config", + default="config.yaml", + help="Path to YAML config file (default: config.yaml)", + ) + p.add_argument( + "--output-dir", + default=None, + help="Override output directory from config.", + ) + p.add_argument( + "--crop-size", + type=int, + nargs=2, + default=None, + metavar=("H", "W"), + help="Override crop size (default: config value or 128 128)", + ) + p.add_argument( + "--plot", + action="store_true", + default=None, + help="Enable per-pair diagnostic plots (off by default).", + ) + p.add_argument( + "--no-save", + action="store_true", + help="Skip saving CSV outputs (useful for quick checks).", + ) + p.add_argument( + "--table", + action="store_true", + help="Print per-model summary table (mean MSE | Pearson).", + ) + sub = p.add_subparsers(dest="command", help="Sub-commands") + + plot_p = sub.add_parser("plot", help="Generate box plots from results.") + plot_p.add_argument( + "--results", + default="results/per_point_results.csv", + help="Path to per_point_results.csv (default: results/per_point_results.csv)", + ) + plot_p.add_argument( + "--output", + default="results/boxplot.pdf", + help="Output path for the figure.", + ) + plot_p.add_argument( + "--log-mse", + action="store_true", + default=True, + help="Plot log(MSE) on y-axis (default: true).", + ) + plot_p.add_argument( + "--no-log-mse", + action="store_false", + dest="log_mse", + help="Plot raw MSE on y-axis.", + ) + plot_p.add_argument( + "--figsize", + type=float, + nargs=2, + default=(20, 6), + metavar=("W", "H"), + help="Figure size in inches (default: 20 6).", + ) + + return p + + +def parse_args(argv: list[str] | None = None): + return build_parser().parse_args(argv) diff --git a/virtual-staining/src/loaders.py b/virtual-staining/src/loaders.py new file mode 100644 index 0000000..8a570c9 --- /dev/null +++ b/virtual-staining/src/loaders.py @@ -0,0 +1,195 @@ +"""Data loaders for immuvis_npz and virtues_npy reconstruction formats.""" + +from __future__ import annotations + +import json +import os +from abc import ABC, abstractmethod +from dataclasses import dataclass +from pathlib import Path + +import numpy as np + +from src.models import ModelConfig + + +@dataclass +class PredictionSet: + model_name: str + pairs: list[tuple[str, str]] + _get_prediction_fn: callable + + def get_prediction(self, image_id: str, marker: str) -> np.ndarray: + return self._get_prediction_fn(image_id, marker) + + +@dataclass +class TargetSet: + model_name: str + image_ids: set[str] + marker_names: list[str] + _get_target_fn: callable + + def get_target(self, image_id: str, marker: str) -> np.ndarray: + return self._get_target_fn(image_id, marker) + + def resolve_marker_index(self, marker: str) -> int: + return self.marker_names.index(marker) + + def has_image(self, image_id: str) -> bool: + return image_id in self.image_ids + + +class Loader(ABC): + @abstractmethod + def load(self, cfg: ModelConfig) -> PredictionSet: + ... + + +class ImmuvisNpzLoader(Loader): + def load(self, cfg: ModelConfig) -> PredictionSet: + path = Path(cfg.path) + dataset_filter = cfg.dataset_filter + pairs: list[tuple[str, str]] = [] + id_to_fname: dict[str, str] = {} + + for fname in os.listdir(path): + if not fname.endswith(".npz"): + continue + fpath = path / fname + with np.load(fpath) as data: + meta = json.loads(data["metadata"].item()) + if dataset_filter and meta.get("dataset_name") != dataset_filter: + continue + img_id = meta["image_path"].split("/")[-1].split(".")[0] + id_to_fname[img_id] = fname + for m in data["marker_names"]: + m_str = str(m) + pairs.append((img_id, m_str)) + + pairs = sorted(set(pairs)) + + def get_pred(image_id: str, marker: str) -> np.ndarray: + fname = id_to_fname.get(image_id) + if fname is None: + return np.array([]) + fpath = path / fname + with np.load(fpath) as data: + marker_names = list(data["marker_names"]) + idx = marker_names.index(marker) + return data["recon"][idx].astype(np.float64) + + return PredictionSet( + model_name=cfg.name, + pairs=pairs, + _get_prediction_fn=get_pred, + ) + + +class VirtuesNpyLoader(Loader): + def load(self, cfg: ModelConfig) -> PredictionSet: + path = Path(cfg.path) + pairs: list[tuple[str, str]] = [] + + def get_pred(image_id: str, marker: str) -> np.ndarray: + return self._read_prediction(path, image_id, marker) + + for fname in os.listdir(path): + if not fname.endswith("_recon.npy"): + continue + stem = fname.replace("_recon.npy", "") + parts = stem.split("_", 1) + if len(parts) < 2: + continue + img_id, marker = parts + marker = self._normalise_marker(marker) + pairs.append((img_id, marker)) + + pairs = sorted(set(pairs)) + return PredictionSet( + model_name=cfg.name, + pairs=pairs, + _get_prediction_fn=get_pred, + ) + + @staticmethod + def _normalise_marker(marker: str) -> str: + if marker == "Carbonic": + return "Carbonic Anhydrase" + if marker == "PARP": + return "cl.PARP" + if marker in ("H3", "Histone"): + return "Histone H3" + return marker.replace("_", " ") + + @staticmethod + def _read_prediction(path: Path, image_id: str, marker: str) -> np.ndarray: + for candidate in (marker, _denormalise_marker(marker)): + fname = f"{image_id}_{candidate}_recon.npy".replace(" ", "_") + fpath = path / fname + if fpath.exists(): + return np.load(fpath).astype(np.float64) + raise FileNotFoundError( + f"Neither canonical nor short form found for " + f"{image_id}_{marker}_recon.npy" + ) + + +def _denormalise_marker(marker: str) -> str: + mapping = { + "Carbonic Anhydrase": "Carbonic", + "cl.PARP": "PARP", + "Histone H3": "H3", + } + return mapping.get(marker, marker) + + +def load_ground_truth(cfg: ModelConfig) -> TargetSet: + path = Path(cfg.path) + dataset_filter = cfg.dataset_filter + image_ids: set[str] = set() + marker_names: list[str] = [] + id_to_fname: dict[str, str] = {} + + for fname in os.listdir(path): + if not fname.endswith(".npz"): + continue + fpath = path / fname + with np.load(fpath) as data: + meta = json.loads(data["metadata"].item()) + if dataset_filter and meta.get("dataset_name") != dataset_filter: + continue + img_id = meta["image_path"].split("/")[-1].split(".")[0] + id_to_fname[img_id] = fname + image_ids.add(img_id) + if not marker_names: + marker_names = [str(m) for m in data["marker_names"]] + + def get_target(image_id: str, marker: str) -> np.ndarray: + fname = id_to_fname.get(image_id) + if fname is None: + return np.array([]) + fpath = path / fname + with np.load(fpath) as data: + idx = list(data["marker_names"]).index(marker) + return data["target"][idx].astype(np.float64) + + return TargetSet( + model_name=cfg.name, + image_ids=image_ids, + marker_names=marker_names, + _get_target_fn=get_target, + ) + + +LOADER_REGISTRY: dict[str, type[Loader]] = { + "immuvis_npz": ImmuvisNpzLoader, + "virtues_npy": VirtuesNpyLoader, +} + + +def get_loader(format: str) -> Loader: + cls = LOADER_REGISTRY.get(format) + if cls is None: + raise ValueError(f"Unknown format '{format}'. Available: {list(LOADER_REGISTRY)}") + return cls() diff --git a/virtual-staining/src/main.py b/virtual-staining/src/main.py new file mode 100644 index 0000000..920f5b4 --- /dev/null +++ b/virtual-staining/src/main.py @@ -0,0 +1,83 @@ +"""Entry point logic: parse args, run pipeline, print/save results.""" + +from __future__ import annotations + +import sys + +import numpy as np +import pandas as pd + +from src.cli import parse_args +from src.metrics import finalize_pearson +from src.models import PipelineConfig, load_config +from src.pipeline import run_pipeline, save_results +from src.plotting import boxplot_virtual_staining + + +def main(): + args = parse_args() + + if args.command == "plot": + boxplot_virtual_staining( + results_csv=args.results, + output_path=args.output, + log_mse=args.log_mse, + figsize=tuple(args.figsize), + ) + return + + if args.command == "all": + pass + + cfg = load_config(args.config) + + if args.output_dir is not None: + cfg.output_dir = args.output_dir + if args.crop_size is not None: + cfg.crop_size = tuple(args.crop_size) + if args.plot is not None: + cfg.plot = args.plot + + if not cfg.models: + print("No models defined in config.", file=sys.stderr) + sys.exit(1) + + df, accums, _ = run_pipeline(cfg) + + if not args.no_save: + save_results(df, accums, list(df["img"].unique()), cfg.output_dir) + else: + print("--no-save set; skipping CSV output.") + + if args.table: + _print_summary_table(df, accums, cfg) + + print("\nDone. To generate a box plot, run:") + print( + f" python evaluate_all.py plot --results {cfg.output_dir}/per_point_results.csv" + ) + + +def _print_summary_table( + df: pd.DataFrame, + accums: dict, + cfg: PipelineConfig | None = None, +) -> None: + mse_summary = df.groupby("model")["mse"].mean() + pearson_summary = { + name: float(np.mean(list(finalize_pearson(stats).values()))) + for name, stats in accums.items() + } + display_map: dict[str, str] = {} + if cfg: + for m in cfg.models: + display_map[m.name] = m.display_name or m.name + print("\n" + "=" * 72) + print(f"{'Model':<48s} {'MSE':>10s} {'Pearson':>10s}") + print("=" * 72) + for model_name in sorted(mse_summary.index): + label = display_map.get(model_name, model_name) + mse_val = mse_summary[model_name] + r_val = pearson_summary.get(model_name, float("nan")) + print(f"{label:<48s} {mse_val:>10.6f} {r_val:>10.6f}") + print("=" * 72) diff --git a/virtual-staining/src/metrics.py b/virtual-staining/src/metrics.py new file mode 100644 index 0000000..a95d227 --- /dev/null +++ b/virtual-staining/src/metrics.py @@ -0,0 +1,47 @@ +"""Metric computation: MSE, Pearson correlation.""" + +import numpy as np + + +def mse(x: np.ndarray, y: np.ndarray) -> float: + return float(((x.flatten() - y.flatten()) ** 2).mean()) + + +def pearson_r(x: np.ndarray, y: np.ndarray) -> float: + r = np.corrcoef(x.flatten(), y.flatten())[0, 1] + return float(r) + + +def center_crop(img: np.ndarray, crop_size: tuple[int, int]) -> np.ndarray: + cx, cy = img.shape[0] // 2, img.shape[1] // 2 + sx, sy = crop_size[0] // 2, crop_size[1] // 2 + return img[cx - sx : cx + sx, cy - sy : cy + sy] + + +def accumulate(stats: dict, marker: str, pred: np.ndarray, gt: np.ndarray): + p = pred.flatten() + g = gt.flatten() + counts = stats.setdefault("count", {}) + counts[marker] = counts.get(marker, 0) + 1 + for key, val in [ + ("sum_pred", p.mean()), + ("sum_gt", g.mean()), + ("sum_pred_sq", (p**2).mean()), + ("sum_gt_sq", (g**2).mean()), + ("sum_pred_gt", (p * g).mean()), + ]: + stats.setdefault(key, {}).setdefault(marker, []).append(val) + + +def finalize_pearson(stats: dict) -> dict[str, float]: + result = {} + keys = ["sum_pred", "sum_gt", "sum_pred_sq", "sum_gt_sq", "sum_pred_gt"] + for marker in stats.get("sum_pred", {}): + mean_pred = np.mean(stats["sum_pred"][marker]) + mean_gt = np.mean(stats["sum_gt"][marker]) + mean_cross = np.mean(stats["sum_pred_gt"][marker]) + sd_pred = (np.mean(stats["sum_pred_sq"][marker]) - mean_pred**2) ** 0.5 + sd_gt = (np.mean(stats["sum_gt_sq"][marker]) - mean_gt**2) ** 0.5 + denom = sd_pred * sd_gt + result[marker] = float((mean_cross - mean_gt * mean_pred) / denom) if denom > 0 else 0.0 + return result diff --git a/virtual-staining/src/models.py b/virtual-staining/src/models.py new file mode 100644 index 0000000..43add8b --- /dev/null +++ b/virtual-staining/src/models.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +import sys +from dataclasses import dataclass, field +from pathlib import Path +from typing import Literal + +import yaml + +TransformType = Literal[ + "immu_offset", + "virtues_zs", + "virtues_zs_op_pred", + "none", +] + + +@dataclass +class TransformConfig: + type: TransformType = "immu_offset" + params: dict = field(default_factory=dict) + + +@dataclass +class GroundTruthConfig: + source_model: str | None = None + transform: TransformConfig = field(default_factory=TransformConfig) + + +@dataclass +class ModelConfig: + name: str + path: str + format: Literal["immuvis_npz", "virtues_npy"] = "immuvis_npz" + dataset_filter: str | None = "hn" + display_name: str | None = None + evaluate: bool = True + filter_pairs_to: str | None = None + prediction_transform: TransformConfig = field(default_factory=TransformConfig) + ground_truth: GroundTruthConfig = field(default_factory=GroundTruthConfig) + + +@dataclass +class PipelineConfig: + models: list[ModelConfig] = field(default_factory=list) + marker_stats_csv: str | None = None + crop_size: tuple[int, int] = (128, 128) + output_dir: str = "results" + plot: bool = False + max_plots: int = 5 + + +def load_config(path: str) -> PipelineConfig: + path = Path(path) + if not path.exists(): + print(f"Config not found: {path}", file=sys.stderr) + sys.exit(1) + with open(path) as f: + raw = yaml.safe_load(f) + + models = [] + for m in raw.get("models", []): + gt = m.get("ground_truth", {}) or {} + pred_t = m.get("prediction_transform", {}) or {} + gt_t = gt.get("transform", {}) or {} + + models.append( + ModelConfig( + name=m["name"], + path=m["path"], + format=m.get("format", "immuvis_npz"), + dataset_filter=m.get("dataset_filter") if "dataset_filter" in m else "hn", + display_name=m.get("display_name"), + evaluate=m.get("evaluate", True), + filter_pairs_to=m.get("filter_pairs_to"), + prediction_transform=TransformConfig( + type=pred_t.get("type", "immu_offset"), + params=pred_t.get("params", {}), + ), + ground_truth=GroundTruthConfig( + source_model=gt.get("source_model"), + transform=TransformConfig( + type=gt_t.get("type", "immu_offset"), + params=gt_t.get("params", {}), + ), + ), + ) + ) + + crop = raw.get("crop_size", [128, 128]) + return PipelineConfig( + models=models, + marker_stats_csv=raw.get("marker_stats_csv"), + crop_size=tuple(crop), + output_dir=raw.get("output_dir", "results"), + plot=raw.get("plot", False), + max_plots=raw.get("max_plots", 5), + ) diff --git a/virtual-staining/src/pipeline.py b/virtual-staining/src/pipeline.py new file mode 100644 index 0000000..229298a --- /dev/null +++ b/virtual-staining/src/pipeline.py @@ -0,0 +1,175 @@ +"""Evaluation pipeline: load data, transform, compute metrics.""" + +from __future__ import annotations + +from pathlib import Path + +import numpy as np +import pandas as pd +from tqdm import tqdm + +from src.loaders import ( + PredictionSet, + TargetSet, + get_loader, + load_ground_truth, +) +from src.metrics import ( + accumulate, + center_crop, + finalize_pearson, + mse, + pearson_r, +) +from src.models import PipelineConfig +from src.transforms import build_transform_pair, load_marker_stats + + +def resolve_target_sources(cfg: PipelineConfig) -> dict[str, TargetSet]: + target_sets: dict[str, TargetSet] = {} + for m in cfg.models: + if m.format == "immuvis_npz" and m.name not in target_sets: + target_sets[m.name] = load_ground_truth(m) + + result: dict[str, TargetSet] = {} + for m in cfg.models: + src_name = m.ground_truth.source_model or m.name + if src_name not in target_sets: + raise ValueError( + f"Model '{m.name}' needs GT from '{src_name}' but that model is " + f"not an immuvis_npz type or is missing from config." + ) + result[m.name] = target_sets[src_name] + return result + + +def run_pipeline(cfg: PipelineConfig) -> tuple[pd.DataFrame, dict, dict[str, int]]: + eval_models = [m for m in cfg.models if m.evaluate] + output_dir = Path(cfg.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + gt_sources = resolve_target_sources(cfg) + print(f"Ground truth sources resolved for {len(gt_sources)} models.") + + marker_stats = None + if cfg.marker_stats_csv and Path(cfg.marker_stats_csv).exists(): + marker_stats = load_marker_stats(cfg.marker_stats_csv) + + pred_sets: dict[str, PredictionSet] = {} + for m in eval_models: + loader = get_loader(m.format) + pred_sets[m.name] = loader.load(m) + print(f"Loaded: {m.name} — {len(pred_sets[m.name].pairs)} pairs") + + rows = [] + accums = {} + model_pair_counts: dict[str, int] = {} + + for m in eval_models: + print(f"\nEvaluating: {m.name}") + ps = pred_sets[m.name] + + pairs = list(ps.pairs) + if m.filter_pairs_to: + ref_name = m.filter_pairs_to + if ref_name not in pred_sets: + raise ValueError( + f"Model '{m.name}' has filter_pairs_to='{ref_name}' " + f"but that model is not in the evaluation set." + ) + ref_pairs = set(pred_sets[ref_name].pairs) + own_set = set(pairs) + dropped = own_set - ref_pairs + if dropped: + print(f" Dropping {len(dropped)} pairs not in {ref_name}") + for img, mrk in sorted(dropped): + print(f" {img} {mrk}") + pairs = sorted(own_set & ref_pairs) + + gt_tfm = build_transform_pair( + m.ground_truth.transform if m.ground_truth.source_model else m.prediction_transform, + cfg.marker_stats_csv, + ).target + + pred_tfm = build_transform_pair( + m.prediction_transform, cfg.marker_stats_csv + ).pred + + ts = gt_sources[m.name] + + pairs = [(img, mrk) for (img, mrk) in pairs if ts.has_image(img)] + model_pair_counts[m.name] = len(pairs) + + accums[m.name] = {} + + for img_id, marker in tqdm(pairs, desc=f" {m.name}"): + pred_raw = ps.get_prediction(img_id, marker) + if pred_raw is None or pred_raw.size == 0: + continue + + raw_gt = ts.get_target(img_id, marker) + if raw_gt.size == 0: + continue + + gt_t = gt_tfm(raw_gt, marker) + crop = center_crop(gt_t, cfg.crop_size) + + pred_t = pred_tfm(pred_raw, marker) + + mse_val = mse(pred_t, crop) + r_val = pearson_r(pred_t, crop) + + rows.append({ + "model": m.name, + "img": img_id, + "marker": marker, + "mse": mse_val, + "pearson": r_val, + }) + + accumulate(accums[m.name], marker, pred_t, crop) + + df = pd.DataFrame(rows) + return df, accums, model_pair_counts + + +def save_results( + df: pd.DataFrame, + accums: dict, + common_pairs: list, + output_dir: str, + add_all_marker: bool = True, +): + out = Path(output_dir) + out.mkdir(parents=True, exist_ok=True) + + pearson_rows = [] + for model_name, stats in accums.items(): + per_marker = finalize_pearson(stats) + for marker, r in per_marker.items(): + pearson_rows.append({"model": model_name, "marker": marker, "pearson": r}) + + pdf = pd.DataFrame(pearson_rows) + pdf.to_csv(out / "per_marker_pearson.csv", index=False) + + df.to_csv(out / "per_point_results.csv", index=False) + + summary = df.groupby(["model", "marker"])[["mse", "pearson"]].agg(["mean", "std"]) + summary.to_csv(out / "summary.csv") + + if add_all_marker: + all_df = df.copy() + all_df["marker"] = "All" + all_summary = all_df.groupby(["model", "marker"])[["mse", "pearson"]].agg( + ["mean", "std"] + ) + all_summary.to_csv(out / "summary_all.csv") + combined = pd.concat( + [summary.reset_index(), all_summary.reset_index()], ignore_index=True + ) + combined.to_csv(out / "summary_with_all.csv", index=False) + + print(f"\nResults saved to {out.resolve()}/") + print(f" per_point_results.csv — per (image, marker, model) metrics") + print(f" per_marker_pearson.csv — per-marker aggregated Pearson") + print(f" summary.csv — per (model, marker) mean ± std") diff --git a/virtual-staining/src/plotting.py b/virtual-staining/src/plotting.py new file mode 100644 index 0000000..555c63a --- /dev/null +++ b/virtual-staining/src/plotting.py @@ -0,0 +1,101 @@ +"""Box-plot generation for paper figures.""" + +from __future__ import annotations + +from pathlib import Path + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +matplotlib.use("Agg") + + +def boxplot_virtual_staining( + results_csv: str, + output_path: str = "boxplot.pdf", + log_mse: bool = True, + figsize: tuple[float, float] = (20, 6), + dpi: int = 150, +): + df = pd.read_csv(results_csv) + + markers = sorted(df["marker"].unique()) + models = sorted(df["model"].unique()) + n_markers = len(markers) + n_models = len(models) + + cmap = matplotlib.colormaps["tab10"] + model_colors = {m: cmap(i / max(n_models, 1)) for i, m in enumerate(models)} + + value_col = "log_mse" if log_mse else "mse" + if log_mse: + df[value_col] = np.log10(df["mse"].clip(lower=1e-10)) + + box_width = 0.8 / n_models + gap_between_markers = 0.4 + marker_spacing = 1.0 + positions = [] + group_centers = [] + start = 0.0 + for mi in range(n_markers): + for ni in range(n_models): + offset = (ni - (n_models - 1) / 2) * box_width + positions.append(start + marker_spacing / 2 + offset) + group_centers.append(start + marker_spacing / 2) + start += marker_spacing + gap_between_markers + + fig, ax = plt.subplots(figsize=figsize) + ax.set_xlim(-marker_spacing / 2, start - gap_between_markers + marker_spacing / 2) + + boxes_data: list[list[float]] = [[] for _ in range(n_markers * n_models)] + model_at_pos: list[str] = [] + for mi, marker in enumerate(markers): + sub = df[df["marker"] == marker] + for ni, model in enumerate(models): + idx = mi * n_models + ni + vals = sub[sub["model"] == model][value_col].dropna().tolist() + boxes_data[idx] = vals + model_at_pos.append(model) + + non_empty = [(i, d) for i, d in enumerate(boxes_data) if len(d) > 0] + plot_positions = [positions[i] for i, _ in non_empty] + plot_data = [d for _, d in non_empty] + plot_model_at = [model_at_pos[i] for i, _ in non_empty] + + bp = ax.boxplot( + plot_data, + positions=plot_positions, + widths=box_width * 0.9, + patch_artist=True, + manage_ticks=False, + ) + + for i, (patch, model_name) in enumerate(zip(bp["boxes"], plot_model_at)): + patch.set_facecolor(model_colors[model_name]) + patch.set_alpha(0.8) + + ylabel = r"$\log_{10}(\mathrm{MSE})$" if log_mse else "MSE" + ax.set_ylabel(ylabel, fontsize=12) + + ax.set_xticks(group_centers) + ax.set_xticklabels(markers, rotation=45, ha="right", fontsize=9) + + handles = [ + plt.Rectangle((0, 0), 1, 1, color=model_colors[m], alpha=0.8) + for m in models + ] + ax.legend( + handles, models, + title="Model", + bbox_to_anchor=(1.05, 1), + loc="upper left", + fontsize=8, + ) + + ax.grid(axis="y", alpha=0.3) + fig.tight_layout() + fig.savefig(output_path, dpi=dpi, bbox_inches="tight") + plt.close(fig) + print(f"Box plot saved to: {output_path}") diff --git a/virtual-staining/src/transforms.py b/virtual-staining/src/transforms.py new file mode 100644 index 0000000..22bca87 --- /dev/null +++ b/virtual-staining/src/transforms.py @@ -0,0 +1,131 @@ +"""Transforms mapping predictions and targets to a common evaluation space.""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from dataclasses import dataclass +from pathlib import Path + +import numpy as np +import pandas as pd + +from src.models import TransformConfig + + +def load_marker_stats(csv_path: str | Path, dataset: str = "hn"): + df = pd.read_csv(csv_path) + df = df[df["dataset"] == dataset] + return { + row["marker"]: {"mean": row["mean"], "std": row["std"]} + for _, row in df.iterrows() + } + + +class PredictionTransform(ABC): + @abstractmethod + def __call__(self, pred: np.ndarray, marker: str) -> np.ndarray: + ... + + +class TargetTransform(ABC): + @abstractmethod + def __call__(self, target: np.ndarray, marker: str) -> np.ndarray: + ... + + +class NoOpTransform(PredictionTransform, TargetTransform): + def __call__(self, arr: np.ndarray, marker: str = "") -> np.ndarray: + return arr + + +class ScalingTransform(PredictionTransform, TargetTransform): + def __init__(self, scale: float = 3.4): + self.scale = scale + + def __call__(self, arr: np.ndarray, marker: str = "") -> np.ndarray: + return arr * self.scale + + +class VirtuesZSPredictionTransform(PredictionTransform): + def __init__( + self, + sinh_factor: float = 5.0, + pre_scale: float | None = None, + marker_stats: dict | None = None, + ): + self.sinh_factor = sinh_factor + self.pre_scale = pre_scale + self.marker_stats = marker_stats or {} + + def __call__(self, pred: np.ndarray, marker: str) -> np.ndarray: + x = pred + if self.pre_scale is not None: + x = x * self.pre_scale + x = np.log1p(np.sinh(x) * self.sinh_factor) + if marker in self.marker_stats: + mu = self.marker_stats[marker]["mean"] + sd = self.marker_stats[marker]["std"] + if sd > 0: + x = (x - mu) / sd + return x + + +class NoTransformTarget(TargetTransform): + def __call__(self, target: np.ndarray, marker: str = "") -> np.ndarray: + return target + + +@dataclass +class TransformPair: + pred: PredictionTransform + target: TargetTransform + + +def build_transform_pair( + transform_cfg: TransformConfig, + marker_stats_csv: str | None = None, +) -> TransformPair: + ttype = transform_cfg.type + params = transform_cfg.params + + if ttype == "none": + return TransformPair(NoOpTransform(), NoOpTransform()) + + if ttype == "immu_offset": + scale = params.get("scale", 3.4) + t = ScalingTransform(scale=scale) + return TransformPair(t, t) + + if ttype == "virtues_zs": + sinh_factor = params.get("sinh_factor", 5.0) + pre_scale = params.get("pre_scale", None) + marker_stats = None + stats_path = params.get("marker_stats_csv") or marker_stats_csv + if stats_path: + marker_stats = load_marker_stats(stats_path) + return TransformPair( + pred=VirtuesZSPredictionTransform( + sinh_factor=sinh_factor, + pre_scale=pre_scale, + marker_stats=marker_stats, + ), + target=NoTransformTarget(), + ) + + if ttype == "virtues_zs_op_pred": + log1p_offset = params.get("log1p_offset", 5.4) + sinh_factor = params.get("sinh_factor", 5.0) + marker_stats = None + stats_path = params.get("marker_stats_csv") or marker_stats_csv + if stats_path: + marker_stats = load_marker_stats(stats_path) + return TransformPair( + pred=VirtuesZSPredictionTransform( + sinh_factor=sinh_factor, + pre_scale=log1p_offset, + marker_stats=marker_stats, + ), + target=NoTransformTarget(), + ) + + raise ValueError(f"Unknown transform type: {ttype}")