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,1498 @@
1
+ """plotting functions"""
2
+
3
+ import os
4
+ import numpy as np
5
+ import pandas as pd
6
+ import matplotlib.pyplot as plt
7
+ import matplotlib as mpl
8
+ from pandas.core.dtypes.common import is_numeric_dtype
9
+ import seaborn as sns
10
+ from adjustText import adjust_text
11
+ from pandas.api.types import (
12
+ is_string_dtype,
13
+ is_categorical_dtype,
14
+ )
15
+ from scipy.sparse import find
16
+ import warnings
17
+ import plotly.express as px
18
+ import plotly.graph_objects as go
19
+
20
+ from .._settings import settings
21
+ from ._utils import (
22
+ generate_palette
23
+ )
24
+
25
+
26
+ def violin(adata,
27
+ list_obs=None,
28
+ list_var=None,
29
+ jitter=0.4,
30
+ size=1,
31
+ log=False,
32
+ pad=1.08,
33
+ w_pad=None,
34
+ h_pad=3,
35
+ fig_size=(3, 3),
36
+ fig_ncol=3,
37
+ save_fig=False,
38
+ fig_path=None,
39
+ fig_name='plot_violin.pdf',
40
+ **kwargs):
41
+ """Violin plot
42
+
43
+ Parameters
44
+ ----------
45
+ adata : `Anndata`
46
+ Annotated data matrix.
47
+ list_obs : `list`, optional (default: None)
48
+ A list of observations to plot.
49
+ list_var : `list`, optional (default: None)
50
+ A list of variables to plot.
51
+ jitter : `float`, optional (default: 0.4)
52
+ Amount of jitter to apply.
53
+ size : `int`, optional (default: 1)
54
+ The marker size
55
+ log : `bool`, optional (default: False)
56
+ If True, natural logarithm transformation will be performed.
57
+ pad: `float`, optional (default: 1.08)
58
+ Padding between the figure edge and the edges of subplots,
59
+ as a fraction of the font size.
60
+ h_pad, w_pad: `float`, optional (default: None)
61
+ Padding (height/width) between edges of adjacent subplots,
62
+ as a fraction of the font size. Defaults to pad.
63
+ fig_size: `tuple`, optional (default: (3,3))
64
+ figure size.
65
+ fig_ncol: `int`, optional (default: 3)
66
+ the number of columns of the figure panel
67
+ save_fig: `bool`, optional (default: False)
68
+ if True,save the figure.
69
+ fig_path: `str`, optional (default: None)
70
+ If save_fig is True, specify figure path.
71
+ fig_name: `str`, optional (default: 'plot_violin.pdf')
72
+ if `save_fig` is True, specify figure name.
73
+ **kwargs: `dict`, optional
74
+ Other keyword arguments are passed through to ``sns.violinplot``
75
+
76
+ Returns
77
+ -------
78
+ None
79
+ """
80
+ if fig_size is None:
81
+ fig_size = mpl.rcParams['figure.figsize']
82
+ if save_fig is None:
83
+ save_fig = settings.save_fig
84
+ if fig_path is None:
85
+ fig_path = os.path.join(settings.workdir, 'figures')
86
+ if list_obs is None:
87
+ list_obs = []
88
+ if list_var is None:
89
+ list_var = []
90
+ for obs in list_obs:
91
+ if obs not in adata.obs_keys():
92
+ raise ValueError(f"could not find {obs} in `adata.obs_keys()`")
93
+ for var in list_var:
94
+ if var not in adata.var_keys():
95
+ raise ValueError(f"could not find {var} in `adata.var_keys()`")
96
+ if len(list_obs) > 0:
97
+ df_plot = adata.obs[list_obs].copy()
98
+ if log:
99
+ df_plot = pd.DataFrame(data=np.log1p(df_plot.values),
100
+ index=df_plot.index,
101
+ columns=df_plot.columns)
102
+ fig_nrow = int(np.ceil(len(list_obs)/fig_ncol))
103
+ fig = plt.figure(figsize=(fig_size[0]*fig_ncol*1.05,
104
+ fig_size[1]*fig_nrow))
105
+ for i, obs in enumerate(list_obs):
106
+ ax_i = fig.add_subplot(fig_nrow, fig_ncol, i+1)
107
+ sns.violinplot(ax=ax_i,
108
+ y=obs,
109
+ data=df_plot,
110
+ inner=None,
111
+ **kwargs)
112
+ sns.stripplot(ax=ax_i,
113
+ y=obs,
114
+ data=df_plot,
115
+ color='black',
116
+ jitter=jitter,
117
+ s=size)
118
+
119
+ ax_i.set_title(obs)
120
+ ax_i.set_ylabel('')
121
+ ax_i.locator_params(axis='y', nbins=6)
122
+ ax_i.tick_params(axis="y", pad=-2)
123
+ ax_i.spines['right'].set_visible(False)
124
+ ax_i.spines['top'].set_visible(False)
125
+ plt.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
126
+ if save_fig:
127
+ if not os.path.exists(fig_path):
128
+ os.makedirs(fig_path)
129
+ plt.savefig(os.path.join(fig_path, fig_name),
130
+ pad_inches=1,
131
+ bbox_inches='tight')
132
+ plt.close(fig)
133
+ if len(list_var) > 0:
134
+ df_plot = adata.var[list_var].copy()
135
+ if log:
136
+ df_plot = pd.DataFrame(data=np.log1p(df_plot.values),
137
+ index=df_plot.index,
138
+ columns=df_plot.columns)
139
+ fig_nrow = int(np.ceil(len(list_obs)/fig_ncol))
140
+ fig = plt.figure(figsize=(fig_size[0]*fig_ncol*1.05,
141
+ fig_size[1]*fig_nrow))
142
+ for i, var in enumerate(list_var):
143
+ ax_i = fig.add_subplot(fig_nrow, fig_ncol, i+1)
144
+ sns.violinplot(ax=ax_i,
145
+ y=var,
146
+ data=df_plot,
147
+ inner=None,
148
+ **kwargs)
149
+ sns.stripplot(ax=ax_i,
150
+ y=var,
151
+ data=df_plot,
152
+ color='black',
153
+ jitter=jitter,
154
+ s=size)
155
+
156
+ ax_i.set_title(var)
157
+ ax_i.set_ylabel('')
158
+ ax_i.locator_params(axis='y', nbins=6)
159
+ ax_i.tick_params(axis="y", pad=-2)
160
+ ax_i.spines['right'].set_visible(False)
161
+ ax_i.spines['top'].set_visible(False)
162
+ plt.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
163
+ if save_fig:
164
+ if not os.path.exists(fig_path):
165
+ os.makedirs(fig_path)
166
+ plt.savefig(os.path.join(fig_path, fig_name),
167
+ pad_inches=1,
168
+ bbox_inches='tight')
169
+ plt.close(fig)
170
+
171
+
172
+ def hist(adata,
173
+ list_obs=None,
174
+ list_var=None,
175
+ kde=True,
176
+ log=False,
177
+ pad=1.08,
178
+ w_pad=None,
179
+ h_pad=3,
180
+ fig_size=(3, 3),
181
+ fig_ncol=3,
182
+ save_fig=False,
183
+ fig_path=None,
184
+ fig_name='plot_histogram.pdf',
185
+ **kwargs
186
+ ):
187
+ """histogram plot
188
+
189
+ Parameters
190
+ ----------
191
+ adata : `Anndata`
192
+ Annotated data matrix.
193
+ list_obs : `list`, optional (default: None)
194
+ A list of observations to plot.
195
+ list_var : `list`, optional (default: None)
196
+ A list of variables to plot.
197
+ kde : `bool`, optional (default: True)
198
+ If True, compute a kernel density estimate to smooth the distribution
199
+ and show on the plot
200
+ log : `bool`, optional (default: False)
201
+ If True, natural logarithm transformation will be performed.
202
+ pad: `float`, optional (default: 1.08)
203
+ Padding between the figure edge and the edges of subplots,
204
+ as a fraction of the font size.
205
+ h_pad, w_pad: `float`, optional (default: None)
206
+ Padding (height/width) between edges of adjacent subplots,
207
+ as a fraction of the font size. Defaults to pad.
208
+ fig_size: `tuple`, optional (default: (3,3))
209
+ figure size.
210
+ fig_ncol: `int`, optional (default: 3)
211
+ the number of columns of the figure panel
212
+ save_fig: `bool`, optional (default: False)
213
+ if True,save the figure.
214
+ fig_path: `str`, optional (default: None)
215
+ If save_fig is True, specify figure path.
216
+ fig_name: `str`, optional (default: 'plot_violin.pdf')
217
+ if `save_fig` is True, specify figure name.
218
+ **kwargs: `dict`, optional
219
+ Other keyword arguments are passed through to ``sns.histplot``
220
+
221
+ Returns
222
+ -------
223
+ None
224
+ """
225
+ if fig_size is None:
226
+ fig_size = mpl.rcParams['figure.figsize']
227
+ if save_fig is None:
228
+ save_fig = settings.save_fig
229
+ if fig_path is None:
230
+ fig_path = os.path.join(settings.workdir, 'figures')
231
+ if list_obs is None:
232
+ list_obs = []
233
+ if list_var is None:
234
+ list_var = []
235
+ for obs in list_obs:
236
+ if obs not in adata.obs_keys():
237
+ raise ValueError(f"could not find {obs} in `adata.obs_keys()`")
238
+ for var in list_var:
239
+ if var not in adata.var_keys():
240
+ raise ValueError(f"could not find {var} in `adata.var_keys()`")
241
+
242
+ if len(list_obs) > 0:
243
+ df_plot = adata.obs[list_obs].copy()
244
+ if log:
245
+ df_plot = pd.DataFrame(data=np.log1p(df_plot.values),
246
+ index=df_plot.index,
247
+ columns=df_plot.columns)
248
+ fig_nrow = int(np.ceil(len(list_obs)/fig_ncol))
249
+ fig = plt.figure(figsize=(fig_size[0]*fig_ncol*1.05,
250
+ fig_size[1]*fig_nrow))
251
+ for i, obs in enumerate(list_obs):
252
+ ax_i = fig.add_subplot(fig_nrow, fig_ncol, i+1)
253
+ sns.histplot(ax=ax_i,
254
+ x=obs,
255
+ data=df_plot,
256
+ kde=kde,
257
+ **kwargs)
258
+ ax_i.locator_params(axis='y', nbins=6)
259
+ ax_i.tick_params(axis="y", pad=-2)
260
+ ax_i.spines['right'].set_visible(False)
261
+ ax_i.spines['top'].set_visible(False)
262
+ plt.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
263
+ if save_fig:
264
+ if not os.path.exists(fig_path):
265
+ os.makedirs(fig_path)
266
+ plt.savefig(os.path.join(fig_path, fig_name),
267
+ pad_inches=1,
268
+ bbox_inches='tight')
269
+ plt.close(fig)
270
+ if len(list_var) > 0:
271
+ df_plot = adata.var[list_var].copy()
272
+ if log:
273
+ df_plot = pd.DataFrame(data=np.log1p(df_plot.values),
274
+ index=df_plot.index,
275
+ columns=df_plot.columns)
276
+ fig_nrow = int(np.ceil(len(list_obs)/fig_ncol))
277
+ fig = plt.figure(figsize=(fig_size[0]*fig_ncol*1.05,
278
+ fig_size[1]*fig_nrow))
279
+ for i, var in enumerate(list_var):
280
+ ax_i = fig.add_subplot(fig_nrow, fig_ncol, i+1)
281
+ sns.histplot(ax=ax_i,
282
+ x=var,
283
+ data=df_plot,
284
+ kde=kde,
285
+ **kwargs)
286
+ ax_i.locator_params(axis='y', nbins=6)
287
+ ax_i.tick_params(axis="y", pad=-2)
288
+ ax_i.spines['right'].set_visible(False)
289
+ ax_i.spines['top'].set_visible(False)
290
+ plt.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
291
+ if save_fig:
292
+ if not os.path.exists(fig_path):
293
+ os.makedirs(fig_path)
294
+ plt.savefig(os.path.join(fig_path, fig_name),
295
+ pad_inches=1,
296
+ bbox_inches='tight')
297
+ plt.close(fig)
298
+
299
+
300
+ def pca_variance_ratio(adata,
301
+ log=True,
302
+ show_cutoff=True,
303
+ fig_size=(4, 4),
304
+ save_fig=None,
305
+ fig_path=None,
306
+ fig_name='plot_variance_ratio.pdf',
307
+ pad=1.08,
308
+ w_pad=None,
309
+ h_pad=None,
310
+ **kwargs):
311
+ """Plot the variance ratio.
312
+
313
+ Parameters
314
+ ----------
315
+ adata : `Anndata`
316
+ Annotated data matrix.
317
+ log : `bool`, optional (default: True)
318
+ If True, variance_ratio will be log-transformed.
319
+ show_cutoff : `bool`, optional (default: True)
320
+ If True, cutoff on `n_pcs` will be shown
321
+ pad: `float`, optional (default: 1.08)
322
+ Padding between the figure edge and the edges of subplots,
323
+ as a fraction of the font size.
324
+ h_pad, w_pad: `float`, optional (default: None)
325
+ Padding (height/width) between edges of adjacent subplots,
326
+ as a fraction of the font size. Defaults to pad.
327
+ fig_size: `tuple`, optional (default: (3,3))
328
+ figure size.
329
+ save_fig: `bool`, optional (default: False)
330
+ if True,save the figure.
331
+ fig_path: `str`, optional (default: None)
332
+ If save_fig is True, specify figure path.
333
+ fig_name: `str`, optional (default: 'plot_variance_ratio.pdf')
334
+ if `save_fig` is True, specify figure name.
335
+ **kwargs: `dict`, optional
336
+ Other keyword arguments are passed through to ``plt.plot``
337
+
338
+ Returns
339
+ -------
340
+ None
341
+ """
342
+ if fig_size is None:
343
+ fig_size = mpl.rcParams['figure.figsize']
344
+ if save_fig is None:
345
+ save_fig = settings.save_fig
346
+ if fig_path is None:
347
+ fig_path = os.path.join(settings.workdir, 'figures')
348
+
349
+ n_components = len(adata.uns['pca']['variance_ratio'])
350
+
351
+ fig = plt.figure(figsize=fig_size)
352
+ if log:
353
+ plt.plot(range(n_components),
354
+ np.log(adata.uns['pca']['variance_ratio']),
355
+ **kwargs)
356
+ else:
357
+ plt.plot(range(n_components),
358
+ adata.uns['pca']['variance_ratio'],
359
+ **kwargs)
360
+ if show_cutoff:
361
+ n_pcs = adata.uns['pca']['n_pcs']
362
+ print(f'the number of selected PC is: {n_pcs}')
363
+ plt.axvline(n_pcs, ls='--', c='red')
364
+ plt.xlabel('Principal Component')
365
+ plt.ylabel('Variance Ratio')
366
+ plt.locator_params(axis='x', nbins=5)
367
+ plt.locator_params(axis='y', nbins=5)
368
+ plt.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
369
+ if save_fig:
370
+ if not os.path.exists(fig_path):
371
+ os.makedirs(fig_path)
372
+ plt.savefig(os.path.join(fig_path, fig_name),
373
+ pad_inches=1,
374
+ bbox_inches='tight')
375
+ plt.close(fig)
376
+
377
+
378
+ def pcs_features(adata,
379
+ log=False,
380
+ size=3,
381
+ show_cutoff=True,
382
+ pad=1.08,
383
+ w_pad=None,
384
+ h_pad=None,
385
+ fig_size=(3, 3),
386
+ fig_ncol=3,
387
+ save_fig=None,
388
+ fig_path=None,
389
+ fig_name='plot_pcs_features.pdf',
390
+ **kwargs):
391
+ """Plot features that contribute to the top PCs.
392
+
393
+ Parameters
394
+ ----------
395
+ adata : `Anndata`
396
+ Annotated data matrix.
397
+ log : `bool`, optional (default: True)
398
+ If True, variance_ratio will be log-transformed.
399
+ show_cutoff : `bool`, optional (default: True)
400
+ If True, cutoff on `n_pcs` will be shown
401
+ size : `int`, optional (default: 3)
402
+ The marker size
403
+ pad: `float`, optional (default: 1.08)
404
+ Padding between the figure edge and the edges of subplots,
405
+ as a fraction of the font size.
406
+ h_pad, w_pad: `float`, optional (default: None)
407
+ Padding (height/width) between edges of adjacent subplots,
408
+ as a fraction of the font size. Defaults to pad.
409
+ fig_size: `tuple`, optional (default: (3,3))
410
+ figure size.
411
+ fig_ncol: `int`, optional (default: 3)
412
+ the number of columns of the figure panel
413
+ save_fig: `bool`, optional (default: False)
414
+ if True,save the figure.
415
+ fig_path: `str`, optional (default: None)
416
+ If save_fig is True, specify figure path.
417
+ fig_name: `str`, optional (default: 'plot_pcs_features.pdf')
418
+ if `save_fig` is True, specify figure name.
419
+ **kwargs: `dict`, optional
420
+ Other keyword arguments are passed through to ``plt.scatter``
421
+
422
+ Returns
423
+ -------
424
+ None
425
+ """
426
+
427
+ if fig_size is None:
428
+ fig_size = mpl.rcParams['figure.figsize']
429
+ if save_fig is None:
430
+ save_fig = settings.save_fig
431
+ if fig_path is None:
432
+ fig_path = os.path.join(settings.workdir, 'figures')
433
+
434
+ n_pcs = adata.uns['pca']['n_pcs']
435
+ n_features = adata.uns['pca']['PCs'].shape[0]
436
+
437
+ fig_nrow = int(np.ceil(n_pcs/fig_ncol))
438
+ fig = plt.figure(figsize=(fig_size[0]*fig_ncol*1.05, fig_size[1]*fig_nrow))
439
+
440
+ for i in range(n_pcs):
441
+ ax_i = fig.add_subplot(fig_nrow, fig_ncol, i+1)
442
+ if log:
443
+ ax_i.scatter(range(n_features),
444
+ np.log(np.sort(
445
+ np.abs(adata.uns['pca']['PCs'][:, i],))[::-1]),
446
+ s=size,
447
+ **kwargs)
448
+ else:
449
+ ax_i.scatter(range(n_features),
450
+ np.sort(
451
+ np.abs(adata.uns['pca']['PCs'][:, i],))[::-1],
452
+ s=size,
453
+ **kwargs)
454
+ n_ft_selected_i = len(adata.uns['pca']['features'][f'pc_{i}'])
455
+ if show_cutoff:
456
+ ax_i.axvline(n_ft_selected_i, ls='--', c='red')
457
+ ax_i.set_xlabel('Feautures')
458
+ ax_i.set_ylabel('Loadings')
459
+ ax_i.locator_params(axis='x', nbins=3)
460
+ ax_i.locator_params(axis='y', nbins=5)
461
+ ax_i.ticklabel_format(axis="x", style="sci", scilimits=(0, 0))
462
+ ax_i.set_title(f'PC {i}')
463
+ plt.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
464
+ if save_fig:
465
+ if not os.path.exists(fig_path):
466
+ os.makedirs(fig_path)
467
+ plt.savefig(os.path.join(fig_path, fig_name),
468
+ pad_inches=1,
469
+ bbox_inches='tight')
470
+ plt.close(fig)
471
+
472
+
473
+ def variable_genes(adata,
474
+ show_texts=False,
475
+ n_texts=10,
476
+ size=8,
477
+ text_size=10,
478
+ pad=1.08,
479
+ w_pad=None,
480
+ h_pad=None,
481
+ fig_size=(4, 4),
482
+ save_fig=None,
483
+ fig_path=None,
484
+ fig_name='plot_variable_genes.pdf',
485
+ **kwargs):
486
+ """Plot highly variable genes.
487
+
488
+ Parameters
489
+ ----------
490
+ adata : `Anndata`
491
+ Annotated data matrix.
492
+ show_texts : `bool`, optional (default: False)
493
+ If True, text annotation will be shown.
494
+ n_texts : `int`, optional (default: 10)
495
+ The number of texts to plot.
496
+ size : `int`, optional (default: 8)
497
+ The marker size
498
+ text_size : `int`, optional (default: 10)
499
+ The text size
500
+ pad: `float`, optional (default: 1.08)
501
+ Padding between the figure edge and the edges of subplots,
502
+ as a fraction of the font size.
503
+ h_pad, w_pad: `float`, optional (default: None)
504
+ Padding (height/width) between edges of adjacent subplots,
505
+ as a fraction of the font size. Defaults to pad.
506
+ fig_size: `tuple`, optional (default: (3,3))
507
+ figure size.
508
+ save_fig: `bool`, optional (default: False)
509
+ if True,save the figure.
510
+ fig_path: `str`, optional (default: None)
511
+ If save_fig is True, specify figure path.
512
+ fig_name: `str`, optional (default: 'plot_variable_genes.pdf')
513
+ if `save_fig` is True, specify figure name.
514
+ **kwargs: `dict`, optional
515
+ Other keyword arguments are passed through to ``plt.scatter``
516
+
517
+ Returns
518
+ -------
519
+ None
520
+ """
521
+
522
+ if fig_size is None:
523
+ fig_size = mpl.rcParams['figure.figsize']
524
+ if save_fig is None:
525
+ save_fig = settings.save_fig
526
+ if fig_path is None:
527
+ fig_path = os.path.join(settings.workdir, 'figures')
528
+
529
+ means = adata.var['means']
530
+ variances_norm = adata.var['variances_norm']
531
+ mask = adata.var['highly_variable']
532
+ genes = adata.var_names
533
+
534
+ fig, ax = plt.subplots(figsize=fig_size)
535
+ ax.scatter(means[~mask],
536
+ variances_norm[~mask],
537
+ s=size,
538
+ c='#1F2433',
539
+ **kwargs)
540
+ ax.scatter(means[mask],
541
+ variances_norm[mask],
542
+ s=size,
543
+ c='#ce3746',
544
+ **kwargs)
545
+ ax.set_xscale(value='log')
546
+
547
+ if show_texts:
548
+ ids = variances_norm.values.argsort()[-n_texts:][::-1]
549
+ texts = [plt.text(means.iloc[i], variances_norm.iloc[i], genes[i],
550
+ fontdict={'family': 'serif',
551
+ 'color': 'black',
552
+ 'weight': 'normal',
553
+ 'size': text_size})
554
+ for i in ids]
555
+ adjust_text(texts,
556
+ arrowprops=dict(arrowstyle='-', color='black'))
557
+
558
+ ax.set_xlabel('average expression')
559
+ ax.set_ylabel('standardized variance')
560
+ ax.locator_params(axis='x', tight=True)
561
+ ax.locator_params(axis='y', tight=True)
562
+ ax.spines['right'].set_visible(False)
563
+ ax.spines['top'].set_visible(False)
564
+ fig.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
565
+ if save_fig:
566
+ if not os.path.exists(fig_path):
567
+ os.makedirs(fig_path)
568
+ fig.savefig(os.path.join(fig_path, fig_name),
569
+ pad_inches=1,
570
+ bbox_inches='tight')
571
+ plt.close(fig)
572
+
573
+
574
+ def _scatterplot2d(df,
575
+ x,
576
+ y,
577
+ list_hue=None,
578
+ hue_palette=None,
579
+ drawing_order='sorted',
580
+ dict_drawing_order=None,
581
+ size=8,
582
+ show_texts=False,
583
+ texts=None,
584
+ text_size=10,
585
+ text_expand=(1.05, 1.2),
586
+ fig_size=None,
587
+ fig_ncol=3,
588
+ fig_legend_ncol=1,
589
+ fig_legend_order=None,
590
+ vmin=None,
591
+ vmax=None,
592
+ alpha=0.8,
593
+ pad=1.08,
594
+ w_pad=None,
595
+ h_pad=None,
596
+ save_fig=None,
597
+ fig_path=None,
598
+ fig_name='scatterplot2d.pdf',
599
+ copy=False,
600
+ **kwargs):
601
+ """2d scatter plot
602
+
603
+ Parameters
604
+ ----------
605
+ data: `pd.DataFrame`
606
+ Input data structure of shape (n_samples, n_features).
607
+ x: `str`
608
+ Variable in `data` that specify positions on the x axis.
609
+ y: `str`
610
+ Variable in `data` that specify positions on the x axis.
611
+ list_hue: `str`, optional (default: None)
612
+ A list of variables that will produce points with different colors.
613
+ drawing_order: `str` (default: 'sorted')
614
+ The order in which values are plotted, This can be
615
+ one of the following values
616
+ - 'original': plot points in the same order as in input dataframe
617
+ - 'sorted' : plot points with higher values on top.
618
+ - 'random' : plot points in a random order
619
+ fig_size: `tuple`, optional (default: None)
620
+ figure size.
621
+ fig_ncol: `int`, optional (default: 3)
622
+ the number of columns of the figure panel
623
+ fig_legend_order: `dict`,optional (default: None)
624
+ Specified order for the appearance of the annotation keys.
625
+ Only valid for categorical/string variable
626
+ e.g. fig_legend_order = {'ann1':['a','b','c'],'ann2':['aa','bb','cc']}
627
+ fig_legend_ncol: `int`, optional (default: 1)
628
+ The number of columns that the legend has.
629
+ vmin,vmax: `float`, optional (default: None)
630
+ The min and max values are used to normalize continuous values.
631
+ If None, the respective min and max of continuous values is used.
632
+ alpha: `float`, optional (default: 0.8)
633
+ 0.0 transparent through 1.0 opaque
634
+ pad: `float`, optional (default: 1.08)
635
+ Padding between the figure edge and the edges of subplots,
636
+ as a fraction of the font size.
637
+ h_pad, w_pad: `float`, optional (default: None)
638
+ Padding (height/width) between edges of adjacent subplots,
639
+ as a fraction of the font size. Defaults to pad.
640
+ save_fig: `bool`, optional (default: False)
641
+ if True,save the figure.
642
+ fig_path: `str`, optional (default: None)
643
+ If save_fig is True, specify figure path.
644
+ fig_name: `str`, optional (default: 'scatterplot2d.pdf')
645
+ if save_fig is True, specify figure name.
646
+ Returns
647
+ -------
648
+ None
649
+ """
650
+
651
+ if fig_size is None:
652
+ fig_size = mpl.rcParams['figure.figsize']
653
+ if save_fig is None:
654
+ save_fig = settings.save_fig
655
+ if fig_path is None:
656
+ fig_path = os.path.join(settings.workdir, 'figures')
657
+
658
+ list_ax = list()
659
+ if list_hue is None:
660
+ list_hue = [None]
661
+ else:
662
+ for hue in list_hue:
663
+ if hue not in df.columns:
664
+ raise ValueError(f"could not find {hue}")
665
+ if hue_palette is None:
666
+ hue_palette = dict()
667
+ assert isinstance(hue_palette, dict), "`hue_palette` must be dict"
668
+ legend_order = {hue: np.unique(df[hue]) for hue in list_hue
669
+ if (is_string_dtype(df[hue])
670
+ or is_categorical_dtype(df[hue]))}
671
+ if fig_legend_order is not None:
672
+ if not isinstance(fig_legend_order, dict):
673
+ raise TypeError("`fig_legend_order` must be a dictionary")
674
+ for hue in fig_legend_order.keys():
675
+ if hue in legend_order.keys():
676
+ legend_order[hue] = fig_legend_order[hue]
677
+ else:
678
+ print(f"{hue} is ignored for ordering legend labels"
679
+ "due to incorrect name or data type")
680
+
681
+ if dict_drawing_order is None:
682
+ dict_drawing_order = dict()
683
+ assert drawing_order in ['sorted', 'random', 'original'],\
684
+ "`drawing_order` must be one of ['original', 'sorted', 'random']"
685
+
686
+ if len(list_hue) < fig_ncol:
687
+ fig_ncol = len(list_hue)
688
+ fig_nrow = int(np.ceil(len(list_hue)/fig_ncol))
689
+ fig = plt.figure(figsize=(fig_size[0]*fig_ncol*1.05, fig_size[1]*fig_nrow))
690
+ for i, hue in enumerate(list_hue):
691
+ ax_i = fig.add_subplot(fig_nrow, fig_ncol, i+1)
692
+ if hue is None:
693
+ sc_i = sns.scatterplot(ax=ax_i,
694
+ x=x,
695
+ y=y,
696
+ data=df,
697
+ alpha=alpha,
698
+ linewidth=0,
699
+ s=size,
700
+ **kwargs)
701
+ else:
702
+ if is_string_dtype(df[hue]) or is_categorical_dtype(df[hue]):
703
+ if hue in hue_palette.keys():
704
+ palette = hue_palette[hue]
705
+ else:
706
+ palette = None
707
+ if hue in dict_drawing_order.keys():
708
+ param_drawing_order = dict_drawing_order[hue]
709
+ else:
710
+ param_drawing_order = drawing_order
711
+ if param_drawing_order == 'sorted':
712
+ df_updated = df.sort_values(by=hue)
713
+ elif param_drawing_order == 'random':
714
+ df_updated = df.sample(frac=1, random_state=100)
715
+ else:
716
+ df_updated = df
717
+ sc_i = sns.scatterplot(ax=ax_i,
718
+ x=x,
719
+ y=y,
720
+ hue=hue,
721
+ hue_order=legend_order[hue],
722
+ data=df_updated,
723
+ alpha=alpha,
724
+ linewidth=0,
725
+ palette=palette,
726
+ s=size,
727
+ **kwargs)
728
+ ax_i.legend(bbox_to_anchor=(1, 0.5),
729
+ loc='center left',
730
+ ncol=fig_legend_ncol,
731
+ frameon=False,
732
+ )
733
+ else:
734
+ vmin_i = df[hue].min() if vmin is None else vmin
735
+ vmax_i = df[hue].max() if vmax is None else vmax
736
+ if hue in dict_drawing_order.keys():
737
+ param_drawing_order = dict_drawing_order[hue]
738
+ else:
739
+ param_drawing_order = drawing_order
740
+ if param_drawing_order == 'sorted':
741
+ df_updated = df.sort_values(by=hue)
742
+ elif param_drawing_order == 'random':
743
+ df_updated = df.sample(frac=1, random_state=100)
744
+ else:
745
+ df_updated = df
746
+ sc_i = ax_i.scatter(df_updated[x],
747
+ df_updated[y],
748
+ c=df_updated[hue],
749
+ vmin=vmin_i,
750
+ vmax=vmax_i,
751
+ alpha=alpha,
752
+ s=size,
753
+ **kwargs)
754
+ cbar = plt.colorbar(sc_i,
755
+ ax=ax_i,
756
+ pad=0.01,
757
+ fraction=0.05,
758
+ aspect=40)
759
+ cbar.solids.set_edgecolor("face")
760
+ cbar.ax.locator_params(nbins=5)
761
+ if show_texts:
762
+ if texts is not None:
763
+ plt_texts = [plt.text(df[x][t],
764
+ df[y][t],
765
+ t,
766
+ fontdict={'family': 'serif',
767
+ 'color': 'black',
768
+ 'weight': 'normal',
769
+ 'size': text_size})
770
+ for t in texts]
771
+ adjust_text(plt_texts,
772
+ expand_text=text_expand,
773
+ expand_points=text_expand,
774
+ expand_objects=text_expand,
775
+ arrowprops=dict(arrowstyle='->', color='black'))
776
+ ax_i.set_xlabel(x)
777
+ ax_i.set_ylabel(y)
778
+ ax_i.locator_params(axis='x', nbins=5)
779
+ ax_i.locator_params(axis='y', nbins=5)
780
+ ax_i.tick_params(axis="both", labelbottom=True, labelleft=True)
781
+ ax_i.set_title(hue)
782
+ list_ax.append(ax_i)
783
+ plt.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
784
+ if save_fig:
785
+ if not os.path.exists(fig_path):
786
+ os.makedirs(fig_path)
787
+ plt.savefig(os.path.join(fig_path, fig_name),
788
+ pad_inches=1,
789
+ bbox_inches='tight')
790
+ plt.close(fig)
791
+ if copy:
792
+ return list_ax
793
+
794
+
795
+ def _scatterplot2d_plotly(df,
796
+ x,
797
+ y,
798
+ list_hue=None,
799
+ hue_palette=None,
800
+ drawing_order='sorted',
801
+ fig_size=None,
802
+ fig_ncol=3,
803
+ fig_legend_order=None,
804
+ alpha=0.8,
805
+ save_fig=None,
806
+ fig_path=None,
807
+ **kwargs):
808
+ """interactive 2d scatter plot by Plotly
809
+
810
+ Parameters
811
+ ----------
812
+ data: `pd.DataFrame`
813
+ Input data structure of shape (n_samples, n_features).
814
+ x: `str`
815
+ Variable in `data` that specify positions on the x axis.
816
+ y: `str`
817
+ Variable in `data` that specify positions on the x axis.
818
+ list_hue: `str`, optional (default: None)
819
+ A list of variables that will produce points with different colors.
820
+ drawing_order: `str` (default: 'sorted')
821
+ The order in which values are plotted, This can be
822
+ one of the following values
823
+ - 'original': plot points in the same order as in input dataframe
824
+ - 'sorted' : plot points with higher values on top.
825
+ - 'random' : plot points in a random order
826
+ fig_size: `tuple`, optional (default: None)
827
+ figure size.
828
+ fig_ncol: `int`, optional (default: 3)
829
+ the number of columns of the figure panel
830
+ fig_legend_order: `dict`,optional (default: None)
831
+ Specified order for the appearance of the annotation keys.
832
+ Only valid for categorical/string variable
833
+ e.g. fig_legend_order = {'ann1':['a','b','c'],
834
+ 'ann2':['aa','bb','cc']}
835
+ fig_legend_ncol: `int`, optional (default: 1)
836
+ The number of columns that the legend has.
837
+ vmin,vmax: `float`, optional (default: None)
838
+ The min and max values are used to normalize continuous values.
839
+ If None, the respective min and max of continuous values is used.
840
+ alpha: `float`, optional (default: 0.8)
841
+ 0.0 transparent through 1.0 opaque
842
+ pad: `float`, optional (default: 1.08)
843
+ Padding between the figure edge and the edges of subplots,
844
+ as a fraction of the font size.
845
+ h_pad, w_pad: `float`, optional (default: None)
846
+ Padding (height/width) between edges of adjacent subplots,
847
+ as a fraction of the font size. Defaults to pad.
848
+ save_fig: `bool`, optional (default: False)
849
+ if True,save the figure.
850
+ fig_path: `str`, optional (default: None)
851
+ If save_fig is True, specify figure path.
852
+ fig_name: `str`, optional (default: 'scatterplot2d.pdf')
853
+ if save_fig is True, specify figure name.
854
+ Returns
855
+ -------
856
+ None
857
+ """
858
+
859
+ if fig_size is None:
860
+ fig_size = mpl.rcParams['figure.figsize']
861
+ if save_fig is None:
862
+ save_fig = settings.save_fig
863
+ if fig_path is None:
864
+ fig_path = os.path.join(settings.workdir, 'figures')
865
+
866
+ for hue in list_hue:
867
+ if(hue not in df.columns):
868
+ raise ValueError(f"could not find {hue} in `df.columns`")
869
+ if hue_palette is None:
870
+ hue_palette = dict()
871
+ assert isinstance(hue_palette, dict), "`hue_palette` must be dict"
872
+
873
+ assert drawing_order in ['sorted', 'random', 'original'],\
874
+ "`drawing_order` must be one of ['original', 'sorted', 'random']"
875
+
876
+ legend_order = {hue: np.unique(df[hue]) for hue in list_hue
877
+ if (is_string_dtype(df[hue])
878
+ or is_categorical_dtype(df[hue]))}
879
+ if(fig_legend_order is not None):
880
+ if(not isinstance(fig_legend_order, dict)):
881
+ raise TypeError("`fig_legend_order` must be a dictionary")
882
+ for hue in fig_legend_order.keys():
883
+ if(hue in legend_order.keys()):
884
+ legend_order[hue] = fig_legend_order[hue]
885
+ else:
886
+ print(f"{hue} is ignored for ordering legend labels"
887
+ "due to incorrect name or data type")
888
+
889
+ if(len(list_hue) < fig_ncol):
890
+ fig_ncol = len(list_hue)
891
+ fig_nrow = int(np.ceil(len(list_hue)/fig_ncol))
892
+ fig = plt.figure(figsize=(fig_size[0]*fig_ncol*1.05,
893
+ fig_size[1]*fig_nrow))
894
+ for hue in list_hue:
895
+ if hue in hue_palette.keys():
896
+ palette = hue_palette[hue]
897
+ else:
898
+ palette = None
899
+ if drawing_order == 'sorted':
900
+ df_updated = df.sort_values(by=hue)
901
+ elif drawing_order == 'random':
902
+ df_updated = df.sample(frac=1, random_state=100)
903
+ else:
904
+ df_updated = df
905
+
906
+ fig = px.scatter(df_updated,
907
+ x=x,
908
+ y=y,
909
+ color=hue,
910
+ opacity=alpha,
911
+ color_continuous_scale=px.colors.sequential.Viridis,
912
+ color_discrete_map=palette,
913
+ template = 'plotly_white',
914
+ **kwargs)
915
+ fig.update_layout(legend={'itemsizing': 'constant'},
916
+ width=fig_size[0]*100,
917
+ height=fig_size[1]*100,
918
+ )
919
+ fig.update_xaxes(mirror = True,
920
+ ticks = 'outside',
921
+ showline = True,
922
+ linecolor = 'black')
923
+ fig.update_yaxes(mirror = True,
924
+ ticks = 'outside',
925
+ showline = True,
926
+ linecolor = 'black')
927
+
928
+ fig.show(renderer="notebook")
929
+
930
+
931
+ # TO-DO add 3D plot
932
+ def umap(adata,
933
+ color=None,
934
+ dict_palette=None,
935
+ n_components=None,
936
+ size=8,
937
+ drawing_order='sorted',
938
+ dict_drawing_order=None,
939
+ show_texts=False,
940
+ texts=None,
941
+ text_size=10,
942
+ text_expand=(1.05, 1.2),
943
+ fig_size=None,
944
+ fig_ncol=3,
945
+ fig_legend_ncol=1,
946
+ fig_legend_order=None,
947
+ vmin=None,
948
+ vmax=None,
949
+ alpha=1,
950
+ pad=1.08,
951
+ w_pad=None,
952
+ h_pad=None,
953
+ save_fig=None,
954
+ fig_path=None,
955
+ fig_name='plot_umap.pdf',
956
+ plotly=False,
957
+ **kwargs):
958
+ """ Plot coordinates in UMAP
959
+
960
+ Parameters
961
+ ----------
962
+ data: `pd.DataFrame`
963
+ Input data structure of shape (n_samples, n_features).
964
+ x: `str`
965
+ Variable in `data` that specify positions on the x axis.
966
+ y: `str`
967
+ Variable in `data` that specify positions on the x axis.
968
+ color: `list`, optional (default: None)
969
+ A list of variables that will produce points with different colors.
970
+ e.g. color = ['anno1', 'anno2']
971
+ dict_palette: `dict`,optional (default: None)
972
+ A dictionary of palettes for different variables in `color`.
973
+ Only valid for categorical/string variables
974
+ e.g. dict_palette = {'ann1': {},'ann2': {}}
975
+ drawing_order: `str` (default: 'sorted')
976
+ The order in which values are plotted, This can be
977
+ one of the following values
978
+ - 'original': plot points in the same order as in input dataframe
979
+ - 'sorted' : plot points with higher values on top.
980
+ - 'random' : plot points in a random order
981
+ dict_drawing_order: `dict`,optional (default: None)
982
+ A dictionary of drawing_order for different variables in `color`.
983
+ Only valid for categorical/string variables
984
+ e.g. dict_drawing_order = {'ann1': 'original','ann2': 'sorted'}
985
+ size: `int` (default: 8)
986
+ Point size.
987
+ show_texts : `bool`, optional (default: False)
988
+ If True, text annotation will be shown.
989
+ text_size : `int`, optional (default: 10)
990
+ The text size.
991
+ texts: `list` optional (default: None)
992
+ Point names to plot.
993
+ text_expand : `tuple`, optional (default: (1.05, 1.2))
994
+ Two multipliers (x, y) by which to expand the bounding box of texts
995
+ when repelling them from each other/points/other objects.
996
+ fig_size: `tuple`, optional (default: None)
997
+ figure size.
998
+ fig_ncol: `int`, optional (default: 3)
999
+ the number of columns of the figure panel
1000
+ fig_legend_order: `dict`,optional (default: None)
1001
+ Specified order for the appearance of the annotation keys.
1002
+ Only valid for categorical/string variable
1003
+ e.g. fig_legend_order = {'ann1':['a','b','c'],'ann2':['aa','bb','cc']}
1004
+ fig_legend_ncol: `int`, optional (default: 1)
1005
+ The number of columns that the legend has.
1006
+ vmin,vmax: `float`, optional (default: None)
1007
+ The min and max values are used to normalize continuous values.
1008
+ If None, the respective min and max of continuous values is used.
1009
+ alpha: `float`, optional (default: 0.8)
1010
+ 0.0 transparent through 1.0 opaque
1011
+ pad: `float`, optional (default: 1.08)
1012
+ Padding between the figure edge and the edges of subplots,
1013
+ as a fraction of the font size.
1014
+ h_pad, w_pad: `float`, optional (default: None)
1015
+ Padding (height/width) between edges of adjacent subplots,
1016
+ as a fraction of the font size. Defaults to pad.
1017
+ save_fig: `bool`, optional (default: False)
1018
+ if True,save the figure.
1019
+ fig_path: `str`, optional (default: None)
1020
+ If save_fig is True, specify figure path.
1021
+ fig_name: `str`, optional (default: 'plot_umap.pdf')
1022
+ if save_fig is True, specify figure name.
1023
+ Returns
1024
+ -------
1025
+ None
1026
+ """
1027
+
1028
+ if fig_size is None:
1029
+ fig_size = mpl.rcParams['figure.figsize']
1030
+ if save_fig is None:
1031
+ save_fig = settings.save_fig
1032
+ if fig_path is None:
1033
+ fig_path = os.path.join(settings.workdir, 'figures')
1034
+
1035
+ if n_components is None:
1036
+ n_components = min(3, adata.obsm['X_umap'].shape[1])
1037
+ if n_components not in [2, 3]:
1038
+ raise ValueError("n_components should be 2 or 3")
1039
+ if n_components > adata.obsm['X_umap'].shape[1]:
1040
+ print(f"`n_components` is greater than the available dimension.\n"
1041
+ f"It is corrected to {adata.obsm['X_umap'].shape[1]}")
1042
+ n_components = adata.obsm['X_umap'].shape[1]
1043
+
1044
+ if dict_palette is None:
1045
+ dict_palette = dict()
1046
+ df_plot = pd.DataFrame(index=adata.obs.index,
1047
+ data=adata.obsm['X_umap'],
1048
+ columns=['UMAP'+str(x+1) for x in
1049
+ range(adata.obsm['X_umap'].shape[1])])
1050
+ if color is None:
1051
+ _scatterplot2d(df_plot,
1052
+ x='UMAP1',
1053
+ y='UMAP2',
1054
+ drawing_order=drawing_order,
1055
+ size=size,
1056
+ show_texts=show_texts,
1057
+ text_size=text_size,
1058
+ texts=texts,
1059
+ text_expand=text_expand,
1060
+ fig_size=fig_size,
1061
+ alpha=alpha,
1062
+ pad=pad,
1063
+ w_pad=w_pad,
1064
+ h_pad=h_pad,
1065
+ save_fig=save_fig,
1066
+ fig_path=fig_path,
1067
+ fig_name=fig_name,
1068
+ **kwargs)
1069
+ else:
1070
+ color = list(dict.fromkeys(color)) # remove duplicate keys
1071
+ for ann in color:
1072
+ if ann in adata.obs_keys():
1073
+ df_plot[ann] = adata.obs[ann]
1074
+ if not is_numeric_dtype(df_plot[ann]):
1075
+ if 'color' not in adata.uns_keys():
1076
+ adata.uns['color'] = dict()
1077
+
1078
+ if ann not in dict_palette.keys():
1079
+ if (ann+'_color' in adata.uns['color'].keys()) \
1080
+ and \
1081
+ (all(np.isin(np.unique(df_plot[ann]),
1082
+ list(adata.uns['color']
1083
+ [ann+'_color'].keys())))):
1084
+ dict_palette[ann] = \
1085
+ adata.uns['color'][ann+'_color']
1086
+ else:
1087
+ dict_palette[ann] = \
1088
+ generate_palette(adata.obs[ann])
1089
+ adata.uns['color'][ann+'_color'] = \
1090
+ dict_palette[ann].copy()
1091
+ else:
1092
+ if ann+'_color' not in adata.uns['color'].keys():
1093
+ adata.uns['color'][ann+'_color'] = \
1094
+ dict_palette[ann].copy()
1095
+
1096
+ elif ann in adata.var_names:
1097
+ df_plot[ann] = adata.obs_vector(ann)
1098
+ else:
1099
+ raise ValueError(f"could not find {ann} in `adata.obs.columns`"
1100
+ " and `adata.var_names`")
1101
+ if plotly:
1102
+ _scatterplot2d_plotly(df_plot,
1103
+ x='UMAP1',
1104
+ y='UMAP2',
1105
+ list_hue=color,
1106
+ hue_palette=dict_palette,
1107
+ drawing_order=drawing_order,
1108
+ fig_size=fig_size,
1109
+ fig_ncol=fig_ncol,
1110
+ fig_legend_order=fig_legend_order,
1111
+ alpha=alpha,
1112
+ save_fig=save_fig,
1113
+ fig_path=fig_path,
1114
+ **kwargs)
1115
+ else:
1116
+ _scatterplot2d(df_plot,
1117
+ x='UMAP1',
1118
+ y='UMAP2',
1119
+ list_hue=color,
1120
+ hue_palette=dict_palette,
1121
+ drawing_order=drawing_order,
1122
+ dict_drawing_order=dict_drawing_order,
1123
+ size=size,
1124
+ show_texts=show_texts,
1125
+ text_size=text_size,
1126
+ text_expand=text_expand,
1127
+ texts=texts,
1128
+ fig_size=fig_size,
1129
+ fig_ncol=fig_ncol,
1130
+ fig_legend_ncol=fig_legend_ncol,
1131
+ fig_legend_order=fig_legend_order,
1132
+ vmin=vmin,
1133
+ vmax=vmax,
1134
+ alpha=alpha,
1135
+ pad=pad,
1136
+ w_pad=w_pad,
1137
+ h_pad=h_pad,
1138
+ save_fig=save_fig,
1139
+ fig_path=fig_path,
1140
+ fig_name=fig_name,
1141
+ **kwargs)
1142
+
1143
+
1144
+ def discretize(adata,
1145
+ kde=None,
1146
+ fig_size=(6, 6),
1147
+ pad=1.08,
1148
+ w_pad=None,
1149
+ h_pad=None,
1150
+ save_fig=None,
1151
+ fig_path=None,
1152
+ fig_name='plot_discretize.pdf',
1153
+ **kwargs):
1154
+ """Plot original data VS discretized data
1155
+
1156
+ Parameters
1157
+ ----------
1158
+ adata : `Anndata`
1159
+ Annotated data matrix.
1160
+ kde : `bool`, optional (default: None)
1161
+ If True, compute a kernel density estimate to smooth the distribution
1162
+ and show on the plot. Invalid as of v0.2.
1163
+ pad: `float`, optional (default: 1.08)
1164
+ Padding between the figure edge and the edges of subplots,
1165
+ as a fraction of the font size.
1166
+ h_pad, w_pad: `float`, optional (default: None)
1167
+ Padding (height/width) between edges of adjacent subplots,
1168
+ as a fraction of the font size. Defaults to pad.
1169
+ fig_size: `tuple`, optional (default: (5,8))
1170
+ figure size.
1171
+ save_fig: `bool`, optional (default: False)
1172
+ if True,save the figure.
1173
+ fig_path: `str`, optional (default: None)
1174
+ If save_fig is True, specify figure path.
1175
+ fig_name: `str`, optional (default: 'plot_discretize.pdf')
1176
+ if `save_fig` is True, specify figure name.
1177
+ **kwargs: `dict`, optional
1178
+ Other keyword arguments are passed through to ``plt.hist()``
1179
+
1180
+ Returns
1181
+ -------
1182
+ None
1183
+ """
1184
+ if kde is not None:
1185
+ warnings.warn("kde is not supported as of v0.2", DeprecationWarning)
1186
+ if fig_size is None:
1187
+ fig_size = mpl.rcParams['figure.figsize']
1188
+ if save_fig is None:
1189
+ save_fig = settings.save_fig
1190
+ if fig_path is None:
1191
+ fig_path = os.path.join(settings.workdir, 'figures')
1192
+
1193
+ assert 'disc' in adata.uns_keys(), \
1194
+ "please run `si.tl.discretize()` first"
1195
+ if kde is not None:
1196
+ warnings.warn("kde is no longer supported as of v1.1",
1197
+ DeprecationWarning)
1198
+
1199
+ hist_edges = adata.uns['disc']['hist_edges']
1200
+ hist_count = adata.uns['disc']['hist_count']
1201
+ bin_edges = adata.uns['disc']['bin_edges']
1202
+ bin_count = adata.uns['disc']['bin_count']
1203
+
1204
+ fig, ax = plt.subplots(2, 1, figsize=fig_size)
1205
+ _ = ax[0].hist(hist_edges[:-1],
1206
+ hist_edges,
1207
+ weights=hist_count,
1208
+ linewidth=0,
1209
+ **kwargs)
1210
+ _ = ax[1].hist(bin_edges[:-1],
1211
+ bin_edges,
1212
+ weights=bin_count,
1213
+ **kwargs)
1214
+ ax[0].set_xlabel('Non-zero values')
1215
+ ax[0].set_ylabel('Count')
1216
+ ax[0].set_title('Original')
1217
+ ax[1].set_xlabel('Non-zero values')
1218
+ ax[1].set_ylabel('Count')
1219
+ ax[1].set_title('Discretized')
1220
+ plt.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
1221
+ if save_fig:
1222
+ if not os.path.exists(fig_path):
1223
+ os.makedirs(fig_path)
1224
+ plt.savefig(os.path.join(fig_path, fig_name),
1225
+ pad_inches=1,
1226
+ bbox_inches='tight')
1227
+ plt.close(fig)
1228
+
1229
+
1230
+ def node_similarity(adata,
1231
+ bins=20,
1232
+ log=True,
1233
+ show_cutoff=True,
1234
+ cutoff=None,
1235
+ n_edges=5000,
1236
+ fig_size=(5, 3),
1237
+ pad=1.08,
1238
+ w_pad=None,
1239
+ h_pad=None,
1240
+ save_fig=None,
1241
+ fig_path=None,
1242
+ fig_name='plot_node_similarity.pdf',
1243
+ ):
1244
+ """Plot similarity scores of nodes
1245
+
1246
+ Parameters
1247
+ ----------
1248
+ adata : `Anndata`
1249
+ Annotated data matrix.
1250
+ bins : `int`, optional (default: 20)
1251
+ The number of equal-width bins in the given range for histogram plot.
1252
+ log : `bool`, optional (default: True)
1253
+ If True, log scale will be used for y axis.
1254
+ show_cutoff : `bool`, optional (default: True)
1255
+ If True, cutoff on scores will be shown
1256
+ cutoff: `int`, optional (default: None)
1257
+ Cutoff used to select edges
1258
+ n_edges: `int`, optional (default: 5000)
1259
+ The number of edges to select.
1260
+ pad: `float`, optional (default: 1.08)
1261
+ Padding between the figure edge and the edges of subplots,
1262
+ as a fraction of the font size.
1263
+ h_pad, w_pad: `float`, optional (default: None)
1264
+ Padding (height/width) between edges of adjacent subplots,
1265
+ as a fraction of the font size. Defaults to pad.
1266
+ fig_size: `tuple`, optional (default: (5,8))
1267
+ figure size.
1268
+ save_fig: `bool`, optional (default: False)
1269
+ if True,save the figure.
1270
+ fig_path: `str`, optional (default: None)
1271
+ If save_fig is True, specify figure path.
1272
+ fig_name: `str`, optional (default: 'plot_node_similarity.pdf')
1273
+ if `save_fig` is True, specify figure name.
1274
+
1275
+ Returns
1276
+ -------
1277
+ None
1278
+ """
1279
+ if fig_size is None:
1280
+ fig_size = mpl.rcParams['figure.figsize']
1281
+ if save_fig is None:
1282
+ save_fig = settings.save_fig
1283
+ if fig_path is None:
1284
+ fig_path = os.path.join(settings.workdir, 'figures')
1285
+
1286
+ mat_sim = adata.X
1287
+
1288
+ fig, ax = plt.subplots(1, 1, figsize=fig_size)
1289
+ ax.hist(mat_sim.data, bins=bins)
1290
+ if log:
1291
+ ax.set_yscale('log')
1292
+ if show_cutoff:
1293
+ if cutoff is None:
1294
+ if n_edges is None:
1295
+ raise ValueError('"cutoff" or "n_edges" has to be specified')
1296
+ else:
1297
+ cutoff = \
1298
+ np.partition(mat_sim.data,
1299
+ (mat_sim.size-n_edges))[mat_sim.size-n_edges]
1300
+ id_x, id_y, _ = find(mat_sim > cutoff)
1301
+ print(f'#selected edges: {len(id_x)}')
1302
+ plt.axvline(cutoff, ls='--', c='red')
1303
+ ax.set_xlabel('similariy scores')
1304
+ ax.set_title('Node similarity')
1305
+ plt.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
1306
+ if save_fig:
1307
+ if not os.path.exists(fig_path):
1308
+ os.makedirs(fig_path)
1309
+ fig.savefig(os.path.join(fig_path, fig_name),
1310
+ pad_inches=1,
1311
+ bbox_inches='tight')
1312
+ plt.close(fig)
1313
+
1314
+
1315
+ def svd_nodes(adata,
1316
+ comp1=1,
1317
+ comp2=2,
1318
+ color=None,
1319
+ dict_palette=None,
1320
+ cutoff=None,
1321
+ n_edges=5000,
1322
+ size=8,
1323
+ drawing_order='random',
1324
+ dict_drawing_order=None,
1325
+ fig_size=(4, 4),
1326
+ fig_ncol=3,
1327
+ fig_legend_ncol=1,
1328
+ fig_legend_order=None,
1329
+ alpha=1,
1330
+ pad=1.08,
1331
+ w_pad=None,
1332
+ h_pad=None,
1333
+ save_fig=None,
1334
+ fig_path=None,
1335
+ fig_name='plot_svd_nodes.pdf',
1336
+ vmin=None,
1337
+ vmax=None,
1338
+ **kwargs):
1339
+ """Plot SVD coordinates
1340
+
1341
+ Parameters
1342
+ ----------
1343
+ adata : `Anndata`
1344
+ Annotated data matrix.
1345
+ comp1: `int`, optional (default: 1)
1346
+ Component used for x axis.
1347
+ comp2: `int`, optional (default: 2)
1348
+ Component used for y axis.
1349
+ color: `list`, optional (default: None)
1350
+ A list of variables that will produce points with different colors.
1351
+ e.g. color = ['anno1', 'anno2']
1352
+ cutoff: `int`, optional (default: None)
1353
+ Cutoff used to select edges
1354
+ n_edges: `int`, optional (default: 5000)
1355
+ The number of edges to select
1356
+ dict_palette: `dict`,optional (default: None)
1357
+ A dictionary of palettes for different variables in `color`.
1358
+ Only valid for categorical/string variables
1359
+ e.g. dict_palette = {'ann1': {},'ann2': {}}
1360
+ drawing_order: `str` (default: 'random')
1361
+ The order in which values are plotted, This can be
1362
+ one of the following values
1363
+ - 'original': plot points in the same order as in input dataframe
1364
+ - 'sorted' : plot points with higher values on top.
1365
+ - 'random' : plot points in a random order
1366
+ dict_drawing_order: `dict`,optional (default: None)
1367
+ A dictionary of drawing_order for different variables in `color`.
1368
+ Only valid for categorical/string variables
1369
+ e.g. dict_drawing_order = {'ann1': 'original','ann2': 'sorted'}
1370
+ size: `int` (default: 8)
1371
+ Point size.
1372
+ fig_size: `tuple`, optional (default: (4, 4))
1373
+ figure size.
1374
+ fig_ncol: `int`, optional (default: 3)
1375
+ the number of columns of the figure panel
1376
+ fig_legend_order: `dict`,optional (default: None)
1377
+ Specified order for the appearance of the annotation keys.
1378
+ Only valid for categorical/string variable
1379
+ e.g. fig_legend_order = {'ann1':['a','b','c'],'ann2':['aa','bb','cc']}
1380
+ fig_legend_ncol: `int`, optional (default: 1)
1381
+ The number of columns that the legend has.
1382
+ vmin,vmax: `float`, optional (default: None)
1383
+ The min and max values are used to normalize continuous values.
1384
+ If None, the respective min and max of continuous values is used.
1385
+ alpha: `float`, optional (default: 1)
1386
+ 0.0 transparent through 1.0 opaque
1387
+ pad: `float`, optional (default: 1.08)
1388
+ Padding between the figure edge and the edges of subplots,
1389
+ as a fraction of the font size.
1390
+ h_pad, w_pad: `float`, optional (default: None)
1391
+ Padding (height/width) between edges of adjacent subplots,
1392
+ as a fraction of the font size. Defaults to pad.
1393
+ save_fig: `bool`, optional (default: False)
1394
+ if True,save the figure.
1395
+ fig_path: `str`, optional (default: None)
1396
+ If save_fig is True, specify figure path.
1397
+ fig_name: `str`, optional (default: 'plot_umap.pdf')
1398
+ if save_fig is True, specify figure name.
1399
+ Returns
1400
+ -------
1401
+ None
1402
+ """
1403
+ if fig_size is None:
1404
+ fig_size = mpl.rcParams['figure.figsize']
1405
+ if save_fig is None:
1406
+ save_fig = settings.save_fig
1407
+ if fig_path is None:
1408
+ fig_path = os.path.join(settings.workdir, 'figures')
1409
+
1410
+ mat_sim = adata.X
1411
+ if cutoff is None:
1412
+ if n_edges is None:
1413
+ raise ValueError('"cutoff" or "n_edges" has to be specified')
1414
+ else:
1415
+ cutoff = \
1416
+ np.partition(mat_sim.data,
1417
+ (mat_sim.size-n_edges))[mat_sim.size-n_edges]
1418
+ id_x, id_y, _ = find(mat_sim > cutoff)
1419
+
1420
+ X_cca_ref = adata.obsm['svd']
1421
+ X_cca_query = adata.varm['svd']
1422
+
1423
+ df_plot_ref = pd.DataFrame(data=X_cca_ref[:, [comp1-1, comp2-1]],
1424
+ index=adata.obs.index,
1425
+ columns=[f'Dim {comp1}', f'Dim {comp2}'])
1426
+ df_plot_ref['group'] = 'ref'
1427
+ df_plot_ref['selected'] = 'no'
1428
+ df_plot_ref.loc[df_plot_ref.index[id_x], 'selected'] = 'yes'
1429
+ df_plot_query = pd.DataFrame(data=X_cca_query[:, [comp1-1, comp2-1]],
1430
+ index=adata.var.index,
1431
+ columns=[f'Dim {comp1}', f'Dim {comp2}'])
1432
+ df_plot_query['group'] = 'query'
1433
+ df_plot_query['selected'] = 'no'
1434
+ df_plot_query.loc[df_plot_query.index[id_y], 'selected'] = 'yes'
1435
+
1436
+ df_plot = pd.concat([df_plot_ref, df_plot_query], axis=0)
1437
+ if dict_palette is None:
1438
+ dict_palette = dict()
1439
+ dict_palette['group'] = {'query': '#4c72b0', 'ref': '#dd8452'}
1440
+ dict_palette['selected'] = {'yes': '#000000', 'no': '#D4D3D3'}
1441
+ if dict_drawing_order is None:
1442
+ dict_drawing_order = dict()
1443
+ dict_drawing_order['group'] = 'random'
1444
+ dict_drawing_order['selected'] = 'sorted'
1445
+
1446
+ adata.uns['color'] = dict_palette.copy()
1447
+ if color is None:
1448
+ color = []
1449
+ else:
1450
+ color = list(dict.fromkeys(color)) # remove duplicate keys
1451
+ for ann in color:
1452
+ if (ann in adata.obs_keys()) and (ann in adata.var_keys()):
1453
+ df_plot[ann] = pd.concat([adata.obs[ann], adata.var[ann]], axis=0)
1454
+ if not is_numeric_dtype(df_plot[ann]):
1455
+ if ann not in dict_palette.keys():
1456
+ if (ann+'_color' in adata.uns['color'].keys()) \
1457
+ and \
1458
+ (all(np.isin(np.unique(df_plot[ann]),
1459
+ list(adata.uns['color']
1460
+ [ann+'_color'].keys())))):
1461
+ dict_palette[ann] = \
1462
+ adata.uns['color'][ann+'_color']
1463
+ else:
1464
+ dict_palette[ann] = \
1465
+ generate_palette(adata.obs[ann])
1466
+ adata.uns['color'][ann+'_color'] = \
1467
+ dict_palette[ann].copy()
1468
+ else:
1469
+ if ann+'_color' not in adata.uns['color'].keys():
1470
+ adata.uns['color'][ann+'_color'] = \
1471
+ dict_palette[ann].copy()
1472
+ else:
1473
+ raise ValueError(f"could not find {ann} in both "
1474
+ "`adata.obs.columns`"
1475
+ " and `adata.var.columns`")
1476
+ color = ['group', 'selected'] + color
1477
+ _scatterplot2d(df_plot,
1478
+ x=f'Dim {comp1}',
1479
+ y=f'Dim {comp2}',
1480
+ list_hue=color,
1481
+ hue_palette=dict_palette,
1482
+ drawing_order=drawing_order,
1483
+ dict_drawing_order=dict_drawing_order,
1484
+ size=size,
1485
+ fig_size=fig_size,
1486
+ fig_ncol=fig_ncol,
1487
+ fig_legend_ncol=fig_legend_ncol,
1488
+ fig_legend_order=fig_legend_order,
1489
+ vmin=vmin,
1490
+ vmax=vmax,
1491
+ alpha=alpha,
1492
+ pad=pad,
1493
+ w_pad=w_pad,
1494
+ h_pad=h_pad,
1495
+ save_fig=save_fig,
1496
+ fig_path=fig_path,
1497
+ fig_name=fig_name,
1498
+ **kwargs)