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.
- MultiChat/Analysis/Intra_strength.py +1758 -0
- MultiChat/Analysis/Processing.py +152 -0
- MultiChat/Analysis/__init__.py +2 -0
- MultiChat/Heterogeneous_g_emb/__init__.py +13 -0
- MultiChat/Heterogeneous_g_emb/_settings.py +156 -0
- MultiChat/Heterogeneous_g_emb/_utils.py +143 -0
- MultiChat/Heterogeneous_g_emb/_version.py +3 -0
- MultiChat/Heterogeneous_g_emb/plotting/__init__.py +19 -0
- MultiChat/Heterogeneous_g_emb/plotting/_palettes.py +180 -0
- MultiChat/Heterogeneous_g_emb/plotting/_plot.py +1498 -0
- MultiChat/Heterogeneous_g_emb/plotting/_post_training.py +742 -0
- MultiChat/Heterogeneous_g_emb/plotting/_utils.py +103 -0
- MultiChat/Heterogeneous_g_emb/preprocessing/__init__.py +26 -0
- MultiChat/Heterogeneous_g_emb/preprocessing/_general.py +91 -0
- MultiChat/Heterogeneous_g_emb/preprocessing/_pca.py +182 -0
- MultiChat/Heterogeneous_g_emb/preprocessing/_qc.py +727 -0
- MultiChat/Heterogeneous_g_emb/preprocessing/_utils.py +60 -0
- MultiChat/Heterogeneous_g_emb/preprocessing/_variable_genes.py +82 -0
- MultiChat/Heterogeneous_g_emb/readwrite.py +250 -0
- MultiChat/Heterogeneous_g_emb/tools/__init__.py +23 -0
- MultiChat/Heterogeneous_g_emb/tools/_gene_scores.py +346 -0
- MultiChat/Heterogeneous_g_emb/tools/_general.py +71 -0
- MultiChat/Heterogeneous_g_emb/tools/_integration.py +197 -0
- MultiChat/Heterogeneous_g_emb/tools/_pbg.py +1184 -0
- MultiChat/Heterogeneous_g_emb/tools/_post_training.py +919 -0
- MultiChat/Heterogeneous_g_emb/tools/_umap.py +58 -0
- MultiChat/Heterogeneous_g_emb/tools/_utils.py +253 -0
- MultiChat/Model/Layers.py +116 -0
- MultiChat/Model/__init__.py +3 -0
- MultiChat/Model/model_training.py +166 -0
- MultiChat/Model/modules.py +93 -0
- MultiChat/Model/utilities.py +234 -0
- MultiChat/Plot/Visualization.py +470 -0
- MultiChat/Plot/__init__.py +1 -0
- MultiChat/__init__.py +12 -0
- scmultichat-0.1.0.dist-info/METADATA +156 -0
- scmultichat-0.1.0.dist-info/RECORD +39 -0
- scmultichat-0.1.0.dist-info/WHEEL +5 -0
- scmultichat-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
from sklearn.neighbors import NearestNeighbors
|
|
4
|
+
import scanpy as sc
|
|
5
|
+
import os
|
|
6
|
+
from tqdm import tqdm
|
|
7
|
+
|
|
8
|
+
def knn_smoothing(mat, k, latent_matrix):
|
|
9
|
+
'''
|
|
10
|
+
KNN smoothing function: Smooth the input matrix using k-nearest neighbors.
|
|
11
|
+
'''
|
|
12
|
+
nbrs = NearestNeighbors(n_neighbors=k, algorithm='auto').fit(latent_matrix)
|
|
13
|
+
distances, indices = nbrs.kneighbors(latent_matrix)
|
|
14
|
+
smoothed_mat = np.zeros_like(mat)
|
|
15
|
+
for i in range(mat.shape[1]):
|
|
16
|
+
smoothed_mat[:, i] = mat[:, indices[i, :]].sum(axis=1)
|
|
17
|
+
|
|
18
|
+
return smoothed_mat
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def Preprocess_CCC_model(base_path, lr_database, cell_rep, expmatrix):
|
|
22
|
+
'''
|
|
23
|
+
smooth the expression matrix using KNN smoothing and normalize the expression of ligands and receptors.
|
|
24
|
+
'''
|
|
25
|
+
latent_fea = cell_rep
|
|
26
|
+
mat = expmatrix.to_numpy()
|
|
27
|
+
mat_smooth = knn_smoothing(mat, k=3, latent_matrix=latent_fea)
|
|
28
|
+
expmatrix_smooth = pd.DataFrame(mat_smooth, index=expmatrix.index, columns=expmatrix.columns)
|
|
29
|
+
expmatrix_smooth.to_csv(os.path.join(base_path, "CCC/expression_smooth.txt"), sep="\t", index=True, header=True)
|
|
30
|
+
|
|
31
|
+
# adata = sc.AnnData(mat_smooth)
|
|
32
|
+
# sc.pp.scale(adata, zero_center=True, max_value=10)
|
|
33
|
+
# data = adata.X
|
|
34
|
+
# mat_it = np.sum(data > 0, axis=1)
|
|
35
|
+
|
|
36
|
+
LR_ls = lr_database.apply(lambda row: f"{row['Ligand_Symbol']}->{row['Receptor_Symbol']}", axis=1).tolist()
|
|
37
|
+
ligand_exps = []
|
|
38
|
+
for ligand in lr_database['Ligand_Symbol']:
|
|
39
|
+
if '_' in ligand:
|
|
40
|
+
genes = ligand.split('_')
|
|
41
|
+
mean_expression = expmatrix_smooth.loc[genes, :].mean(axis=0)
|
|
42
|
+
ligand_exps.append(mean_expression)
|
|
43
|
+
else:
|
|
44
|
+
ligand_exps.append(expmatrix_smooth.loc[ligand, :])
|
|
45
|
+
ligand_exps = pd.DataFrame(ligand_exps, index=LR_ls, columns=expmatrix_smooth.columns)
|
|
46
|
+
receptor_exps = []
|
|
47
|
+
for receptor in lr_database['Receptor_Symbol']:
|
|
48
|
+
if '_' in receptor:
|
|
49
|
+
genes = receptor.split('_')
|
|
50
|
+
mean_expression = expmatrix_smooth.loc[genes, :].mean(axis=0)
|
|
51
|
+
receptor_exps.append(mean_expression)
|
|
52
|
+
else:
|
|
53
|
+
receptor_exps.append(expmatrix_smooth.loc[receptor, :])
|
|
54
|
+
receptor_exps = pd.DataFrame(receptor_exps, index=LR_ls, columns=expmatrix_smooth.columns)
|
|
55
|
+
|
|
56
|
+
ligand_exps_n = (ligand_exps - ligand_exps.min(axis=1).values[:, None]) / (ligand_exps.max(axis=1).values[:, None] - ligand_exps.min(axis=1).values[:, None])
|
|
57
|
+
receptor_exps_n = (receptor_exps - receptor_exps.min(axis=1).values[:, None]) / (receptor_exps.max(axis=1).values[:, None] - receptor_exps.min(axis=1).values[:, None])
|
|
58
|
+
|
|
59
|
+
pd.DataFrame(ligand_exps_n.T).to_csv(os.path.join(base_path, "CCC/ligands_expression.txt"), sep="\t", index=True, header=True)
|
|
60
|
+
pd.DataFrame(receptor_exps_n.T).to_csv(os.path.join(base_path, "CCC/receptors_expression.txt"), sep="\t", index=True, header=True)
|
|
61
|
+
|
|
62
|
+
return ligand_exps_n.T, receptor_exps_n.T
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def select_peaks_by_genes_location(gene_info, hvg_genes, peaks_to_filter, scope = 250000):
|
|
66
|
+
filtered_gene_info = preprocess_gene_info(gene_info, scope)
|
|
67
|
+
gene_peaks = gene_peaks_pairs_by_location(filtered_gene_info, hvg_genes, peaks_to_filter)
|
|
68
|
+
filtered_peaks = select_peaks_from_pairs(gene_peaks)
|
|
69
|
+
|
|
70
|
+
return filtered_peaks, gene_peaks
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def preprocess_gene_info(gene_info, scope = 250000):
|
|
74
|
+
filtered_gene_info = []
|
|
75
|
+
columns = ['id', 'chr', 'starts', 'ends', 'forward', 'backward', 'gene']
|
|
76
|
+
print("Preprocessing gene_info:")
|
|
77
|
+
for info in tqdm(gene_info.itertuples()):
|
|
78
|
+
chr = info.chr
|
|
79
|
+
starts = info.starts
|
|
80
|
+
ends = int(info.ends)
|
|
81
|
+
genes = info.genes
|
|
82
|
+
gene_info_id = chr + '-' + str(starts) + '-' + str(ends) + '-' + genes
|
|
83
|
+
forward = max(0, starts - scope)
|
|
84
|
+
backward = starts + scope
|
|
85
|
+
filtered_gene_info.append([gene_info_id, chr, starts, ends, forward, backward, genes])
|
|
86
|
+
filtered_gene_info = pd.DataFrame(filtered_gene_info, columns=columns)
|
|
87
|
+
filtered_gene_info = filtered_gene_info.drop_duplicates(subset=['id'])
|
|
88
|
+
return filtered_gene_info
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def gene_peaks_pairs_by_location(filtered_gene_info, hvg_genes, peaks_to_filter):
|
|
92
|
+
gene_peaks = {}
|
|
93
|
+
print("Search the genes-peaks correspondence based on gene_info and scope:")
|
|
94
|
+
for info in tqdm(filtered_gene_info.itertuples()):
|
|
95
|
+
if not info.gene in hvg_genes:
|
|
96
|
+
continue
|
|
97
|
+
id = info.id
|
|
98
|
+
chr = info.chr
|
|
99
|
+
starts = info.starts
|
|
100
|
+
ends = info.ends
|
|
101
|
+
forward = info.forward
|
|
102
|
+
backward = info.backward
|
|
103
|
+
gene = info.gene
|
|
104
|
+
if not gene in gene_peaks:
|
|
105
|
+
gene_peaks[gene] = set()
|
|
106
|
+
for peak in peaks_to_filter:
|
|
107
|
+
peak_chr, coordinates = peak.split(':')
|
|
108
|
+
peak_start, peak_end = coordinates.split('-')
|
|
109
|
+
if peak_chr == chr and int(peak_start) >= forward and int(peak_end) <= backward:
|
|
110
|
+
gene_peaks[gene].add(peak)
|
|
111
|
+
gene_peaks = {gene: peaks for gene, peaks in gene_peaks.items() if len(peaks) > 0}
|
|
112
|
+
return gene_peaks
|
|
113
|
+
|
|
114
|
+
def select_peaks_from_pairs(gene_peaks):
|
|
115
|
+
filtered_peaks = set()
|
|
116
|
+
print("Search the filtered peaks:")
|
|
117
|
+
for key in tqdm(gene_peaks.keys()):
|
|
118
|
+
filtered_peaks.update(gene_peaks[key])
|
|
119
|
+
filtered_peaks = list(filtered_peaks)
|
|
120
|
+
print("After filtering peaks:", len(filtered_peaks))
|
|
121
|
+
return filtered_peaks
|
|
122
|
+
|
|
123
|
+
def cicero_peaks_peaks(cicero_conn, peakitems, cicero_cotoff):
|
|
124
|
+
filtered_conn = cicero_conn[abs(cicero_conn['coaccess']) >= cicero_cotoff]
|
|
125
|
+
|
|
126
|
+
result_dict = {}
|
|
127
|
+
for peakitem in tqdm(peakitems, desc="Processing peakitems"):
|
|
128
|
+
peak2_values = filtered_conn.loc[filtered_conn['Peak1'] == peakitem, 'Peak2'].tolist()
|
|
129
|
+
if peak2_values:
|
|
130
|
+
result_dict[peakitem] = peak2_values
|
|
131
|
+
|
|
132
|
+
return result_dict
|
|
133
|
+
|
|
134
|
+
def cicero_peaks_peaks_score(cicero_conn, peakitems, cicero_cotoff):
|
|
135
|
+
filtered_conn = cicero_conn[abs(cicero_conn['coaccess']) >= cicero_cotoff]
|
|
136
|
+
|
|
137
|
+
relevant_peaks = filtered_conn[filtered_conn['Peak1'].isin(peakitems)]
|
|
138
|
+
|
|
139
|
+
grouped = relevant_peaks.groupby('Peak1')
|
|
140
|
+
|
|
141
|
+
peaks_lst = []
|
|
142
|
+
scores_lst = []
|
|
143
|
+
|
|
144
|
+
for peakitem in tqdm(peakitems, desc="Processing peakitems"):
|
|
145
|
+
try:
|
|
146
|
+
group = grouped.get_group(peakitem)
|
|
147
|
+
peaks_lst.extend(group['Peak2'].tolist())
|
|
148
|
+
scores_lst.extend(group['coaccess'].tolist())
|
|
149
|
+
except KeyError:
|
|
150
|
+
continue # No matches for this peakitem
|
|
151
|
+
|
|
152
|
+
return peaks_lst, scores_lst
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Heterogeneous Graph Embedding Module of MultiChat"""
|
|
2
|
+
|
|
3
|
+
from ._settings import settings
|
|
4
|
+
from . import preprocessing as pp
|
|
5
|
+
from . import tools as tl
|
|
6
|
+
from . import plotting as pl
|
|
7
|
+
from .readwrite import *
|
|
8
|
+
from ._version import __version__
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
sys.modules.update(
|
|
13
|
+
{f'{__name__}.{m}': globals()[m] for m in ['tl', 'pp', 'pl']})
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Configuration for HGE"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import seaborn as sns
|
|
5
|
+
import matplotlib as mpl
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HgeConfig:
|
|
9
|
+
"""configuration class for HGE"""
|
|
10
|
+
|
|
11
|
+
def __init__(self,
|
|
12
|
+
workdir='./result_hge',
|
|
13
|
+
save_fig=False,
|
|
14
|
+
n_jobs=1):
|
|
15
|
+
self.workdir = workdir
|
|
16
|
+
self.save_fig = save_fig
|
|
17
|
+
self.n_jobs = n_jobs
|
|
18
|
+
self.set_pbg_params()
|
|
19
|
+
self.graph_stats = dict()
|
|
20
|
+
|
|
21
|
+
def set_figure_params(self,
|
|
22
|
+
context='notebook',
|
|
23
|
+
style='white',
|
|
24
|
+
palette='deep',
|
|
25
|
+
font='sans-serif',
|
|
26
|
+
font_scale=1.1,
|
|
27
|
+
color_codes=True,
|
|
28
|
+
dpi=80,
|
|
29
|
+
dpi_save=150,
|
|
30
|
+
fig_size=[5.4, 4.8],
|
|
31
|
+
rc=None):
|
|
32
|
+
""" Set global parameters for figures. Modified from sns.set()
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
context : string or dict
|
|
37
|
+
Plotting context parameters, see `seaborn.plotting_context`
|
|
38
|
+
style: `string`,optional (default: 'white')
|
|
39
|
+
Axes style parameters, see `seaborn.axes_style`
|
|
40
|
+
palette : string or sequence
|
|
41
|
+
Color palette, see `seaborn.color_palette`
|
|
42
|
+
font_scale: `float`, optional (default: 1.3)
|
|
43
|
+
Separate scaling factor to independently
|
|
44
|
+
scale the size of the font elements.
|
|
45
|
+
color_codes : `bool`, optional (default: True)
|
|
46
|
+
If ``True`` and ``palette`` is a seaborn palette,
|
|
47
|
+
remap the shorthand color codes (e.g. "b", "g", "r", etc.)
|
|
48
|
+
to the colors from this palette.
|
|
49
|
+
dpi: `int`,optional (default: 80)
|
|
50
|
+
Resolution of rendered figures.
|
|
51
|
+
dpi_save: `int`,optional (default: 150)
|
|
52
|
+
Resolution of saved figures.
|
|
53
|
+
rc: `dict`,optional (default: None)
|
|
54
|
+
rc settings properties.
|
|
55
|
+
Parameter mappings to override the values in the preset style.
|
|
56
|
+
Please see "`matplotlibrc file
|
|
57
|
+
<https://matplotlib.org/tutorials/introductory/customizing.html#a-sample-matplotlibrc-file>`__"
|
|
58
|
+
"""
|
|
59
|
+
sns.set(context=context,
|
|
60
|
+
style=style,
|
|
61
|
+
palette=palette,
|
|
62
|
+
font=font,
|
|
63
|
+
font_scale=font_scale,
|
|
64
|
+
color_codes=color_codes,
|
|
65
|
+
rc={'figure.dpi': dpi,
|
|
66
|
+
'savefig.dpi': dpi_save,
|
|
67
|
+
'figure.figsize': fig_size,
|
|
68
|
+
'image.cmap': 'viridis',
|
|
69
|
+
'lines.markersize': 6,
|
|
70
|
+
'legend.columnspacing': 0.1,
|
|
71
|
+
'legend.borderaxespad': 0.1,
|
|
72
|
+
'legend.handletextpad': 0.1,
|
|
73
|
+
'pdf.fonttype': 42,
|
|
74
|
+
})
|
|
75
|
+
if rc is not None:
|
|
76
|
+
assert isinstance(rc, dict), "rc must be dict"
|
|
77
|
+
for key, value in rc.items():
|
|
78
|
+
if key in mpl.rcParams.keys():
|
|
79
|
+
mpl.rcParams[key] = value
|
|
80
|
+
else:
|
|
81
|
+
raise Exception("unrecognized property '%s'" % key)
|
|
82
|
+
|
|
83
|
+
def set_workdir(self, workdir=None):
|
|
84
|
+
"""Set working directory.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
workdir: `str`, optional (default: None)
|
|
89
|
+
Working directory.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
"""
|
|
94
|
+
if workdir is None:
|
|
95
|
+
workdir = self.workdir
|
|
96
|
+
print("Using default working directory.")
|
|
97
|
+
if not os.path.exists(workdir):
|
|
98
|
+
os.makedirs(workdir)
|
|
99
|
+
self.workdir = workdir
|
|
100
|
+
self.set_pbg_params()
|
|
101
|
+
print('Saving results in: %s' % workdir)
|
|
102
|
+
|
|
103
|
+
def set_pbg_params(self, config=None):
|
|
104
|
+
"""Set PBG parameters
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
config : `dict`, optional (default: None)
|
|
109
|
+
PBG training configuration parameters.
|
|
110
|
+
By default it resets parameters to the default setting.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
"""
|
|
115
|
+
if config is None:
|
|
116
|
+
config = dict(
|
|
117
|
+
# I/O data
|
|
118
|
+
entity_path="",
|
|
119
|
+
edge_paths=["", ],
|
|
120
|
+
checkpoint_path="",
|
|
121
|
+
|
|
122
|
+
# Graph structure
|
|
123
|
+
entities={},
|
|
124
|
+
relations=[],
|
|
125
|
+
dynamic_relations=False,
|
|
126
|
+
|
|
127
|
+
# Scoring model
|
|
128
|
+
dimension=50,
|
|
129
|
+
global_emb=False,
|
|
130
|
+
comparator='dot',
|
|
131
|
+
|
|
132
|
+
# Training
|
|
133
|
+
num_epochs=10,
|
|
134
|
+
workers=4,
|
|
135
|
+
num_batch_negs=50,
|
|
136
|
+
num_uniform_negs=50,
|
|
137
|
+
loss_fn='softmax',
|
|
138
|
+
lr=0.1,
|
|
139
|
+
|
|
140
|
+
early_stopping=False,
|
|
141
|
+
regularization_coef=0.0,
|
|
142
|
+
wd=0.0,
|
|
143
|
+
wd_interval=50,
|
|
144
|
+
|
|
145
|
+
# Evaluation during training
|
|
146
|
+
eval_fraction=0.05,
|
|
147
|
+
eval_num_batch_negs=50,
|
|
148
|
+
eval_num_uniform_negs=50,
|
|
149
|
+
|
|
150
|
+
checkpoint_preservation_interval=None,
|
|
151
|
+
)
|
|
152
|
+
assert isinstance(config, dict), "`config` must be dict"
|
|
153
|
+
self.pbg_params = config
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
settings = HgeConfig()
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Utility functions and classes"""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from kneed import KneeLocator
|
|
5
|
+
import tables
|
|
6
|
+
from anndata import AnnData
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def locate_elbow(x, y, S=10, min_elbow=0,
|
|
10
|
+
curve='convex', direction='decreasing', online=False,
|
|
11
|
+
**kwargs):
|
|
12
|
+
"""Detect knee points
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
x : `array-like`
|
|
17
|
+
x values
|
|
18
|
+
y : `array-like`
|
|
19
|
+
y values
|
|
20
|
+
S : `float`, optional (default: 10)
|
|
21
|
+
Sensitivity
|
|
22
|
+
min_elbow: `int`, optional (default: 0)
|
|
23
|
+
The minimum elbow location
|
|
24
|
+
curve: `str`, optional (default: 'convex')
|
|
25
|
+
Choose from {'convex','concave'}
|
|
26
|
+
If 'concave', algorithm will detect knees,
|
|
27
|
+
If 'convex', algorithm will detect elbows.
|
|
28
|
+
direction: `str`, optional (default: 'decreasing')
|
|
29
|
+
Choose from {'decreasing','increasing'}
|
|
30
|
+
online: `bool`, optional (default: False)
|
|
31
|
+
kneed will correct old knee points if True,
|
|
32
|
+
kneed will return first knee if False.
|
|
33
|
+
**kwargs: `dict`, optional
|
|
34
|
+
Extra arguments to KneeLocator.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
elbow: `int`
|
|
39
|
+
elbow point
|
|
40
|
+
"""
|
|
41
|
+
kneedle = KneeLocator(x[int(min_elbow):], y[int(min_elbow):],
|
|
42
|
+
S=S, curve=curve,
|
|
43
|
+
direction=direction,
|
|
44
|
+
online=online,
|
|
45
|
+
**kwargs,
|
|
46
|
+
)
|
|
47
|
+
if kneedle.elbow is None:
|
|
48
|
+
elbow = len(y)
|
|
49
|
+
else:
|
|
50
|
+
elbow = int(kneedle.elbow)
|
|
51
|
+
return elbow
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# modifed from
|
|
55
|
+
# scanpy https://github.com/theislab/scanpy/blob/master/scanpy/readwrite.py
|
|
56
|
+
def _read_legacy_10x_h5(filename, genome=None):
|
|
57
|
+
"""
|
|
58
|
+
Read hdf5 file from Cell Ranger v2 or earlier versions.
|
|
59
|
+
"""
|
|
60
|
+
with tables.open_file(str(filename), 'r') as f:
|
|
61
|
+
try:
|
|
62
|
+
children = [x._v_name for x in f.list_nodes(f.root)]
|
|
63
|
+
if not genome:
|
|
64
|
+
if len(children) > 1:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"'{filename}' contains more than one genome. "
|
|
67
|
+
"For legacy 10x h5 "
|
|
68
|
+
"files you must specify the genome "
|
|
69
|
+
"if more than one is present. "
|
|
70
|
+
f"Available genomes are: {children}"
|
|
71
|
+
)
|
|
72
|
+
genome = children[0]
|
|
73
|
+
elif genome not in children:
|
|
74
|
+
raise ValueError(
|
|
75
|
+
f"Could not find genome '{genome}' in '{filename}'. "
|
|
76
|
+
f'Available genomes are: {children}'
|
|
77
|
+
)
|
|
78
|
+
dsets = {}
|
|
79
|
+
for node in f.walk_nodes('/' + genome, 'Array'):
|
|
80
|
+
dsets[node.name] = node.read()
|
|
81
|
+
# AnnData works with csr matrices
|
|
82
|
+
# 10x stores the transposed data, so we do the transposition
|
|
83
|
+
from scipy.sparse import csr_matrix
|
|
84
|
+
|
|
85
|
+
M, N = dsets['shape']
|
|
86
|
+
data = dsets['data']
|
|
87
|
+
if dsets['data'].dtype == np.dtype('int32'):
|
|
88
|
+
data = dsets['data'].view('float32')
|
|
89
|
+
data[:] = dsets['data']
|
|
90
|
+
matrix = csr_matrix(
|
|
91
|
+
(data, dsets['indices'], dsets['indptr']),
|
|
92
|
+
shape=(N, M),
|
|
93
|
+
)
|
|
94
|
+
# the csc matrix is automatically the transposed csr matrix
|
|
95
|
+
# as scanpy expects it, so, no need for a further transpostion
|
|
96
|
+
adata = AnnData(
|
|
97
|
+
matrix,
|
|
98
|
+
obs=dict(obs_names=dsets['barcodes'].astype(str)),
|
|
99
|
+
var=dict(
|
|
100
|
+
var_names=dsets['gene_names'].astype(str),
|
|
101
|
+
gene_ids=dsets['genes'].astype(str),
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
return adata
|
|
105
|
+
except KeyError:
|
|
106
|
+
raise Exception('File is missing one or more required datasets.')
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# modifed from
|
|
110
|
+
# scanpy https://github.com/theislab/scanpy/blob/master/scanpy/readwrite.py
|
|
111
|
+
def _read_v3_10x_h5(filename):
|
|
112
|
+
"""
|
|
113
|
+
Read hdf5 file from Cell Ranger v3 or later versions.
|
|
114
|
+
"""
|
|
115
|
+
with tables.open_file(str(filename), 'r') as f:
|
|
116
|
+
try:
|
|
117
|
+
dsets = {}
|
|
118
|
+
for node in f.walk_nodes('/matrix', 'Array'):
|
|
119
|
+
dsets[node.name] = node.read()
|
|
120
|
+
from scipy.sparse import csr_matrix
|
|
121
|
+
|
|
122
|
+
M, N = dsets['shape']
|
|
123
|
+
data = dsets['data']
|
|
124
|
+
if dsets['data'].dtype == np.dtype('int32'):
|
|
125
|
+
data = dsets['data'].view('float32')
|
|
126
|
+
data[:] = dsets['data']
|
|
127
|
+
matrix = csr_matrix(
|
|
128
|
+
(data, dsets['indices'], dsets['indptr']),
|
|
129
|
+
shape=(N, M),
|
|
130
|
+
)
|
|
131
|
+
adata = AnnData(
|
|
132
|
+
matrix,
|
|
133
|
+
obs=dict(obs_names=dsets['barcodes'].astype(str)),
|
|
134
|
+
var=dict(
|
|
135
|
+
var_names=dsets['name'].astype(str),
|
|
136
|
+
gene_ids=dsets['id'].astype(str),
|
|
137
|
+
feature_types=dsets['feature_type'].astype(str),
|
|
138
|
+
genome=dsets['genome'].astype(str),
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
return adata
|
|
142
|
+
except KeyError:
|
|
143
|
+
raise Exception('File is missing one or more required datasets.')
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Plotting"""
|
|
2
|
+
|
|
3
|
+
from ._plot import (
|
|
4
|
+
pca_variance_ratio,
|
|
5
|
+
pcs_features,
|
|
6
|
+
variable_genes,
|
|
7
|
+
violin,
|
|
8
|
+
hist,
|
|
9
|
+
umap,
|
|
10
|
+
discretize,
|
|
11
|
+
node_similarity,
|
|
12
|
+
svd_nodes,
|
|
13
|
+
)
|
|
14
|
+
from ._post_training import (
|
|
15
|
+
pbg_metrics,
|
|
16
|
+
entity_metrics,
|
|
17
|
+
entity_barcode,
|
|
18
|
+
query
|
|
19
|
+
)
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Color palettes in addition to matplotlib's palettes.
|
|
2
|
+
This is modifed from
|
|
3
|
+
scanpy palettes https://github.com/theislab/scanpy/blob/master/scanpy/plotting/palettes.py # noqa
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from matplotlib import cm, colors
|
|
7
|
+
|
|
8
|
+
# Colorblindness adjusted vega_10
|
|
9
|
+
# See https://github.com/theislab/scanpy/issues/387
|
|
10
|
+
vega_10 = list(map(colors.to_hex, cm.tab10.colors))
|
|
11
|
+
vega_10_scanpy = vega_10.copy()
|
|
12
|
+
vega_10_scanpy[2] = "#279e68" # green
|
|
13
|
+
vega_10_scanpy[4] = "#aa40fc" # purple
|
|
14
|
+
vega_10_scanpy[8] = "#b5bd61" # kakhi
|
|
15
|
+
|
|
16
|
+
# default matplotlib 2.0 palette
|
|
17
|
+
# see 'category20' on https://github.com/vega/vega/wiki/Scales#scale-range-literals # noqa
|
|
18
|
+
vega_20 = list(map(colors.to_hex, cm.tab20.colors))
|
|
19
|
+
|
|
20
|
+
# reorderd, some removed, some added
|
|
21
|
+
vega_20_scanpy = [
|
|
22
|
+
*vega_20[0:14:2],
|
|
23
|
+
*vega_20[16::2], # dark without grey
|
|
24
|
+
*vega_20[1:15:2],
|
|
25
|
+
*vega_20[17::2], # light without grey
|
|
26
|
+
"#ad494a",
|
|
27
|
+
"#8c6d31", # manual additions
|
|
28
|
+
]
|
|
29
|
+
vega_20_scanpy[2] = vega_10_scanpy[2]
|
|
30
|
+
vega_20_scanpy[4] = vega_10_scanpy[4]
|
|
31
|
+
vega_20_scanpy[7] = vega_10_scanpy[8] # kakhi shifted by missing grey
|
|
32
|
+
# TODO: also replace pale colors if necessary
|
|
33
|
+
|
|
34
|
+
default_20 = vega_20_scanpy
|
|
35
|
+
|
|
36
|
+
# https://graphicdesign.stackexchange.com/questions/3682/where-can-i-find-a-large-palette-set-of-contrasting-colors-for-coloring-many-d
|
|
37
|
+
# update 1
|
|
38
|
+
# orig reference http://epub.wu.ac.at/1692/1/document.pdf
|
|
39
|
+
zeileis_28 = [
|
|
40
|
+
"#023fa5",
|
|
41
|
+
"#7d87b9",
|
|
42
|
+
"#bec1d4",
|
|
43
|
+
"#d6bcc0",
|
|
44
|
+
"#bb7784",
|
|
45
|
+
"#8e063b",
|
|
46
|
+
"#4a6fe3",
|
|
47
|
+
"#8595e1",
|
|
48
|
+
"#b5bbe3",
|
|
49
|
+
"#e6afb9",
|
|
50
|
+
"#e07b91",
|
|
51
|
+
"#d33f6a",
|
|
52
|
+
"#11c638",
|
|
53
|
+
"#8dd593",
|
|
54
|
+
"#c6dec7",
|
|
55
|
+
"#ead3c6",
|
|
56
|
+
"#f0b98d",
|
|
57
|
+
"#ef9708",
|
|
58
|
+
"#0fcfc0",
|
|
59
|
+
"#9cded6",
|
|
60
|
+
"#d5eae7",
|
|
61
|
+
"#f3e1eb",
|
|
62
|
+
"#f6c4e1",
|
|
63
|
+
"#f79cd4",
|
|
64
|
+
"#7f7f7f",
|
|
65
|
+
"#c7c7c7",
|
|
66
|
+
"#1CE6FF",
|
|
67
|
+
"#336600", # these last ones were added,
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
default_28 = zeileis_28
|
|
71
|
+
|
|
72
|
+
# from http://godsnotwheregodsnot.blogspot.de/2012/09/color-distribution-methodology.html # noqa
|
|
73
|
+
godsnot_102 = [
|
|
74
|
+
# "#000000",
|
|
75
|
+
# remove the black, as often, we have black colored annotation
|
|
76
|
+
"#FFFF00",
|
|
77
|
+
"#1CE6FF",
|
|
78
|
+
"#FF34FF",
|
|
79
|
+
"#FF4A46",
|
|
80
|
+
"#008941",
|
|
81
|
+
"#006FA6",
|
|
82
|
+
"#A30059",
|
|
83
|
+
"#FFDBE5",
|
|
84
|
+
"#7A4900",
|
|
85
|
+
"#0000A6",
|
|
86
|
+
"#63FFAC",
|
|
87
|
+
"#B79762",
|
|
88
|
+
"#004D43",
|
|
89
|
+
"#8FB0FF",
|
|
90
|
+
"#997D87",
|
|
91
|
+
"#5A0007",
|
|
92
|
+
"#809693",
|
|
93
|
+
"#6A3A4C",
|
|
94
|
+
"#1B4400",
|
|
95
|
+
"#4FC601",
|
|
96
|
+
"#3B5DFF",
|
|
97
|
+
"#4A3B53",
|
|
98
|
+
"#FF2F80",
|
|
99
|
+
"#61615A",
|
|
100
|
+
"#BA0900",
|
|
101
|
+
"#6B7900",
|
|
102
|
+
"#00C2A0",
|
|
103
|
+
"#FFAA92",
|
|
104
|
+
"#FF90C9",
|
|
105
|
+
"#B903AA",
|
|
106
|
+
"#D16100",
|
|
107
|
+
"#DDEFFF",
|
|
108
|
+
"#000035",
|
|
109
|
+
"#7B4F4B",
|
|
110
|
+
"#A1C299",
|
|
111
|
+
"#300018",
|
|
112
|
+
"#0AA6D8",
|
|
113
|
+
"#013349",
|
|
114
|
+
"#00846F",
|
|
115
|
+
"#372101",
|
|
116
|
+
"#FFB500",
|
|
117
|
+
"#C2FFED",
|
|
118
|
+
"#A079BF",
|
|
119
|
+
"#CC0744",
|
|
120
|
+
"#C0B9B2",
|
|
121
|
+
"#C2FF99",
|
|
122
|
+
"#001E09",
|
|
123
|
+
"#00489C",
|
|
124
|
+
"#6F0062",
|
|
125
|
+
"#0CBD66",
|
|
126
|
+
"#EEC3FF",
|
|
127
|
+
"#456D75",
|
|
128
|
+
"#B77B68",
|
|
129
|
+
"#7A87A1",
|
|
130
|
+
"#788D66",
|
|
131
|
+
"#885578",
|
|
132
|
+
"#FAD09F",
|
|
133
|
+
"#FF8A9A",
|
|
134
|
+
"#D157A0",
|
|
135
|
+
"#BEC459",
|
|
136
|
+
"#456648",
|
|
137
|
+
"#0086ED",
|
|
138
|
+
"#886F4C",
|
|
139
|
+
"#34362D",
|
|
140
|
+
"#B4A8BD",
|
|
141
|
+
"#00A6AA",
|
|
142
|
+
"#452C2C",
|
|
143
|
+
"#636375",
|
|
144
|
+
"#A3C8C9",
|
|
145
|
+
"#FF913F",
|
|
146
|
+
"#938A81",
|
|
147
|
+
"#575329",
|
|
148
|
+
"#00FECF",
|
|
149
|
+
"#B05B6F",
|
|
150
|
+
"#8CD0FF",
|
|
151
|
+
"#3B9700",
|
|
152
|
+
"#04F757",
|
|
153
|
+
"#C8A1A1",
|
|
154
|
+
"#1E6E00",
|
|
155
|
+
"#7900D7",
|
|
156
|
+
"#A77500",
|
|
157
|
+
"#6367A9",
|
|
158
|
+
"#A05837",
|
|
159
|
+
"#6B002C",
|
|
160
|
+
"#772600",
|
|
161
|
+
"#D790FF",
|
|
162
|
+
"#9B9700",
|
|
163
|
+
"#549E79",
|
|
164
|
+
"#FFF69F",
|
|
165
|
+
"#201625",
|
|
166
|
+
"#72418F",
|
|
167
|
+
"#BC23FF",
|
|
168
|
+
"#99ADC0",
|
|
169
|
+
"#3A2465",
|
|
170
|
+
"#922329",
|
|
171
|
+
"#5B4534",
|
|
172
|
+
"#FDE8DC",
|
|
173
|
+
"#404E55",
|
|
174
|
+
"#0089A3",
|
|
175
|
+
"#CB7E98",
|
|
176
|
+
"#A4E804",
|
|
177
|
+
"#324E72",
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
default_102 = godsnot_102
|