File size: 4,451 Bytes
9a5439c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"""MIPHEI-ViT vs GigaTIME virtual mIF on ORION, against the real mIF of the same tiles (tile means).

MIPHEI was trained on the ORION official train split -> 'clean' = official TEST tiles (2 patients, never seen);
'seen' = official TRAIN tiles (training data of MIPHEI, optimistic). GigaTIME never saw ORION (clean everywhere).
  (a) per-channel Spearman of tile means (pooled and within-slide)
  (b) direct real-mean <-> virtual-mean retrieval (rank-gaussianised means, cosine; no learned head), 100-galleries
Output results/miphei_diag.csv, results/miphei_diag_retrieval.csv
"""
import json
import os

import numpy as np
import pandas as pd
from scipy.special import ndtri
from scipy.stats import rankdata, spearmanr

from common import CACHE, ROOT, SPLITS, load_all_tiles

OUT = os.path.join(ROOT, "results")
ORION_CH = ["Hoechst", "CD31", "CD45", "CD68", "CD4", "FOXP3", "CD8a", "CD45RO", "CD20", "PD-L1", "CD3e",
            "CD163", "E-cadherin", "PD-1", "Ki67", "Pan-CK", "SMA"]  # order of orion_real_if_means.npy (25_)
GT = {"Hoechst": ["DAPI"], "CD68": ["CD68"], "CD4": ["CD4"], "CD8a": ["CD8"], "CD20": ["CD20"], "PD-L1": ["PD-L1"],
      "CD3e": ["CD3"], "Ki67": ["Ki67"], "Pan-CK": ["CK"], "SMA": ["Actin-D", "Transgelin"]}
df = load_all_tiles()
slide = df.in_slide_name.values
real = np.load(os.path.join(CACHE, "gigatime", "orion_real_if_means.npy"))
mch = json.load(open(os.path.join(CACHE, "miphei", "channels.json")))["channels"]
mip = np.load(os.path.join(CACHE, "miphei", "orion_means.npy"))
gch = json.load(open(os.path.join(CACHE, "gigatime", "channels.json")))["channels"]
gm = np.load(os.path.join(CACHE, "gigatime", "orion_means.npy"))
off = pd.read_csv(os.path.join(SPLITS, "orion_official", "fold0.csv")).split.values
sets = {"clean (official test)": off == "test", "seen (official train)": off == "train"}


def within(a, b, m):
    r, w = [], []
    for s in np.unique(slide[m]):
        i = m & (slide == s)
        if i.sum() >= 15 and a[i].std() > 0 and b[i].std() > 0:
            r.append(spearmanr(a[i], b[i])[0]); w.append(i.sum())
    return float(np.average(r, weights=w)) if r else np.nan


rows = []
for ch in mch:
    r = real[:, ORION_CH.index(ch)]
    for sname, m in sets.items():
        row = {"channel": ch, "set": sname,
               "MIPHEI pooled": spearmanr(mip[m, mch.index(ch)], r[m])[0],
               "MIPHEI within-slide": within(mip[:, mch.index(ch)], r, m)}
        if ch in GT:
            g = gm[:, [gch.index(x) for x in GT[ch]]].mean(1)
            row.update({"GigaTIME pooled": spearmanr(g[m], r[m])[0], "GigaTIME within-slide": within(g, r, m)})
        rows.append(row)
C = pd.DataFrame(rows)
rg = lambda x: ndtri((np.apply_along_axis(rankdata, 0, x) - 0.5) / len(x))


def g100(a, b, draws=50, seed=0):
    a = a / np.linalg.norm(a, axis=1, keepdims=True); b = b / np.linalg.norm(b, axis=1, keepdims=True)
    rng = np.random.default_rng(seed); r = []
    for _ in range(draws):
        s = rng.choice(len(a), min(100, len(a)), replace=False)
        sim = a[s] @ b[s].T; d = np.diag(sim)[:, None]
        r.append(((sim > d).sum(1) == 0).mean() / 2 + ((sim.T > d).sum(1) == 0).mean() / 2)
    return float(np.mean(r))


ten = list(GT)
Rt = []
for sname, m in sets.items():
    R16 = real[m][:, [ORION_CH.index(c) for c in mch]]
    R10 = real[m][:, [ORION_CH.index(c) for c in ten]]
    G10 = np.stack([gm[m][:, [gch.index(x) for x in GT[c]]].mean(1) for c in ten], 1)
    Rt += [{"set": sname, "n": int(m.sum()), "pair": "real <-> MIPHEI (16 ch)", "R@1_g100": g100(rg(R16), rg(mip[m]))},
           {"set": sname, "n": int(m.sum()), "pair": "real <-> MIPHEI (10 shared ch)", "R@1_g100": g100(rg(R10), rg(mip[m][:, [mch.index(c) for c in ten]]))},
           {"set": sname, "n": int(m.sum()), "pair": "real <-> GigaTIME (10 shared ch)", "R@1_g100": g100(rg(R10), rg(G10))}]
Rt = pd.DataFrame(Rt)
pd.set_option("display.width", 200)
print(C.round(3).to_string(index=False))
print("\nmean over channels\n", C.groupby("set")[[c for c in C.columns if c not in ("channel", "set")]].mean().round(3).to_string())
print("\nmean over the 10 GigaTIME-shared channels\n", C[C.channel.isin(ten)].groupby("set")[[c for c in C.columns if c not in ("channel", "set")]].mean().round(3).to_string())
print("\n", Rt.round(3).to_string(index=False))
C.round(4).to_csv(os.path.join(OUT, "miphei_diag.csv"), index=False)
Rt.round(4).to_csv(os.path.join(OUT, "miphei_diag_retrieval.csv"), index=False)