darabos commited on
Commit
f6f2bcd
·
1 Parent(s): a99eb56

GNN to text example.

Browse files
examples/GNN to Text/Demo.lynxkite.json ADDED
@@ -0,0 +1,3103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "edges": [
3
+ {
4
+ "id": "Load GAT model 1 output Predict with attribution 1 b",
5
+ "source": "Load GAT model 1",
6
+ "sourceHandle": "output",
7
+ "target": "Predict with attribution 1",
8
+ "targetHandle": "b"
9
+ },
10
+ {
11
+ "id": "Predict with attribution 1 output Visualize attribution 1 b",
12
+ "source": "Predict with attribution 1",
13
+ "sourceHandle": "output",
14
+ "target": "Visualize attribution 1",
15
+ "targetHandle": "b"
16
+ },
17
+ {
18
+ "id": "Predict with attribution 1 output Attribution to text 1 b",
19
+ "source": "Predict with attribution 1",
20
+ "sourceHandle": "output",
21
+ "target": "Attribution to text 1",
22
+ "targetHandle": "b"
23
+ }
24
+ ],
25
+ "env": "LynxKite Graph Analytics",
26
+ "execution_options": {},
27
+ "nodes": [
28
+ {
29
+ "data": {
30
+ "__execution_delay": 0.0,
31
+ "collapsed": null,
32
+ "display": null,
33
+ "error": null,
34
+ "expanded_height": null,
35
+ "input_metadata": [],
36
+ "message": null,
37
+ "meta": null,
38
+ "op_id": "Attribution > Load GAT model",
39
+ "params": {
40
+ "model_path": "gat_model.pt",
41
+ "root_path": "uploads/ode-gnn"
42
+ },
43
+ "status": "done",
44
+ "title": "Load GAT model"
45
+ },
46
+ "height": 245.0,
47
+ "id": "Load GAT model 1",
48
+ "position": {
49
+ "x": 105.0,
50
+ "y": 70.0
51
+ },
52
+ "type": "basic",
53
+ "width": 315.0
54
+ },
55
+ {
56
+ "data": {
57
+ "__execution_delay": 0.0,
58
+ "collapsed": null,
59
+ "display": null,
60
+ "error": null,
61
+ "expanded_height": null,
62
+ "input_metadata": [
63
+ {
64
+ "dataframes": {
65
+ "clin_df": {
66
+ "columns": [
67
+ "% BW Difference",
68
+ "% TVol Difference",
69
+ "Days Post T0",
70
+ "Model",
71
+ "Treatment",
72
+ "Tumor Type",
73
+ "Volume (mm3)",
74
+ "body weight (g)"
75
+ ],
76
+ "key": "clin_df"
77
+ },
78
+ "disease": {
79
+ "columns": [
80
+ "disease",
81
+ "globalScore",
82
+ "symbol"
83
+ ],
84
+ "key": "disease"
85
+ },
86
+ "drug_gene": {
87
+ "columns": [
88
+ "drug_name",
89
+ "gene_name"
90
+ ],
91
+ "key": "drug_gene"
92
+ },
93
+ "gene2esm2": {
94
+ "columns": [
95
+ "embedding"
96
+ ],
97
+ "key": "gene2esm2"
98
+ },
99
+ "gene_to_idx": {
100
+ "columns": [
101
+ "idx"
102
+ ],
103
+ "key": "gene_to_idx"
104
+ },
105
+ "meta": {
106
+ "columns": [
107
+ "Model",
108
+ "Treatment",
109
+ "Tumor Type",
110
+ "future_timestamps",
111
+ "future_volumes",
112
+ "mrecist_class",
113
+ "past_volumes"
114
+ ],
115
+ "key": "meta"
116
+ },
117
+ "rna_df": {
118
+ "columns": [
119
+ "X-1004",
120
+ "X-1008",
121
+ "X-1027",
122
+ "X-1095",
123
+ "X-1119",
124
+ "X-1156",
125
+ "X-1167",
126
+ "X-1169",
127
+ "X-1172",
128
+ "X-1173",
129
+ "X-1199",
130
+ "X-1210",
131
+ "X-1234",
132
+ "X-1256",
133
+ "X-1270",
134
+ "X-1289",
135
+ "X-1290",
136
+ "X-1298",
137
+ "X-1303",
138
+ "X-1317",
139
+ "X-1318",
140
+ "X-1323",
141
+ "X-1329",
142
+ "X-1337",
143
+ "X-1349",
144
+ "X-1362",
145
+ "X-1367",
146
+ "X-1371",
147
+ "X-1381",
148
+ "X-1383",
149
+ "X-1407",
150
+ "X-1441",
151
+ "X-1442",
152
+ "X-1443",
153
+ "X-1451",
154
+ "X-1468",
155
+ "X-1473",
156
+ "X-1479",
157
+ "X-1480",
158
+ "X-1481",
159
+ "X-1493",
160
+ "X-1499",
161
+ "X-1500",
162
+ "X-1536",
163
+ "X-1538",
164
+ "X-1543",
165
+ "X-1569",
166
+ "X-1586",
167
+ "X-1600",
168
+ "X-1631",
169
+ "X-1633",
170
+ "X-1655",
171
+ "X-1658",
172
+ "X-1678",
173
+ "X-1683",
174
+ "X-1725",
175
+ "X-1726",
176
+ "X-1755",
177
+ "X-1784",
178
+ "X-1786",
179
+ "X-1787",
180
+ "X-1795",
181
+ "X-1823",
182
+ "X-1828",
183
+ "X-1832",
184
+ "X-1834",
185
+ "X-1835",
186
+ "X-1855",
187
+ "X-1865",
188
+ "X-1867",
189
+ "X-1906",
190
+ "X-1916",
191
+ "X-1921",
192
+ "X-1934",
193
+ "X-1948",
194
+ "X-1959",
195
+ "X-1963",
196
+ "X-1970",
197
+ "X-1979",
198
+ "X-1980",
199
+ "X-1993",
200
+ "X-2017",
201
+ "X-2026",
202
+ "X-2030",
203
+ "X-2042",
204
+ "X-2043",
205
+ "X-2046",
206
+ "X-2081",
207
+ "X-2082",
208
+ "X-2088",
209
+ "X-2094",
210
+ "X-2096",
211
+ "X-2116",
212
+ "X-2127",
213
+ "X-2129",
214
+ "X-2145",
215
+ "X-2148",
216
+ "X-2163",
217
+ "X-2182",
218
+ "X-2195",
219
+ "X-2198",
220
+ "X-2207",
221
+ "X-2208",
222
+ "X-2219",
223
+ "X-2220",
224
+ "X-2239",
225
+ "X-2263",
226
+ "X-2283",
227
+ "X-2306",
228
+ "X-2336",
229
+ "X-2339",
230
+ "X-2344",
231
+ "X-2347",
232
+ "X-2353",
233
+ "X-2374",
234
+ "X-2376",
235
+ "X-2403",
236
+ "X-2406",
237
+ "X-2428",
238
+ "X-2454",
239
+ "X-2458",
240
+ "X-2470",
241
+ "X-2471",
242
+ "X-2483",
243
+ "X-2484",
244
+ "X-2487",
245
+ "X-2511",
246
+ "X-2514",
247
+ "X-2524",
248
+ "X-2526",
249
+ "X-2538",
250
+ "X-2564",
251
+ "X-2569",
252
+ "X-2573",
253
+ "X-2592",
254
+ "X-2597",
255
+ "X-2602",
256
+ "X-2607",
257
+ "X-2613",
258
+ "X-2628",
259
+ "X-2633",
260
+ "X-2640",
261
+ "X-2655",
262
+ "X-2659",
263
+ "X-2681",
264
+ "X-2684",
265
+ "X-2697",
266
+ "X-2700",
267
+ "X-2709",
268
+ "X-2715",
269
+ "X-2723",
270
+ "X-2725",
271
+ "X-2729",
272
+ "X-2742",
273
+ "X-2753",
274
+ "X-2757",
275
+ "X-2761",
276
+ "X-2762",
277
+ "X-2780",
278
+ "X-2781",
279
+ "X-2793",
280
+ "X-2805",
281
+ "X-2822",
282
+ "X-2838",
283
+ "X-2846",
284
+ "X-2861",
285
+ "X-2863",
286
+ "X-2880",
287
+ "X-2914",
288
+ "X-2921",
289
+ "X-2930",
290
+ "X-2967",
291
+ "X-2983",
292
+ "X-2990",
293
+ "X-2991",
294
+ "X-2997",
295
+ "X-3001",
296
+ "X-3009",
297
+ "X-3019",
298
+ "X-3020",
299
+ "X-3028",
300
+ "X-3029",
301
+ "X-3038",
302
+ "X-3041",
303
+ "X-3042",
304
+ "X-3050",
305
+ "X-3052",
306
+ "X-3056",
307
+ "X-3077",
308
+ "X-3078",
309
+ "X-3083",
310
+ "X-3093",
311
+ "X-3102",
312
+ "X-3114",
313
+ "X-3127",
314
+ "X-3135",
315
+ "X-3147",
316
+ "X-3158",
317
+ "X-3189",
318
+ "X-3196",
319
+ "X-3200",
320
+ "X-3201",
321
+ "X-3202",
322
+ "X-3205",
323
+ "X-3211",
324
+ "X-3224",
325
+ "X-3237",
326
+ "X-3239",
327
+ "X-3267",
328
+ "X-3268",
329
+ "X-3274",
330
+ "X-3286",
331
+ "X-3298",
332
+ "X-3400",
333
+ "X-3408",
334
+ "X-3419",
335
+ "X-3425",
336
+ "X-3447",
337
+ "X-3450",
338
+ "X-3453",
339
+ "X-3468",
340
+ "X-3483",
341
+ "X-3486",
342
+ "X-3503",
343
+ "X-3558",
344
+ "X-3559",
345
+ "X-3570",
346
+ "X-3575",
347
+ "X-3588",
348
+ "X-3604",
349
+ "X-3615",
350
+ "X-3629",
351
+ "X-3662",
352
+ "X-3671",
353
+ "X-3676",
354
+ "X-3682",
355
+ "X-3684",
356
+ "X-3694",
357
+ "X-3697",
358
+ "X-3711",
359
+ "X-3717",
360
+ "X-3729",
361
+ "X-3735",
362
+ "X-3736",
363
+ "X-3739",
364
+ "X-3746",
365
+ "X-3749",
366
+ "X-3765",
367
+ "X-3766",
368
+ "X-3769",
369
+ "X-3773",
370
+ "X-3782",
371
+ "X-3783",
372
+ "X-3787",
373
+ "X-3792",
374
+ "X-3800",
375
+ "X-3801",
376
+ "X-3811",
377
+ "X-3816",
378
+ "X-3829",
379
+ "X-3843",
380
+ "X-3846",
381
+ "X-3851",
382
+ "X-3861",
383
+ "X-3867",
384
+ "X-3870",
385
+ "X-3873",
386
+ "X-3880",
387
+ "X-3888",
388
+ "X-3898",
389
+ "X-3901",
390
+ "X-3904",
391
+ "X-3926",
392
+ "X-3939",
393
+ "X-3947",
394
+ "X-3949",
395
+ "X-3966",
396
+ "X-3982",
397
+ "X-3990",
398
+ "X-4015",
399
+ "X-4018",
400
+ "X-4026",
401
+ "X-4073",
402
+ "X-4087",
403
+ "X-4132",
404
+ "X-4138",
405
+ "X-4145",
406
+ "X-4157",
407
+ "X-4163",
408
+ "X-4173",
409
+ "X-4213",
410
+ "X-4215",
411
+ "X-4226",
412
+ "X-4244",
413
+ "X-4261",
414
+ "X-4279",
415
+ "X-4304",
416
+ "X-4316",
417
+ "X-4339",
418
+ "X-4347",
419
+ "X-4361",
420
+ "X-4367",
421
+ "X-4377",
422
+ "X-4378",
423
+ "X-4379",
424
+ "X-4412",
425
+ "X-4426",
426
+ "X-4454",
427
+ "X-4455",
428
+ "X-4476",
429
+ "X-4484",
430
+ "X-4530",
431
+ "X-4536",
432
+ "X-4537",
433
+ "X-4538",
434
+ "X-4567",
435
+ "X-4579",
436
+ "X-4597",
437
+ "X-4624",
438
+ "X-4644",
439
+ "X-4649",
440
+ "X-4653",
441
+ "X-4664",
442
+ "X-4668",
443
+ "X-4676",
444
+ "X-4797",
445
+ "X-4799",
446
+ "X-4818",
447
+ "X-4819",
448
+ "X-4824",
449
+ "X-4826",
450
+ "X-4829",
451
+ "X-4832",
452
+ "X-4833",
453
+ "X-4849",
454
+ "X-4852",
455
+ "X-4863",
456
+ "X-4869",
457
+ "X-4882",
458
+ "X-4886",
459
+ "X-4888",
460
+ "X-4905",
461
+ "X-4927",
462
+ "X-4948",
463
+ "X-4949",
464
+ "X-4979",
465
+ "X-5018",
466
+ "X-5041",
467
+ "X-5077",
468
+ "X-5094",
469
+ "X-5189",
470
+ "X-5205",
471
+ "X-5238",
472
+ "X-5249",
473
+ "X-5254",
474
+ "X-5261",
475
+ "X-5273",
476
+ "X-5307",
477
+ "X-5316",
478
+ "X-5355",
479
+ "X-5374",
480
+ "X-5388",
481
+ "X-5405",
482
+ "X-5415",
483
+ "X-5421",
484
+ "X-5438",
485
+ "X-5441",
486
+ "X-5446",
487
+ "X-5462",
488
+ "X-5490",
489
+ "X-5493",
490
+ "X-5494",
491
+ "X-5495",
492
+ "X-5502",
493
+ "X-5508",
494
+ "X-5541",
495
+ "X-5563",
496
+ "X-5566",
497
+ "X-5578",
498
+ "X-5584",
499
+ "X-5593",
500
+ "X-5594",
501
+ "X-5595",
502
+ "X-5627",
503
+ "X-5643",
504
+ "X-5645",
505
+ "X-5693",
506
+ "X-5694",
507
+ "X-5696",
508
+ "X-5713",
509
+ "X-5717",
510
+ "X-5727",
511
+ "X-5739",
512
+ "X-5808",
513
+ "X-5959",
514
+ "X-5974",
515
+ "X-5975",
516
+ "X-6030",
517
+ "X-6047"
518
+ ],
519
+ "key": "rna_df"
520
+ }
521
+ },
522
+ "other": {
523
+ "disease_to_id": {
524
+ "key": "disease_to_id"
525
+ },
526
+ "drug_to_id": {
527
+ "key": "drug_to_id"
528
+ },
529
+ "model": {
530
+ "key": "model"
531
+ },
532
+ "root_path": {
533
+ "key": "root_path"
534
+ }
535
+ },
536
+ "relations": []
537
+ }
538
+ ],
539
+ "message": null,
540
+ "meta": null,
541
+ "op_id": "Attribution > Predict with attribution",
542
+ "params": {
543
+ "disease": "PDAC",
544
+ "drug": "BYL719 + LEE011"
545
+ },
546
+ "status": "done",
547
+ "title": "Predict with attribution"
548
+ },
549
+ "height": 245.0,
550
+ "id": "Predict with attribution 1",
551
+ "position": {
552
+ "x": 525.0,
553
+ "y": 70.0
554
+ },
555
+ "type": "basic",
556
+ "width": 315.0
557
+ },
558
+ {
559
+ "data": {
560
+ "collapsed": null,
561
+ "display": {
562
+ "animationDuration": 500.0,
563
+ "animationEasingUpdate": "quinticInOut",
564
+ "series": [
565
+ {
566
+ "data": [
567
+ {
568
+ "id": "PAK6",
569
+ "itemStyle": {
570
+ "color": "#481e70"
571
+ },
572
+ "label": {
573
+ "show": false
574
+ },
575
+ "name": "PAK6",
576
+ "symbolSize": 10.0,
577
+ "value": "2.0000",
578
+ "x": -19.42382626450975,
579
+ "y": 48.47212158740607
580
+ },
581
+ {
582
+ "id": "IKBKG",
583
+ "itemStyle": {
584
+ "color": "#440154"
585
+ },
586
+ "label": {
587
+ "show": false
588
+ },
589
+ "name": "IKBKG",
590
+ "symbolSize": 10.0,
591
+ "value": "2.0000",
592
+ "x": 0.3021725576229701,
593
+ "y": 21.078447285269206
594
+ },
595
+ {
596
+ "id": "CTNNBIP1",
597
+ "itemStyle": {
598
+ "color": "#287b8e"
599
+ },
600
+ "label": {
601
+ "show": true
602
+ },
603
+ "name": "CTNNBIP1",
604
+ "symbolSize": 20.0,
605
+ "value": "2.0000",
606
+ "x": 23.879445881520912,
607
+ "y": -18.786026350958153
608
+ },
609
+ {
610
+ "id": "WNT5B",
611
+ "itemStyle": {
612
+ "color": "#443982"
613
+ },
614
+ "label": {
615
+ "show": false
616
+ },
617
+ "name": "WNT5B",
618
+ "symbolSize": 10.0,
619
+ "value": "2.0000",
620
+ "x": -40.80058274584641,
621
+ "y": -1.6602104674625986
622
+ },
623
+ {
624
+ "id": "CASP3",
625
+ "itemStyle": {
626
+ "color": "#440154"
627
+ },
628
+ "label": {
629
+ "show": false
630
+ },
631
+ "name": "CASP3",
632
+ "symbolSize": 10.0,
633
+ "value": "2.0000",
634
+ "x": -10.188791006749959,
635
+ "y": 11.822674390306865
636
+ },
637
+ {
638
+ "id": "XRCC2",
639
+ "itemStyle": {
640
+ "color": "#440154"
641
+ },
642
+ "label": {
643
+ "show": false
644
+ },
645
+ "name": "XRCC2",
646
+ "symbolSize": 10.0,
647
+ "value": "2.0000",
648
+ "x": 38.414021355723186,
649
+ "y": 22.61863075941045
650
+ },
651
+ {
652
+ "id": "ATP6V1B1",
653
+ "itemStyle": {
654
+ "color": "#30678d"
655
+ },
656
+ "label": {
657
+ "show": false
658
+ },
659
+ "name": "ATP6V1B1",
660
+ "symbolSize": 10.0,
661
+ "value": "2.0000",
662
+ "x": 57.07039232134191,
663
+ "y": 1.3081711287735565
664
+ },
665
+ {
666
+ "id": "SMO",
667
+ "itemStyle": {
668
+ "color": "#440154"
669
+ },
670
+ "label": {
671
+ "show": false
672
+ },
673
+ "name": "SMO",
674
+ "symbolSize": 10.0,
675
+ "value": "2.0000",
676
+ "x": -32.866348896187134,
677
+ "y": -28.876555583747056
678
+ },
679
+ {
680
+ "id": "CHD2",
681
+ "itemStyle": {
682
+ "color": "#443982"
683
+ },
684
+ "label": {
685
+ "show": false
686
+ },
687
+ "name": "CHD2",
688
+ "symbolSize": 10.0,
689
+ "value": "2.0000",
690
+ "x": 54.550548701217814,
691
+ "y": 21.088743790429866
692
+ },
693
+ {
694
+ "id": "ATP8B1",
695
+ "itemStyle": {
696
+ "color": "#481e70"
697
+ },
698
+ "label": {
699
+ "show": false
700
+ },
701
+ "name": "ATP8B1",
702
+ "symbolSize": 10.0,
703
+ "value": "2.0000",
704
+ "x": -3.5075869068308125,
705
+ "y": -100.0
706
+ },
707
+ {
708
+ "id": "SELP",
709
+ "itemStyle": {
710
+ "color": "#443982"
711
+ },
712
+ "label": {
713
+ "show": false
714
+ },
715
+ "name": "SELP",
716
+ "symbolSize": 10.0,
717
+ "value": "2.0000",
718
+ "x": 61.63340241291208,
719
+ "y": -32.838611073702964
720
+ },
721
+ {
722
+ "id": "GNAI3",
723
+ "itemStyle": {
724
+ "color": "#30678d"
725
+ },
726
+ "label": {
727
+ "show": false
728
+ },
729
+ "name": "GNAI3",
730
+ "symbolSize": 10.0,
731
+ "value": "2.0000",
732
+ "x": -52.9449301467592,
733
+ "y": -4.052889824073507
734
+ },
735
+ {
736
+ "id": "IL7",
737
+ "itemStyle": {
738
+ "color": "#443982"
739
+ },
740
+ "label": {
741
+ "show": false
742
+ },
743
+ "name": "IL7",
744
+ "symbolSize": 10.0,
745
+ "value": "2.0000",
746
+ "x": 26.629950910864448,
747
+ "y": 43.28069622584979
748
+ },
749
+ {
750
+ "id": "EPHA1",
751
+ "itemStyle": {
752
+ "color": "#440154"
753
+ },
754
+ "label": {
755
+ "show": false
756
+ },
757
+ "name": "EPHA1",
758
+ "symbolSize": 10.0,
759
+ "value": "2.0000",
760
+ "x": -69.15957998735114,
761
+ "y": 14.29122637970244
762
+ },
763
+ {
764
+ "id": "NRTN",
765
+ "itemStyle": {
766
+ "color": "#481e70"
767
+ },
768
+ "label": {
769
+ "show": false
770
+ },
771
+ "name": "NRTN",
772
+ "symbolSize": 10.0,
773
+ "value": "2.0000",
774
+ "x": 21.426971876846405,
775
+ "y": -72.26164554719745
776
+ },
777
+ {
778
+ "id": "TXN2",
779
+ "itemStyle": {
780
+ "color": "#481e70"
781
+ },
782
+ "label": {
783
+ "show": false
784
+ },
785
+ "name": "TXN2",
786
+ "symbolSize": 10.0,
787
+ "value": "2.0000",
788
+ "x": 8.99339278412953,
789
+ "y": -27.9722418977822
790
+ },
791
+ {
792
+ "id": "CDCA8",
793
+ "itemStyle": {
794
+ "color": "#481e70"
795
+ },
796
+ "label": {
797
+ "show": false
798
+ },
799
+ "name": "CDCA8",
800
+ "symbolSize": 10.0,
801
+ "value": "2.0000",
802
+ "x": -47.26302381279268,
803
+ "y": -60.141398827231676
804
+ },
805
+ {
806
+ "id": "EP300",
807
+ "itemStyle": {
808
+ "color": "#443982"
809
+ },
810
+ "label": {
811
+ "show": false
812
+ },
813
+ "name": "EP300",
814
+ "symbolSize": 10.0,
815
+ "value": "2.0000",
816
+ "x": 8.525046953484674,
817
+ "y": 3.8533224260029435
818
+ },
819
+ {
820
+ "id": "LAMA2",
821
+ "itemStyle": {
822
+ "color": "#287b8e"
823
+ },
824
+ "label": {
825
+ "show": true
826
+ },
827
+ "name": "LAMA2",
828
+ "symbolSize": 20.0,
829
+ "value": "2.0000",
830
+ "x": -40.64067955981509,
831
+ "y": -12.69085326433022
832
+ },
833
+ {
834
+ "id": "SMAP1",
835
+ "itemStyle": {
836
+ "color": "#443982"
837
+ },
838
+ "label": {
839
+ "show": false
840
+ },
841
+ "name": "SMAP1",
842
+ "symbolSize": 10.0,
843
+ "value": "2.0000",
844
+ "x": 79.00813493429104,
845
+ "y": 7.109325676320026
846
+ },
847
+ {
848
+ "id": "MB",
849
+ "itemStyle": {
850
+ "color": "#440154"
851
+ },
852
+ "label": {
853
+ "show": false
854
+ },
855
+ "name": "MB",
856
+ "symbolSize": 10.0,
857
+ "value": "2.0000",
858
+ "x": -10.267256927830365,
859
+ "y": -30.480478177620256
860
+ },
861
+ {
862
+ "id": "PPARG",
863
+ "itemStyle": {
864
+ "color": "#20908c"
865
+ },
866
+ "label": {
867
+ "show": true
868
+ },
869
+ "name": "PPARG",
870
+ "symbolSize": 20.0,
871
+ "value": "2.0000",
872
+ "x": -26.797476134955446,
873
+ "y": 8.128454663968185
874
+ },
875
+ {
876
+ "id": "SCARB2",
877
+ "itemStyle": {
878
+ "color": "#443982"
879
+ },
880
+ "label": {
881
+ "show": false
882
+ },
883
+ "name": "SCARB2",
884
+ "symbolSize": 10.0,
885
+ "value": "2.0000",
886
+ "x": 8.350390034417561,
887
+ "y": 95.15332096136808
888
+ },
889
+ {
890
+ "id": "VLDLR",
891
+ "itemStyle": {
892
+ "color": "#443982"
893
+ },
894
+ "label": {
895
+ "show": false
896
+ },
897
+ "name": "VLDLR",
898
+ "symbolSize": 10.0,
899
+ "value": "2.0000",
900
+ "x": -38.0054637894368,
901
+ "y": 28.022049827517737
902
+ },
903
+ {
904
+ "id": "AKT2",
905
+ "itemStyle": {
906
+ "color": "#fde724"
907
+ },
908
+ "label": {
909
+ "show": true
910
+ },
911
+ "name": "AKT2",
912
+ "symbolSize": 20.0,
913
+ "value": "2.0000",
914
+ "x": -14.824434481647419,
915
+ "y": 33.73069510233134
916
+ },
917
+ {
918
+ "id": "NPR2",
919
+ "itemStyle": {
920
+ "color": "#20908c"
921
+ },
922
+ "label": {
923
+ "show": true
924
+ },
925
+ "name": "NPR2",
926
+ "symbolSize": 20.0,
927
+ "value": "2.0000",
928
+ "x": -2.073646304452374,
929
+ "y": -20.801692619079045
930
+ },
931
+ {
932
+ "id": "RELA",
933
+ "itemStyle": {
934
+ "color": "#35b778"
935
+ },
936
+ "label": {
937
+ "show": true
938
+ },
939
+ "name": "RELA",
940
+ "symbolSize": 20.0,
941
+ "value": "2.0000",
942
+ "x": -25.96769855224908,
943
+ "y": 24.426814055796253
944
+ },
945
+ {
946
+ "id": "AKT1",
947
+ "itemStyle": {
948
+ "color": "#20908c"
949
+ },
950
+ "label": {
951
+ "show": true
952
+ },
953
+ "name": "AKT1",
954
+ "symbolSize": 20.0,
955
+ "value": "2.0000",
956
+ "x": -5.296448927371995,
957
+ "y": 4.780165351810345
958
+ },
959
+ {
960
+ "id": "MAPKAPK5",
961
+ "itemStyle": {
962
+ "color": "#443982"
963
+ },
964
+ "label": {
965
+ "show": false
966
+ },
967
+ "name": "MAPKAPK5",
968
+ "symbolSize": 10.0,
969
+ "value": "2.0000",
970
+ "x": -54.57632064930378,
971
+ "y": 23.18859203370709
972
+ },
973
+ {
974
+ "id": "NR4A2",
975
+ "itemStyle": {
976
+ "color": "#443982"
977
+ },
978
+ "label": {
979
+ "show": false
980
+ },
981
+ "name": "NR4A2",
982
+ "symbolSize": 10.0,
983
+ "value": "2.0000",
984
+ "x": 32.32579136578825,
985
+ "y": 7.444893193996152
986
+ },
987
+ {
988
+ "id": "CDKN2A",
989
+ "itemStyle": {
990
+ "color": "#287b8e"
991
+ },
992
+ "label": {
993
+ "show": true
994
+ },
995
+ "name": "CDKN2A",
996
+ "symbolSize": 20.0,
997
+ "value": "2.0000",
998
+ "x": 19.20417859372585,
999
+ "y": 27.89594485445154
1000
+ },
1001
+ {
1002
+ "id": "CORO1A",
1003
+ "itemStyle": {
1004
+ "color": "#30678d"
1005
+ },
1006
+ "label": {
1007
+ "show": true
1008
+ },
1009
+ "name": "CORO1A",
1010
+ "symbolSize": 20.0,
1011
+ "value": "2.0000",
1012
+ "x": 7.182464349283451,
1013
+ "y": 40.87345514606569
1014
+ },
1015
+ {
1016
+ "id": "MAPK9",
1017
+ "itemStyle": {
1018
+ "color": "#443982"
1019
+ },
1020
+ "label": {
1021
+ "show": false
1022
+ },
1023
+ "name": "MAPK9",
1024
+ "symbolSize": 10.0,
1025
+ "value": "2.0000",
1026
+ "x": -8.911060785673385,
1027
+ "y": -4.41712644873975
1028
+ },
1029
+ {
1030
+ "id": "GPX1",
1031
+ "itemStyle": {
1032
+ "color": "#30678d"
1033
+ },
1034
+ "label": {
1035
+ "show": false
1036
+ },
1037
+ "name": "GPX1",
1038
+ "symbolSize": 10.0,
1039
+ "value": "2.0000",
1040
+ "x": -21.412551972442557,
1041
+ "y": -4.238119271373616
1042
+ },
1043
+ {
1044
+ "id": "MLLT3",
1045
+ "itemStyle": {
1046
+ "color": "#3a528b"
1047
+ },
1048
+ "label": {
1049
+ "show": false
1050
+ },
1051
+ "name": "MLLT3",
1052
+ "symbolSize": 10.0,
1053
+ "value": "2.0000",
1054
+ "x": 46.19757670726592,
1055
+ "y": -54.70481169278493
1056
+ },
1057
+ {
1058
+ "id": "PAXIP1",
1059
+ "itemStyle": {
1060
+ "color": "#20908c"
1061
+ },
1062
+ "label": {
1063
+ "show": true
1064
+ },
1065
+ "name": "PAXIP1",
1066
+ "symbolSize": 20.0,
1067
+ "value": "2.0000",
1068
+ "x": 24.524037670564823,
1069
+ "y": -30.881582282872248
1070
+ },
1071
+ {
1072
+ "id": "PPP3CA",
1073
+ "itemStyle": {
1074
+ "color": "#440154"
1075
+ },
1076
+ "label": {
1077
+ "show": false
1078
+ },
1079
+ "name": "PPP3CA",
1080
+ "symbolSize": 10.0,
1081
+ "value": "2.0000",
1082
+ "x": -15.869586417849533,
1083
+ "y": 18.58650466080172
1084
+ },
1085
+ {
1086
+ "id": "CD74",
1087
+ "itemStyle": {
1088
+ "color": "#440154"
1089
+ },
1090
+ "label": {
1091
+ "show": false
1092
+ },
1093
+ "name": "CD74",
1094
+ "symbolSize": 10.0,
1095
+ "value": "2.0000",
1096
+ "x": 22.579374859054546,
1097
+ "y": -2.3500061723308625
1098
+ }
1099
+ ],
1100
+ "emphasis": {
1101
+ "focus": "adjacency",
1102
+ "lineStyle": {
1103
+ "width": 3.0
1104
+ }
1105
+ },
1106
+ "label": {
1107
+ "formatter": "{b}",
1108
+ "position": "top"
1109
+ },
1110
+ "layout": "none",
1111
+ "lineStyle": {
1112
+ "color": "#cccccc",
1113
+ "curveness": 0.1
1114
+ },
1115
+ "links": [
1116
+ {
1117
+ "source": "PAK6",
1118
+ "target": "IKBKG"
1119
+ },
1120
+ {
1121
+ "source": "PAK6",
1122
+ "target": "RELA"
1123
+ },
1124
+ {
1125
+ "source": "PAK6",
1126
+ "target": "MAPK9"
1127
+ },
1128
+ {
1129
+ "source": "PAK6",
1130
+ "target": "AKT1"
1131
+ },
1132
+ {
1133
+ "source": "PAK6",
1134
+ "target": "PPP3CA"
1135
+ },
1136
+ {
1137
+ "source": "PAK6",
1138
+ "target": "AKT2"
1139
+ },
1140
+ {
1141
+ "source": "PAK6",
1142
+ "target": "NPR2"
1143
+ },
1144
+ {
1145
+ "source": "PAK6",
1146
+ "target": "EP300"
1147
+ },
1148
+ {
1149
+ "source": "PAK6",
1150
+ "target": "CASP3"
1151
+ },
1152
+ {
1153
+ "source": "PAK6",
1154
+ "target": "LAMA2"
1155
+ },
1156
+ {
1157
+ "source": "PAK6",
1158
+ "target": "SCARB2"
1159
+ },
1160
+ {
1161
+ "source": "IKBKG",
1162
+ "target": "PAXIP1"
1163
+ },
1164
+ {
1165
+ "source": "IKBKG",
1166
+ "target": "GPX1"
1167
+ },
1168
+ {
1169
+ "source": "IKBKG",
1170
+ "target": "EPHA1"
1171
+ },
1172
+ {
1173
+ "source": "IKBKG",
1174
+ "target": "GNAI3"
1175
+ },
1176
+ {
1177
+ "source": "IKBKG",
1178
+ "target": "MB"
1179
+ },
1180
+ {
1181
+ "source": "IKBKG",
1182
+ "target": "AKT2"
1183
+ },
1184
+ {
1185
+ "source": "IKBKG",
1186
+ "target": "PPP3CA"
1187
+ },
1188
+ {
1189
+ "source": "IKBKG",
1190
+ "target": "SELP"
1191
+ },
1192
+ {
1193
+ "source": "IKBKG",
1194
+ "target": "EP300"
1195
+ },
1196
+ {
1197
+ "source": "IKBKG",
1198
+ "target": "CDKN2A"
1199
+ },
1200
+ {
1201
+ "source": "IKBKG",
1202
+ "target": "NR4A2"
1203
+ },
1204
+ {
1205
+ "source": "IKBKG",
1206
+ "target": "MAPKAPK5"
1207
+ },
1208
+ {
1209
+ "source": "IKBKG",
1210
+ "target": "LAMA2"
1211
+ },
1212
+ {
1213
+ "source": "IKBKG",
1214
+ "target": "PPARG"
1215
+ },
1216
+ {
1217
+ "source": "IKBKG",
1218
+ "target": "SCARB2"
1219
+ },
1220
+ {
1221
+ "source": "IKBKG",
1222
+ "target": "MAPK9"
1223
+ },
1224
+ {
1225
+ "source": "IKBKG",
1226
+ "target": "CHD2"
1227
+ },
1228
+ {
1229
+ "source": "IKBKG",
1230
+ "target": "RELA"
1231
+ },
1232
+ {
1233
+ "source": "IKBKG",
1234
+ "target": "CASP3"
1235
+ },
1236
+ {
1237
+ "source": "IKBKG",
1238
+ "target": "AKT1"
1239
+ },
1240
+ {
1241
+ "source": "IKBKG",
1242
+ "target": "XRCC2"
1243
+ },
1244
+ {
1245
+ "source": "CTNNBIP1",
1246
+ "target": "WNT5B"
1247
+ },
1248
+ {
1249
+ "source": "CTNNBIP1",
1250
+ "target": "GPX1"
1251
+ },
1252
+ {
1253
+ "source": "CTNNBIP1",
1254
+ "target": "CDKN2A"
1255
+ },
1256
+ {
1257
+ "source": "CTNNBIP1",
1258
+ "target": "MAPK9"
1259
+ },
1260
+ {
1261
+ "source": "CTNNBIP1",
1262
+ "target": "TXN2"
1263
+ },
1264
+ {
1265
+ "source": "CTNNBIP1",
1266
+ "target": "IL7"
1267
+ },
1268
+ {
1269
+ "source": "CTNNBIP1",
1270
+ "target": "PPP3CA"
1271
+ },
1272
+ {
1273
+ "source": "CTNNBIP1",
1274
+ "target": "EP300"
1275
+ },
1276
+ {
1277
+ "source": "CTNNBIP1",
1278
+ "target": "NRTN"
1279
+ },
1280
+ {
1281
+ "source": "CTNNBIP1",
1282
+ "target": "NPR2"
1283
+ },
1284
+ {
1285
+ "source": "CTNNBIP1",
1286
+ "target": "MLLT3"
1287
+ },
1288
+ {
1289
+ "source": "CTNNBIP1",
1290
+ "target": "MB"
1291
+ },
1292
+ {
1293
+ "source": "CTNNBIP1",
1294
+ "target": "ATP6V1B1"
1295
+ },
1296
+ {
1297
+ "source": "WNT5B",
1298
+ "target": "EPHA1"
1299
+ },
1300
+ {
1301
+ "source": "WNT5B",
1302
+ "target": "AKT2"
1303
+ },
1304
+ {
1305
+ "source": "WNT5B",
1306
+ "target": "MB"
1307
+ },
1308
+ {
1309
+ "source": "WNT5B",
1310
+ "target": "GPX1"
1311
+ },
1312
+ {
1313
+ "source": "WNT5B",
1314
+ "target": "SMO"
1315
+ },
1316
+ {
1317
+ "source": "WNT5B",
1318
+ "target": "EP300"
1319
+ },
1320
+ {
1321
+ "source": "WNT5B",
1322
+ "target": "PPP3CA"
1323
+ },
1324
+ {
1325
+ "source": "WNT5B",
1326
+ "target": "PPARG"
1327
+ },
1328
+ {
1329
+ "source": "WNT5B",
1330
+ "target": "VLDLR"
1331
+ },
1332
+ {
1333
+ "source": "WNT5B",
1334
+ "target": "MAPK9"
1335
+ },
1336
+ {
1337
+ "source": "WNT5B",
1338
+ "target": "AKT1"
1339
+ },
1340
+ {
1341
+ "source": "WNT5B",
1342
+ "target": "CASP3"
1343
+ },
1344
+ {
1345
+ "source": "CASP3",
1346
+ "target": "VLDLR"
1347
+ },
1348
+ {
1349
+ "source": "CASP3",
1350
+ "target": "GPX1"
1351
+ },
1352
+ {
1353
+ "source": "CASP3",
1354
+ "target": "AKT1"
1355
+ },
1356
+ {
1357
+ "source": "CASP3",
1358
+ "target": "CORO1A"
1359
+ },
1360
+ {
1361
+ "source": "CASP3",
1362
+ "target": "XRCC2"
1363
+ },
1364
+ {
1365
+ "source": "CASP3",
1366
+ "target": "LAMA2"
1367
+ },
1368
+ {
1369
+ "source": "CASP3",
1370
+ "target": "EP300"
1371
+ },
1372
+ {
1373
+ "source": "CASP3",
1374
+ "target": "MB"
1375
+ },
1376
+ {
1377
+ "source": "CASP3",
1378
+ "target": "CD74"
1379
+ },
1380
+ {
1381
+ "source": "CASP3",
1382
+ "target": "MAPK9"
1383
+ },
1384
+ {
1385
+ "source": "CASP3",
1386
+ "target": "RELA"
1387
+ },
1388
+ {
1389
+ "source": "CASP3",
1390
+ "target": "PPARG"
1391
+ },
1392
+ {
1393
+ "source": "CASP3",
1394
+ "target": "PPP3CA"
1395
+ },
1396
+ {
1397
+ "source": "CASP3",
1398
+ "target": "PAXIP1"
1399
+ },
1400
+ {
1401
+ "source": "CASP3",
1402
+ "target": "AKT2"
1403
+ },
1404
+ {
1405
+ "source": "CASP3",
1406
+ "target": "CHD2"
1407
+ },
1408
+ {
1409
+ "source": "CASP3",
1410
+ "target": "GNAI3"
1411
+ },
1412
+ {
1413
+ "source": "CASP3",
1414
+ "target": "MAPKAPK5"
1415
+ },
1416
+ {
1417
+ "source": "CASP3",
1418
+ "target": "CDKN2A"
1419
+ },
1420
+ {
1421
+ "source": "CASP3",
1422
+ "target": "SMO"
1423
+ },
1424
+ {
1425
+ "source": "XRCC2",
1426
+ "target": "CHD2"
1427
+ },
1428
+ {
1429
+ "source": "XRCC2",
1430
+ "target": "AKT2"
1431
+ },
1432
+ {
1433
+ "source": "XRCC2",
1434
+ "target": "NPR2"
1435
+ },
1436
+ {
1437
+ "source": "XRCC2",
1438
+ "target": "PAXIP1"
1439
+ },
1440
+ {
1441
+ "source": "XRCC2",
1442
+ "target": "TXN2"
1443
+ },
1444
+ {
1445
+ "source": "XRCC2",
1446
+ "target": "CDKN2A"
1447
+ },
1448
+ {
1449
+ "source": "ATP6V1B1",
1450
+ "target": "EP300"
1451
+ },
1452
+ {
1453
+ "source": "ATP6V1B1",
1454
+ "target": "MAPK9"
1455
+ },
1456
+ {
1457
+ "source": "ATP6V1B1",
1458
+ "target": "CDKN2A"
1459
+ },
1460
+ {
1461
+ "source": "ATP6V1B1",
1462
+ "target": "PAXIP1"
1463
+ },
1464
+ {
1465
+ "source": "ATP6V1B1",
1466
+ "target": "CORO1A"
1467
+ },
1468
+ {
1469
+ "source": "ATP6V1B1",
1470
+ "target": "CHD2"
1471
+ },
1472
+ {
1473
+ "source": "SMO",
1474
+ "target": "GPX1"
1475
+ },
1476
+ {
1477
+ "source": "SMO",
1478
+ "target": "MAPKAPK5"
1479
+ },
1480
+ {
1481
+ "source": "SMO",
1482
+ "target": "NPR2"
1483
+ },
1484
+ {
1485
+ "source": "SMO",
1486
+ "target": "GNAI3"
1487
+ },
1488
+ {
1489
+ "source": "SMO",
1490
+ "target": "ATP8B1"
1491
+ },
1492
+ {
1493
+ "source": "SMO",
1494
+ "target": "MAPK9"
1495
+ },
1496
+ {
1497
+ "source": "SMO",
1498
+ "target": "TXN2"
1499
+ },
1500
+ {
1501
+ "source": "SMO",
1502
+ "target": "EP300"
1503
+ },
1504
+ {
1505
+ "source": "SMO",
1506
+ "target": "CDKN2A"
1507
+ },
1508
+ {
1509
+ "source": "SMO",
1510
+ "target": "RELA"
1511
+ },
1512
+ {
1513
+ "source": "SMO",
1514
+ "target": "EPHA1"
1515
+ },
1516
+ {
1517
+ "source": "CHD2",
1518
+ "target": "MAPK9"
1519
+ },
1520
+ {
1521
+ "source": "CHD2",
1522
+ "target": "CD74"
1523
+ },
1524
+ {
1525
+ "source": "CHD2",
1526
+ "target": "PAXIP1"
1527
+ },
1528
+ {
1529
+ "source": "ATP8B1",
1530
+ "target": "PAXIP1"
1531
+ },
1532
+ {
1533
+ "source": "ATP8B1",
1534
+ "target": "NRTN"
1535
+ },
1536
+ {
1537
+ "source": "SELP",
1538
+ "target": "MLLT3"
1539
+ },
1540
+ {
1541
+ "source": "SELP",
1542
+ "target": "SMAP1"
1543
+ },
1544
+ {
1545
+ "source": "SELP",
1546
+ "target": "NPR2"
1547
+ },
1548
+ {
1549
+ "source": "SELP",
1550
+ "target": "TXN2"
1551
+ },
1552
+ {
1553
+ "source": "SELP",
1554
+ "target": "PAXIP1"
1555
+ },
1556
+ {
1557
+ "source": "GNAI3",
1558
+ "target": "CDCA8"
1559
+ },
1560
+ {
1561
+ "source": "GNAI3",
1562
+ "target": "LAMA2"
1563
+ },
1564
+ {
1565
+ "source": "GNAI3",
1566
+ "target": "AKT2"
1567
+ },
1568
+ {
1569
+ "source": "GNAI3",
1570
+ "target": "RELA"
1571
+ },
1572
+ {
1573
+ "source": "GNAI3",
1574
+ "target": "EP300"
1575
+ },
1576
+ {
1577
+ "source": "GNAI3",
1578
+ "target": "PPP3CA"
1579
+ },
1580
+ {
1581
+ "source": "GNAI3",
1582
+ "target": "EPHA1"
1583
+ },
1584
+ {
1585
+ "source": "GNAI3",
1586
+ "target": "MAPK9"
1587
+ },
1588
+ {
1589
+ "source": "GNAI3",
1590
+ "target": "AKT1"
1591
+ },
1592
+ {
1593
+ "source": "IL7",
1594
+ "target": "CORO1A"
1595
+ },
1596
+ {
1597
+ "source": "IL7",
1598
+ "target": "EP300"
1599
+ },
1600
+ {
1601
+ "source": "IL7",
1602
+ "target": "AKT1"
1603
+ },
1604
+ {
1605
+ "source": "IL7",
1606
+ "target": "VLDLR"
1607
+ },
1608
+ {
1609
+ "source": "IL7",
1610
+ "target": "AKT2"
1611
+ },
1612
+ {
1613
+ "source": "IL7",
1614
+ "target": "PAXIP1"
1615
+ },
1616
+ {
1617
+ "source": "EPHA1",
1618
+ "target": "PPP3CA"
1619
+ },
1620
+ {
1621
+ "source": "EPHA1",
1622
+ "target": "VLDLR"
1623
+ },
1624
+ {
1625
+ "source": "EPHA1",
1626
+ "target": "NPR2"
1627
+ },
1628
+ {
1629
+ "source": "NRTN",
1630
+ "target": "CD74"
1631
+ },
1632
+ {
1633
+ "source": "NRTN",
1634
+ "target": "MB"
1635
+ },
1636
+ {
1637
+ "source": "NRTN",
1638
+ "target": "MLLT3"
1639
+ },
1640
+ {
1641
+ "source": "NRTN",
1642
+ "target": "NPR2"
1643
+ },
1644
+ {
1645
+ "source": "TXN2",
1646
+ "target": "AKT1"
1647
+ },
1648
+ {
1649
+ "source": "TXN2",
1650
+ "target": "VLDLR"
1651
+ },
1652
+ {
1653
+ "source": "TXN2",
1654
+ "target": "EP300"
1655
+ },
1656
+ {
1657
+ "source": "TXN2",
1658
+ "target": "MB"
1659
+ },
1660
+ {
1661
+ "source": "TXN2",
1662
+ "target": "PPARG"
1663
+ },
1664
+ {
1665
+ "source": "TXN2",
1666
+ "target": "NR4A2"
1667
+ },
1668
+ {
1669
+ "source": "TXN2",
1670
+ "target": "GPX1"
1671
+ },
1672
+ {
1673
+ "source": "CDCA8",
1674
+ "target": "PAXIP1"
1675
+ },
1676
+ {
1677
+ "source": "CDCA8",
1678
+ "target": "LAMA2"
1679
+ },
1680
+ {
1681
+ "source": "CDCA8",
1682
+ "target": "MAPK9"
1683
+ },
1684
+ {
1685
+ "source": "EP300",
1686
+ "target": "CDKN2A"
1687
+ },
1688
+ {
1689
+ "source": "EP300",
1690
+ "target": "NPR2"
1691
+ },
1692
+ {
1693
+ "source": "EP300",
1694
+ "target": "VLDLR"
1695
+ },
1696
+ {
1697
+ "source": "EP300",
1698
+ "target": "AKT2"
1699
+ },
1700
+ {
1701
+ "source": "EP300",
1702
+ "target": "SMAP1"
1703
+ },
1704
+ {
1705
+ "source": "EP300",
1706
+ "target": "NR4A2"
1707
+ },
1708
+ {
1709
+ "source": "EP300",
1710
+ "target": "PPP3CA"
1711
+ },
1712
+ {
1713
+ "source": "EP300",
1714
+ "target": "AKT1"
1715
+ },
1716
+ {
1717
+ "source": "EP300",
1718
+ "target": "RELA"
1719
+ },
1720
+ {
1721
+ "source": "EP300",
1722
+ "target": "MB"
1723
+ },
1724
+ {
1725
+ "source": "EP300",
1726
+ "target": "MLLT3"
1727
+ },
1728
+ {
1729
+ "source": "EP300",
1730
+ "target": "CORO1A"
1731
+ },
1732
+ {
1733
+ "source": "EP300",
1734
+ "target": "CD74"
1735
+ },
1736
+ {
1737
+ "source": "EP300",
1738
+ "target": "MAPK9"
1739
+ },
1740
+ {
1741
+ "source": "EP300",
1742
+ "target": "GPX1"
1743
+ },
1744
+ {
1745
+ "source": "EP300",
1746
+ "target": "PPARG"
1747
+ },
1748
+ {
1749
+ "source": "LAMA2",
1750
+ "target": "CD74"
1751
+ },
1752
+ {
1753
+ "source": "LAMA2",
1754
+ "target": "AKT2"
1755
+ },
1756
+ {
1757
+ "source": "LAMA2",
1758
+ "target": "MAPK9"
1759
+ },
1760
+ {
1761
+ "source": "LAMA2",
1762
+ "target": "GPX1"
1763
+ },
1764
+ {
1765
+ "source": "LAMA2",
1766
+ "target": "RELA"
1767
+ },
1768
+ {
1769
+ "source": "LAMA2",
1770
+ "target": "AKT1"
1771
+ },
1772
+ {
1773
+ "source": "SMAP1",
1774
+ "target": "CD74"
1775
+ },
1776
+ {
1777
+ "source": "SMAP1",
1778
+ "target": "NR4A2"
1779
+ },
1780
+ {
1781
+ "source": "SMAP1",
1782
+ "target": "CDKN2A"
1783
+ },
1784
+ {
1785
+ "source": "MB",
1786
+ "target": "NR4A2"
1787
+ },
1788
+ {
1789
+ "source": "MB",
1790
+ "target": "PAXIP1"
1791
+ },
1792
+ {
1793
+ "source": "MB",
1794
+ "target": "GPX1"
1795
+ },
1796
+ {
1797
+ "source": "MB",
1798
+ "target": "AKT1"
1799
+ },
1800
+ {
1801
+ "source": "MB",
1802
+ "target": "PPARG"
1803
+ },
1804
+ {
1805
+ "source": "MB",
1806
+ "target": "VLDLR"
1807
+ },
1808
+ {
1809
+ "source": "MB",
1810
+ "target": "MAPK9"
1811
+ },
1812
+ {
1813
+ "source": "PPARG",
1814
+ "target": "GPX1"
1815
+ },
1816
+ {
1817
+ "source": "PPARG",
1818
+ "target": "CORO1A"
1819
+ },
1820
+ {
1821
+ "source": "PPARG",
1822
+ "target": "AKT1"
1823
+ },
1824
+ {
1825
+ "source": "PPARG",
1826
+ "target": "VLDLR"
1827
+ },
1828
+ {
1829
+ "source": "PPARG",
1830
+ "target": "NR4A2"
1831
+ },
1832
+ {
1833
+ "source": "PPARG",
1834
+ "target": "NPR2"
1835
+ },
1836
+ {
1837
+ "source": "PPARG",
1838
+ "target": "RELA"
1839
+ },
1840
+ {
1841
+ "source": "PPARG",
1842
+ "target": "MAPK9"
1843
+ },
1844
+ {
1845
+ "source": "PPARG",
1846
+ "target": "AKT2"
1847
+ },
1848
+ {
1849
+ "source": "PPARG",
1850
+ "target": "PPP3CA"
1851
+ },
1852
+ {
1853
+ "source": "SCARB2",
1854
+ "target": "CDKN2A"
1855
+ },
1856
+ {
1857
+ "source": "VLDLR",
1858
+ "target": "AKT1"
1859
+ },
1860
+ {
1861
+ "source": "VLDLR",
1862
+ "target": "GPX1"
1863
+ },
1864
+ {
1865
+ "source": "AKT2",
1866
+ "target": "PPP3CA"
1867
+ },
1868
+ {
1869
+ "source": "AKT2",
1870
+ "target": "MAPKAPK5"
1871
+ },
1872
+ {
1873
+ "source": "AKT2",
1874
+ "target": "CD74"
1875
+ },
1876
+ {
1877
+ "source": "AKT2",
1878
+ "target": "AKT1"
1879
+ },
1880
+ {
1881
+ "source": "AKT2",
1882
+ "target": "GPX1"
1883
+ },
1884
+ {
1885
+ "source": "AKT2",
1886
+ "target": "MAPK9"
1887
+ },
1888
+ {
1889
+ "source": "AKT2",
1890
+ "target": "CORO1A"
1891
+ },
1892
+ {
1893
+ "source": "AKT2",
1894
+ "target": "RELA"
1895
+ },
1896
+ {
1897
+ "source": "AKT2",
1898
+ "target": "CDKN2A"
1899
+ },
1900
+ {
1901
+ "source": "NPR2",
1902
+ "target": "PPP3CA"
1903
+ },
1904
+ {
1905
+ "source": "RELA",
1906
+ "target": "MAPKAPK5"
1907
+ },
1908
+ {
1909
+ "source": "RELA",
1910
+ "target": "CD74"
1911
+ },
1912
+ {
1913
+ "source": "RELA",
1914
+ "target": "CDKN2A"
1915
+ },
1916
+ {
1917
+ "source": "RELA",
1918
+ "target": "MAPK9"
1919
+ },
1920
+ {
1921
+ "source": "RELA",
1922
+ "target": "GPX1"
1923
+ },
1924
+ {
1925
+ "source": "RELA",
1926
+ "target": "AKT1"
1927
+ },
1928
+ {
1929
+ "source": "RELA",
1930
+ "target": "CORO1A"
1931
+ },
1932
+ {
1933
+ "source": "RELA",
1934
+ "target": "NR4A2"
1935
+ },
1936
+ {
1937
+ "source": "RELA",
1938
+ "target": "PPP3CA"
1939
+ },
1940
+ {
1941
+ "source": "AKT1",
1942
+ "target": "GPX1"
1943
+ },
1944
+ {
1945
+ "source": "AKT1",
1946
+ "target": "MLLT3"
1947
+ },
1948
+ {
1949
+ "source": "AKT1",
1950
+ "target": "MAPK9"
1951
+ },
1952
+ {
1953
+ "source": "AKT1",
1954
+ "target": "MAPKAPK5"
1955
+ },
1956
+ {
1957
+ "source": "AKT1",
1958
+ "target": "CDKN2A"
1959
+ },
1960
+ {
1961
+ "source": "AKT1",
1962
+ "target": "CORO1A"
1963
+ },
1964
+ {
1965
+ "source": "AKT1",
1966
+ "target": "CD74"
1967
+ },
1968
+ {
1969
+ "source": "AKT1",
1970
+ "target": "PPP3CA"
1971
+ },
1972
+ {
1973
+ "source": "MAPKAPK5",
1974
+ "target": "PPP3CA"
1975
+ },
1976
+ {
1977
+ "source": "MAPKAPK5",
1978
+ "target": "MAPK9"
1979
+ },
1980
+ {
1981
+ "source": "NR4A2",
1982
+ "target": "PPP3CA"
1983
+ },
1984
+ {
1985
+ "source": "CDKN2A",
1986
+ "target": "GPX1"
1987
+ },
1988
+ {
1989
+ "source": "CDKN2A",
1990
+ "target": "CORO1A"
1991
+ },
1992
+ {
1993
+ "source": "CDKN2A",
1994
+ "target": "MAPK9"
1995
+ },
1996
+ {
1997
+ "source": "CORO1A",
1998
+ "target": "PPP3CA"
1999
+ },
2000
+ {
2001
+ "source": "CORO1A",
2002
+ "target": "MAPK9"
2003
+ },
2004
+ {
2005
+ "source": "CORO1A",
2006
+ "target": "GPX1"
2007
+ },
2008
+ {
2009
+ "source": "CORO1A",
2010
+ "target": "CD74"
2011
+ },
2012
+ {
2013
+ "source": "MAPK9",
2014
+ "target": "CD74"
2015
+ },
2016
+ {
2017
+ "source": "MAPK9",
2018
+ "target": "PPP3CA"
2019
+ },
2020
+ {
2021
+ "source": "MAPK9",
2022
+ "target": "GPX1"
2023
+ },
2024
+ {
2025
+ "source": "GPX1",
2026
+ "target": "PPP3CA"
2027
+ },
2028
+ {
2029
+ "source": "MLLT3",
2030
+ "target": "PAXIP1"
2031
+ },
2032
+ {
2033
+ "source": "PAXIP1",
2034
+ "target": "PPP3CA"
2035
+ },
2036
+ {
2037
+ "source": "PPP3CA",
2038
+ "target": "CD74"
2039
+ }
2040
+ ],
2041
+ "roam": true,
2042
+ "type": "graph"
2043
+ }
2044
+ ],
2045
+ "tooltip": {
2046
+ "formatter": "{b}: {c}",
2047
+ "show": true
2048
+ },
2049
+ "visualMap": {
2050
+ "calculable": true,
2051
+ "inRange": {
2052
+ "color": [
2053
+ "#440154",
2054
+ "#21918c",
2055
+ "#fde725"
2056
+ ]
2057
+ },
2058
+ "max": 2.000004291534424,
2059
+ "min": 2.0000014305114746,
2060
+ "realtime": false,
2061
+ "text": [
2062
+ "High",
2063
+ "Low"
2064
+ ]
2065
+ }
2066
+ },
2067
+ "error": null,
2068
+ "expanded_height": null,
2069
+ "input_metadata": [
2070
+ {
2071
+ "dataframes": {
2072
+ "attribution": {
2073
+ "columns": [
2074
+ "attention_score",
2075
+ "node"
2076
+ ],
2077
+ "key": "attribution"
2078
+ },
2079
+ "clin_df": {
2080
+ "columns": [
2081
+ "% BW Difference",
2082
+ "% TVol Difference",
2083
+ "Days Post T0",
2084
+ "Model",
2085
+ "Treatment",
2086
+ "Tumor Type",
2087
+ "Volume (mm3)",
2088
+ "body weight (g)"
2089
+ ],
2090
+ "key": "clin_df"
2091
+ },
2092
+ "disease": {
2093
+ "columns": [
2094
+ "disease",
2095
+ "globalScore",
2096
+ "symbol"
2097
+ ],
2098
+ "key": "disease"
2099
+ },
2100
+ "drug_gene": {
2101
+ "columns": [
2102
+ "drug_name",
2103
+ "gene_name"
2104
+ ],
2105
+ "key": "drug_gene"
2106
+ },
2107
+ "gene2esm2": {
2108
+ "columns": [
2109
+ "embedding"
2110
+ ],
2111
+ "key": "gene2esm2"
2112
+ },
2113
+ "gene_to_idx": {
2114
+ "columns": [
2115
+ "idx"
2116
+ ],
2117
+ "key": "gene_to_idx"
2118
+ },
2119
+ "meta": {
2120
+ "columns": [
2121
+ "Model",
2122
+ "Treatment",
2123
+ "Tumor Type",
2124
+ "future_timestamps",
2125
+ "future_volumes",
2126
+ "mrecist_class",
2127
+ "past_volumes"
2128
+ ],
2129
+ "key": "meta"
2130
+ },
2131
+ "rna_df": {
2132
+ "columns": [
2133
+ "X-1004",
2134
+ "X-1008",
2135
+ "X-1027",
2136
+ "X-1095",
2137
+ "X-1119",
2138
+ "X-1156",
2139
+ "X-1167",
2140
+ "X-1169",
2141
+ "X-1172",
2142
+ "X-1173",
2143
+ "X-1199",
2144
+ "X-1210",
2145
+ "X-1234",
2146
+ "X-1256",
2147
+ "X-1270",
2148
+ "X-1289",
2149
+ "X-1290",
2150
+ "X-1298",
2151
+ "X-1303",
2152
+ "X-1317",
2153
+ "X-1318",
2154
+ "X-1323",
2155
+ "X-1329",
2156
+ "X-1337",
2157
+ "X-1349",
2158
+ "X-1362",
2159
+ "X-1367",
2160
+ "X-1371",
2161
+ "X-1381",
2162
+ "X-1383",
2163
+ "X-1407",
2164
+ "X-1441",
2165
+ "X-1442",
2166
+ "X-1443",
2167
+ "X-1451",
2168
+ "X-1468",
2169
+ "X-1473",
2170
+ "X-1479",
2171
+ "X-1480",
2172
+ "X-1481",
2173
+ "X-1493",
2174
+ "X-1499",
2175
+ "X-1500",
2176
+ "X-1536",
2177
+ "X-1538",
2178
+ "X-1543",
2179
+ "X-1569",
2180
+ "X-1586",
2181
+ "X-1600",
2182
+ "X-1631",
2183
+ "X-1633",
2184
+ "X-1655",
2185
+ "X-1658",
2186
+ "X-1678",
2187
+ "X-1683",
2188
+ "X-1725",
2189
+ "X-1726",
2190
+ "X-1755",
2191
+ "X-1784",
2192
+ "X-1786",
2193
+ "X-1787",
2194
+ "X-1795",
2195
+ "X-1823",
2196
+ "X-1828",
2197
+ "X-1832",
2198
+ "X-1834",
2199
+ "X-1835",
2200
+ "X-1855",
2201
+ "X-1865",
2202
+ "X-1867",
2203
+ "X-1906",
2204
+ "X-1916",
2205
+ "X-1921",
2206
+ "X-1934",
2207
+ "X-1948",
2208
+ "X-1959",
2209
+ "X-1963",
2210
+ "X-1970",
2211
+ "X-1979",
2212
+ "X-1980",
2213
+ "X-1993",
2214
+ "X-2017",
2215
+ "X-2026",
2216
+ "X-2030",
2217
+ "X-2042",
2218
+ "X-2043",
2219
+ "X-2046",
2220
+ "X-2081",
2221
+ "X-2082",
2222
+ "X-2088",
2223
+ "X-2094",
2224
+ "X-2096",
2225
+ "X-2116",
2226
+ "X-2127",
2227
+ "X-2129",
2228
+ "X-2145",
2229
+ "X-2148",
2230
+ "X-2163",
2231
+ "X-2182",
2232
+ "X-2195",
2233
+ "X-2198",
2234
+ "X-2207",
2235
+ "X-2208",
2236
+ "X-2219",
2237
+ "X-2220",
2238
+ "X-2239",
2239
+ "X-2263",
2240
+ "X-2283",
2241
+ "X-2306",
2242
+ "X-2336",
2243
+ "X-2339",
2244
+ "X-2344",
2245
+ "X-2347",
2246
+ "X-2353",
2247
+ "X-2374",
2248
+ "X-2376",
2249
+ "X-2403",
2250
+ "X-2406",
2251
+ "X-2428",
2252
+ "X-2454",
2253
+ "X-2458",
2254
+ "X-2470",
2255
+ "X-2471",
2256
+ "X-2483",
2257
+ "X-2484",
2258
+ "X-2487",
2259
+ "X-2511",
2260
+ "X-2514",
2261
+ "X-2524",
2262
+ "X-2526",
2263
+ "X-2538",
2264
+ "X-2564",
2265
+ "X-2569",
2266
+ "X-2573",
2267
+ "X-2592",
2268
+ "X-2597",
2269
+ "X-2602",
2270
+ "X-2607",
2271
+ "X-2613",
2272
+ "X-2628",
2273
+ "X-2633",
2274
+ "X-2640",
2275
+ "X-2655",
2276
+ "X-2659",
2277
+ "X-2681",
2278
+ "X-2684",
2279
+ "X-2697",
2280
+ "X-2700",
2281
+ "X-2709",
2282
+ "X-2715",
2283
+ "X-2723",
2284
+ "X-2725",
2285
+ "X-2729",
2286
+ "X-2742",
2287
+ "X-2753",
2288
+ "X-2757",
2289
+ "X-2761",
2290
+ "X-2762",
2291
+ "X-2780",
2292
+ "X-2781",
2293
+ "X-2793",
2294
+ "X-2805",
2295
+ "X-2822",
2296
+ "X-2838",
2297
+ "X-2846",
2298
+ "X-2861",
2299
+ "X-2863",
2300
+ "X-2880",
2301
+ "X-2914",
2302
+ "X-2921",
2303
+ "X-2930",
2304
+ "X-2967",
2305
+ "X-2983",
2306
+ "X-2990",
2307
+ "X-2991",
2308
+ "X-2997",
2309
+ "X-3001",
2310
+ "X-3009",
2311
+ "X-3019",
2312
+ "X-3020",
2313
+ "X-3028",
2314
+ "X-3029",
2315
+ "X-3038",
2316
+ "X-3041",
2317
+ "X-3042",
2318
+ "X-3050",
2319
+ "X-3052",
2320
+ "X-3056",
2321
+ "X-3077",
2322
+ "X-3078",
2323
+ "X-3083",
2324
+ "X-3093",
2325
+ "X-3102",
2326
+ "X-3114",
2327
+ "X-3127",
2328
+ "X-3135",
2329
+ "X-3147",
2330
+ "X-3158",
2331
+ "X-3189",
2332
+ "X-3196",
2333
+ "X-3200",
2334
+ "X-3201",
2335
+ "X-3202",
2336
+ "X-3205",
2337
+ "X-3211",
2338
+ "X-3224",
2339
+ "X-3237",
2340
+ "X-3239",
2341
+ "X-3267",
2342
+ "X-3268",
2343
+ "X-3274",
2344
+ "X-3286",
2345
+ "X-3298",
2346
+ "X-3400",
2347
+ "X-3408",
2348
+ "X-3419",
2349
+ "X-3425",
2350
+ "X-3447",
2351
+ "X-3450",
2352
+ "X-3453",
2353
+ "X-3468",
2354
+ "X-3483",
2355
+ "X-3486",
2356
+ "X-3503",
2357
+ "X-3558",
2358
+ "X-3559",
2359
+ "X-3570",
2360
+ "X-3575",
2361
+ "X-3588",
2362
+ "X-3604",
2363
+ "X-3615",
2364
+ "X-3629",
2365
+ "X-3662",
2366
+ "X-3671",
2367
+ "X-3676",
2368
+ "X-3682",
2369
+ "X-3684",
2370
+ "X-3694",
2371
+ "X-3697",
2372
+ "X-3711",
2373
+ "X-3717",
2374
+ "X-3729",
2375
+ "X-3735",
2376
+ "X-3736",
2377
+ "X-3739",
2378
+ "X-3746",
2379
+ "X-3749",
2380
+ "X-3765",
2381
+ "X-3766",
2382
+ "X-3769",
2383
+ "X-3773",
2384
+ "X-3782",
2385
+ "X-3783",
2386
+ "X-3787",
2387
+ "X-3792",
2388
+ "X-3800",
2389
+ "X-3801",
2390
+ "X-3811",
2391
+ "X-3816",
2392
+ "X-3829",
2393
+ "X-3843",
2394
+ "X-3846",
2395
+ "X-3851",
2396
+ "X-3861",
2397
+ "X-3867",
2398
+ "X-3870",
2399
+ "X-3873",
2400
+ "X-3880",
2401
+ "X-3888",
2402
+ "X-3898",
2403
+ "X-3901",
2404
+ "X-3904",
2405
+ "X-3926",
2406
+ "X-3939",
2407
+ "X-3947",
2408
+ "X-3949",
2409
+ "X-3966",
2410
+ "X-3982",
2411
+ "X-3990",
2412
+ "X-4015",
2413
+ "X-4018",
2414
+ "X-4026",
2415
+ "X-4073",
2416
+ "X-4087",
2417
+ "X-4132",
2418
+ "X-4138",
2419
+ "X-4145",
2420
+ "X-4157",
2421
+ "X-4163",
2422
+ "X-4173",
2423
+ "X-4213",
2424
+ "X-4215",
2425
+ "X-4226",
2426
+ "X-4244",
2427
+ "X-4261",
2428
+ "X-4279",
2429
+ "X-4304",
2430
+ "X-4316",
2431
+ "X-4339",
2432
+ "X-4347",
2433
+ "X-4361",
2434
+ "X-4367",
2435
+ "X-4377",
2436
+ "X-4378",
2437
+ "X-4379",
2438
+ "X-4412",
2439
+ "X-4426",
2440
+ "X-4454",
2441
+ "X-4455",
2442
+ "X-4476",
2443
+ "X-4484",
2444
+ "X-4530",
2445
+ "X-4536",
2446
+ "X-4537",
2447
+ "X-4538",
2448
+ "X-4567",
2449
+ "X-4579",
2450
+ "X-4597",
2451
+ "X-4624",
2452
+ "X-4644",
2453
+ "X-4649",
2454
+ "X-4653",
2455
+ "X-4664",
2456
+ "X-4668",
2457
+ "X-4676",
2458
+ "X-4797",
2459
+ "X-4799",
2460
+ "X-4818",
2461
+ "X-4819",
2462
+ "X-4824",
2463
+ "X-4826",
2464
+ "X-4829",
2465
+ "X-4832",
2466
+ "X-4833",
2467
+ "X-4849",
2468
+ "X-4852",
2469
+ "X-4863",
2470
+ "X-4869",
2471
+ "X-4882",
2472
+ "X-4886",
2473
+ "X-4888",
2474
+ "X-4905",
2475
+ "X-4927",
2476
+ "X-4948",
2477
+ "X-4949",
2478
+ "X-4979",
2479
+ "X-5018",
2480
+ "X-5041",
2481
+ "X-5077",
2482
+ "X-5094",
2483
+ "X-5189",
2484
+ "X-5205",
2485
+ "X-5238",
2486
+ "X-5249",
2487
+ "X-5254",
2488
+ "X-5261",
2489
+ "X-5273",
2490
+ "X-5307",
2491
+ "X-5316",
2492
+ "X-5355",
2493
+ "X-5374",
2494
+ "X-5388",
2495
+ "X-5405",
2496
+ "X-5415",
2497
+ "X-5421",
2498
+ "X-5438",
2499
+ "X-5441",
2500
+ "X-5446",
2501
+ "X-5462",
2502
+ "X-5490",
2503
+ "X-5493",
2504
+ "X-5494",
2505
+ "X-5495",
2506
+ "X-5502",
2507
+ "X-5508",
2508
+ "X-5541",
2509
+ "X-5563",
2510
+ "X-5566",
2511
+ "X-5578",
2512
+ "X-5584",
2513
+ "X-5593",
2514
+ "X-5594",
2515
+ "X-5595",
2516
+ "X-5627",
2517
+ "X-5643",
2518
+ "X-5645",
2519
+ "X-5693",
2520
+ "X-5694",
2521
+ "X-5696",
2522
+ "X-5713",
2523
+ "X-5717",
2524
+ "X-5727",
2525
+ "X-5739",
2526
+ "X-5808",
2527
+ "X-5959",
2528
+ "X-5974",
2529
+ "X-5975",
2530
+ "X-6030",
2531
+ "X-6047"
2532
+ ],
2533
+ "key": "rna_df"
2534
+ }
2535
+ },
2536
+ "other": {
2537
+ "disease_to_id": {
2538
+ "key": "disease_to_id"
2539
+ },
2540
+ "drug_to_id": {
2541
+ "key": "drug_to_id"
2542
+ },
2543
+ "model": {
2544
+ "key": "model"
2545
+ },
2546
+ "prediction": {
2547
+ "key": "prediction"
2548
+ },
2549
+ "root_path": {
2550
+ "key": "root_path"
2551
+ }
2552
+ },
2553
+ "relations": []
2554
+ }
2555
+ ],
2556
+ "message": null,
2557
+ "meta": null,
2558
+ "op_id": "Attribution > Visualize attribution",
2559
+ "params": {
2560
+ "neighbors_per_node": 10.0,
2561
+ "top_n": 10.0
2562
+ },
2563
+ "status": "done",
2564
+ "title": "Visualize attribution"
2565
+ },
2566
+ "height": 735.0,
2567
+ "id": "Visualize attribution 1",
2568
+ "position": {
2569
+ "x": 105.0,
2570
+ "y": 350.0
2571
+ },
2572
+ "type": "visualization",
2573
+ "width": 735.0
2574
+ },
2575
+ {
2576
+ "data": {
2577
+ "collapsed": null,
2578
+ "display": {
2579
+ "dataframes": {
2580
+ "explanation": {
2581
+ "columns": [
2582
+ "text"
2583
+ ],
2584
+ "data": [
2585
+ [
2586
+ "## Prediction\n\n**BYL719 + LEE011 is effective against pancreatic ductal adenocarcinoma** (p=0.985)\n\nTop 3 genes: AKT2, RELA, NPR2\n\n## Explanation\n\nBYL719 and LEE011 are targeted therapies that inhibit specific molecular pathways involved in cancer cell growth and survival. BYL719, also known as alpelisib, is a PI3K inhibitor, while LEE011, known as ribociclib, is a CDK4/6 inhibitor. The neural network's prediction that this combination is effective against pancreatic ductal adenocarcinoma likely hinges on the roles of AKT2, RELA, and NPR2 in the cancer's biology.\n\n1. **AKT2**: This gene encodes a protein kinase involved in the PI3K/AKT signaling pathway, which promotes cell proliferation and survival. Hyperactivity of this pathway is commonly found in various cancers, including pancreatic ductal adenocarcinoma. BYL719\u2019s inhibitory action on PI3K can suppress this pathway, thereby reducing cancer cell proliferation and survival.\n\n2. **RELA**: This gene encodes a subunit of the NF-kB transcription factor, which is involved in inflammatory responses, immune regulation, and cell survival pathways. Dysregulation of NF-kB signaling has been implicated in cancer progression and resistance to apoptosis. Targeting this pathway could help in reducing the survival signals and potentially improving the effectiveness of the treatment.\n\n3. **NPR2**: Although not as directly linked to cancer pathways as the other two genes, NPR2 is part of the natriuretic peptide signaling pathway, which can influence cellular proliferation and growth under certain conditions. Its role suggests possible cross-talk with pathways targeted by BYL719 and LEE011, either enhancing the effect of the blockade or modulating other growth processes in cancer cells.\n\nThe combination of BYL719 and LEE011 targets both the PI3K/AKT pathway and the cell cycle regulation (via CDK4/6 inhibition), potentially offering a comprehensive approach to disrupt multiple cancer-promoting pathways simultaneously. This multi-targeted approach could be particularly effective in treating pancreatic ductal adenocarcinoma, given its aggressive nature and the typically complex molecular signaling involved in its development and progression."
2587
+ ]
2588
+ ]
2589
+ }
2590
+ },
2591
+ "other": {},
2592
+ "relations": []
2593
+ },
2594
+ "error": null,
2595
+ "expanded_height": null,
2596
+ "input_metadata": [
2597
+ {
2598
+ "dataframes": {
2599
+ "attribution": {
2600
+ "columns": [
2601
+ "attention_score",
2602
+ "node"
2603
+ ],
2604
+ "key": "attribution"
2605
+ },
2606
+ "clin_df": {
2607
+ "columns": [
2608
+ "% BW Difference",
2609
+ "% TVol Difference",
2610
+ "Days Post T0",
2611
+ "Model",
2612
+ "Treatment",
2613
+ "Tumor Type",
2614
+ "Volume (mm3)",
2615
+ "body weight (g)"
2616
+ ],
2617
+ "key": "clin_df"
2618
+ },
2619
+ "disease": {
2620
+ "columns": [
2621
+ "disease",
2622
+ "globalScore",
2623
+ "symbol"
2624
+ ],
2625
+ "key": "disease"
2626
+ },
2627
+ "drug_gene": {
2628
+ "columns": [
2629
+ "drug_name",
2630
+ "gene_name"
2631
+ ],
2632
+ "key": "drug_gene"
2633
+ },
2634
+ "gene2esm2": {
2635
+ "columns": [
2636
+ "embedding"
2637
+ ],
2638
+ "key": "gene2esm2"
2639
+ },
2640
+ "gene_to_idx": {
2641
+ "columns": [
2642
+ "idx"
2643
+ ],
2644
+ "key": "gene_to_idx"
2645
+ },
2646
+ "meta": {
2647
+ "columns": [
2648
+ "Model",
2649
+ "Treatment",
2650
+ "Tumor Type",
2651
+ "future_timestamps",
2652
+ "future_volumes",
2653
+ "mrecist_class",
2654
+ "past_volumes"
2655
+ ],
2656
+ "key": "meta"
2657
+ },
2658
+ "rna_df": {
2659
+ "columns": [
2660
+ "X-1004",
2661
+ "X-1008",
2662
+ "X-1027",
2663
+ "X-1095",
2664
+ "X-1119",
2665
+ "X-1156",
2666
+ "X-1167",
2667
+ "X-1169",
2668
+ "X-1172",
2669
+ "X-1173",
2670
+ "X-1199",
2671
+ "X-1210",
2672
+ "X-1234",
2673
+ "X-1256",
2674
+ "X-1270",
2675
+ "X-1289",
2676
+ "X-1290",
2677
+ "X-1298",
2678
+ "X-1303",
2679
+ "X-1317",
2680
+ "X-1318",
2681
+ "X-1323",
2682
+ "X-1329",
2683
+ "X-1337",
2684
+ "X-1349",
2685
+ "X-1362",
2686
+ "X-1367",
2687
+ "X-1371",
2688
+ "X-1381",
2689
+ "X-1383",
2690
+ "X-1407",
2691
+ "X-1441",
2692
+ "X-1442",
2693
+ "X-1443",
2694
+ "X-1451",
2695
+ "X-1468",
2696
+ "X-1473",
2697
+ "X-1479",
2698
+ "X-1480",
2699
+ "X-1481",
2700
+ "X-1493",
2701
+ "X-1499",
2702
+ "X-1500",
2703
+ "X-1536",
2704
+ "X-1538",
2705
+ "X-1543",
2706
+ "X-1569",
2707
+ "X-1586",
2708
+ "X-1600",
2709
+ "X-1631",
2710
+ "X-1633",
2711
+ "X-1655",
2712
+ "X-1658",
2713
+ "X-1678",
2714
+ "X-1683",
2715
+ "X-1725",
2716
+ "X-1726",
2717
+ "X-1755",
2718
+ "X-1784",
2719
+ "X-1786",
2720
+ "X-1787",
2721
+ "X-1795",
2722
+ "X-1823",
2723
+ "X-1828",
2724
+ "X-1832",
2725
+ "X-1834",
2726
+ "X-1835",
2727
+ "X-1855",
2728
+ "X-1865",
2729
+ "X-1867",
2730
+ "X-1906",
2731
+ "X-1916",
2732
+ "X-1921",
2733
+ "X-1934",
2734
+ "X-1948",
2735
+ "X-1959",
2736
+ "X-1963",
2737
+ "X-1970",
2738
+ "X-1979",
2739
+ "X-1980",
2740
+ "X-1993",
2741
+ "X-2017",
2742
+ "X-2026",
2743
+ "X-2030",
2744
+ "X-2042",
2745
+ "X-2043",
2746
+ "X-2046",
2747
+ "X-2081",
2748
+ "X-2082",
2749
+ "X-2088",
2750
+ "X-2094",
2751
+ "X-2096",
2752
+ "X-2116",
2753
+ "X-2127",
2754
+ "X-2129",
2755
+ "X-2145",
2756
+ "X-2148",
2757
+ "X-2163",
2758
+ "X-2182",
2759
+ "X-2195",
2760
+ "X-2198",
2761
+ "X-2207",
2762
+ "X-2208",
2763
+ "X-2219",
2764
+ "X-2220",
2765
+ "X-2239",
2766
+ "X-2263",
2767
+ "X-2283",
2768
+ "X-2306",
2769
+ "X-2336",
2770
+ "X-2339",
2771
+ "X-2344",
2772
+ "X-2347",
2773
+ "X-2353",
2774
+ "X-2374",
2775
+ "X-2376",
2776
+ "X-2403",
2777
+ "X-2406",
2778
+ "X-2428",
2779
+ "X-2454",
2780
+ "X-2458",
2781
+ "X-2470",
2782
+ "X-2471",
2783
+ "X-2483",
2784
+ "X-2484",
2785
+ "X-2487",
2786
+ "X-2511",
2787
+ "X-2514",
2788
+ "X-2524",
2789
+ "X-2526",
2790
+ "X-2538",
2791
+ "X-2564",
2792
+ "X-2569",
2793
+ "X-2573",
2794
+ "X-2592",
2795
+ "X-2597",
2796
+ "X-2602",
2797
+ "X-2607",
2798
+ "X-2613",
2799
+ "X-2628",
2800
+ "X-2633",
2801
+ "X-2640",
2802
+ "X-2655",
2803
+ "X-2659",
2804
+ "X-2681",
2805
+ "X-2684",
2806
+ "X-2697",
2807
+ "X-2700",
2808
+ "X-2709",
2809
+ "X-2715",
2810
+ "X-2723",
2811
+ "X-2725",
2812
+ "X-2729",
2813
+ "X-2742",
2814
+ "X-2753",
2815
+ "X-2757",
2816
+ "X-2761",
2817
+ "X-2762",
2818
+ "X-2780",
2819
+ "X-2781",
2820
+ "X-2793",
2821
+ "X-2805",
2822
+ "X-2822",
2823
+ "X-2838",
2824
+ "X-2846",
2825
+ "X-2861",
2826
+ "X-2863",
2827
+ "X-2880",
2828
+ "X-2914",
2829
+ "X-2921",
2830
+ "X-2930",
2831
+ "X-2967",
2832
+ "X-2983",
2833
+ "X-2990",
2834
+ "X-2991",
2835
+ "X-2997",
2836
+ "X-3001",
2837
+ "X-3009",
2838
+ "X-3019",
2839
+ "X-3020",
2840
+ "X-3028",
2841
+ "X-3029",
2842
+ "X-3038",
2843
+ "X-3041",
2844
+ "X-3042",
2845
+ "X-3050",
2846
+ "X-3052",
2847
+ "X-3056",
2848
+ "X-3077",
2849
+ "X-3078",
2850
+ "X-3083",
2851
+ "X-3093",
2852
+ "X-3102",
2853
+ "X-3114",
2854
+ "X-3127",
2855
+ "X-3135",
2856
+ "X-3147",
2857
+ "X-3158",
2858
+ "X-3189",
2859
+ "X-3196",
2860
+ "X-3200",
2861
+ "X-3201",
2862
+ "X-3202",
2863
+ "X-3205",
2864
+ "X-3211",
2865
+ "X-3224",
2866
+ "X-3237",
2867
+ "X-3239",
2868
+ "X-3267",
2869
+ "X-3268",
2870
+ "X-3274",
2871
+ "X-3286",
2872
+ "X-3298",
2873
+ "X-3400",
2874
+ "X-3408",
2875
+ "X-3419",
2876
+ "X-3425",
2877
+ "X-3447",
2878
+ "X-3450",
2879
+ "X-3453",
2880
+ "X-3468",
2881
+ "X-3483",
2882
+ "X-3486",
2883
+ "X-3503",
2884
+ "X-3558",
2885
+ "X-3559",
2886
+ "X-3570",
2887
+ "X-3575",
2888
+ "X-3588",
2889
+ "X-3604",
2890
+ "X-3615",
2891
+ "X-3629",
2892
+ "X-3662",
2893
+ "X-3671",
2894
+ "X-3676",
2895
+ "X-3682",
2896
+ "X-3684",
2897
+ "X-3694",
2898
+ "X-3697",
2899
+ "X-3711",
2900
+ "X-3717",
2901
+ "X-3729",
2902
+ "X-3735",
2903
+ "X-3736",
2904
+ "X-3739",
2905
+ "X-3746",
2906
+ "X-3749",
2907
+ "X-3765",
2908
+ "X-3766",
2909
+ "X-3769",
2910
+ "X-3773",
2911
+ "X-3782",
2912
+ "X-3783",
2913
+ "X-3787",
2914
+ "X-3792",
2915
+ "X-3800",
2916
+ "X-3801",
2917
+ "X-3811",
2918
+ "X-3816",
2919
+ "X-3829",
2920
+ "X-3843",
2921
+ "X-3846",
2922
+ "X-3851",
2923
+ "X-3861",
2924
+ "X-3867",
2925
+ "X-3870",
2926
+ "X-3873",
2927
+ "X-3880",
2928
+ "X-3888",
2929
+ "X-3898",
2930
+ "X-3901",
2931
+ "X-3904",
2932
+ "X-3926",
2933
+ "X-3939",
2934
+ "X-3947",
2935
+ "X-3949",
2936
+ "X-3966",
2937
+ "X-3982",
2938
+ "X-3990",
2939
+ "X-4015",
2940
+ "X-4018",
2941
+ "X-4026",
2942
+ "X-4073",
2943
+ "X-4087",
2944
+ "X-4132",
2945
+ "X-4138",
2946
+ "X-4145",
2947
+ "X-4157",
2948
+ "X-4163",
2949
+ "X-4173",
2950
+ "X-4213",
2951
+ "X-4215",
2952
+ "X-4226",
2953
+ "X-4244",
2954
+ "X-4261",
2955
+ "X-4279",
2956
+ "X-4304",
2957
+ "X-4316",
2958
+ "X-4339",
2959
+ "X-4347",
2960
+ "X-4361",
2961
+ "X-4367",
2962
+ "X-4377",
2963
+ "X-4378",
2964
+ "X-4379",
2965
+ "X-4412",
2966
+ "X-4426",
2967
+ "X-4454",
2968
+ "X-4455",
2969
+ "X-4476",
2970
+ "X-4484",
2971
+ "X-4530",
2972
+ "X-4536",
2973
+ "X-4537",
2974
+ "X-4538",
2975
+ "X-4567",
2976
+ "X-4579",
2977
+ "X-4597",
2978
+ "X-4624",
2979
+ "X-4644",
2980
+ "X-4649",
2981
+ "X-4653",
2982
+ "X-4664",
2983
+ "X-4668",
2984
+ "X-4676",
2985
+ "X-4797",
2986
+ "X-4799",
2987
+ "X-4818",
2988
+ "X-4819",
2989
+ "X-4824",
2990
+ "X-4826",
2991
+ "X-4829",
2992
+ "X-4832",
2993
+ "X-4833",
2994
+ "X-4849",
2995
+ "X-4852",
2996
+ "X-4863",
2997
+ "X-4869",
2998
+ "X-4882",
2999
+ "X-4886",
3000
+ "X-4888",
3001
+ "X-4905",
3002
+ "X-4927",
3003
+ "X-4948",
3004
+ "X-4949",
3005
+ "X-4979",
3006
+ "X-5018",
3007
+ "X-5041",
3008
+ "X-5077",
3009
+ "X-5094",
3010
+ "X-5189",
3011
+ "X-5205",
3012
+ "X-5238",
3013
+ "X-5249",
3014
+ "X-5254",
3015
+ "X-5261",
3016
+ "X-5273",
3017
+ "X-5307",
3018
+ "X-5316",
3019
+ "X-5355",
3020
+ "X-5374",
3021
+ "X-5388",
3022
+ "X-5405",
3023
+ "X-5415",
3024
+ "X-5421",
3025
+ "X-5438",
3026
+ "X-5441",
3027
+ "X-5446",
3028
+ "X-5462",
3029
+ "X-5490",
3030
+ "X-5493",
3031
+ "X-5494",
3032
+ "X-5495",
3033
+ "X-5502",
3034
+ "X-5508",
3035
+ "X-5541",
3036
+ "X-5563",
3037
+ "X-5566",
3038
+ "X-5578",
3039
+ "X-5584",
3040
+ "X-5593",
3041
+ "X-5594",
3042
+ "X-5595",
3043
+ "X-5627",
3044
+ "X-5643",
3045
+ "X-5645",
3046
+ "X-5693",
3047
+ "X-5694",
3048
+ "X-5696",
3049
+ "X-5713",
3050
+ "X-5717",
3051
+ "X-5727",
3052
+ "X-5739",
3053
+ "X-5808",
3054
+ "X-5959",
3055
+ "X-5974",
3056
+ "X-5975",
3057
+ "X-6030",
3058
+ "X-6047"
3059
+ ],
3060
+ "key": "rna_df"
3061
+ }
3062
+ },
3063
+ "other": {
3064
+ "disease_to_id": {
3065
+ "key": "disease_to_id"
3066
+ },
3067
+ "drug_to_id": {
3068
+ "key": "drug_to_id"
3069
+ },
3070
+ "model": {
3071
+ "key": "model"
3072
+ },
3073
+ "prediction": {
3074
+ "key": "prediction"
3075
+ },
3076
+ "root_path": {
3077
+ "key": "root_path"
3078
+ }
3079
+ },
3080
+ "relations": []
3081
+ }
3082
+ ],
3083
+ "message": null,
3084
+ "meta": null,
3085
+ "op_id": "Attribution > Attribution to text",
3086
+ "params": {
3087
+ "top_n": 3.0
3088
+ },
3089
+ "status": "done",
3090
+ "title": "Attribution to text"
3091
+ },
3092
+ "height": 1295.0,
3093
+ "id": "Attribution to text 1",
3094
+ "position": {
3095
+ "x": 945.0,
3096
+ "y": 70.0
3097
+ },
3098
+ "type": "table_view",
3099
+ "width": 735.0
3100
+ }
3101
+ ],
3102
+ "paused": true
3103
+ }
examples/GNN to Text/gnn_to_text.py ADDED
@@ -0,0 +1,1027 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Custom operations for the "GNN to Text" example."""
2
+
3
+ import enum
4
+ import itertools
5
+ from lynxkite_core import ops
6
+ from lynxkite_graph_analytics import core
7
+ import torch
8
+ import torch.nn.functional as F
9
+ import torch_geometric.utils as pyg_utils
10
+ from torch_geometric.data import Data
11
+ from torch_geometric.nn import GATConv
12
+ import os.path
13
+ import numpy as np
14
+ import pandas as pd
15
+ from tqdm import tqdm
16
+ import click
17
+
18
+ if __name__ == "__main__":
19
+ import joblib
20
+
21
+ mem = joblib.Memory(".joblib-cache")
22
+ ops.CACHE_WRAPPER = mem.cache
23
+
24
+
25
+ op = ops.op_registration("LynxKite Graph Analytics", "Attribution", icon="microscope")
26
+
27
+
28
+ def load_disease_gene_data(*, root_path: str) -> core.Bundle:
29
+ b = core.Bundle()
30
+ b.other["root_path"] = root_path
31
+ DIS_GENE_ROOT = root_path
32
+ disease_dfs = []
33
+ for file in os.listdir(DIS_GENE_ROOT):
34
+ if file.endswith(("OT.tsv")):
35
+ file_path = os.path.join(DIS_GENE_ROOT, file)
36
+ # Derive disease name from the filename without extension
37
+ disease = os.path.splitext(file)[0]
38
+ # Read the file; assume it has a header with 'symbol' and 'globalScore'
39
+ df = pd.read_csv(
40
+ file_path,
41
+ sep="\t",
42
+ header=0, # use the file's header
43
+ usecols=["symbol", "globalScore"], # ensure we only read needed cols
44
+ )
45
+ # Add the disease column
46
+ df["disease"] = disease
47
+ disease_dfs.append(df)
48
+
49
+ # Concatenate all disease DataFrames
50
+ disease_df = pd.concat(disease_dfs, ignore_index=True)
51
+
52
+ # Clean and filter
53
+ disease_df["globalScore"] = pd.to_numeric(disease_df["globalScore"], errors="coerce")
54
+ disease_df = (
55
+ disease_df.dropna(subset=["globalScore"])
56
+ .query("globalScore >= 0.5")
57
+ .loc[:, ["symbol", "globalScore", "disease"]]
58
+ )
59
+ b.dfs["disease"] = disease_df
60
+ return b
61
+
62
+
63
+ def load_drug_gene_data(*, root_path: str) -> core.Bundle:
64
+ b = core.Bundle()
65
+ b.other["root_path"] = root_path
66
+ drug_gene_df = pd.read_csv(f"{root_path}/DGIdb_interactions.tsv", sep="\t")[
67
+ ["gene_name", "drug_name"]
68
+ ].assign(drug_name=lambda d: d.drug_name.str.lower())
69
+ b.dfs["drug_gene"] = drug_gene_df
70
+ return b
71
+
72
+
73
+ def load_rna_data(*, root_path: str) -> core.Bundle:
74
+ b = core.Bundle()
75
+ b.other["root_path"] = root_path
76
+ rna_df = pd.read_excel(f"{root_path}/rna_data.xlsx", sheet_name="RNAseq_fpkm")
77
+ b.dfs["rna_df"] = rna_df.set_index("Sample")
78
+ return b
79
+
80
+
81
+ def load_esm2_data(*, root_path: str) -> core.Bundle:
82
+ b = core.Bundle()
83
+ b.other["root_path"] = root_path
84
+ # RNA + ESM2 embeddings
85
+ esm2_df = pd.read_parquet(f"{root_path}/esm2-gene-embeddings.parquet")
86
+ gene2esm2 = dict(zip(esm2_df["genes"], esm2_df["embeddings"]))
87
+ b.dfs["gene2esm2"] = pd.DataFrame(
88
+ {"embedding": list(gene2esm2.values())}, index=list(gene2esm2.keys())
89
+ )
90
+ return b
91
+
92
+
93
+ def load_pct_data(*, root_path: str) -> core.Bundle:
94
+ b = core.Bundle()
95
+ b.other["root_path"] = root_path
96
+ clin_df = pd.read_excel(f"{root_path}/rna_data.xlsx", sheet_name="PCT raw data")
97
+ clin_df = clin_df[
98
+ clin_df["Tumor Type"].notna() # Not NaN
99
+ & (clin_df["Tumor Type"].astype(str).str.strip() != "") # Not empty
100
+ ].copy()
101
+ b.dfs["clin_df"] = clin_df
102
+ b.dfs["meta"] = (
103
+ clin_df[["Model", "Treatment", "Tumor Type"]].drop_duplicates().reset_index(drop=True)
104
+ )
105
+ return b
106
+
107
+
108
+ def index_genes(b: core.Bundle) -> core.Bundle:
109
+ """Enumerates all the genes we have data for, and gives them sequential numbers."""
110
+ root_path = b.other["root_path"]
111
+ all_rna_genes = set(b.dfs["rna_df"].index)
112
+ all_ppi_genes = set(
113
+ itertools.chain.from_iterable(
114
+ load_ppi_for_tissue(root_path, t)[["gene1", "gene2"]].values.flatten()
115
+ for t in os.listdir(root_path + "/Tissue")
116
+ )
117
+ )
118
+ esm2_genes = set(b.dfs["gene2esm2"].index)
119
+ avail_genes = sorted(all_rna_genes & all_ppi_genes & esm2_genes)
120
+ gene_to_idx = {g: i for i, g in enumerate(avail_genes)}
121
+ b.dfs["gene_to_idx"] = pd.DataFrame(
122
+ list(gene_to_idx.values()), index=list(gene_to_idx.keys()), columns=["idx"]
123
+ )
124
+ return b
125
+
126
+
127
+ def classify_mrecist(b: core.Bundle) -> core.Bundle:
128
+ num_timestamps_in_past = 10
129
+ clin_df = b.dfs["clin_df"]
130
+ times_col = []
131
+ vols_col = []
132
+ seqs_col = []
133
+ class_col = []
134
+ for _, row in b.dfs["meta"].iterrows():
135
+ pdx_id = row["Model"]
136
+ tumor = row["Tumor Type"]
137
+ drug = row["Treatment"]
138
+ sub = clin_df[
139
+ (clin_df["Model"] == pdx_id)
140
+ & (clin_df["Tumor Type"] == tumor)
141
+ & (clin_df["Treatment"] == drug)
142
+ ].sort_values("Days Post T0")
143
+ times = sub["Days Post T0"].values
144
+ vols = sub["Volume (mm3)"].values
145
+ norm = vols / vols[0]
146
+ seqs_col.append(norm[:num_timestamps_in_past])
147
+ future_vols = vols[num_timestamps_in_past:]
148
+ vols_col.append(future_vols)
149
+ times_col.append(times[num_timestamps_in_past:])
150
+ baseline = vols[0]
151
+ percent_growth = 100 * (future_vols - baseline) / baseline
152
+ if len(percent_growth) == 0:
153
+ class_col.append("unknown")
154
+ else:
155
+ class_col.append("good" if percent_growth.min() <= 35 else "bad")
156
+ b = b.copy()
157
+ b.dfs["meta"] = b.dfs["meta"].copy()
158
+ b.dfs["meta"]["future_timestamps"] = pd.Series(times_col)
159
+ b.dfs["meta"]["future_volumes"] = pd.Series(vols_col)
160
+ b.dfs["meta"]["past_volumes"] = pd.Series(seqs_col)
161
+ b.dfs["meta"]["mrecist_class"] = pd.Series(class_col)
162
+ b.dfs["meta"] = b.dfs["meta"].query("future_volumes.str.len() > 2").reset_index(drop=True)
163
+ return b
164
+
165
+
166
+ def filter_to_samples_with_rna_data(b: core.Bundle) -> core.Bundle:
167
+ b = b.copy()
168
+ valid_pdx = set(b.dfs["rna_df"].columns)
169
+ b.dfs["meta"] = b.dfs["meta"][b.dfs["meta"]["Model"].isin(valid_pdx)].reset_index(drop=True)
170
+ return b
171
+
172
+
173
+ @ops.cached
174
+ def load_ppi_for_tissue(root_path: str, tissue: str) -> pd.DataFrame:
175
+ path = os.path.join(root_path, "Tissue", tissue, "edges.tsv")
176
+ return pd.read_csv(path, sep="\t", header=None, names=["gene1", "gene2", "score"])
177
+
178
+
179
+ def gene_interacts_gene_input(
180
+ b: core.Bundle,
181
+ batch_index: int,
182
+ *,
183
+ table_name: core.TableName = "meta",
184
+ tumor_type_column_name: core.ColumnNameByTableName = "Tumor Type",
185
+ ):
186
+ """
187
+ Args:
188
+ table_name: The table with samples.
189
+ tumor_type_column_name: The column indicating the tissue type for the sample.
190
+ """
191
+ row = b.dfs[table_name].iloc[batch_index]
192
+ ppi_df = load_ppi_for_tissue(b.other["root_path"], row[tumor_type_column_name])
193
+ gene_to_idx = b.dfs["gene_to_idx"]["idx"].to_dict()
194
+ rows, cols = [], []
195
+ for u, v in zip(ppi_df.gene1, ppi_df.gene2):
196
+ if u in gene_to_idx and v in gene_to_idx:
197
+ rows.append(gene_to_idx[u])
198
+ cols.append(gene_to_idx[v])
199
+ edge_idx = torch.tensor([rows, cols], dtype=torch.long)
200
+ edge_idx = pyg_utils.to_undirected(edge_idx)
201
+ edge_idx, _ = pyg_utils.add_self_loops(edge_idx)
202
+ return edge_idx
203
+
204
+
205
+ def drug_targets_gene_input(
206
+ b: core.Bundle,
207
+ batch_index: int,
208
+ *,
209
+ table_name: core.TableName = "meta",
210
+ treatment_column_name: core.ColumnNameByTableName = "Treatment",
211
+ ):
212
+ """
213
+ Args:
214
+ table_name: The table with samples.
215
+ treatment_column_name: The column indicating the treatment drug for the sample.
216
+ """
217
+ gene_to_idx = b.dfs["gene_to_idx"]["idx"].to_dict()
218
+ row = b.dfs[table_name].iloc[batch_index]
219
+ drug = row[treatment_column_name]
220
+ df = b.dfs["drug_gene"]
221
+ dg = df[df["drug_name"] == drug].gene_name
222
+ dg_rows = [gene_to_idx[g] for g in dg if g in gene_to_idx]
223
+ return (
224
+ torch.tensor([[0] * len(dg_rows), dg_rows], dtype=torch.long)
225
+ if dg_rows
226
+ else torch.empty((2, 0), dtype=torch.long)
227
+ )
228
+
229
+
230
+ def disease_assoc_gene_input(
231
+ b: core.Bundle,
232
+ batch_index: int,
233
+ *,
234
+ table_name: core.TableName = "meta",
235
+ tumor_type_column_name: core.ColumnNameByTableName = "Tumor Type",
236
+ ):
237
+ """
238
+ Args:
239
+ table_name: The table with samples.
240
+ tumor_type_column_name: The column indicating the tissue type for the sample.
241
+ """
242
+ gene_to_idx = b.dfs["gene_to_idx"]["idx"].to_dict()
243
+ row = b.dfs[table_name].iloc[batch_index]
244
+ tumor = row[tumor_type_column_name]
245
+ df = b.dfs["disease"]
246
+ dis = df[df["disease"] == tumor].symbol
247
+ dis_rows = [gene_to_idx[g] for g in dis if g in gene_to_idx]
248
+ return (
249
+ torch.tensor([[0] * len(dis_rows), dis_rows], dtype=torch.long)
250
+ if dis_rows
251
+ else torch.empty((2, 0), dtype=torch.long)
252
+ )
253
+
254
+
255
+ def gene_features_input(
256
+ b: core.Bundle,
257
+ batch_index: int,
258
+ *,
259
+ table_name: core.TableName = "meta",
260
+ pdx_id_column_name: core.ColumnNameByTableName = "Model",
261
+ ):
262
+ """
263
+ Args:
264
+ table_name: The table with samples.
265
+ pdx_id_column_name: The column indicating the PDX model for the sample.
266
+ """
267
+ row = b.dfs[table_name].iloc[batch_index]
268
+ pdx_id = row[pdx_id_column_name]
269
+ avail_genes = b.dfs["gene_to_idx"].index
270
+ gene2esm2 = b.dfs["gene2esm2"]["embedding"]
271
+ rna_df = b.dfs["rna_df"]
272
+ expr = rna_df[pdx_id].reindex(avail_genes).fillna(0).values
273
+ expr_t = torch.tensor(expr, dtype=torch.float32).view(-1, 1)
274
+ esm2_mat = torch.stack([torch.tensor(gene2esm2[g], dtype=torch.float32) for g in avail_genes])
275
+ return torch.cat([esm2_mat, expr_t], dim=1)
276
+
277
+
278
+ @ops.cached
279
+ def load(root_path):
280
+ b = load_disease_gene_data(root_path=root_path)
281
+ b.merge(load_drug_gene_data(root_path=root_path))
282
+ b.merge(load_rna_data(root_path=root_path))
283
+ b.merge(load_esm2_data(root_path=root_path))
284
+ pct = load_pct_data(root_path=root_path)
285
+ pct = classify_mrecist(pct)
286
+ b.merge(pct)
287
+ b = index_genes(b)
288
+ b = filter_to_samples_with_rna_data(b)
289
+ b = classify_mrecist(b)
290
+ return b
291
+
292
+
293
+ @ops.cached
294
+ def precompute_tissue_graphs(b: core.Bundle):
295
+ """Precompute PPI edges, disease gene indices, and ESM2 features for each tumor type."""
296
+ gene_to_idx = b.dfs["gene_to_idx"]["idx"].to_dict()
297
+ avail_genes = b.dfs["gene_to_idx"].index
298
+ gene2esm2 = b.dfs["gene2esm2"]["embedding"]
299
+ esm2_mat = torch.stack([torch.tensor(gene2esm2[g], dtype=torch.float32) for g in avail_genes])
300
+ root_path = b.other["root_path"]
301
+ tumor_types = b.dfs["meta"]["Tumor Type"].unique()
302
+ # Build drug and disease vocabularies
303
+ drugs = sorted(b.dfs["meta"]["Treatment"].unique())
304
+ drug_to_id = {d: i for i, d in enumerate(drugs)}
305
+ disease_to_id = {t: i for i, t in enumerate(sorted(tumor_types))}
306
+ cache = {}
307
+ for tumor in tumor_types:
308
+ # PPI edges (gene-gene only, no drug/disease nodes)
309
+ ppi_df = load_ppi_for_tissue(root_path, tumor)
310
+ rows, cols = [], []
311
+ for u, v in zip(ppi_df.gene1, ppi_df.gene2):
312
+ if u in gene_to_idx and v in gene_to_idx:
313
+ rows.append(gene_to_idx[u])
314
+ cols.append(gene_to_idx[v])
315
+ ppi_edges = torch.tensor([rows, cols], dtype=torch.long)
316
+ ppi_edges = pyg_utils.to_undirected(ppi_edges)
317
+ ppi_edges, _ = pyg_utils.add_self_loops(ppi_edges)
318
+ # Disease-associated gene indices
319
+ df_dis = b.dfs["disease"]
320
+ dis = df_dis[df_dis["disease"] == tumor].symbol
321
+ dis_idx = [gene_to_idx[g] for g in dis if g in gene_to_idx]
322
+ cache[tumor] = {
323
+ "ppi_edges": ppi_edges,
324
+ "disease_gene_idx": torch.tensor(dis_idx, dtype=torch.long)
325
+ if dis_idx
326
+ else torch.empty(0, dtype=torch.long),
327
+ }
328
+ return esm2_mat, cache, drug_to_id, disease_to_id
329
+
330
+
331
+ def _get_drug_gene_idx(b: core.Bundle, drug: str):
332
+ gene_to_idx = b.dfs["gene_to_idx"]["idx"].to_dict()
333
+ df = b.dfs["drug_gene"]
334
+ dg = df[df["drug_name"] == drug].gene_name
335
+ idx = [gene_to_idx[g] for g in dg if g in gene_to_idx]
336
+ return torch.tensor(idx, dtype=torch.long) if idx else torch.empty(0, dtype=torch.long)
337
+
338
+
339
+ def make_graph(b: core.Bundle, batch_index: int, esm2_mat, tissue_cache, drug_to_id, disease_to_id):
340
+ """Construct a PyG Data graph for the sample at batch_index in meta."""
341
+ row = b.dfs["meta"].iloc[batch_index]
342
+ tumor = row["Tumor Type"]
343
+ drug = row["Treatment"]
344
+ pdx_id = row["Model"]
345
+ avail_genes = b.dfs["gene_to_idx"].index
346
+ rna_df = b.dfs["rna_df"]
347
+
348
+ # Node features: cached ESM2 + per-sample RNA expression
349
+ expr = rna_df[pdx_id].reindex(avail_genes).fillna(0).values
350
+ expr_t = torch.tensor(expr, dtype=torch.float32).view(-1, 1)
351
+ x = torch.cat([esm2_mat, expr_t], dim=1)
352
+
353
+ # PPI-only edges
354
+ tc = tissue_cache[tumor]
355
+ drug_gene_idx = _get_drug_gene_idx(b, drug)
356
+ return Data(
357
+ x=x,
358
+ edge_index=tc["ppi_edges"],
359
+ drug_gene_idx=drug_gene_idx,
360
+ disease_gene_idx=tc["disease_gene_idx"],
361
+ drug_id=torch.tensor([drug_to_id[drug]], dtype=torch.long),
362
+ disease_id=torch.tensor([disease_to_id[tumor]], dtype=torch.long),
363
+ )
364
+
365
+
366
+ def make_graph_from_strings(
367
+ b: core.Bundle,
368
+ drug: str,
369
+ disease: str,
370
+ esm2_mat=None,
371
+ tissue_cache=None,
372
+ drug_to_id=None,
373
+ disease_to_id=None,
374
+ ):
375
+ """Build a PyG graph for a (drug, disease) pair without needing a meta row."""
376
+ avail_genes = b.dfs["gene_to_idx"].index
377
+ num_genes = len(avail_genes)
378
+
379
+ if esm2_mat is None or tissue_cache is None:
380
+ esm2_mat, tissue_cache, drug_to_id, disease_to_id = precompute_tissue_graphs(b)
381
+
382
+ x = torch.cat([esm2_mat, torch.zeros(num_genes, 1)], dim=1)
383
+
384
+ tc = tissue_cache[disease]
385
+ drug_gene_idx = _get_drug_gene_idx(b, drug)
386
+ return Data(
387
+ x=x,
388
+ edge_index=tc["ppi_edges"],
389
+ drug_gene_idx=drug_gene_idx,
390
+ disease_gene_idx=tc["disease_gene_idx"],
391
+ drug_id=torch.tensor([drug_to_id.get(drug, 0)], dtype=torch.long),
392
+ disease_id=torch.tensor([disease_to_id.get(disease, 0)], dtype=torch.long),
393
+ )
394
+
395
+
396
+ class GATModel(torch.nn.Module):
397
+ def __init__(
398
+ self,
399
+ in_channels,
400
+ num_drugs,
401
+ num_diseases,
402
+ embed_dim=16,
403
+ hidden_channels=32,
404
+ heads=2,
405
+ dropout=0.2,
406
+ ):
407
+ super().__init__()
408
+ self.norm = torch.nn.BatchNorm1d(in_channels)
409
+ self.drug_embedding = torch.nn.Embedding(num_drugs, embed_dim)
410
+ self.disease_embedding = torch.nn.Embedding(num_diseases, embed_dim)
411
+ self.gat1 = GATConv(in_channels, hidden_channels, heads=heads, dropout=dropout)
412
+ self.gat2 = GATConv(hidden_channels * heads, hidden_channels, heads=1, dropout=dropout)
413
+ self.dropout = torch.nn.Dropout(dropout)
414
+ # Classify from pooled gene embeddings + categorical drug/disease embeddings
415
+ self.classifier = torch.nn.Sequential(
416
+ torch.nn.Linear(hidden_channels * 2 + embed_dim * 2, hidden_channels),
417
+ torch.nn.ELU(),
418
+ torch.nn.Dropout(dropout),
419
+ torch.nn.Linear(hidden_channels, 1),
420
+ )
421
+
422
+ def forward(
423
+ self,
424
+ x,
425
+ edge_index,
426
+ drug_gene_idx,
427
+ disease_gene_idx,
428
+ drug_id,
429
+ disease_id,
430
+ return_attention=False,
431
+ ):
432
+ x = self.norm(x)
433
+ # Phase 1: GAT on PPI graph to get contextualized gene embeddings
434
+ if return_attention:
435
+ x, attn1 = self.gat1(x, edge_index, return_attention_weights=True)
436
+ else:
437
+ x = self.gat1(x, edge_index)
438
+ attn1 = None
439
+ x = F.elu(x)
440
+ x = self.dropout(x)
441
+ if return_attention:
442
+ x, attn2 = self.gat2(x, edge_index, return_attention_weights=True)
443
+ else:
444
+ x = self.gat2(x, edge_index)
445
+ attn2 = None
446
+ x = F.elu(x)
447
+ # Phase 2: Pool drug-target and disease-associated gene embeddings
448
+ if drug_gene_idx.numel() > 0:
449
+ drug_repr = x[drug_gene_idx].mean(dim=0)
450
+ else:
451
+ drug_repr = torch.zeros(x.shape[1], device=x.device)
452
+ if disease_gene_idx.numel() > 0:
453
+ disease_repr = x[disease_gene_idx].mean(dim=0)
454
+ else:
455
+ disease_repr = torch.zeros(x.shape[1], device=x.device)
456
+ # Categorical embeddings
457
+ drug_emb = self.drug_embedding(drug_id).squeeze(0)
458
+ disease_emb = self.disease_embedding(disease_id).squeeze(0)
459
+ pooled = torch.cat([drug_repr, disease_repr, drug_emb, disease_emb], dim=0)
460
+ logit = self.classifier(pooled).squeeze()
461
+ if return_attention:
462
+ return logit, attn1, attn2
463
+ return logit
464
+
465
+
466
+ def save_model(
467
+ model,
468
+ path="gat_model.pt",
469
+ drug_to_id=None,
470
+ disease_to_id=None,
471
+ optimizer=None,
472
+ scaler=None,
473
+ epoch=None,
474
+ ):
475
+ """Save the trained GAT model to disk."""
476
+ ckpt = {
477
+ "state_dict": model.state_dict(),
478
+ "in_channels": model.norm.num_features,
479
+ "num_drugs": model.drug_embedding.num_embeddings,
480
+ "num_diseases": model.disease_embedding.num_embeddings,
481
+ "drug_to_id": drug_to_id,
482
+ "disease_to_id": disease_to_id,
483
+ }
484
+ if optimizer is not None:
485
+ ckpt["optimizer_state_dict"] = optimizer.state_dict()
486
+ if scaler is not None:
487
+ ckpt["scaler_state_dict"] = scaler.state_dict()
488
+ if epoch is not None:
489
+ ckpt["epoch"] = epoch
490
+ torch.save(ckpt, path)
491
+ print(f"Model saved to {path}")
492
+
493
+
494
+ def load_model(path="gat_model.pt", device=None):
495
+ """Load a saved GAT model from disk."""
496
+ device = device or DEVICE
497
+ ckpt = torch.load(path, map_location=device, weights_only=False)
498
+ model = GATModel(
499
+ in_channels=ckpt["in_channels"],
500
+ num_drugs=ckpt["num_drugs"],
501
+ num_diseases=ckpt["num_diseases"],
502
+ ).to(device)
503
+ model.load_state_dict(ckpt["state_dict"])
504
+ model.eval()
505
+ return model, ckpt["drug_to_id"], ckpt["disease_to_id"]
506
+
507
+
508
+ def predict(
509
+ model,
510
+ b: core.Bundle,
511
+ drug: str,
512
+ disease: str,
513
+ esm2_mat=None,
514
+ tissue_cache=None,
515
+ drug_to_id=None,
516
+ disease_to_id=None,
517
+ ) -> float:
518
+ """Return the predicted probability that the drug is effective for the disease."""
519
+ model.eval()
520
+ graph = make_graph_from_strings(
521
+ b, drug, disease, esm2_mat, tissue_cache, drug_to_id, disease_to_id
522
+ )
523
+ with torch.no_grad(), torch.amp.autocast("cuda"):
524
+ logit = model(
525
+ graph.x.to(DEVICE),
526
+ graph.edge_index.to(DEVICE),
527
+ graph.drug_gene_idx.to(DEVICE),
528
+ graph.disease_gene_idx.to(DEVICE),
529
+ graph.drug_id.to(DEVICE),
530
+ graph.disease_id.to(DEVICE),
531
+ )
532
+ return torch.sigmoid(logit).item()
533
+
534
+
535
+ def predict_with_attribution(
536
+ model,
537
+ b: core.Bundle,
538
+ drug: str,
539
+ disease: str,
540
+ esm2_mat=None,
541
+ tissue_cache=None,
542
+ drug_to_id=None,
543
+ disease_to_id=None,
544
+ ):
545
+ """Return probability and per-node attention scores.
546
+
547
+ Returns:
548
+ prob: Predicted probability that the drug is effective.
549
+ node_scores: DataFrame with gene names and their
550
+ aggregated attention scores across both GAT layers.
551
+ """
552
+ model.eval()
553
+ graph = make_graph_from_strings(
554
+ b, drug, disease, esm2_mat, tissue_cache, drug_to_id, disease_to_id
555
+ )
556
+ avail_genes = list(b.dfs["gene_to_idx"].index)
557
+ node_names = avail_genes
558
+ num_nodes = len(node_names)
559
+
560
+ with torch.no_grad(), torch.amp.autocast("cuda"):
561
+ logit, (ei1, aw1), (ei2, aw2) = model(
562
+ graph.x.to(DEVICE),
563
+ graph.edge_index.to(DEVICE),
564
+ graph.drug_gene_idx.to(DEVICE),
565
+ graph.disease_gene_idx.to(DEVICE),
566
+ graph.drug_id.to(DEVICE),
567
+ graph.disease_id.to(DEVICE),
568
+ return_attention=True,
569
+ )
570
+ prob = torch.sigmoid(logit).item()
571
+
572
+ # Aggregate attention: for each node, sum the attention it receives as a target.
573
+ # aw1/aw2 shape: (num_edges, heads) — average over heads, then scatter-add to target nodes.
574
+ def aggregate_attention(edge_index, attn_weights):
575
+ attn = attn_weights.mean(dim=1) if attn_weights.dim() > 1 else attn_weights
576
+ scores = torch.zeros(num_nodes, device=attn.device)
577
+ targets = edge_index[1]
578
+ scores.scatter_add_(0, targets, attn)
579
+ return scores.cpu()
580
+
581
+ scores1 = aggregate_attention(ei1, aw1)
582
+ scores2 = aggregate_attention(ei2, aw2)
583
+ combined = scores1 + scores2
584
+
585
+ df = pd.DataFrame({"node": node_names, "attention_score": combined.numpy()})
586
+ df = df.sort_values("attention_score", ascending=False).reset_index(drop=True)
587
+ return prob, df
588
+
589
+
590
+ def top_genes(attribution_df: pd.DataFrame, n: int = 10) -> list[str]:
591
+ """Return the names of the top N most important genes from an attribution DataFrame."""
592
+ genes = attribution_df[~attribution_df["node"].str.contains(":")]
593
+ return genes.head(n)["node"].tolist()
594
+
595
+
596
+ @op("Load GAT model", icon="file-filled", color="green")
597
+ async def load_model_op(*, model_path, root_path) -> core.Bundle:
598
+ """Load the model from a checkpoint."""
599
+ b = load(root_path)
600
+ b.other["model"], b.other["drug_to_id"], b.other["disease_to_id"] = load_model(model_path)
601
+ b.other["model"] = model_path
602
+ return b
603
+
604
+
605
+ class TumorTypes(enum.StrEnum):
606
+ BRCA = "BRCA"
607
+ CM = "CM"
608
+ CRC = "CRC"
609
+ GC = "GC"
610
+ NSCLC = "NSCLC"
611
+ PDAC = "PDAC"
612
+
613
+
614
+ class Drugs(enum.StrEnum):
615
+ _5FU = "5FU"
616
+ BGJ398 = "BGJ398"
617
+ BKM120 = "BKM120"
618
+ BKM120_LDE225 = "BKM120 + LDE225"
619
+ BKM120_LJC049 = "BKM120 + LJC049"
620
+ BKM120_binimetinib = "BKM120 + binimetinib"
621
+ BKM120_encorafenib = "BKM120 + encorafenib"
622
+ BYL719 = "BYL719"
623
+ BYL719_HSP990 = "BYL719 + HSP990"
624
+ BYL719_LEE011 = "BYL719 + LEE011"
625
+ BYL719_LGH447 = "BYL719 + LGH447"
626
+ BYL719_LJM716 = "BYL719 + LJM716"
627
+ BYL719_binimetinib = "BYL719 + binimetinib"
628
+ BYL719_cetuximab = "BYL719 + cetuximab"
629
+ BYL719_cetuximab_encorafenib = "BYL719 + cetuximab + encorafenib"
630
+ BYL719_encorafenib = "BYL719 + encorafenib"
631
+ CGM097 = "CGM097"
632
+ CKX620 = "CKX620"
633
+ CLR457 = "CLR457"
634
+ HDM201 = "HDM201"
635
+ HSP990 = "HSP990"
636
+ INC280 = "INC280"
637
+ INC280_trastuzumab = "INC280 + trastuzumab"
638
+ INC424 = "INC424"
639
+ INC424_binimetinib = "INC424 + binimetinib"
640
+ LCL161_paclitaxel = "LCL161 + paclitaxel"
641
+ LDE225 = "LDE225"
642
+ LDK378 = "LDK378"
643
+ LEE011 = "LEE011"
644
+ LEE011_binimetinib = "LEE011 + binimetinib"
645
+ LEE011_encorafenib = "LEE011 + encorafenib"
646
+ LEE011_everolimus = "LEE011 + everolimus"
647
+ LFA102 = "LFA102"
648
+ LFW527_binimetinib = "LFW527 + binimetinib"
649
+ LFW527_everolimus = "LFW527 + everolimus"
650
+ LGH447 = "LGH447"
651
+ LGW813 = "LGW813"
652
+ LJC049 = "LJC049"
653
+ LJM716 = "LJM716"
654
+ LJM716_trastuzumab = "LJM716 + trastuzumab"
655
+ LKA136 = "LKA136"
656
+ LLM871 = "LLM871"
657
+ TAS266 = "TAS266"
658
+ WNT974 = "WNT974"
659
+ abraxane = "abraxane"
660
+ binimetinib = "binimetinib"
661
+ binimetinib_3_5mpk = "binimetinib-3.5mpk"
662
+ cetuximab = "cetuximab"
663
+ cetuximab_encorafenib = "cetuximab + encorafenib"
664
+ dacarbazine = "dacarbazine"
665
+ encorafenib = "encorafenib"
666
+ encorafenib_binimetinib = "encorafenib + binimetinib"
667
+ erlotinib = "erlotinib"
668
+ everolimus = "everolimus"
669
+ figitumumab = "figitumumab"
670
+ figitumumab_binimetinib = "figitumumab + binimetinib"
671
+ gemcitabine_50mpk = "gemcitabine-50mpk"
672
+ paclitaxel = "paclitaxel"
673
+ tamoxifen = "tamoxifen"
674
+ trametinib = "trametinib"
675
+ trastuzumab = "trastuzumab"
676
+ untreated = "untreated"
677
+
678
+
679
+ @op("Predict with attribution", icon="sparkles", color="orange")
680
+ async def predict_with_attribution_op(
681
+ b: core.Bundle, *, drug: Drugs, disease: TumorTypes
682
+ ) -> core.Bundle:
683
+ """Run inference with attribution, and save the results."""
684
+ esm2_mat, tissue_cache, _, _ = precompute_tissue_graphs(b)
685
+ model, drug_to_id, disease_to_id = (
686
+ b.other["model"],
687
+ b.other["drug_to_id"],
688
+ b.other["disease_to_id"],
689
+ )
690
+ model = load_model(model)[0]
691
+ prob, attr_df = predict_with_attribution(
692
+ model, b, drug, disease, esm2_mat, tissue_cache, drug_to_id, disease_to_id
693
+ )
694
+ b = b.copy()
695
+ b.dfs["attribution"] = attr_df
696
+ b.other["prediction"] = {"prob": prob, "drug": drug, "disease": disease}
697
+ return b
698
+
699
+
700
+ DISEASE_NAMES = {
701
+ "BRCA": "breast cancer",
702
+ "CM": "cutaneous melanoma",
703
+ "CRC": "colorectal cancer",
704
+ "GC": "gastric cancer",
705
+ "NSCLC": "non-small cell lung cancer",
706
+ "PDAC": "pancreatic ductal adenocarcinoma",
707
+ }
708
+
709
+
710
+ @op("Attribution to text", icon="message-circle-filled", color="blue", view="table_view")
711
+ def attribution_to_text(b: core.Bundle, *, top_n=3):
712
+ """Generate an explanation based on the attribution data."""
713
+ genes = top_genes(b.dfs["attribution"], n=top_n)
714
+ prediction = b.other["prediction"]
715
+ prob = prediction["prob"]
716
+ drug = str(prediction["drug"])
717
+ disease = str(prediction["disease"])
718
+ disease = DISEASE_NAMES.get(disease, disease) # Map to nicer name if available
719
+
720
+ effective = "effective" if prob >= 0.5 else "not effective"
721
+ lines = []
722
+ lines.append("## Prediction")
723
+ lines.append(f"**{drug} is {effective} against {disease}** (p={prob:.3f})")
724
+ lines.append(f"Top {top_n} genes: {', '.join(genes)}")
725
+
726
+ explanation = explain_prediction(drug, disease, prob, genes)
727
+ lines.append("## Explanation")
728
+ lines.append(explanation)
729
+ return core.Bundle(
730
+ dfs={"explanation": pd.DataFrame({"text": ["\n\n".join(lines)]})}
731
+ ).to_table_view()
732
+
733
+
734
+ @op("Visualize attribution", view="visualization", icon="eye", color="blue")
735
+ def visualize_attribution(
736
+ b: core.Bundle,
737
+ *,
738
+ top_n: int = 10,
739
+ neighbors_per_node: int = 10,
740
+ ) -> dict:
741
+ """Visualize the attribution on the top N genes and their neighbors in the PPI network.
742
+
743
+ Args:
744
+ b: Bundle containing gene_to_idx and PPI data.
745
+ top_n: Number of top attention nodes to include.
746
+ neighbors_per_node: Number of top neighbors to include for each top node.
747
+
748
+ Returns:
749
+ ECharts configuration dict for graph visualization.
750
+ """
751
+ import matplotlib.cm
752
+ import networkx as nx
753
+
754
+ # Get top nodes by attention
755
+ attribution_df = b.dfs["attribution"]
756
+ top_nodes = attribution_df.head(top_n)["node"].tolist()
757
+ attention_scores = dict(zip(attribution_df["node"], attribution_df["attention_score"]))
758
+
759
+ # Load PPI edges for this tissue
760
+ root_path = b.other["root_path"]
761
+ disease = str(b.other["prediction"]["disease"])
762
+ ppi_df = load_ppi_for_tissue(root_path, disease)
763
+ gene_to_idx = set(b.dfs["gene_to_idx"].index)
764
+
765
+ # Build adjacency from PPI - iterate over columns directly (much faster than iterrows)
766
+ adjacency = {}
767
+ for g1, g2 in zip(ppi_df["gene1"], ppi_df["gene2"]):
768
+ if g1 in gene_to_idx and g2 in gene_to_idx:
769
+ adjacency.setdefault(g1, []).append(g2)
770
+ adjacency.setdefault(g2, []).append(g1)
771
+
772
+ # Collect subgraph nodes: top nodes + their top neighbors
773
+ subgraph_nodes = set(top_nodes)
774
+ for node in top_nodes:
775
+ if node in adjacency:
776
+ # Sort neighbors by attention score, take top ones
777
+ neighbors = adjacency[node]
778
+ neighbors_sorted = sorted(
779
+ neighbors, key=lambda n: attention_scores.get(n, 0), reverse=True
780
+ )
781
+ subgraph_nodes.update(neighbors_sorted[:neighbors_per_node])
782
+
783
+ # Build edges for subgraph
784
+ edges = []
785
+ seen_edges = set()
786
+ for node in subgraph_nodes:
787
+ if node in adjacency:
788
+ for neighbor in adjacency[node]:
789
+ if neighbor in subgraph_nodes:
790
+ edge_key = tuple(sorted([node, neighbor]))
791
+ if edge_key not in seen_edges:
792
+ seen_edges.add(edge_key)
793
+ edges.append((node, neighbor))
794
+
795
+ # Create NetworkX graph for layout
796
+ G = nx.Graph()
797
+ G.add_nodes_from(subgraph_nodes)
798
+ G.add_edges_from(edges)
799
+ pos = nx.spring_layout(G, iterations=50, seed=42)
800
+
801
+ # Color mapping based on attention scores
802
+ scores = [attention_scores.get(n, 0) for n in subgraph_nodes]
803
+ min_score, max_score = min(scores), max(scores)
804
+ score_range = max_score - min_score if max_score > min_score else 1
805
+
806
+ cmap = matplotlib.cm.get_cmap("viridis")
807
+
808
+ def score_to_color(score):
809
+ normalized = (score - min_score) / score_range
810
+ r, g, b, _ = cmap(normalized)
811
+ return "#{:02x}{:02x}{:02x}".format(int(r * 255), int(g * 255), int(b * 255))
812
+
813
+ # Build ECharts config
814
+ node_list = list(subgraph_nodes)
815
+
816
+ data = []
817
+ for node in node_list:
818
+ score = attention_scores.get(node, 0)
819
+ x, y = pos[node]
820
+ is_top_node = node in top_nodes
821
+ data.append(
822
+ {
823
+ "id": node,
824
+ "name": node,
825
+ "x": float(x * 100),
826
+ "y": float(y * 100),
827
+ "symbolSize": 20 if is_top_node else 10,
828
+ "itemStyle": {"color": score_to_color(score)},
829
+ "label": {"show": is_top_node},
830
+ "value": f"{score:.4f}",
831
+ }
832
+ )
833
+
834
+ links = [{"source": e[0], "target": e[1]} for e in edges]
835
+
836
+ return {
837
+ "animationDuration": 500,
838
+ "animationEasingUpdate": "quinticInOut",
839
+ "tooltip": {"show": True, "formatter": "{b}: {c}"},
840
+ "visualMap": {
841
+ "min": min_score,
842
+ "max": max_score,
843
+ "text": ["High", "Low"],
844
+ "realtime": False,
845
+ "calculable": True,
846
+ "inRange": {"color": ["#440154", "#21918c", "#fde725"]}, # viridis-like
847
+ },
848
+ "series": [
849
+ {
850
+ "type": "graph",
851
+ "layout": "none",
852
+ "roam": True,
853
+ "lineStyle": {
854
+ "color": "#cccccc",
855
+ "curveness": 0.1,
856
+ },
857
+ "emphasis": {
858
+ "focus": "adjacency",
859
+ "lineStyle": {"width": 3},
860
+ },
861
+ "label": {"position": "top", "formatter": "{b}"},
862
+ "data": data,
863
+ "links": links,
864
+ },
865
+ ],
866
+ }
867
+
868
+
869
+ @ops.cached
870
+ def explain_prediction(drug: str, disease: str, prob: float, gene_list: list[str]) -> str:
871
+ """Ask an OpenAI model to explain why the GNN made its prediction based on top genes."""
872
+ effective = "effective" if prob >= 0.5 else "not effective"
873
+ genes_str = ", ".join(gene_list)
874
+ prompt = (
875
+ f"A neural network predicts that {drug} is {effective} against {disease} "
876
+ f"due to genes {genes_str}. "
877
+ f"What is the likely underlying biological explanation?"
878
+ )
879
+ return ask_ai(prompt)
880
+
881
+
882
+ @ops.cached
883
+ def ask_ai(prompt: str) -> str:
884
+ import openai
885
+
886
+ client = openai.OpenAI()
887
+ response = client.chat.completions.create(
888
+ model="gpt-4o",
889
+ messages=[
890
+ {
891
+ "role": "system",
892
+ "content": "Your task is to generate explanations. The user is unable to respond. Do not suggest follow-up questions or ask for clarification. Just provide a concise explanation based on the prompt.",
893
+ },
894
+ {"role": "user", "content": prompt},
895
+ ],
896
+ )
897
+ return response.choices[0].message.content
898
+
899
+
900
+ EPOCHS = 3
901
+ LABEL_MAP = {"good": 1.0, "bad": 0.0}
902
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
903
+
904
+
905
+ @click.group()
906
+ def cli():
907
+ pass
908
+
909
+
910
+ @cli.command()
911
+ @click.option("--epochs", default=EPOCHS, help="Number of training epochs.")
912
+ @click.option("--samples", default=100, help="Number of samples to train on.")
913
+ @click.option("--output", default="gat_model.pt", help="Path to save the model.")
914
+ @click.option(
915
+ "--continue", "continue_training", is_flag=True, help="Continue training from checkpoint."
916
+ )
917
+ def train(epochs, samples, output, continue_training):
918
+ """Train the GAT model and save it."""
919
+ b = load(root_path="uploads/ode-gnn")
920
+ meta = b.dfs["meta"].head(samples)
921
+ print(meta.head())
922
+ print(f"Using device: {DEVICE}")
923
+
924
+ esm2_mat, tissue_cache, drug_to_id, disease_to_id = precompute_tissue_graphs(b)
925
+ in_channels = esm2_mat.shape[1] + 1 # ESM2 + RNA expression
926
+
927
+ start_epoch = 0
928
+ if continue_training and os.path.exists(output):
929
+ print(f"Loading checkpoint from {output}...")
930
+ ckpt = torch.load(output, map_location=DEVICE, weights_only=False)
931
+ model = GATModel(
932
+ in_channels=ckpt["in_channels"],
933
+ num_drugs=ckpt["num_drugs"],
934
+ num_diseases=ckpt["num_diseases"],
935
+ ).to(DEVICE)
936
+ model.load_state_dict(ckpt["state_dict"])
937
+ drug_to_id = ckpt["drug_to_id"]
938
+ disease_to_id = ckpt["disease_to_id"]
939
+ optimizer = torch.optim.Adam(model.parameters(), lr=3e-4)
940
+ if "optimizer_state_dict" in ckpt:
941
+ optimizer.load_state_dict(ckpt["optimizer_state_dict"])
942
+ scaler = torch.amp.GradScaler("cuda")
943
+ if "scaler_state_dict" in ckpt:
944
+ scaler.load_state_dict(ckpt["scaler_state_dict"])
945
+ if "epoch" in ckpt:
946
+ start_epoch = ckpt["epoch"] + 1
947
+ print(f"Resuming from epoch {start_epoch + 1}")
948
+ else:
949
+ model = GATModel(
950
+ in_channels=in_channels, num_drugs=len(drug_to_id), num_diseases=len(disease_to_id)
951
+ ).to(DEVICE)
952
+ optimizer = torch.optim.Adam(model.parameters(), lr=3e-4)
953
+ scaler = torch.amp.GradScaler("cuda")
954
+
955
+ loss_fn = torch.nn.BCEWithLogitsLoss()
956
+
957
+ for epoch in range(start_epoch, start_epoch + epochs):
958
+ model.train()
959
+ epoch_loss = 0.0
960
+ correct = 0
961
+ total = 0
962
+ indices = np.random.permutation(len(meta))
963
+ for i in tqdm(indices, desc=f"Epoch {epoch + 1}"):
964
+ row = meta.iloc[i]
965
+ label_str = row["mrecist_class"]
966
+ if label_str not in LABEL_MAP:
967
+ continue
968
+ label = torch.tensor(LABEL_MAP[label_str], device=DEVICE)
969
+ graph = make_graph(b, i, esm2_mat, tissue_cache, drug_to_id, disease_to_id)
970
+ with torch.amp.autocast("cuda"):
971
+ out = model(
972
+ graph.x.to(DEVICE),
973
+ graph.edge_index.to(DEVICE),
974
+ graph.drug_gene_idx.to(DEVICE),
975
+ graph.disease_gene_idx.to(DEVICE),
976
+ graph.drug_id.to(DEVICE),
977
+ graph.disease_id.to(DEVICE),
978
+ )
979
+ loss = loss_fn(out, label)
980
+
981
+ optimizer.zero_grad()
982
+ scaler.scale(loss).backward()
983
+ scaler.unscale_(optimizer)
984
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
985
+ scaler.step(optimizer)
986
+ scaler.update()
987
+
988
+ epoch_loss += loss.item()
989
+ pred = (out.detach() > 0).float()
990
+ correct += (pred == label).item()
991
+ total += 1
992
+
993
+ avg_loss = epoch_loss / max(total, 1)
994
+ accuracy = correct / max(total, 1)
995
+ print(
996
+ f"Epoch {epoch + 1}/{start_epoch + epochs} - Loss: {avg_loss:.4f} - Accuracy: {accuracy:.4f}"
997
+ )
998
+
999
+ save_model(model, output, drug_to_id, disease_to_id, optimizer, scaler, epoch)
1000
+
1001
+
1002
+ @cli.command()
1003
+ @click.argument("drug")
1004
+ @click.argument("disease")
1005
+ @click.option("--model-path", default="gat_model.pt", help="Path to the saved model.")
1006
+ @click.option("--top-n", default=3, help="Number of top genes to include in the explanation.")
1007
+ def explain(drug, disease, model_path, top_n):
1008
+ """Load the model, run inference with attribution, and print an AI explanation."""
1009
+ b = load(root_path="uploads/ode-gnn")
1010
+ esm2_mat, tissue_cache, _, _ = precompute_tissue_graphs(b)
1011
+ model, drug_to_id, disease_to_id = load_model(model_path)
1012
+ prob, attr_df = predict_with_attribution(
1013
+ model, b, drug, disease, esm2_mat, tissue_cache, drug_to_id, disease_to_id
1014
+ )
1015
+ genes = top_genes(attr_df, n=top_n)
1016
+
1017
+ effective = "effective" if prob >= 0.5 else "not effective"
1018
+ print(f"Prediction:\n{drug} is {effective} against {disease} (p={prob:.3f})")
1019
+ print(f"Top {top_n} genes: {', '.join(genes)}")
1020
+
1021
+ explanation = explain_prediction(drug, disease, prob, genes)
1022
+ print("\nExplanation:")
1023
+ print(explanation)
1024
+
1025
+
1026
+ if __name__ == "__main__":
1027
+ cli()