scMultiChat 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. MultiChat/Analysis/Intra_strength.py +1758 -0
  2. MultiChat/Analysis/Processing.py +152 -0
  3. MultiChat/Analysis/__init__.py +2 -0
  4. MultiChat/Heterogeneous_g_emb/__init__.py +13 -0
  5. MultiChat/Heterogeneous_g_emb/_settings.py +156 -0
  6. MultiChat/Heterogeneous_g_emb/_utils.py +143 -0
  7. MultiChat/Heterogeneous_g_emb/_version.py +3 -0
  8. MultiChat/Heterogeneous_g_emb/plotting/__init__.py +19 -0
  9. MultiChat/Heterogeneous_g_emb/plotting/_palettes.py +180 -0
  10. MultiChat/Heterogeneous_g_emb/plotting/_plot.py +1498 -0
  11. MultiChat/Heterogeneous_g_emb/plotting/_post_training.py +742 -0
  12. MultiChat/Heterogeneous_g_emb/plotting/_utils.py +103 -0
  13. MultiChat/Heterogeneous_g_emb/preprocessing/__init__.py +26 -0
  14. MultiChat/Heterogeneous_g_emb/preprocessing/_general.py +91 -0
  15. MultiChat/Heterogeneous_g_emb/preprocessing/_pca.py +182 -0
  16. MultiChat/Heterogeneous_g_emb/preprocessing/_qc.py +727 -0
  17. MultiChat/Heterogeneous_g_emb/preprocessing/_utils.py +60 -0
  18. MultiChat/Heterogeneous_g_emb/preprocessing/_variable_genes.py +82 -0
  19. MultiChat/Heterogeneous_g_emb/readwrite.py +250 -0
  20. MultiChat/Heterogeneous_g_emb/tools/__init__.py +23 -0
  21. MultiChat/Heterogeneous_g_emb/tools/_gene_scores.py +346 -0
  22. MultiChat/Heterogeneous_g_emb/tools/_general.py +71 -0
  23. MultiChat/Heterogeneous_g_emb/tools/_integration.py +197 -0
  24. MultiChat/Heterogeneous_g_emb/tools/_pbg.py +1184 -0
  25. MultiChat/Heterogeneous_g_emb/tools/_post_training.py +919 -0
  26. MultiChat/Heterogeneous_g_emb/tools/_umap.py +58 -0
  27. MultiChat/Heterogeneous_g_emb/tools/_utils.py +253 -0
  28. MultiChat/Model/Layers.py +116 -0
  29. MultiChat/Model/__init__.py +3 -0
  30. MultiChat/Model/model_training.py +166 -0
  31. MultiChat/Model/modules.py +93 -0
  32. MultiChat/Model/utilities.py +234 -0
  33. MultiChat/Plot/Visualization.py +470 -0
  34. MultiChat/Plot/__init__.py +1 -0
  35. MultiChat/__init__.py +12 -0
  36. scmultichat-0.1.0.dist-info/METADATA +156 -0
  37. scmultichat-0.1.0.dist-info/RECORD +39 -0
  38. scmultichat-0.1.0.dist-info/WHEEL +5 -0
  39. scmultichat-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1184 @@
1
+ """PyTorch-BigGraph (PBG) for learning graph embeddings"""
2
+
3
+ from random import shuffle
4
+ from typing import Dict
5
+ import numpy as np
6
+ import pandas as pd
7
+ import anndata as ad
8
+ import os
9
+ import json
10
+ from tqdm.auto import tqdm
11
+
12
+ from pathlib import Path
13
+ import attr
14
+ from torchbiggraph.config import (
15
+ add_to_sys_path,
16
+ ConfigFileLoader
17
+ )
18
+ from torchbiggraph.converters.importers import (
19
+ convert_input_data,
20
+ TSVEdgelistReader
21
+ )
22
+ from torchbiggraph.train import train
23
+ from torchbiggraph.util import (
24
+ set_logging_verbosity,
25
+ setup_logging,
26
+ SubprocessInitializer,
27
+ )
28
+
29
+ from ._utils import _randomize_matrix
30
+ from .._settings import settings
31
+
32
+
33
+ def gen_graph(list_CP=None,
34
+ list_PM=None,
35
+ list_PK=None,
36
+ list_CG=None,
37
+ list_CC=None,
38
+ list_adata=None,
39
+ prefix_C='C',
40
+ prefix_P='P',
41
+ prefix_M='M',
42
+ prefix_K='K',
43
+ prefix_G='G',
44
+ prefix='E',
45
+ layer='hge',
46
+ copy=False,
47
+ dirname='graph0',
48
+ add_edge_weights=None,
49
+ use_highly_variable=True,
50
+ use_top_pcs=True,
51
+ use_top_pcs_CP=None,
52
+ use_top_pcs_PM=None,
53
+ use_top_pcs_PK=None,
54
+ get_marker_significance=False,
55
+ fold_null_nodes = 1.0,
56
+ ):
57
+ """Generate graph for PBG training.
58
+
59
+ Observations and variables of each Anndata object will be encoded
60
+ as nodes (entities). The non-zero values in `.layers['hge']` (by default)
61
+ or `.X` (if `.layers['hge']` does not exist) indicate the edges
62
+ between nodes. The values of `.layers['hge']` or `.X` will be used
63
+ as the edge weights if `add_edge_weights` True.
64
+
65
+ Nodes between different anndata objects will be automatically matched
66
+ based on `.obs_names` and `.var_names`. Each anndata object indicates one
67
+ or more relation types.
68
+
69
+ It also generates an accompanying file 'entity_alias.tsv' to map
70
+ the indices to the aliases used in the graph.
71
+
72
+ Note when `add_edge_weights` is True, `list_CG` will only generate
73
+ one relation of cells and genes, as opposed to multiple relations
74
+ based on discretized levels.
75
+
76
+ Parameters
77
+ ----------
78
+ list_CP: `list`, optional (default: None)
79
+ A list of anndata objects that store ATAC-seq data (Cells by Peaks)
80
+ The default weight of cell-peak relation type is 1.0.
81
+ list_PM: `list`, optional (default: None)
82
+ A list of anndata objects that store relation between Peaks and Motifs
83
+ list_PK: `list`, optional (default: None)
84
+ A list of anndata objects that store relation between Peaks and Kmers
85
+ list_CG: `list`, optional (default: None)
86
+ A list of anndata objects that store RNA-seq data (Cells by Genes)
87
+ list_CC: `list`, optional (default: None)
88
+ A list of anndata objects that store relation between Cells
89
+ from two conditions
90
+ list_adata: `list`, optional (default: None)
91
+ A list of anndata objects. `.obs_names` and `.var_names`
92
+ between anndata objects will be automatically matched.
93
+ If `list_adata` is specified, the other lists including
94
+ `list_CP`, `list_PM`,`list_PK`, `list_CG`, `list_CC` will be ignored.
95
+ prefix_C: `str`, optional (default: 'C')
96
+ Prefix to indicate the entity type of cells
97
+ prefix_G: `str`, optional (default: 'G')
98
+ Prefix to indicate the entity type of genes
99
+ prefix: `str`, optional (default: 'E')
100
+ Prefix to indicate general entities in `list_adata`
101
+ layer: `str`, optional (default: 'hge')
102
+ The layer in AnnData to use for constructing the graph.
103
+ If `layer` is None or the specificed layer does not exist,
104
+ `.X` in AnnData will be used instead.
105
+ dirname: `str`, (default: 'graph0')
106
+ The name of the directory in which each graph will be stored
107
+ add_edge_weights: `bool`, optional (default: None)
108
+ If True, the column of edge weigths will be added.
109
+ If `list_adata` is specified, `add_edge_weights` is set True
110
+ by default. Otherwise, it is set False.
111
+ use_highly_variable: `bool`, optional (default: True)
112
+ Use highly variable genes. Only valid for list_CG.
113
+ use_top_pcs: `bool`, optional (default: True)
114
+ Use top-PCs-associated features for CP, PM, PK
115
+ use_top_pcs_CP: `bool`, optional (default: None)
116
+ Use top-PCs-associated features for CP
117
+ Once specified, it will overwrite `use_top_pcs`
118
+ use_top_pcs_PM: `bool`, optional (default: None)
119
+ Use top-PCs-associated features for PM
120
+ Once specified, it will overwrite `use_top_pcs`
121
+ use_top_pcs_PK: `bool`, optional (default: None)
122
+ Use top-PCs-associated features for PK
123
+ Once specified, it will overwrite `use_top_pcs`
124
+ copy: `bool`, optional (default: False)
125
+ If True, it returns the graph file as a data frame
126
+ Returns
127
+ -------
128
+ If `copy` is True,
129
+ edges: `pd.DataFrame`
130
+ The edges of the graph used for PBG training.
131
+ Each line contains information about one edge.
132
+ Using tabs as separators, each line contains the identifiers of
133
+ the source entities, the relation types and the target entities.
134
+
135
+ updates `.settings.pbg_params` with the following parameters.
136
+ entity_path: `str`
137
+ The path of the directory containing entity count files.
138
+ edge_paths: `list`
139
+ A list of paths to directories containing (partitioned) edgelists.
140
+ Typically a single path is provided.
141
+ entities: `dict`
142
+ The entity types.
143
+ relations: `list`
144
+ The relation types.
145
+
146
+ updates `.settings.graph_stats` with the following parameters.
147
+ `dirname`: `dict`
148
+ Statistics of input graph
149
+ """
150
+
151
+ if sum(list(map(lambda x: x is None,
152
+ [list_CP,
153
+ list_PM,
154
+ list_PK,
155
+ list_CG,
156
+ list_CC,
157
+ list_adata]))) == 6:
158
+ return 'No graph is generated'
159
+ if get_marker_significance:
160
+ gen_graph(list_CP=list_CP,
161
+ list_PM=list_PM,
162
+ list_PK=list_PK,
163
+ list_CG=list_CG,
164
+ list_CC=list_CC,
165
+ prefix_C=prefix_C,
166
+ prefix_P=prefix_P,
167
+ prefix_M=prefix_M,
168
+ prefix_K=prefix_K,
169
+ prefix_G=prefix_G,
170
+ layer=layer,
171
+ copy=copy,
172
+ dirname=dirname,
173
+ add_edge_weights=add_edge_weights,
174
+ use_highly_variable=use_highly_variable,
175
+ use_top_pcs=use_top_pcs,
176
+ use_top_pcs_CP=use_top_pcs_CP,
177
+ use_top_pcs_PM=use_top_pcs_PM,
178
+ use_top_pcs_PK=use_top_pcs_PK,
179
+ get_marker_significance=False,
180
+ )
181
+ dirname_orig = dirname
182
+ dirname += "_with_sig"
183
+ filepath = os.path.join(settings.workdir, 'pbg', dirname)
184
+ settings.pbg_params['entity_path'] = \
185
+ os.path.join(filepath, "input/entity")
186
+ settings.pbg_params['edge_paths'] = \
187
+ [os.path.join(filepath, "input/edge"), ]
188
+ settings.pbg_params['entity_path'] = \
189
+ os.path.join(filepath, "input/entity")
190
+ if not os.path.exists(filepath):
191
+ os.makedirs(filepath)
192
+ if add_edge_weights is None:
193
+ if list_adata is None:
194
+ add_edge_weights = False
195
+ else:
196
+ add_edge_weights = True
197
+ def _get_df_edges(adj_mat, df_source, df_dest, adata, relation_id, include_weight = True, weight_scale = 1):
198
+ col_names = ["source", "relation", "destination"]
199
+ if include_weight:
200
+ col_names.append("weight")
201
+ df_edges_x = pd.DataFrame(columns=col_names)
202
+ df_edges_x['source'] = df_source.loc[
203
+ adata.obs_names[adj_mat.nonzero()[0]],
204
+ 'alias'].values
205
+ df_edges_x['relation'] = relation_id
206
+ df_edges_x['destination'] = df_dest.loc[
207
+ adata.var_names[adj_mat.nonzero()[1]],
208
+ 'alias'].values
209
+ if include_weight:
210
+ df_edges_x['weight'] = weight_scale
211
+ return(df_edges_x)
212
+ if list_adata is not None:
213
+ id_ent = pd.Index([]) # ids of all entities
214
+ dict_ent_type = dict()
215
+ ctr_ent = 0 # counter for entity types
216
+ entity_alias = pd.DataFrame(columns=['alias'])
217
+ dict_graph_stats = dict()
218
+ if add_edge_weights:
219
+ col_names = ["source", "relation", "destination", "weight"]
220
+ else:
221
+ col_names = ["source", "relation", "destination"]
222
+ df_edges = pd.DataFrame(columns=col_names)
223
+ settings.pbg_params['relations'] = []
224
+ if get_marker_significance and list_adata:
225
+ raise NotImplementedError("Marker gene significance is not yet implemented for graph generation from AnnData list.")
226
+ for ctr_rel, adata_ori in enumerate(list_adata):
227
+ obs_names = adata_ori.obs_names
228
+ var_names = adata_ori.var_names
229
+ if len(set(obs_names).intersection(id_ent)) == 0:
230
+ prefix_i = f'{prefix}{ctr_ent}'
231
+ id_ent = id_ent.union(adata_ori.obs_names)
232
+ entity_alias_obs = pd.DataFrame(
233
+ index=obs_names,
234
+ columns=['alias'],
235
+ data=[f'{prefix_i}.{x}'
236
+ for x in range(len(obs_names))])
237
+ settings.pbg_params['entities'][
238
+ prefix_i] = {'num_partitions': 1}
239
+ dict_ent_type[prefix_i] = obs_names
240
+ entity_alias = pd.concat(
241
+ [entity_alias, entity_alias_obs],
242
+ ignore_index=False)
243
+ obs_type = prefix_i
244
+ ctr_ent += 1
245
+ else:
246
+ for k, item in dict_ent_type.items():
247
+ if len(set(obs_names).intersection(item)) > 0:
248
+ obs_type = k
249
+ break
250
+ if not set(obs_names).issubset(id_ent):
251
+ id_ent = id_ent.union(adata_ori.obs_names)
252
+ adt_obs_names = list(set(obs_names)-set(item))
253
+ entity_alias_obs = pd.DataFrame(
254
+ index=adt_obs_names,
255
+ columns=['alias'],
256
+ data=[f'{prefix_i}.{len(item)+x}'
257
+ for x in range(len(adt_obs_names))])
258
+ dict_ent_type[obs_type] = obs_names.union(adt_obs_names)
259
+ entity_alias = pd.concat(
260
+ [entity_alias, entity_alias_obs],
261
+ ignore_index=False)
262
+ if len(set(var_names).intersection(id_ent)) == 0:
263
+ prefix_i = f'{prefix}{ctr_ent}'
264
+ id_ent = id_ent.union(adata_ori.var_names)
265
+ entity_alias_var = pd.DataFrame(
266
+ index=var_names,
267
+ columns=['alias'],
268
+ data=[f'{prefix_i}.{x}'
269
+ for x in range(len(var_names))])
270
+ settings.pbg_params['entities'][
271
+ prefix_i] = {'num_partitions': 1}
272
+ dict_ent_type[prefix_i] = var_names
273
+ entity_alias = pd.concat(
274
+ [entity_alias, entity_alias_var],
275
+ ignore_index=False)
276
+ var_type = prefix_i
277
+ ctr_ent += 1
278
+ else:
279
+ for k, item in dict_ent_type.items():
280
+ if len(set(var_names).intersection(item)) > 0:
281
+ var_type = k
282
+ break
283
+ if not set(var_names).issubset(id_ent):
284
+ id_ent = id_ent.union(adata_ori.var_names)
285
+ adt_var_names = list(set(var_names)-set(item))
286
+ entity_alias_var = pd.DataFrame(
287
+ index=adt_var_names,
288
+ columns=['alias'],
289
+ data=[f'{prefix_i}.{len(item)+x}'
290
+ for x in range(len(adt_var_names))])
291
+ dict_ent_type[var_type] = var_names.union(adt_var_names)
292
+ entity_alias = pd.concat(
293
+ [entity_alias, entity_alias_var],
294
+ ignore_index=False)
295
+
296
+ # generate edges
297
+ if layer is not None:
298
+ if layer in adata_ori.layers.keys():
299
+ arr_hge = adata_ori.layers[layer]
300
+ else:
301
+ print(f'`{layer}` does not exist in adata {ctr_rel} '
302
+ 'in `list_adata`.`.X` is being used instead.')
303
+ arr_hge = adata_ori.X
304
+ else:
305
+ arr_hge = adata_ori.X
306
+ _row, _col = arr_hge.nonzero()
307
+ df_edges_x = pd.DataFrame(columns=col_names)
308
+ df_edges_x['source'] = entity_alias.loc[
309
+ obs_names[_row], 'alias'].values
310
+ df_edges_x['relation'] = f'r{ctr_rel}'
311
+ df_edges_x['destination'] = entity_alias.loc[
312
+ var_names[_col], 'alias'].values
313
+ if add_edge_weights:
314
+ df_edges_x['weight'] = \
315
+ arr_hge[_row, _col].A.flatten()
316
+ settings.pbg_params['relations'].append({
317
+ 'name': f'r{ctr_rel}',
318
+ 'lhs': f'{obs_type}',
319
+ 'rhs': f'{var_type}',
320
+ 'operator': 'none',
321
+ 'weight': 1.0
322
+ })
323
+ dict_graph_stats[f'relation{ctr_rel}'] = {
324
+ 'source': obs_type,
325
+ 'destination': var_type,
326
+ 'n_edges': df_edges_x.shape[0]}
327
+ print(
328
+ f'relation{ctr_rel}: '
329
+ f'source: {obs_type}, '
330
+ f'destination: {var_type}\n'
331
+ f'#edges: {df_edges_x.shape[0]}')
332
+
333
+ df_edges = pd.concat(
334
+ [df_edges, df_edges_x],
335
+ ignore_index=True)
336
+ adata_ori.obs['pbg_id'] = ""
337
+ adata_ori.var['pbg_id'] = ""
338
+ adata_ori.obs.loc[obs_names, 'pbg_id'] = \
339
+ entity_alias.loc[obs_names, 'alias'].copy()
340
+ adata_ori.var.loc[var_names, 'pbg_id'] = \
341
+ entity_alias.loc[var_names, 'alias'].copy()
342
+
343
+ else:
344
+ # Collect the indices of entities
345
+ dict_cells = dict() # unique cell indices from all anndata objects
346
+ ids_genes = pd.Index([])
347
+ ids_peaks = pd.Index([])
348
+ ids_kmers = pd.Index([])
349
+ ids_motifs = pd.Index([])
350
+
351
+ if list_CP is not None:
352
+ for adata_ori in list_CP:
353
+ if use_top_pcs_CP is None:
354
+ flag_top_pcs = use_top_pcs
355
+ else:
356
+ flag_top_pcs = use_top_pcs_CP
357
+ if flag_top_pcs:
358
+ adata = adata_ori[:, adata_ori.var['top_pcs']].copy()
359
+ else:
360
+ adata = adata_ori.copy()
361
+ ids_cells_i = adata.obs.index
362
+ if len(dict_cells) == 0:
363
+ dict_cells[prefix_C] = ids_cells_i
364
+ else:
365
+ # check if cell indices are included in dict_cells
366
+ flag_included = False
367
+ for k in dict_cells.keys():
368
+ ids_cells_k = dict_cells[k]
369
+ if set(ids_cells_i) <= set(ids_cells_k):
370
+ flag_included = True
371
+ break
372
+ if not flag_included:
373
+ # create a new set of entities
374
+ # when not all indices are included
375
+ dict_cells[
376
+ f'{prefix_C}{len(dict_cells)+1}'] = \
377
+ ids_cells_i
378
+ ids_peaks = ids_peaks.union(adata.var.index)
379
+ if get_marker_significance:
380
+ n_npeaks = min(int(len(ids_peaks)*fold_null_nodes), len(ids_peaks))
381
+ ids_npeaks = pd.Index([f'n{prefix_P}.{x}' for x in range(n_npeaks)])
382
+ if list_PM is not None:
383
+ for adata_ori in list_PM:
384
+ if use_top_pcs_PM is None:
385
+ flag_top_pcs = use_top_pcs
386
+ else:
387
+ flag_top_pcs = use_top_pcs_PM
388
+ if flag_top_pcs:
389
+ adata = adata_ori[:, adata_ori.var['top_pcs']].copy()
390
+ else:
391
+ adata = adata_ori.copy()
392
+ ids_peaks = ids_peaks.union(adata.obs.index)
393
+ ids_motifs = ids_motifs.union(adata.var.index)
394
+ if get_marker_significance:
395
+ n_nmotifs = int(len(ids_motifs)*fold_null_nodes)
396
+ ids_nmotifs = pd.Index([f'n{prefix_M}.{x}' for x in range(n_nmotifs)])
397
+ if list_PK is not None:
398
+ for adata_ori in list_PK:
399
+ if use_top_pcs_PK is None:
400
+ flag_top_pcs = use_top_pcs
401
+ else:
402
+ flag_top_pcs = use_top_pcs_PK
403
+ if flag_top_pcs:
404
+ adata = adata_ori[:, adata_ori.var['top_pcs']].copy()
405
+ else:
406
+ adata = adata_ori.copy()
407
+ ids_peaks = ids_peaks.union(adata.obs.index)
408
+ ids_kmers = ids_kmers.union(adata.var.index)
409
+ if get_marker_significance:
410
+ n_nkmers = int(len(ids_kmers)*fold_null_nodes)
411
+ ids_nkmers = pd.Index([f'n{prefix_K}.{x}' for x in range(n_nkmers)])
412
+ if list_CG is not None:
413
+ for adata_ori in list_CG:
414
+ if use_highly_variable:
415
+ adata = adata_ori[
416
+ :, adata_ori.var['highly_variable']].copy()
417
+ else:
418
+ adata = adata_ori.copy()
419
+ ids_cells_i = adata.obs.index
420
+ if len(dict_cells) == 0:
421
+ dict_cells[prefix_C] = ids_cells_i
422
+ else:
423
+ # check if cell indices are included in dict_cells
424
+ flag_included = False
425
+ for k in dict_cells.keys():
426
+ ids_cells_k = dict_cells[k]
427
+ if set(ids_cells_i) <= set(ids_cells_k):
428
+ flag_included = True
429
+ break
430
+ if not flag_included:
431
+ # create a new set of entities
432
+ # when not all indices are included
433
+ dict_cells[
434
+ f'{prefix_C}{len(dict_cells)+1}'] = \
435
+ ids_cells_i
436
+ ids_genes = ids_genes.union(adata.var.index)
437
+ if get_marker_significance:
438
+ n_ngenes = int(len(ids_genes)*fold_null_nodes)
439
+ ids_ngenes = pd.Index([f'n{prefix_G}.{x}' for x in range(n_ngenes)])
440
+
441
+ entity_alias = pd.DataFrame(columns=['alias'])
442
+ dict_df_cells = dict() # unique cell dataframes
443
+ for k in dict_cells.keys():
444
+ dict_df_cells[k] = pd.DataFrame(
445
+ index=dict_cells[k],
446
+ columns=['alias'],
447
+ data=[f'{k}.{x}' for x in range(len(dict_cells[k]))])
448
+ settings.pbg_params['entities'][k] = {'num_partitions': 1}
449
+ entity_alias = pd.concat(
450
+ [entity_alias, dict_df_cells[k]],
451
+ ignore_index=False)
452
+ if len(ids_genes) > 0:
453
+ df_genes = pd.DataFrame(
454
+ index=ids_genes,
455
+ columns=['alias'],
456
+ data=[f'{prefix_G}.{x}' for x in range(len(ids_genes))])
457
+ settings.pbg_params['entities'][prefix_G] = {'num_partitions': 1}
458
+ entity_alias = pd.concat(
459
+ [entity_alias, df_genes],
460
+ ignore_index=False)
461
+ if get_marker_significance and (len(ids_ngenes) > 0):
462
+ df_ngenes = pd.DataFrame(
463
+ index=ids_ngenes,
464
+ columns=['alias'],
465
+ data=ids_ngenes.tolist())
466
+ settings.pbg_params['entities'][f'n{prefix_G}'] = {'num_partitions': 1}
467
+ entity_alias = pd.concat([entity_alias, df_ngenes],
468
+ ignore_index=False)
469
+ if len(ids_peaks) > 0:
470
+ df_peaks = pd.DataFrame(
471
+ index=ids_peaks,
472
+ columns=['alias'],
473
+ data=[f'{prefix_P}.{x}' for x in range(len(ids_peaks))])
474
+ settings.pbg_params['entities'][prefix_P] = {'num_partitions': 1}
475
+ entity_alias = pd.concat(
476
+ [entity_alias, df_peaks],
477
+ ignore_index=False)
478
+ if get_marker_significance and (len(ids_npeaks) > 0):
479
+ df_npeaks = pd.DataFrame(
480
+ index=ids_npeaks,
481
+ columns=['alias'],
482
+ data=ids_npeaks.tolist())
483
+ settings.pbg_params['entities'][f'n{prefix_P}'] = {'num_partitions': 1}
484
+ entity_alias = pd.concat([entity_alias,df_npeaks],
485
+ ignore_index=False)
486
+ if len(ids_kmers) > 0:
487
+ df_kmers = pd.DataFrame(
488
+ index=ids_kmers,
489
+ columns=['alias'],
490
+ data=[f'{prefix_K}.{x}' for x in range(len(ids_kmers))])
491
+ settings.pbg_params['entities'][prefix_K] = {'num_partitions': 1}
492
+ entity_alias = pd.concat(
493
+ [entity_alias, df_kmers],
494
+ ignore_index=False)
495
+ if get_marker_significance and (len(ids_nkmers) > 0):
496
+ df_nkmers = pd.DataFrame(
497
+ index=ids_nkmers,
498
+ columns=['alias'],
499
+ data=ids_nkmers.tolist())
500
+ settings.pbg_params['entities'][f'n{prefix_K}'] = {'num_partitions': 1}
501
+ entity_alias = pd.concat([entity_alias,df_nkmers],
502
+ ignore_index=False)
503
+ if len(ids_motifs) > 0:
504
+ df_motifs = pd.DataFrame(
505
+ index=ids_motifs,
506
+ columns=['alias'],
507
+ data=[f'{prefix_M}.{x}' for x in range(len(ids_motifs))])
508
+ settings.pbg_params['entities'][prefix_M] = {'num_partitions': 1}
509
+ entity_alias = pd.concat(
510
+ [entity_alias, df_motifs],
511
+ ignore_index=False)
512
+ if get_marker_significance and (len(ids_nmotifs) > 0):
513
+ df_nmotifs = pd.DataFrame(
514
+ index=ids_nmotifs,
515
+ columns=['alias'],
516
+ data=ids_nmotifs.tolist())
517
+ settings.pbg_params['entities'][f'n{prefix_M}'] = {'num_partitions': 1}
518
+ entity_alias = pd.concat([entity_alias,df_nmotifs],
519
+ ignore_index=False)
520
+ # generate edges
521
+ dict_graph_stats = dict()
522
+ if add_edge_weights:
523
+ col_names = ["source", "relation", "destination", "weight"]
524
+ else:
525
+ col_names = ["source", "relation", "destination"]
526
+ df_edges = pd.DataFrame(columns=col_names)
527
+ id_r = 0
528
+ settings.pbg_params['relations'] = []
529
+
530
+ if list_CP is not None:
531
+ for i, adata_ori in enumerate(list_CP):
532
+ if use_top_pcs:
533
+ adata = adata_ori[:, adata_ori.var['top_pcs']].copy()
534
+ else:
535
+ adata = adata_ori.copy()
536
+ # select reference of cells
537
+ for key, df_cells in dict_df_cells.items():
538
+ if set(adata.obs_names) <= set(df_cells.index):
539
+ break
540
+ if layer is not None:
541
+ if layer in adata.layers.keys():
542
+ arr_hge = adata.layers[layer]
543
+ else:
544
+ print(f'`{layer}` does not exist in anndata {i} '
545
+ 'in `list_CP`.`.X` is being used instead.')
546
+ arr_hge = adata.X
547
+ else:
548
+ arr_hge = adata.X
549
+ if get_marker_significance:
550
+ #n_npeaks = int(len(ids_peaks)*fold_null_nodes)
551
+ null_matrix = _randomize_matrix(arr_hge, n_npeaks, method='degPreserving')
552
+ null_adata = ad.AnnData(obs=adata.obs, var=df_npeaks, layers={"disc":null_matrix})
553
+ _row, _col = arr_hge.nonzero()
554
+ df_edges_x = pd.DataFrame(columns=col_names)
555
+ df_edges_x['source'] = df_cells.loc[
556
+ adata.obs_names[_row], 'alias'].values
557
+ df_edges_x['relation'] = f'r{id_r}'
558
+ df_edges_x['destination'] = df_peaks.loc[
559
+ adata.var_names[_col], 'alias'].values
560
+ if add_edge_weights:
561
+ df_edges_x['weight'] = \
562
+ arr_hge[_row, _col].A.flatten()
563
+ settings.pbg_params['relations'].append({
564
+ 'name': f'r{id_r}',
565
+ 'lhs': f'{key}',
566
+ 'rhs': f'{prefix_P}',
567
+ 'operator': 'none',
568
+ 'weight': 1.0
569
+ })
570
+ dict_graph_stats[f'relation{id_r}'] = {
571
+ 'source': key,
572
+ 'destination': prefix_P,
573
+ 'n_edges': df_edges_x.shape[0]}
574
+ print(
575
+ f'relation{id_r}: '
576
+ f'source: {key}, '
577
+ f'destination: {prefix_P}\n'
578
+ f'#edges: {df_edges_x.shape[0]}')
579
+ id_r += 1
580
+ df_edges = pd.concat(
581
+ [df_edges, df_edges_x],
582
+ ignore_index=True)
583
+ adata_ori.obs['pbg_id'] = ""
584
+ adata_ori.var['pbg_id'] = ""
585
+ adata_ori.obs.loc[adata.obs_names, 'pbg_id'] = \
586
+ df_cells.loc[adata.obs_names, 'alias'].copy()
587
+ adata_ori.var.loc[adata.var_names, 'pbg_id'] = \
588
+ df_peaks.loc[adata.var_names, 'alias'].copy()
589
+ if get_marker_significance:
590
+ _col, _row = null_matrix.transpose().nonzero()
591
+ df_edges_x = pd.DataFrame(columns=col_names)
592
+ df_edges_x['destination'] = df_cells.loc[
593
+ null_adata.obs_names[_row], 'alias'].values
594
+ df_edges_x['relation'] = f'r{id_r}'
595
+ df_edges_x['source'] = df_npeaks.loc[
596
+ null_adata.var_names[_col], 'alias'].values
597
+ df_edges_x['weight'] = \
598
+ null_matrix.transpose()[_col, _row].A.flatten()
599
+ settings.pbg_params['relations'].append({
600
+ 'name': f'r{id_r}',
601
+ 'lhs': f'n{prefix_P}',
602
+ 'rhs': f'{key}',
603
+ 'operator': 'fix',
604
+ 'weight': 1.0,
605
+ })
606
+ print(
607
+ f'relation{id_r}: '
608
+ f'source: n{prefix_P}, '
609
+ f'destination: {key}\n'
610
+ f'#edges: {df_edges_x.shape[0]}')
611
+ dict_graph_stats[f'relation{id_r}'] = {
612
+ 'source': f'n{prefix_P}',
613
+ 'destination': key,
614
+ 'n_edges': df_edges_x.shape[0]}
615
+ id_r += 1
616
+ df_edges = pd.concat(
617
+ [df_edges, df_edges_x],
618
+ ignore_index=True)
619
+
620
+ if list_PM is not None:
621
+ for i, adata_ori in enumerate(list_PM):
622
+ if use_top_pcs:
623
+ adata = adata_ori[:, adata_ori.var['top_pcs']].copy()
624
+ else:
625
+ adata = adata_ori.copy()
626
+ if layer is not None:
627
+ if layer in adata.layers.keys():
628
+ arr_hge = adata.layers[layer]
629
+ else:
630
+ print(f'`{layer}` does not exist in anndata {i} '
631
+ 'in `list_PM`.`.X` is being used instead.')
632
+ arr_hge = adata.X
633
+ else:
634
+ arr_hge = adata.X
635
+ if get_marker_significance:
636
+ n_nmotifs = int(len(ids_motifs)*fold_null_nodes)
637
+ null_matrix = _randomize_matrix(arr_hge, n_nmotifs, method='degPreserving')
638
+ null_adata = ad.AnnData(obs=adata.obs, var=df_nmotifs, layers={"disc":null_matrix})
639
+ _row, _col = arr_hge.nonzero()
640
+ df_edges_x = pd.DataFrame(columns=col_names)
641
+ df_edges_x['source'] = df_peaks.loc[
642
+ adata.obs_names[_row], 'alias'].values
643
+ df_edges_x['relation'] = f'r{id_r}'
644
+ df_edges_x['destination'] = df_motifs.loc[
645
+ adata.var_names[_col], 'alias'].values
646
+ if add_edge_weights:
647
+ df_edges_x['weight'] = \
648
+ arr_hge[_row, _col].A.flatten()
649
+ settings.pbg_params['relations'].append({
650
+ 'name': f'r{id_r}',
651
+ 'lhs': f'{prefix_P}',
652
+ 'rhs': f'{prefix_M}',
653
+ 'operator': 'none',
654
+ 'weight': 0.2
655
+ })
656
+ else:
657
+ settings.pbg_params['relations'].append({
658
+ 'name': f'r{id_r}',
659
+ 'lhs': f'{prefix_P}',
660
+ 'rhs': f'{prefix_M}',
661
+ 'operator': 'none',
662
+ 'weight': 0.2
663
+ })
664
+ dict_graph_stats[f'relation{id_r}'] = {
665
+ 'source': prefix_P,
666
+ 'destination': prefix_M,
667
+ 'n_edges': df_edges_x.shape[0]}
668
+ print(
669
+ f'relation{id_r}: '
670
+ f'source: {prefix_P}, '
671
+ f'destination: {prefix_M}\n'
672
+ f'#edges: {df_edges_x.shape[0]}')
673
+
674
+ id_r += 1
675
+ df_edges = pd.concat(
676
+ [df_edges, df_edges_x],
677
+ ignore_index=True)
678
+ adata_ori.obs['pbg_id'] = ""
679
+ adata_ori.var['pbg_id'] = ""
680
+ adata_ori.obs.loc[adata.obs_names, 'pbg_id'] = \
681
+ df_peaks.loc[adata.obs_names, 'alias'].copy()
682
+ adata_ori.var.loc[adata.var_names, 'pbg_id'] = \
683
+ df_motifs.loc[adata.var_names, 'alias'].copy()
684
+ if get_marker_significance:
685
+ _col, _row = null_matrix.transpose().nonzero()
686
+ df_edges_x = pd.DataFrame(columns=col_names)
687
+ df_edges_x['destination'] = df_peaks.loc[
688
+ null_adata.obs_names[_row], 'alias'].values
689
+ df_edges_x['relation'] = f'r{id_r}'
690
+ df_edges_x['source'] = df_nmotifs.loc[
691
+ null_adata.var_names[_col], 'alias'].values
692
+ df_edges_x['weight'] = \
693
+ null_matrix.transpose()[_col, _row].A.flatten()
694
+ settings.pbg_params['relations'].append({
695
+ 'name': f'r{id_r}',
696
+ 'lhs': f'n{prefix_M}',
697
+ 'rhs': f'{prefix_P}',
698
+ 'operator': 'fix',
699
+ 'weight': 1.0,
700
+ })
701
+ print(
702
+ f'relation{id_r}: '
703
+ f'source: n{prefix_M}, '
704
+ f'destination: {prefix_P}\n'
705
+ f'#edges: {df_edges_x.shape[0]}')
706
+ dict_graph_stats[f'relation{id_r}'] = {
707
+ 'source': f'n{prefix_M}',
708
+ 'destination': prefix_P,
709
+ 'n_edges': df_edges_x.shape[0]}
710
+ id_r += 1
711
+ df_edges = pd.concat(
712
+ [df_edges, df_edges_x],
713
+ ignore_index=True)
714
+
715
+ if list_PK is not None:
716
+ for i, adata_ori in enumerate(list_PK):
717
+ if use_top_pcs:
718
+ adata = adata_ori[:, adata_ori.var['top_pcs']].copy()
719
+ else:
720
+ adata = adata_ori.copy()
721
+ if layer is not None:
722
+ if layer in adata.layers.keys():
723
+ arr_hge = adata.layers[layer]
724
+ else:
725
+ print(f'`{layer}` does not exist in anndata {i} '
726
+ 'in `list_PK`.`.X` is being used instead.')
727
+ arr_hge = adata.X
728
+ else:
729
+ arr_hge = adata.X
730
+ if get_marker_significance:
731
+ n_nkmers = int(len(ids_kmers)*fold_null_nodes)
732
+ null_matrix = _randomize_matrix(arr_hge, n_nkmers, method='degPreserving')
733
+ null_adata = ad.AnnData(obs=adata.obs, var=df_nkmers, layers={"disc":null_matrix})
734
+ _row, _col = arr_hge.nonzero()
735
+ df_edges_x = pd.DataFrame(columns=col_names)
736
+ df_edges_x['source'] = df_peaks.loc[
737
+ adata.obs_names[_row], 'alias'].values
738
+ df_edges_x['relation'] = f'r{id_r}'
739
+ df_edges_x['destination'] = df_kmers.loc[
740
+ adata.var_names[_col], 'alias'].values
741
+ if add_edge_weights:
742
+ df_edges_x['weight'] = \
743
+ arr_hge[_row, _col].A.flatten()
744
+ settings.pbg_params['relations'].append({
745
+ 'name': f'r{id_r}',
746
+ 'lhs': f'{prefix_P}',
747
+ 'rhs': f'{prefix_K}',
748
+ 'operator': 'none',
749
+ 'weight': 0.02
750
+ })
751
+ else:
752
+ settings.pbg_params['relations'].append({
753
+ 'name': f'r{id_r}',
754
+ 'lhs': f'{prefix_P}',
755
+ 'rhs': f'{prefix_K}',
756
+ 'operator': 'none',
757
+ 'weight': 0.02
758
+ })
759
+ print(
760
+ f'relation{id_r}: '
761
+ f'source: {prefix_P}, '
762
+ f'destination: {prefix_K}\n'
763
+ f'#edges: {df_edges_x.shape[0]}')
764
+ dict_graph_stats[f'relation{id_r}'] = {
765
+ 'source': prefix_P,
766
+ 'destination': prefix_K,
767
+ 'n_edges': df_edges_x.shape[0]}
768
+
769
+ id_r += 1
770
+ df_edges = pd.concat(
771
+ [df_edges, df_edges_x],
772
+ ignore_index=True)
773
+ adata_ori.obs['pbg_id'] = ""
774
+ adata_ori.var['pbg_id'] = ""
775
+ adata_ori.obs.loc[adata.obs_names, 'pbg_id'] = \
776
+ df_peaks.loc[adata.obs_names, 'alias'].copy()
777
+ adata_ori.var.loc[adata.var_names, 'pbg_id'] = \
778
+ df_kmers.loc[adata.var_names, 'alias'].copy()
779
+ if get_marker_significance:
780
+ _col, _row = null_matrix.transpose().nonzero()
781
+ df_edges_x = pd.DataFrame(columns=col_names)
782
+ df_edges_x['destination'] = df_peaks.loc[
783
+ null_adata.obs_names[_row], 'alias'].values
784
+ df_edges_x['relation'] = f'r{id_r}'
785
+ df_edges_x['source'] = df_nkmers.loc[
786
+ null_adata.var_names[_col], 'alias'].values
787
+ df_edges_x['weight'] = \
788
+ null_matrix.transpose()[_col, _row].A.flatten()
789
+ settings.pbg_params['relations'].append({
790
+ 'name': f'r{id_r}',
791
+ 'lhs': f'n{prefix_K}',
792
+ 'rhs': f'{prefix_P}',
793
+ 'operator': 'fix',
794
+ 'weight': 0.02,
795
+ })
796
+ print(
797
+ f'relation{id_r}: '
798
+ f'source: n{prefix_K}, '
799
+ f'destination: {prefix_P}\n'
800
+ f'#edges: {df_edges_x.shape[0]}')
801
+ dict_graph_stats[f'relation{id_r}'] = {
802
+ 'source': f'n{prefix_K}',
803
+ 'destination': prefix_P,
804
+ 'n_edges': df_edges_x.shape[0]}
805
+ id_r += 1
806
+ df_edges = pd.concat(
807
+ [df_edges, df_edges_x],
808
+ ignore_index=True)
809
+
810
+ if list_CG is not None:
811
+ for i, adata_ori in enumerate(list_CG):
812
+ if use_highly_variable:
813
+ adata = adata_ori[
814
+ :, adata_ori.var['highly_variable']].copy()
815
+ else:
816
+ adata = adata_ori.copy()
817
+ # select reference of cells
818
+ for key, df_cells in dict_df_cells.items():
819
+ if set(adata.obs_names) <= set(df_cells.index):
820
+ break
821
+ if layer is not None:
822
+ if layer in adata.layers.keys():
823
+ arr_hge = adata.layers[layer]
824
+ else:
825
+ print(f'`{layer}` does not exist in anndata {i} '
826
+ 'in `list_CG`.`.X` is being used instead.')
827
+ arr_hge = adata.X
828
+ else:
829
+ arr_hge = adata.X
830
+ if get_marker_significance:
831
+ n_ngenes = int(len(ids_genes)*fold_null_nodes)
832
+ null_exp_matrix = _randomize_matrix(arr_hge, n_ngenes, method='degPreserving')
833
+ null_adata = ad.AnnData(obs=adata.obs, var=df_ngenes, layers={"disc":null_exp_matrix})
834
+ if add_edge_weights:
835
+ _row, _col = arr_hge.nonzero()
836
+ df_edges_x = pd.DataFrame(columns=col_names)
837
+ df_edges_x['source'] = df_cells.loc[
838
+ adata.obs_names[_row], 'alias'].values
839
+ df_edges_x['relation'] = f'r{id_r}'
840
+ df_edges_x['destination'] = df_genes.loc[
841
+ adata.var_names[_col], 'alias'].values
842
+ df_edges_x['weight'] = \
843
+ arr_hge[_row, _col].A.flatten()
844
+ settings.pbg_params['relations'].append({
845
+ 'name': f'r{id_r}',
846
+ 'lhs': f'{key}',
847
+ 'rhs': f'{prefix_G}',
848
+ 'operator': 'none',
849
+ 'weight': 1.0,
850
+ })
851
+ print(
852
+ f'relation{id_r}: '
853
+ f'source: {key}, '
854
+ f'destination: {prefix_G}\n'
855
+ f'#edges: {df_edges_x.shape[0]}')
856
+ dict_graph_stats[f'relation{id_r}'] = {
857
+ 'source': key,
858
+ 'destination': prefix_G,
859
+ 'n_edges': df_edges_x.shape[0]}
860
+ id_r += 1
861
+ df_edges = pd.concat(
862
+ [df_edges, df_edges_x],
863
+ ignore_index=True)
864
+ if get_marker_significance:
865
+ _col, _row = null_exp_matrix.transpose().nonzero()
866
+ df_edges_x = pd.DataFrame(columns=col_names)
867
+ df_edges_x['destination'] = df_cells.loc[
868
+ null_adata.obs_names[_row], 'alias'].values
869
+ df_edges_x['relation'] = f'r{id_r}'
870
+ df_edges_x['source'] = df_ngenes.loc[
871
+ null_adata.var_names[_col], 'alias'].values
872
+ df_edges_x['weight'] = \
873
+ null_exp_matrix.transpose()[_col, _row].A.flatten()
874
+ settings.pbg_params['relations'].append({
875
+ 'name': f'r{id_r}',
876
+ 'lhs': f'n{prefix_G}',
877
+ 'rhs': f'{key}',
878
+ 'operator': 'fix',
879
+ 'weight': 1.0,
880
+ })
881
+ print(
882
+ f'relation{id_r}: '
883
+ f'source: n{prefix_G}, '
884
+ f'destination: {key}\n'
885
+ f'#edges: {df_edges_x.shape[0]}')
886
+ dict_graph_stats[f'relation{id_r}'] = {
887
+ 'source': f'n{prefix_G}',
888
+ 'destination': key,
889
+ 'n_edges': df_edges_x.shape[0]}
890
+ id_r += 1
891
+ df_edges = pd.concat(
892
+ [df_edges, df_edges_x],
893
+ ignore_index=True)
894
+ else:
895
+ expr_level = np.unique(arr_hge.data)
896
+ expr_weight = np.linspace(
897
+ start=1, stop=5, num=len(expr_level))
898
+ for i_lvl, lvl in enumerate(expr_level):
899
+ _row, _col = (arr_hge == lvl).astype(int).nonzero()
900
+ df_edges_x = pd.DataFrame(columns=col_names)
901
+ df_edges_x['source'] = df_cells.loc[
902
+ adata.obs_names[_row], 'alias'].values
903
+ df_edges_x['relation'] = f'r{id_r}'
904
+ df_edges_x['destination'] = df_genes.loc[
905
+ adata.var_names[_col], 'alias'].values
906
+ settings.pbg_params['relations'].append({
907
+ 'name': f'r{id_r}',
908
+ 'lhs': f'{key}',
909
+ 'rhs': f'{prefix_G}',
910
+ 'operator': 'none',
911
+ 'weight': round(expr_weight[i_lvl], 2),
912
+ })
913
+ print(
914
+ f'relation{id_r}: '
915
+ f'source: {key}, '
916
+ f'destination: {prefix_G}\n'
917
+ f'#edges: {df_edges_x.shape[0]}')
918
+ dict_graph_stats[f'relation{id_r}'] = {
919
+ 'source': key,
920
+ 'destination': prefix_G,
921
+ 'n_edges': df_edges_x.shape[0]}
922
+ id_r += 1
923
+ df_edges = pd.concat(
924
+ [df_edges, df_edges_x], ignore_index=True)
925
+ if get_marker_significance:
926
+ # generate null AnnData with cells x null genes
927
+ df_edges_v = _get_df_edges((null_exp_matrix == lvl).astype(int).T,
928
+ df_ngenes, df_cells, null_adata.transpose(), f'r{id_r}', include_weight=True, weight_scale=lvl)
929
+ print(f'relation{id_r}: '
930
+ f'source: n{prefix_G}, '
931
+ f'destination: {key}\n'
932
+ f'#edges: {df_edges_v.shape[0]}')
933
+ dict_graph_stats[f'relation{id_r}'] = \
934
+ {'source': f'n{prefix_G}',
935
+ 'destination': key,
936
+ 'n_edges': df_edges_v.shape[0]}
937
+ df_edges = pd.concat([df_edges, df_edges_v],
938
+ ignore_index=True)
939
+ settings.pbg_params['relations'].append(
940
+ {'name': f'r{id_r}',
941
+ 'lhs': f'n{prefix_G}',
942
+ 'rhs': f'{key}',
943
+ 'operator': 'fix',
944
+ 'weight': round(expr_weight[i_lvl], 2),
945
+ })
946
+ id_r += 1
947
+
948
+ adata_ori.obs['pbg_id'] = ""
949
+ adata_ori.var['pbg_id'] = ""
950
+ adata_ori.obs.loc[adata.obs_names, 'pbg_id'] = \
951
+ df_cells.loc[adata.obs_names, 'alias'].copy()
952
+ adata_ori.var.loc[adata.var_names, 'pbg_id'] = \
953
+ df_genes.loc[adata.var_names, 'alias'].copy()
954
+
955
+ if list_CC is not None:
956
+ for i, adata in enumerate(list_CC):
957
+ # select reference of cells
958
+ for key_obs, df_cells_obs in dict_df_cells.items():
959
+ if set(adata.obs_names) <= set(df_cells_obs.index):
960
+ break
961
+ for key_var, df_cells_var in dict_df_cells.items():
962
+ if set(adata.var_names) <= set(df_cells_var.index):
963
+ break
964
+ if layer is not None:
965
+ if layer in adata.layers.keys():
966
+ arr_hge = adata.layers[layer]
967
+ else:
968
+ print(f'`{layer}` does not exist in anndata {i} '
969
+ 'in `list_PM`.`.X` is being used instead.')
970
+ arr_hge = adata.X
971
+ else:
972
+ arr_hge = adata.X
973
+ _row, _col = arr_hge.nonzero()
974
+ # edges between ref and query
975
+ df_edges_x = pd.DataFrame(columns=col_names)
976
+ df_edges_x['source'] = df_cells_obs.loc[
977
+ adata.obs_names[_row], 'alias'].values
978
+ df_edges_x['relation'] = f'r{id_r}'
979
+ df_edges_x['destination'] = df_cells_var.loc[
980
+ adata.var_names[_col], 'alias'].values
981
+ if add_edge_weights:
982
+ df_edges_x['weight'] = \
983
+ arr_hge[_row, _col].A.flatten()
984
+ settings.pbg_params['relations'].append({
985
+ 'name': f'r{id_r}',
986
+ 'lhs': f'{key_obs}',
987
+ 'rhs': f'{key_var}',
988
+ 'operator': 'none',
989
+ 'weight': 1.0
990
+ })
991
+ else:
992
+ settings.pbg_params['relations'].append({
993
+ 'name': f'r{id_r}',
994
+ 'lhs': f'{key_obs}',
995
+ 'rhs': f'{key_var}',
996
+ 'operator': 'none',
997
+ 'weight': 10.0
998
+ })
999
+ print(
1000
+ f'relation{id_r}: '
1001
+ f'source: {key_obs}, '
1002
+ f'destination: {key_var}\n'
1003
+ f'#edges: {df_edges_x.shape[0]}')
1004
+ dict_graph_stats[f'relation{id_r}'] = {
1005
+ 'source': key_obs,
1006
+ 'destination': key_var,
1007
+ 'n_edges': df_edges_x.shape[0]}
1008
+
1009
+ id_r += 1
1010
+ df_edges = pd.concat(
1011
+ [df_edges, df_edges_x],
1012
+ ignore_index=True)
1013
+ adata.obs['pbg_id'] = df_cells_obs.loc[
1014
+ adata.obs_names, 'alias'].copy()
1015
+ adata.var['pbg_id'] = df_cells_var.loc[
1016
+ adata.var_names, 'alias'].copy()
1017
+
1018
+ print(f'Total number of edges: {df_edges.shape[0]}')
1019
+ dict_graph_stats['n_edges'] = df_edges.shape[0]
1020
+ settings.graph_stats[dirname] = dict_graph_stats
1021
+
1022
+ print(f'Writing graph file "pbg_graph.txt" to "{filepath}" ...')
1023
+ df_edges.to_csv(os.path.join(filepath, "pbg_graph.txt"),
1024
+ header=False,
1025
+ index=False,
1026
+ sep='\t')
1027
+ entity_alias.to_csv(os.path.join(filepath, 'entity_alias.txt'),
1028
+ header=True,
1029
+ index=True,
1030
+ sep='\t')
1031
+ with open(os.path.join(filepath, 'graph_stats.json'), 'w') as fp:
1032
+ json.dump(dict_graph_stats,
1033
+ fp,
1034
+ sort_keys=True,
1035
+ indent=4,
1036
+ separators=(',', ': '))
1037
+ print("Finished.")
1038
+ settings.graph_stats[dirname]['entities'] = settings.pbg_params['entities']
1039
+ settings.graph_stats[dirname]['relations'] = settings.pbg_params['relations']
1040
+ if get_marker_significance:
1041
+ filepath = os.path.join(settings.workdir, 'pbg', dirname_orig)
1042
+ settings.pbg_params['entity_path'] = \
1043
+ os.path.join(filepath, "input/entity")
1044
+ settings.pbg_params['edge_paths'] = \
1045
+ [os.path.join(filepath, "input/edge"), ]
1046
+ settings.pbg_params['entities'] = settings.graph_stats[dirname_orig]['entities']
1047
+ settings.pbg_params['relations'] = settings.graph_stats[dirname_orig]['relations']
1048
+ if copy:
1049
+ return df_edges
1050
+ else:
1051
+ return None
1052
+
1053
+
1054
+ def pbg_train(dirname=None,
1055
+ pbg_params=None,
1056
+ output='model',
1057
+ auto_wd=True,
1058
+ save_wd=False,
1059
+ use_edge_weights=False,
1060
+ get_marker_significance=False,):
1061
+ """PBG training
1062
+
1063
+ Parameters
1064
+ ----------
1065
+ dirname: `str`, optional (default: None)
1066
+ The name of the directory in which graph is stored
1067
+ If None, it will be inferred from `pbg_params['entity_path']`
1068
+ pbg_params: `dict`, optional (default: None)
1069
+ Configuration for pbg training.
1070
+ If specified, it will be used instead of the default setting
1071
+ output: `str`, optional (default: 'model')
1072
+ The name of the directory where training output will be written to.
1073
+ It overrides `pbg_params` if `checkpoint_path` is specified in it
1074
+ auto_wd: `bool`, optional (default: True)
1075
+ If True, it will override `pbg_params['wd']` with a new weight decay
1076
+ estimated based on training sample size
1077
+ Recommended for relative small training sample size (<1e7)
1078
+ save_wd: `bool`, optional (default: False)
1079
+ If True, estimated `wd` will be saved to `settings.pbg_params['wd']`
1080
+ use_edge_weights: `bool`, optional (default: False)
1081
+ If True, the edge weights are used for the training;
1082
+ If False, the weights of relation types are used instead,
1083
+ and edge weights will be ignored.
1084
+
1085
+ Returns
1086
+ -------
1087
+ updates `settings.pbg_params` with the following parameter
1088
+ checkpoint_path:
1089
+ The path to the directory where checkpoints (and thus the output)
1090
+ will be written to.
1091
+ If checkpoints are found in it, training will resume from them.
1092
+ """
1093
+ if pbg_params is None:
1094
+ pbg_params = settings.pbg_params.copy()
1095
+ else:
1096
+ assert isinstance(pbg_params, dict),\
1097
+ "`pbg_params` must be dict"
1098
+
1099
+ if dirname is None:
1100
+ filepath = Path(pbg_params['entity_path']).parent.parent.as_posix()
1101
+ dirname = os.path.basename(filepath)
1102
+ else:
1103
+ filepath = os.path.join(settings.workdir, 'pbg', dirname)
1104
+ pbg_params['checkpoint_path'] = os.path.join(filepath, output)
1105
+ settings.pbg_params['checkpoint_path'] = pbg_params['checkpoint_path']
1106
+ if get_marker_significance:
1107
+ pbg_train(dirname=dirname,
1108
+ pbg_params=pbg_params,
1109
+ output=output,
1110
+ auto_wd=auto_wd,
1111
+ save_wd=True,
1112
+ use_edge_weights=use_edge_weights,
1113
+ get_marker_significance=False)
1114
+ pbg_params = pbg_params.copy()
1115
+ n_edges = settings.graph_stats[
1116
+ os.path.basename(filepath)]['n_edges']
1117
+ filepath += "_with_sig"
1118
+ pbg_params['checkpoint_path'] = os.path.join(filepath, output)
1119
+ pbg_params['entity_path'] = os.path.join(filepath, "input/entity")
1120
+ pbg_params['edge_paths'] = [os.path.join(filepath, "input/edge"), ]
1121
+ pbg_params['relations'] = settings.graph_stats[dirname + "_with_sig"]['relations']
1122
+ auto_wd = False
1123
+ pbg_params['wd'] = settings.pbg_params['wd'] * n_edges / settings.graph_stats[
1124
+ os.path.basename(filepath)]['n_edges']
1125
+ settings.pbg_params['wd'] = pbg_params['wd']
1126
+
1127
+ if auto_wd:
1128
+ # empirical numbers from simulation experiments
1129
+ if settings.graph_stats[
1130
+ os.path.basename(filepath)]['n_edges'] < 5e7:
1131
+ # optimial wd (0.013) for sample size (2725781)
1132
+ wd = 0.013 * 2725781 / settings.graph_stats[
1133
+ os.path.basename(filepath)]['n_edges']
1134
+ else:
1135
+ # optimial wd (0.0004) for sample size (59103481)
1136
+ wd = 0.0004 * 59103481 / settings.graph_stats[
1137
+ os.path.basename(filepath)]['n_edges']
1138
+ print(f'Auto-estimated weight decay is {wd:.6E}')
1139
+ pbg_params['wd'] = wd
1140
+ if save_wd:
1141
+ settings.pbg_params['wd'] = pbg_params['wd']
1142
+ print(f"`.settings.pbg_params['wd']` has been updated to {wd:.6E}")
1143
+
1144
+
1145
+ # to avoid oversubscription issues in workloads
1146
+ # that involve nested parallelism
1147
+ os.environ["OMP_NUM_THREADS"] = "1"
1148
+
1149
+ loader = ConfigFileLoader()
1150
+ config = loader.load_config_simba(pbg_params)
1151
+ set_logging_verbosity(config.verbose)
1152
+
1153
+ list_filenames = [os.path.join(filepath, "pbg_graph.txt")]
1154
+ input_edge_paths = [Path(name) for name in list_filenames]
1155
+ print("Converting input data ...")
1156
+ if use_edge_weights:
1157
+ print("Edge weights are being used ...")
1158
+ convert_input_data(
1159
+ config.entities,
1160
+ config.relations,
1161
+ config.entity_path,
1162
+ config.edge_paths,
1163
+ input_edge_paths,
1164
+ TSVEdgelistReader(lhs_col=0, rhs_col=2, rel_col=1, weight_col=3),
1165
+ dynamic_relations=config.dynamic_relations,
1166
+ )
1167
+ else:
1168
+ convert_input_data(
1169
+ config.entities,
1170
+ config.relations,
1171
+ config.entity_path,
1172
+ config.edge_paths,
1173
+ input_edge_paths,
1174
+ TSVEdgelistReader(lhs_col=0, rhs_col=2, rel_col=1),
1175
+ dynamic_relations=config.dynamic_relations,
1176
+ )
1177
+ subprocess_init = SubprocessInitializer()
1178
+ subprocess_init.register(setup_logging, config.verbose)
1179
+ subprocess_init.register(add_to_sys_path, loader.config_dir.name)
1180
+
1181
+ train_config = attr.evolve(config, edge_paths=config.edge_paths)
1182
+ print("Starting training ...")
1183
+ train(train_config, subprocess_init=subprocess_init)
1184
+ print("Finished")