exprmat 0.1.4__tar.gz → 0.1.6__tar.gz
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.
- {exprmat-0.1.4 → exprmat-0.1.6}/PKG-INFO +3 -2
- exprmat-0.1.6/exprmat/clustering/rogue.py +333 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/data/finders.py +31 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/plotting/expression.py +31 -2
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/plotting/palettes.py +1 -1
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/__init__.py +1 -1
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/plot.py +21 -1
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/qc.py +10 -9
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reader/experiment.py +145 -25
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reader/matcher.py +46 -5
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reduction/plot.py +137 -34
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/utils.py +2 -2
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat.egg-info/PKG-INFO +3 -2
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat.egg-info/SOURCES.txt +1 -1
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat.egg-info/requires.txt +1 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/setup.py +3 -2
- exprmat-0.1.4/exprmat/data/mmu/gencode.tsv.gz +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/ansi.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/clustering/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/clustering/leiden.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/clustering/spectral.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/clustering/utils.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/cnv/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/cnv/cnvscore.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/cnv/infercnv.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/configuration.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/data/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/data/delim.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/data/gff.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/data/hsa/genome.tsv.gz +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/data/mmu/genome.tsv.gz +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/data/private.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/data/ranges.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/descriptive/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/descriptive/de.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/descriptive/kde.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/plotting/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/plotting/cnv.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/plotting/de.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/doublets/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/doublets/composite.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/doublets/scrublet.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/filter.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/integrate.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/linear.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/normal.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/sctransform/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/sctransform/estimate.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/sctransform/ksmooth.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/sctransform/residual.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/sctransform/transform.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/sparse.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/preprocessing/uniform.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reader/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reader/metadata.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reduction/__init__.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reduction/forceatlas.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reduction/mde.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reduction/nn.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reduction/pca.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reduction/pumap.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reduction/tsne.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat/reduction/umap.py +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat.egg-info/dependency_links.txt +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat.egg-info/not-zip-safe +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/exprmat.egg-info/top_level.txt +0 -0
- {exprmat-0.1.4 → exprmat-0.1.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: exprmat
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: routines to process expression matrices
|
|
5
5
|
Home-page: https://github.com/yang-z-03/exprmat
|
|
6
6
|
Author: Zheng Yang
|
|
@@ -28,6 +28,7 @@ Requires-Dist: datashader
|
|
|
28
28
|
Requires-Dist: fa2_modified
|
|
29
29
|
Requires-Dist: pynndescent
|
|
30
30
|
Requires-Dist: gseapy
|
|
31
|
+
Requires-Dist: sh
|
|
31
32
|
Dynamic: author
|
|
32
33
|
Dynamic: author-email
|
|
33
34
|
Dynamic: home-page
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import statsmodels.api as sm
|
|
5
|
+
from skmisc import loess
|
|
6
|
+
from scipy.stats import norm
|
|
7
|
+
from statsmodels.stats.multitest import multipletests
|
|
8
|
+
from exprmat.ansi import error, warning, info
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# the entropy of the genes across cell j is defined to be:
|
|
12
|
+
#
|
|
13
|
+
# s1 = ln(mean(expr_j))
|
|
14
|
+
#
|
|
15
|
+
# this assumes that all cells in the cluster is homogenous. while the alternative
|
|
16
|
+
# assumption is that cells are totally heterogenous, and each cell represent its
|
|
17
|
+
# distinct state. the entropy for a totally heterogenous dataset is:
|
|
18
|
+
#
|
|
19
|
+
# s2 = mean(log(expr_ij))
|
|
20
|
+
#
|
|
21
|
+
# for calculation, this is simply a conversion of mean and log transform.
|
|
22
|
+
# r is a small positive value to avoid nan's been introduced due to invalid log.
|
|
23
|
+
# we require that the expression matrix have gene names as row names.
|
|
24
|
+
|
|
25
|
+
def rogue_entropy(C, varname, r = 1):
|
|
26
|
+
'''
|
|
27
|
+
Calculate entropy for genes. A completely homogenous gene will yield entropy
|
|
28
|
+
as mean logrithmized counts (mean.expr). Expect raw count matrix.
|
|
29
|
+
'''
|
|
30
|
+
|
|
31
|
+
entropy = np.log(C + 1).mean(axis = 0) # per gene.
|
|
32
|
+
mean_expr = np.log(C.mean(axis = 0) + r)
|
|
33
|
+
return pd.DataFrame({
|
|
34
|
+
'gene': varname,
|
|
35
|
+
'entropy': entropy.tolist()[0],
|
|
36
|
+
'mean.expr': mean_expr.tolist()[0]
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# fit the relationship between expression entropy and mean gene expression:
|
|
41
|
+
#
|
|
42
|
+
# in real-world dataset, there must be to some extent, heterogenous components
|
|
43
|
+
# in the dataset clusters. s1 > s2. and for different techniques for rna sequencing
|
|
44
|
+
# there is a systematic bias on the data distribution. we need a fitting step
|
|
45
|
+
# to make sure that the highly-variable genes in the clusters are picked.
|
|
46
|
+
#
|
|
47
|
+
#' @param span is a parameter for loess for controls of fit smoothness.
|
|
48
|
+
#' @param mt.method multiple testing method used in p.adjust.
|
|
49
|
+
#
|
|
50
|
+
# the returning dataframe consists of [1] gene names, [2] log mean expression (s1),
|
|
51
|
+
# [3] fit, the fitted curve of real world log mean expression trajectory (s1'),
|
|
52
|
+
# [4] ds, delta of entropy, the difference between s2 and s1',
|
|
53
|
+
# [5] p.value, assume that ds is normal, and the p value of normal distribution.
|
|
54
|
+
# [6] p.adj, the multi-test adjusted p value for normal approximation. and
|
|
55
|
+
# [7] entropy, the expression entropy per se.
|
|
56
|
+
#
|
|
57
|
+
# the returning values are sorted according to decreasing delta of entropy, so
|
|
58
|
+
# that the most highly-variable genes ranked on the top.
|
|
59
|
+
|
|
60
|
+
# available correction methods:
|
|
61
|
+
# * bonferroni : one-step correction
|
|
62
|
+
# * sidak : one-step correction
|
|
63
|
+
# * holm-sidak : step down method using Sidak adjustments
|
|
64
|
+
# * holm : step-down method using Bonferroni adjustments
|
|
65
|
+
# * simes-hochberg : step-up method (independent)
|
|
66
|
+
# * hommel : closed method based on Simes tests (non-negative)
|
|
67
|
+
# * fdr_bh : Benjamini/Hochberg (non-negative)
|
|
68
|
+
# * fdr_by : Benjamini/Yekutieli (negative)
|
|
69
|
+
# * fdr_tsbh : two stage fdr correction (non-negative)
|
|
70
|
+
# * fdr_tsbky : two stage fdr correction (non-negative)
|
|
71
|
+
|
|
72
|
+
def smoothen_entropy_lowess(df, span = 0.5, correction = "fdr_bh"):
|
|
73
|
+
|
|
74
|
+
df = df.loc[
|
|
75
|
+
np.isfinite(df['mean.expr']) &
|
|
76
|
+
df['entropy'] > 0, :
|
|
77
|
+
].copy()
|
|
78
|
+
|
|
79
|
+
fit = sm.nonparametric.lowess(
|
|
80
|
+
exog = df['mean.expr'],
|
|
81
|
+
endog = df['entropy'], frac = span,
|
|
82
|
+
xvals = df['mean.expr']
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
df['fit'] = fit
|
|
86
|
+
df['ds'] = df['fit'] - df['entropy']
|
|
87
|
+
df['pval'] = 1 - norm.cdf(df['ds'], np.mean(df['ds']), np.std(df['ds']))
|
|
88
|
+
training = df.loc[df['pval'] > 0.1, :].copy()
|
|
89
|
+
|
|
90
|
+
fit2 = sm.nonparametric.lowess(
|
|
91
|
+
exog = training['mean.expr'],
|
|
92
|
+
endog = training['entropy'], frac = span,
|
|
93
|
+
xvals = df['mean.expr']
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
df['fit'] = fit2
|
|
97
|
+
df['ds'] = df['fit'] - df['entropy']
|
|
98
|
+
df['pval'] = 1 - norm.cdf(df['ds'], np.mean(df['ds']), np.std(df['ds']))
|
|
99
|
+
training = df.loc[df['pval'] > 0.1, :].copy()
|
|
100
|
+
|
|
101
|
+
fit3 = sm.nonparametric.lowess(
|
|
102
|
+
exog = training['mean.expr'],
|
|
103
|
+
endog = training['entropy'], frac = span,
|
|
104
|
+
xvals = df['mean.expr']
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
df['fit'] = fit3
|
|
108
|
+
df['ds'] = df['fit'] - df['entropy']
|
|
109
|
+
df = df.loc[np.isfinite(df['ds']), :]
|
|
110
|
+
df['pval'] = 1 - norm.cdf(df['ds'], np.mean(df['ds']), np.std(df['ds']))
|
|
111
|
+
df['qval'] = multipletests(df['pval'], method = correction)[1]
|
|
112
|
+
|
|
113
|
+
return df.sort_values(['ds'], ascending = False)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def smoothen_entropy_loess(df, span = 0.5, correction = "fdr_bh"):
|
|
117
|
+
|
|
118
|
+
df = df.loc[
|
|
119
|
+
np.isfinite(df['mean.expr']) &
|
|
120
|
+
df['entropy'] > 0, :
|
|
121
|
+
].copy()
|
|
122
|
+
|
|
123
|
+
model = loess.loess(df['mean.expr'], df['entropy'], span = span)
|
|
124
|
+
model.fit()
|
|
125
|
+
fit = model.predict(df['mean.expr'])
|
|
126
|
+
|
|
127
|
+
df['fit'] = fit.values
|
|
128
|
+
df['ds'] = df['fit'] - df['entropy']
|
|
129
|
+
df['pval'] = 1 - norm.cdf(df['ds'], np.mean(df['ds']), np.std(df['ds']))
|
|
130
|
+
training = df.loc[df['pval'] > 0.1, :].copy()
|
|
131
|
+
|
|
132
|
+
df = df.loc[df['mean.expr'] <= np.max(training['mean.expr']), :].copy()
|
|
133
|
+
model = loess.loess(training['mean.expr'], training['entropy'], span = span)
|
|
134
|
+
model.fit()
|
|
135
|
+
fit2 = model.predict(df['mean.expr'])
|
|
136
|
+
|
|
137
|
+
df['fit'] = fit2.values
|
|
138
|
+
df['ds'] = df['fit'] - df['entropy']
|
|
139
|
+
df['pval'] = 1 - norm.cdf(df['ds'], np.mean(df['ds']), np.std(df['ds']))
|
|
140
|
+
training = df.loc[df['pval'] > 0.1, :].copy()
|
|
141
|
+
|
|
142
|
+
df = df.loc[df['mean.expr'] <= np.max(training['mean.expr']), :].copy()
|
|
143
|
+
model = loess.loess(training['mean.expr'], training['entropy'], span = span)
|
|
144
|
+
model.fit()
|
|
145
|
+
fit3 = model.predict(df['mean.expr'])
|
|
146
|
+
|
|
147
|
+
df['fit'] = fit3.values
|
|
148
|
+
df['ds'] = df['fit'] - df['entropy']
|
|
149
|
+
df = df.loc[np.isfinite(df['ds']), :]
|
|
150
|
+
df['pval'] = 1 - norm.cdf(df['ds'], np.mean(df['ds']), np.std(df['ds']))
|
|
151
|
+
df['qval'] = multipletests(df['pval'], method = correction)[1]
|
|
152
|
+
|
|
153
|
+
return df.sort_values(['ds'], ascending = False)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
smoothen_entropy = smoothen_entropy_loess
|
|
157
|
+
|
|
158
|
+
# identify highly informative genes using rogue's s-e model
|
|
159
|
+
#
|
|
160
|
+
# returns a tibble object with seven columns:
|
|
161
|
+
# [1] gene, the gene name.
|
|
162
|
+
# [2] mean.expr, the mean expression levels of genes.
|
|
163
|
+
# [3] entropy, the expected expression entropy from a given mean gene expression.
|
|
164
|
+
# [4] fit, the mean expression levels of genes.
|
|
165
|
+
# [5] ds, the entropy reduction against the null expectation.
|
|
166
|
+
# [6] p.value, the significance of ds against normal distribution.
|
|
167
|
+
# [7] p.adj, adjusted p value, or a copy of p.value if specifying not to adjust it.
|
|
168
|
+
|
|
169
|
+
def rogue_hvg(C, varname, span = 0.5, r = 1, correction = "fdr_bh", adjust_p = True):
|
|
170
|
+
|
|
171
|
+
entropy = rogue_entropy(C, varname, r = r)
|
|
172
|
+
entropy = smoothen_entropy(entropy, span = span, correction = correction)
|
|
173
|
+
if not adjust_p: entropy['qval'] = entropy["pval"]
|
|
174
|
+
return entropy
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# assess the purity of single cell population by rogue index.
|
|
178
|
+
#
|
|
179
|
+
# we may either specify the platform to access the default k value, or specify
|
|
180
|
+
# a k value manually. the rogue test is meaned to be run with highly variable
|
|
181
|
+
# genes. you can either specify them manually in `features`, or it is recommended
|
|
182
|
+
# to use the s-e model to pick the hvgs for you.
|
|
183
|
+
|
|
184
|
+
def rogue_index(df, platform = "umi", cutoff = 0.05, k = None, features = None):
|
|
185
|
+
|
|
186
|
+
if k is None:
|
|
187
|
+
if platform is None:
|
|
188
|
+
warning("you should supply a constant k yourself, or specify the platform for ")
|
|
189
|
+
warning("us to automatically pick the k constant for you. possible platforms ")
|
|
190
|
+
warning("are `umi` or `full-length`, the default k value for each case is 45 ")
|
|
191
|
+
warning("and 500 respectively.")
|
|
192
|
+
error('routine exit.')
|
|
193
|
+
|
|
194
|
+
elif platform == 'umi': k = 45
|
|
195
|
+
elif platform == 'full-length': k = 500
|
|
196
|
+
else: error('platform should be one of `umi` or `full-length`.')
|
|
197
|
+
|
|
198
|
+
if features is not None:
|
|
199
|
+
df = df.loc[[x in features for x in df['gene'].tolist()], :]
|
|
200
|
+
sig = np.abs(df['ds'].to_numpy()).sum()
|
|
201
|
+
rogue = 1 - sig / (sig + k)
|
|
202
|
+
|
|
203
|
+
else:
|
|
204
|
+
|
|
205
|
+
df = df.loc[
|
|
206
|
+
(df['pval'] < cutoff) &
|
|
207
|
+
(df['qval'] < cutoff), :
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
sig = np.abs(df['ds'].to_numpy()).sum()
|
|
211
|
+
rogue = 1 - sig / (sig + k)
|
|
212
|
+
|
|
213
|
+
return rogue
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# remove outlier cells when calculating ROGUE
|
|
217
|
+
#' @param n remove this many outlier cells.
|
|
218
|
+
|
|
219
|
+
def remove_outliers(df, C, varname, n = 2, span = 0.5, r = 1, correction = 'fdr_bh'):
|
|
220
|
+
|
|
221
|
+
from exprmat.utils import choose_layer
|
|
222
|
+
signif_genes = df.loc[df['qval'] < 0.05, :]['gene'].tolist()
|
|
223
|
+
ngenes = len(signif_genes)
|
|
224
|
+
bool_mask_gene = [x in signif_genes for x in varname]
|
|
225
|
+
expr_cutoff = df['mean.expr'].to_numpy().min()
|
|
226
|
+
|
|
227
|
+
expr = C[:, bool_mask_gene]
|
|
228
|
+
signif_genes = []
|
|
229
|
+
for imask in range(len(bool_mask_gene)):
|
|
230
|
+
if bool_mask_gene[imask]: signif_genes.append(varname[imask])
|
|
231
|
+
|
|
232
|
+
for i in range(ngenes):
|
|
233
|
+
gene_expr = np.flip(np.array(np.sort(expr[:, i].T))[0])
|
|
234
|
+
gene_expr = gene_expr[n:]
|
|
235
|
+
mean = np.log(gene_expr.mean() + r)
|
|
236
|
+
entropy = np.mean(np.log(gene_expr + 1))
|
|
237
|
+
df.loc[df['gene'] == signif_genes[i], 'mean.expr'] = mean
|
|
238
|
+
df.loc[df['gene'] == signif_genes[i], 'entropy'] = entropy
|
|
239
|
+
|
|
240
|
+
df = df.loc[df['mean.expr'] > expr_cutoff, :]
|
|
241
|
+
del df['qval']
|
|
242
|
+
df = smoothen_entropy(df, span = span, correction = correction)
|
|
243
|
+
return df
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def rogue(
|
|
247
|
+
adata, counts = 'X', platform = "umi", k = None,
|
|
248
|
+
min_cells = 10, min_genes = 10, outlier_n = 2,
|
|
249
|
+
span = 0.5, r = 1, correction = 'fdr_bh'
|
|
250
|
+
):
|
|
251
|
+
'''
|
|
252
|
+
Calculate ROGUE heterogeneity index for the whole annotated data.
|
|
253
|
+
Users are recommended to filter the minimally expressing genes and cells,
|
|
254
|
+
and pick out the subset of interest before feeding into this method. Or
|
|
255
|
+
more conveniently, run the analysis on given categorical properties via
|
|
256
|
+
the wrapper function ``experiment.run_rna_rogue``.
|
|
257
|
+
|
|
258
|
+
Parameters
|
|
259
|
+
-----------
|
|
260
|
+
|
|
261
|
+
adata : anndata.AnnData
|
|
262
|
+
The set of data you would like to test for heterogeneity
|
|
263
|
+
|
|
264
|
+
counts : str = 'X'
|
|
265
|
+
ROGUE index is calculated on raw counts. Specify a layer of raw counts.
|
|
266
|
+
|
|
267
|
+
platform : Literal['umi', 'full-length']
|
|
268
|
+
Choose k value with preset technology
|
|
269
|
+
|
|
270
|
+
k : int = None
|
|
271
|
+
Manually picks a k value. It can be estimated from dataset using
|
|
272
|
+
``estimate_k`` function, or use the recommended value via ``platform``.
|
|
273
|
+
|
|
274
|
+
outlier_n : int = 2
|
|
275
|
+
Remove n top-expressing cells during analysis
|
|
276
|
+
|
|
277
|
+
span : float = 0.5
|
|
278
|
+
Controls the smoothness of LOESS regression
|
|
279
|
+
|
|
280
|
+
correction : str = 'fdr_bh'
|
|
281
|
+
Multitest correction of p-value. Available correction methods:
|
|
282
|
+
* bonferroni : one-step correction
|
|
283
|
+
* sidak : one-step correction
|
|
284
|
+
* holm-sidak : step down method using Sidak adjustments
|
|
285
|
+
* holm : step-down method using Bonferroni adjustments
|
|
286
|
+
* simes-hochberg : step-up method (independent)
|
|
287
|
+
* hommel : closed method based on Simes tests (non-negative)
|
|
288
|
+
* fdr_bh : Benjamini/Hochberg (non-negative)
|
|
289
|
+
* fdr_by : Benjamini/Yekutieli (negative)
|
|
290
|
+
* fdr_tsbh : two stage fdr correction (non-negative)
|
|
291
|
+
* fdr_tsbky : two stage fdr correction (non-negative)
|
|
292
|
+
|
|
293
|
+
Returns
|
|
294
|
+
-----------
|
|
295
|
+
A floating point value, rogue index
|
|
296
|
+
'''
|
|
297
|
+
|
|
298
|
+
from exprmat.utils import choose_layer
|
|
299
|
+
C = choose_layer(adata, layer = counts).todense()
|
|
300
|
+
hvg = rogue_hvg(
|
|
301
|
+
C, adata.var_names.tolist(),
|
|
302
|
+
span = span, r = r, correction = correction
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
hvg = remove_outliers(
|
|
306
|
+
hvg, C, adata.var_names.tolist(),
|
|
307
|
+
n = outlier_n, span = span, r = r, correction = correction
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
return rogue_index(hvg, platform = platform, k = k)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
# calculate the value of the reference factor k
|
|
314
|
+
|
|
315
|
+
def estimate_k(C, varname, span = 0.5, r = 1, correction = "fdr_bh", adjust_p = True):
|
|
316
|
+
|
|
317
|
+
entropy = rogue_entropy(C, varname, r = r)
|
|
318
|
+
entropy = smoothen_entropy(entropy, span = span, correction = correction)
|
|
319
|
+
if not adjust_p: entropy['qval'] = entropy["pval"]
|
|
320
|
+
entropy = entropy.loc[entropy['qval'] < 0.05, :]
|
|
321
|
+
k = entropy['ds'].to_numpy().sum()
|
|
322
|
+
return k // 2
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def se_plot(df):
|
|
326
|
+
|
|
327
|
+
import seaborn as sns
|
|
328
|
+
return sns.scatterplot(
|
|
329
|
+
data = df,
|
|
330
|
+
x = 'mean.expr',
|
|
331
|
+
y = 'entropy',
|
|
332
|
+
hue = df['pval'].to_numpy() < 0.05
|
|
333
|
+
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import os
|
|
3
3
|
import pandas
|
|
4
|
+
from exprmat.ansi import warning, info
|
|
4
5
|
from exprmat.configuration import default as cfg
|
|
5
6
|
|
|
6
7
|
basepath = os.path.dirname(__file__)
|
|
@@ -124,6 +125,36 @@ def get_mapper_ensembl(taxa):
|
|
|
124
125
|
return ensembl_finder
|
|
125
126
|
|
|
126
127
|
|
|
128
|
+
def update_mapper(taxa, name, ens):
|
|
129
|
+
'''
|
|
130
|
+
Update the name ENSEMBL ID pair.
|
|
131
|
+
'''
|
|
132
|
+
|
|
133
|
+
if taxa not in genome.keys():
|
|
134
|
+
get_mapper_name(taxa)
|
|
135
|
+
|
|
136
|
+
if 'mapper.name' not in genome[taxa].keys():
|
|
137
|
+
get_mapper_name(taxa)
|
|
138
|
+
|
|
139
|
+
if 'mapper.ensembl' not in genome[taxa].keys():
|
|
140
|
+
get_mapper_ensembl(taxa)
|
|
141
|
+
|
|
142
|
+
if (name not in genome[taxa]['mapper.name'].keys()):
|
|
143
|
+
if (ens in genome[taxa]['mapper.ensembl'].keys()):
|
|
144
|
+
genome[taxa]['mapper.name'][name] = genome[taxa]['mapper.ensembl'][ens]
|
|
145
|
+
else: info(f'{ens} -> {name} pair could not be established.')
|
|
146
|
+
|
|
147
|
+
else:
|
|
148
|
+
if (ens not in genome[taxa]['mapper.ensembl'].keys()):
|
|
149
|
+
genome[taxa]['mapper.ensembl'][ens] = genome[taxa]['mapper.name'][name]
|
|
150
|
+
else: # both name and ens present in the dictionary, check for inconsistancy
|
|
151
|
+
if genome[taxa]['mapper.ensembl'][ens] != genome[taxa]['mapper.name'][name]:
|
|
152
|
+
warning(f'{ens} -> {name} is conflicted. ({ens} registered to {genome[taxa]["mapper.ensembl"][ens]}, while {name} to {genome[taxa]["mapper.name"][name]})')
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
|
|
127
158
|
def update_mapper_name(taxa, name, ugene):
|
|
128
159
|
'''
|
|
129
160
|
Update the gene name to unique gene id mapping.
|
|
@@ -16,7 +16,7 @@ def barplot(
|
|
|
16
16
|
colnames = ['logc' + x for x in [gene]]
|
|
17
17
|
cellnames = ['c' + str(i) for i in range(len(adata.obs_names.tolist()))]
|
|
18
18
|
lognorm = find_variable(adata, gene, layer = slot)
|
|
19
|
-
data = pd.DataFrame(lognorm
|
|
19
|
+
data = pd.DataFrame(lognorm)
|
|
20
20
|
|
|
21
21
|
data.columns = colnames
|
|
22
22
|
if split == None: data['split'] = 'default.split'
|
|
@@ -61,7 +61,36 @@ def barplot(
|
|
|
61
61
|
axes.set_ylabel(gene)
|
|
62
62
|
axes.set_xticks(selected_groups)
|
|
63
63
|
axes.set_yticks([0.0, 2.5])
|
|
64
|
-
|
|
64
|
+
|
|
65
|
+
# axes.set_xticklabels(selected_groups, rotation = 45)
|
|
66
|
+
|
|
67
|
+
from scipy import stats
|
|
68
|
+
# here, we will run the wilcox test
|
|
69
|
+
gene_data = dropzeros.loc[dropzeros['name'] == gene,:]
|
|
70
|
+
if group != None:
|
|
71
|
+
xtl = []
|
|
72
|
+
for ct in selected_groups:
|
|
73
|
+
cell_data = gene_data.loc[gene_data['row'] == ct,:]
|
|
74
|
+
groups = [x for _, x in cell_data.groupby(cell_data['split'])]
|
|
75
|
+
values = [x['logc'].tolist() for x in groups]
|
|
76
|
+
if len(values) == 2:
|
|
77
|
+
w = stats.mannwhitneyu(values[0], values[1])
|
|
78
|
+
xtl += [ct + '\n{:.3f}'.format(w.pvalue)]
|
|
79
|
+
else: xtl += [ct]
|
|
80
|
+
|
|
81
|
+
axes.set_xticks(selected_groups)
|
|
82
|
+
axes.set_xticklabels(xtl, rotation = 45)
|
|
83
|
+
|
|
84
|
+
else:
|
|
85
|
+
groups = [x for _, x in gene_data.groupby(gene_data['split'])]
|
|
86
|
+
values = [x['logc'].tolist() for x in groups]
|
|
87
|
+
xtl = 'whole'
|
|
88
|
+
if len(values) == 2:
|
|
89
|
+
w = stats.mannwhitneyu(values[0], values[1])
|
|
90
|
+
xtl = 'whole\n{:.3f}'.format(w.pvalue)
|
|
91
|
+
axes.set_xticklabels([xtl], rotation = 45)
|
|
92
|
+
pass
|
|
93
|
+
|
|
65
94
|
axes.spines[['left', 'top']].set_visible(False)
|
|
66
95
|
axes.yaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.1f}"))
|
|
67
96
|
fig.tight_layout()
|
|
@@ -29,7 +29,7 @@ module attribute, and the 'small' palettes (i.e. excluding the ones with 256
|
|
|
29
29
|
colors) are collected and in a ``small_palettes`` attribute.
|
|
30
30
|
|
|
31
31
|
This module includes some palettes that are useful for addressing color deficiencies,
|
|
32
|
-
which contains
|
|
32
|
+
which contains Paul Tol's color schemes, and ``Colorblind`` from
|
|
33
33
|
https://jfly.uni-koeln.de/color/#pallet.
|
|
34
34
|
|
|
35
35
|
The following palettes are also introduced in `Paul Tol's color schemes` but with
|
|
@@ -84,7 +84,7 @@ def highly_variable(
|
|
|
84
84
|
elif method == 'vst':
|
|
85
85
|
df = highly_variable_genes_seurat(
|
|
86
86
|
m_count, obs = adata.obs, var = adata.var, subset = False,
|
|
87
|
-
**kwargs
|
|
87
|
+
n_top_genes = n_top_genes, **kwargs
|
|
88
88
|
)
|
|
89
89
|
adata.var[dest + '.means'] = df['means']
|
|
90
90
|
adata.var[dest + '.vars'] = df['variances']
|
|
@@ -57,4 +57,24 @@ def rna_plot_qc_metrics(adata, sample_name, figsize = (10, 2.2)):
|
|
|
57
57
|
fig.set_dpi(100)
|
|
58
58
|
fig.tight_layout(pad = 0.1)
|
|
59
59
|
|
|
60
|
-
return fig
|
|
60
|
+
return fig
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def rna_plot_gene_histogram(adata, sample_name, ax):
|
|
64
|
+
|
|
65
|
+
h = ax.hist(adata.obs['n.genes'].tolist(), bins = 100)
|
|
66
|
+
ax.set_xlabel('Gene detection')
|
|
67
|
+
ax.set_ylabel('Frequency')
|
|
68
|
+
ax.set_title(sample_name if len(sample_name) < 20 else sample_name[:17] + ' ..')
|
|
69
|
+
|
|
70
|
+
ax.vlines(
|
|
71
|
+
x = [0, 150], ymin = 0, ymax = 10,
|
|
72
|
+
colors = 'red', linestyles = 'dotted'
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
ax.vlines(
|
|
76
|
+
x = [300, 600], ymin = 0, ymax = 10,
|
|
77
|
+
colors = 'black', linestyles = 'dotted'
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
ax.set_yscale('log')
|
|
@@ -153,15 +153,16 @@ def rna_qc(
|
|
|
153
153
|
adata.obs['score.doublet.se'] = 0.0
|
|
154
154
|
adata.obs['is.doublet'] = False
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
subset.obs['score.doublet']
|
|
158
|
-
|
|
159
|
-
subset.obs['score.doublet.se']
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
subset.obs['is.doublet']
|
|
163
|
-
|
|
164
|
-
|
|
156
|
+
if doublet_method == 'scrublet':
|
|
157
|
+
adata.obs.loc[subset.obs['score.doublet'].index, 'score.doublet'] = \
|
|
158
|
+
subset.obs['score.doublet']
|
|
159
|
+
adata.obs.loc[subset.obs['score.doublet.se'].index, 'score.doublet.se'] = \
|
|
160
|
+
subset.obs['score.doublet.se']
|
|
161
|
+
if 'is.doublet' in subset.obs.columns.tolist():
|
|
162
|
+
adata.obs.loc[subset.obs['score.doublet'].index, 'is.doublet'] = \
|
|
163
|
+
subset.obs['is.doublet']
|
|
164
|
+
adata.uns['scrublet'] = subset.uns['scrublet']
|
|
165
|
+
|
|
165
166
|
adata.obs['qc'] = adata.obs['filter']
|
|
166
167
|
adata.var['qc'] = f_var
|
|
167
168
|
|