scez 0.0.1__py2.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.
Potentially problematic release.
This version of scez might be problematic. Click here for more details.
- scez/__init__.py +36 -0
- scez/diffexp.py +199 -0
- scez/preprocess.py +74 -0
- scez/representation.py +44 -0
- scez/tests/__init__.py +0 -0
- scez/tests/test_scez.py +18 -0
- scez/utils.py +64 -0
- scez-0.0.1.dist-info/LICENSE +21 -0
- scez-0.0.1.dist-info/METADATA +48 -0
- scez-0.0.1.dist-info/RECORD +11 -0
- scez-0.0.1.dist-info/WHEEL +4 -0
scez/__init__.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""scez – Single Cell Analysis, Easy Mode!"""
|
|
2
|
+
|
|
3
|
+
from . import diffexp as de
|
|
4
|
+
from . import preprocess as pp
|
|
5
|
+
from . import representation as rp
|
|
6
|
+
from . import utils
|
|
7
|
+
import scanpy as sc
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
|
|
10
|
+
import tomli
|
|
11
|
+
|
|
12
|
+
toml_dict = tomli.load(open('pyproject.toml','rb'))
|
|
13
|
+
__version__ = toml_dict['tool']['poetry']['version']
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
sc.settings.verbosity = 1 # verbosity: errors (0), warnings (1), info (2), hints (3)
|
|
17
|
+
sc.settings.set_figure_params(dpi=100, dpi_save=300, frameon=False, figsize=(5, 5), facecolor='white')
|
|
18
|
+
sc.logging.print_header()
|
|
19
|
+
|
|
20
|
+
# https://stackoverflow.com/questions/21884271/warning-about-too-many-open-figures
|
|
21
|
+
plt.rcParams.update({'figure.max_open_warning': 0})
|
|
22
|
+
plt.close('all')
|
|
23
|
+
|
|
24
|
+
# https://stackoverflow.com/questions/3899980/how-to-change-the-font-size-on-a-matplotlib-plot
|
|
25
|
+
|
|
26
|
+
SMALL_SIZE = 6
|
|
27
|
+
MEDIUM_SIZE = 8
|
|
28
|
+
BIGGER_SIZE = 10
|
|
29
|
+
|
|
30
|
+
plt.rc('font', size=SMALL_SIZE) # controls default text sizes
|
|
31
|
+
plt.rc('axes', titlesize=SMALL_SIZE) # font size of the axes title
|
|
32
|
+
plt.rc('axes', labelsize=MEDIUM_SIZE) # font size of the x and y labels
|
|
33
|
+
plt.rc('xtick', labelsize=SMALL_SIZE) # font size of the tick labels
|
|
34
|
+
plt.rc('ytick', labelsize=SMALL_SIZE) # font size of the tick labels
|
|
35
|
+
plt.rc('legend', fontsize=SMALL_SIZE) # legend font size
|
|
36
|
+
plt.rc('figure', titlesize=BIGGER_SIZE) # font size of the figure title
|
scez/diffexp.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import seaborn as sns
|
|
5
|
+
import anndata as ad
|
|
6
|
+
|
|
7
|
+
from pydeseq2.dds import DeseqDataSet
|
|
8
|
+
from pydeseq2.default_inference import DefaultInference
|
|
9
|
+
from pydeseq2.ds import DeseqStats
|
|
10
|
+
from .utils import run_adjust_text
|
|
11
|
+
from adpbulk import ADPBulk
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def pseudobulk_by_clusters(adt, condition, cluster_col='leiden', method="mean"):
|
|
15
|
+
# initialize the object
|
|
16
|
+
adpb = ADPBulk(adt, [cluster_col, condition], method=method)
|
|
17
|
+
|
|
18
|
+
# perform the pseudobulking
|
|
19
|
+
pseudobulk_matrix = adpb.fit_transform()
|
|
20
|
+
|
|
21
|
+
# retrieve the sample metadata (useful for easy incorporation with edgeR)
|
|
22
|
+
sample_meta = adpb.get_meta()
|
|
23
|
+
|
|
24
|
+
out = ad.AnnData(
|
|
25
|
+
X=pseudobulk_matrix,
|
|
26
|
+
obs=sample_meta.set_index('SampleName')
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
return out
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def run_deseq(adata, design, tested_level, ref_level, n_cpus=8):
|
|
33
|
+
|
|
34
|
+
inference = DefaultInference(n_cpus=n_cpus)
|
|
35
|
+
|
|
36
|
+
dds = DeseqDataSet(
|
|
37
|
+
counts=adata.to_df().astype(int),
|
|
38
|
+
metadata=adata.obs,
|
|
39
|
+
design_factors=design, # compare samples based on the "condition"
|
|
40
|
+
refit_cooks=True,
|
|
41
|
+
inference=inference,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
dds.deseq2()
|
|
45
|
+
|
|
46
|
+
stat_res = DeseqStats(
|
|
47
|
+
dds,
|
|
48
|
+
contrast=[design, tested_level, ref_level],
|
|
49
|
+
inference=inference
|
|
50
|
+
)
|
|
51
|
+
stat_res.summary()
|
|
52
|
+
|
|
53
|
+
df = stat_res.results_df
|
|
54
|
+
|
|
55
|
+
return df
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def plot_volcano(df, title=None, labels=None, n_genes=False, side='both',
|
|
59
|
+
font_scale=1, dot_size = 5,
|
|
60
|
+
color = '#1f77b4', color_highlight = '#FFA500',
|
|
61
|
+
ax = None, **kwargs):
|
|
62
|
+
dot_size_highlight = dot_size * 1.1
|
|
63
|
+
annotate_font_size = 5 * font_scale
|
|
64
|
+
scatter_font_size = 8 * font_scale
|
|
65
|
+
label_font_size = 9 * font_scale
|
|
66
|
+
title_font_size = 10 * font_scale
|
|
67
|
+
|
|
68
|
+
if 'name' not in df.columns: df['name'] = df.index.to_list()
|
|
69
|
+
df['-log10(pvalue)'] = - np.log10(df.pvalue)
|
|
70
|
+
|
|
71
|
+
if not ax: fig, ax = plt.subplots(figsize=(3, 3))
|
|
72
|
+
|
|
73
|
+
# Scatter plot
|
|
74
|
+
ax.scatter(
|
|
75
|
+
df['log2FoldChange'],
|
|
76
|
+
df['-log10(pvalue)'],
|
|
77
|
+
alpha=0.9, s=dot_size, c=color,
|
|
78
|
+
**kwargs
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Set background color to transparent
|
|
82
|
+
ax.set_facecolor('none')
|
|
83
|
+
|
|
84
|
+
# Set smaller font size
|
|
85
|
+
ax.tick_params(axis='both', which='both', labelsize=scatter_font_size)
|
|
86
|
+
|
|
87
|
+
# Set labels
|
|
88
|
+
ax.set_xlabel('log2FoldChange', fontsize=label_font_size)
|
|
89
|
+
ax.set_ylabel('-log10(pvalue)', fontsize=label_font_size)
|
|
90
|
+
|
|
91
|
+
# Set plot title
|
|
92
|
+
if not title:
|
|
93
|
+
ax.set_title('Volcano Plot', fontsize=title_font_size)
|
|
94
|
+
else:
|
|
95
|
+
ax.set_title(title, fontsize=title_font_size)
|
|
96
|
+
|
|
97
|
+
ax.grid(False)
|
|
98
|
+
|
|
99
|
+
# check if `labels` is provided or set that based on `n_genes` and `side`
|
|
100
|
+
if labels and n_genes:
|
|
101
|
+
# error message if both labels and n_genes are provided and say one of them is allowed
|
|
102
|
+
raise ValueError('Provide either labels or n_genes, not both!')
|
|
103
|
+
|
|
104
|
+
elif n_genes and side == 'positive':
|
|
105
|
+
# Highlight top genes
|
|
106
|
+
top_genes = df.query('log2FoldChange > 0').nlargest(n_genes, '-log10(pvalue)')
|
|
107
|
+
labels = [row['name'] for _, row in top_genes.iterrows()]
|
|
108
|
+
|
|
109
|
+
elif n_genes and side == 'negative':
|
|
110
|
+
# Highlight top genes
|
|
111
|
+
top_genes = df.query('log2FoldChange < 0').nlargest(n_genes, '-log10(pvalue)')
|
|
112
|
+
labels = [row['name'] for _, row in top_genes.iterrows()]
|
|
113
|
+
|
|
114
|
+
elif n_genes and side == 'both':
|
|
115
|
+
# Highlight top genes
|
|
116
|
+
top_genes = df.nlargest(n_genes, '-log10(pvalue)')
|
|
117
|
+
labels = [row['name'] for _, row in top_genes.iterrows()]
|
|
118
|
+
|
|
119
|
+
# Highlight the points from given labels
|
|
120
|
+
if labels:
|
|
121
|
+
for label in labels:
|
|
122
|
+
ax.scatter(
|
|
123
|
+
df.loc[label, 'log2FoldChange'],
|
|
124
|
+
df.loc[label, '-log10(pvalue)'],
|
|
125
|
+
s=dot_size_highlight, c=color_highlight
|
|
126
|
+
)
|
|
127
|
+
run_adjust_text(
|
|
128
|
+
df.loc[labels, 'log2FoldChange'],
|
|
129
|
+
df.loc[labels, '-log10(pvalue)'],
|
|
130
|
+
labels,
|
|
131
|
+
font_size=annotate_font_size, ax=ax, use_arrow=False
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if not ax:
|
|
135
|
+
plt.tight_layout()
|
|
136
|
+
plt.show()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def plot_top_DEG_violinplot(adata, df, title=None, labels=None, n_genes=False, side='both', font_scale=1, figsize=(10, 4), **kwargs):
|
|
140
|
+
label_font_size = 9 * font_scale
|
|
141
|
+
title_font_size = 10 * font_scale
|
|
142
|
+
|
|
143
|
+
if 'name' not in df.columns: df['name'] = df.index.to_list()
|
|
144
|
+
|
|
145
|
+
if labels and n_genes:
|
|
146
|
+
# error message if both labels and n_genes are provided and say one of them is allowed
|
|
147
|
+
raise ValueError('Provide either labels or n_genes, not both!')
|
|
148
|
+
|
|
149
|
+
if not labels and not n_genes:
|
|
150
|
+
# error message if neither labels nor n_genes are provided
|
|
151
|
+
raise ValueError('Provide either labels or n_genes!')
|
|
152
|
+
|
|
153
|
+
if labels:
|
|
154
|
+
# Highlight the points from given list
|
|
155
|
+
selected_genes = df.loc[labels]
|
|
156
|
+
|
|
157
|
+
elif n_genes and side == 'positive':
|
|
158
|
+
# Highlight top genes
|
|
159
|
+
selected_genes = df.query('log2FoldChange > 0').nlargest(n_genes, '-log10(pvalue)')
|
|
160
|
+
|
|
161
|
+
elif n_genes and side == 'negative':
|
|
162
|
+
# Highlight top genes
|
|
163
|
+
selected_genes = df.query('log2FoldChange < 0').nlargest(n_genes, '-log10(pvalue)')
|
|
164
|
+
|
|
165
|
+
elif n_genes and side == 'both':
|
|
166
|
+
# Highlight top genes
|
|
167
|
+
selected_genes = df.nlargest(n_genes, '-log10(pvalue)')
|
|
168
|
+
|
|
169
|
+
# Filter the single-cell dataset for the selected genes
|
|
170
|
+
subset_adata = adata[:, selected_genes.index]
|
|
171
|
+
subset_adata.var.index = subset_adata.var.index.str.split('_').str[0]
|
|
172
|
+
|
|
173
|
+
# Convert the subset of adata to a DataFrame
|
|
174
|
+
subset_df = subset_adata.to_df()
|
|
175
|
+
|
|
176
|
+
# Merge the DataFrame with .obs to include the 'sample' information
|
|
177
|
+
merged_df = pd.merge(subset_df, adata.obs[['sample']], left_index=True, right_index=True)
|
|
178
|
+
|
|
179
|
+
# Melt the DataFrame to prepare for violin plot
|
|
180
|
+
melted_df = pd.melt(merged_df, id_vars='sample', var_name='Gene', value_name='Counts')
|
|
181
|
+
|
|
182
|
+
# Create a violin plot
|
|
183
|
+
plt.figure(figsize=figsize)
|
|
184
|
+
sns.violinplot(x='Gene', y='Counts', hue='sample', data=melted_df, split=True, inner='quartile', palette='Set2', **kwargs)
|
|
185
|
+
sns.stripplot(x='Gene', y='Counts', hue='sample', data=melted_df, dodge=True, jitter=True, color='black', size=1, alpha=0.3, **kwargs)
|
|
186
|
+
|
|
187
|
+
plt.xticks(rotation=45, ha='right', fontsize=label_font_size)
|
|
188
|
+
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', fontsize=label_font_size)
|
|
189
|
+
|
|
190
|
+
if not title:
|
|
191
|
+
plt.title('Top Differentially Expressed Genes', fontsize=title_font_size)
|
|
192
|
+
else:
|
|
193
|
+
plt.title(title, fontsize=title_font_size)
|
|
194
|
+
plt.show()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def write_top_DEGs(df, sample_id, result_dir='.', n_hits=200):
|
|
198
|
+
df['-log10(pvalue)'] = - np.log10(df.pvalue)
|
|
199
|
+
df.nlargest(n_hits, '-log10(pvalue)').to_csv(f'{result_dir}/{sample_id}_top_{n_hits}.csv') # Adjust the number as needed
|
scez/preprocess.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import scanpy as sc
|
|
3
|
+
import scar
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def normalization(adata, target_sum=1e4, max_value=10, final_layer='scaled', keep_initial_layer=True):
|
|
7
|
+
if keep_initial_layer == True:
|
|
8
|
+
adata.layers['raw_counts'] = adata.X.copy()
|
|
9
|
+
elif type(keep_initial_layer) == str:
|
|
10
|
+
adata.layers[keep_initial_layer] = adata.X.copy()
|
|
11
|
+
|
|
12
|
+
# normalize counts to target_sum (default 1e4)
|
|
13
|
+
counts = sc.pp.normalize_total(adata, target_sum=target_sum, inplace=False)
|
|
14
|
+
# log1p transform
|
|
15
|
+
adata.layers["log1p_norm"] = sc.pp.log1p(counts["X"], copy=True)
|
|
16
|
+
# scale counts
|
|
17
|
+
adata.layers['scaled'] = sc.pp.scale(adata, max_value=max_value, copy=True).X
|
|
18
|
+
# set the final layer
|
|
19
|
+
adata.X = adata.layers[final_layer]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def remove_ambient_rna(adata_filtered_feature_bc, adata_raw_feature_bc):
|
|
23
|
+
scar.setup_anndata(
|
|
24
|
+
adata = adata_filtered_feature_bc,
|
|
25
|
+
raw_adata = adata_raw_feature_bc,
|
|
26
|
+
prob = 0.995,
|
|
27
|
+
kneeplot = True
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
adata_scar = scar.model(
|
|
31
|
+
raw_count=adata_filtered_feature_bc.to_df(), # In the case of Anndata object, scar will automatically use the estimated ambient_profile present in adata.uns.
|
|
32
|
+
# ambient_profile=adata_filtered_feature_bc.uns['ambient_profile_Gene Expression'],
|
|
33
|
+
feature_type='mRNA',
|
|
34
|
+
sparsity=1,
|
|
35
|
+
# device=device # Both cpu and cuda are supported.
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
adata_scar.train(
|
|
39
|
+
epochs=200,
|
|
40
|
+
batch_size=64,
|
|
41
|
+
verbose=True
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# After training, we can infer the native true signal
|
|
45
|
+
adata_scar.inference(batch_size=256) # by defaut, batch_size = None, set a batch_size if getting a memory issue
|
|
46
|
+
|
|
47
|
+
denoised_count = pd.DataFrame(
|
|
48
|
+
adata_scar.native_counts,
|
|
49
|
+
index=adata_filtered_feature_bc.obs_names,
|
|
50
|
+
columns=adata_filtered_feature_bc.var_names
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
adata = adata_filtered_feature_bc.copy()
|
|
54
|
+
adata.layers['raw_counts'] = adata.X
|
|
55
|
+
adata.layers['scar_denoised_counts'] = denoised_count.to_numpy()
|
|
56
|
+
|
|
57
|
+
return adata
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def clustering(
|
|
61
|
+
adata
|
|
62
|
+
):
|
|
63
|
+
pass
|
|
64
|
+
# , n_pcs=50, n_neighbors=30, use_highly_variable='Yes',
|
|
65
|
+
# use_rep=None, resolution=None
|
|
66
|
+
|
|
67
|
+
# if use_highly_variable == 'Yes':
|
|
68
|
+
# sc.pp.highly_variable_genes(adata, min_mean=0.0125, max_mean=3, min_disp=0.5)
|
|
69
|
+
# sc.tl.pca(adata, svd_solver='arpack', use_highly_variable=True)
|
|
70
|
+
# else:
|
|
71
|
+
# sc.pp.pca(adata, n_comps=n_pcs)
|
|
72
|
+
# sc.pp.neighbors(adata, use_rep=use_rep, n_neighbors=n_neighbors)#, n_pcs=n_pcs)
|
|
73
|
+
# sc.tl.umap(adata)
|
|
74
|
+
# sc.tl.leiden(adata, resolution=resolution)
|
scez/representation.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from itertools import product
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import scanpy as sc
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def optimising_umap_layout(adata, cluster_key='leiden',MIN_DISTS = [0.1, 1, 2], SPREADS = [0.5, 1, 5]):
|
|
8
|
+
# https://scanpy-tutorials.readthedocs.io/en/latest/plotting/advanced.html
|
|
9
|
+
# Copy adata not to modify UMAP in the original adata object
|
|
10
|
+
adata_temp = adata.copy()
|
|
11
|
+
|
|
12
|
+
# Create grid of plots, with a little extra room for the legends
|
|
13
|
+
fig, axes = plt.subplots(
|
|
14
|
+
len(MIN_DISTS), len(SPREADS), figsize=(len(SPREADS) * 3 + 2, len(MIN_DISTS) * 3)
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Loop through different umap parameters, recomputting and replotting UMAP for each of them
|
|
18
|
+
for (i, min_dist), (j, spread) in product(enumerate(MIN_DISTS), enumerate(SPREADS)):
|
|
19
|
+
ax = axes[i][j]
|
|
20
|
+
param_str = " ".join(["min_dist =", str(min_dist), "and spread =", str(spread)])
|
|
21
|
+
# Recompute UMAP with new parameters
|
|
22
|
+
sc.tl.umap(adata_temp, min_dist=min_dist, spread=spread)
|
|
23
|
+
# Create plot, placing it in grid
|
|
24
|
+
sc.pl.umap(
|
|
25
|
+
adata_temp,
|
|
26
|
+
color=[cluster_key],
|
|
27
|
+
title=param_str,
|
|
28
|
+
s=40,
|
|
29
|
+
ax=ax,
|
|
30
|
+
show=False,
|
|
31
|
+
)
|
|
32
|
+
plt.tight_layout()
|
|
33
|
+
plt.show()
|
|
34
|
+
plt.close()
|
|
35
|
+
del adata_temp
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def random_ordering(adata):
|
|
39
|
+
# Randomly order cells by making a random index and subsetting AnnData based on it
|
|
40
|
+
# Set a random seed to ensure that the cell ordering will be reproducible
|
|
41
|
+
np.random.seed(0)
|
|
42
|
+
random_indices = np.random.permutation(list(range(adata.shape[0])))
|
|
43
|
+
|
|
44
|
+
return random_indices
|
scez/tests/__init__.py
ADDED
|
File without changes
|
scez/tests/test_scez.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import scanpy as sc
|
|
4
|
+
import scez
|
|
5
|
+
import tomli
|
|
6
|
+
|
|
7
|
+
toml_dict = tomli.load(open('pyproject.toml','rb'))
|
|
8
|
+
version = toml_dict['tool']['poetry']['version']
|
|
9
|
+
|
|
10
|
+
class TestScezConfig(unittest.TestCase):
|
|
11
|
+
def test_version(self):
|
|
12
|
+
self.assertEqual(scez.__version__, version)
|
|
13
|
+
|
|
14
|
+
def test_scanpy_settings(self):
|
|
15
|
+
self.assertEqual(sc.settings.verbosity, 1)
|
|
16
|
+
|
|
17
|
+
if __name__ == '__main__':
|
|
18
|
+
unittest.main()
|
scez/utils.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from matplotlib import pyplot as plt
|
|
3
|
+
from adjustText import adjust_text
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def rank_genes_to_df(adata, n=50):
|
|
7
|
+
result = adata.uns['rank_genes_groups']
|
|
8
|
+
|
|
9
|
+
groups = result['names'].dtype.names
|
|
10
|
+
|
|
11
|
+
df = pd.DataFrame(
|
|
12
|
+
{group + '_' + key: result[key][group]
|
|
13
|
+
for group in groups for key in ['names', 'scores']}).head(n)
|
|
14
|
+
|
|
15
|
+
return df
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def add_marker_feature(adata, marker, marker_name, clusters_name, thr = 0, figsize=(10, 4)):
|
|
19
|
+
|
|
20
|
+
adata.obs[marker_name] = ''
|
|
21
|
+
adata.obs.loc[adata.to_df().loc[:,marker] <= thr, marker_name] = f'{marker}-'
|
|
22
|
+
adata.obs.loc[adata.to_df().loc[:,marker] > thr, marker_name] = f'{marker}+'
|
|
23
|
+
|
|
24
|
+
df = pd.concat([
|
|
25
|
+
adata.obs.groupby([marker_name,clusters_name]).size()[f'{marker}+'],
|
|
26
|
+
adata.obs.groupby([marker_name,clusters_name]).size()[f'{marker}-']
|
|
27
|
+
],axis=1).rename(columns={0:f'{marker}+',1:f'{marker}-'})
|
|
28
|
+
|
|
29
|
+
# Make some labels.
|
|
30
|
+
labels = df[f'{marker}+'] / df.sum(axis=1) * 100
|
|
31
|
+
labels = labels.round(decimals=1)
|
|
32
|
+
labels.sort_values(ascending=False,inplace=True)
|
|
33
|
+
df = df.loc[labels.index,]
|
|
34
|
+
|
|
35
|
+
ax = df.plot.bar(stacked=True,rot=0,figsize=figsize)
|
|
36
|
+
|
|
37
|
+
rects = ax.patches
|
|
38
|
+
|
|
39
|
+
for rect, label in zip(rects, labels):
|
|
40
|
+
height = rect.get_height()
|
|
41
|
+
ax.text(
|
|
42
|
+
rect.get_x() + rect.get_width() / 2, height + 5, str(label) + "%",
|
|
43
|
+
ha="center", va="bottom", fontsize=8
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
ax.set_yscale('log')
|
|
47
|
+
ax.set_ylabel('# of cells')
|
|
48
|
+
return ax
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def run_adjust_text(x, y, labels, ax=None, use_arrow=True, font_weight='bold', font_size=8):
|
|
52
|
+
texts = [
|
|
53
|
+
plt.text(
|
|
54
|
+
x[i], y[i],
|
|
55
|
+
labels[i],
|
|
56
|
+
fontdict={'weight': font_weight, 'size': font_size},
|
|
57
|
+
ha='center', va='center'
|
|
58
|
+
) for i in range(len(x))
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
if use_arrow:
|
|
62
|
+
adjust_text(texts, arrowprops=dict(arrowstyle='->', color='red'), ax = ax)
|
|
63
|
+
else:
|
|
64
|
+
adjust_text(texts, ax = ax)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Abolfazl (Abe)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: scez
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Single Cell Analysis, Easy Mode!
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Abe Arab
|
|
7
|
+
Author-email: abarbiology@gmail.com
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 2
|
|
10
|
+
Classifier: Programming Language :: Python :: 2.7
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.4
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.5
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Project-URL: Source, https://github.com/abearab/scez
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
## scez – single cell, easy mode
|
|
25
|
+
[](https://github.com/abearab/scez/actions/workflows/main.yml)
|
|
26
|
+
|
|
27
|
+
### Installation
|
|
28
|
+
Make sure you have mamba installed in your base environment. If not, install it with:
|
|
29
|
+
```bash
|
|
30
|
+
conda install mamba -n base -c conda-forge
|
|
31
|
+
```
|
|
32
|
+
Then, create a new conda environment with the provided `environment.yml` file and activate it. This will install all necessary dependencies for scez.
|
|
33
|
+
```bash
|
|
34
|
+
conda env create -f environment.yml
|
|
35
|
+
|
|
36
|
+
conda activate scez
|
|
37
|
+
```
|
|
38
|
+
Finally, install scez with:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install scez
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or, if you want to install the latest version from the repository:
|
|
45
|
+
```bash
|
|
46
|
+
pip install git+https://github.com/abearab/scez.git
|
|
47
|
+
```
|
|
48
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
scez/__init__.py,sha256=kmN55oxZUHJfsInsnDWvAtxSNn5KnESir65NGr_ssyo,1356
|
|
2
|
+
scez/diffexp.py,sha256=V8qjqpTZVaoAb-iCa3jYmPDmDBtg5AK9OqNzRow6ic0,6770
|
|
3
|
+
scez/preprocess.py,sha256=3iaxACkmKQjKZdaFBLRAXz1kwkNnhbdnd_gwF8rI4nw,2572
|
|
4
|
+
scez/representation.py,sha256=IW0pwa_yoKf-2a3lbtxRwLTdCjB2PkS9oN4WGCLwED0,1596
|
|
5
|
+
scez/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
scez/tests/test_scez.py,sha256=hoBSYB5k1WtF1hY_kyWXw1W7NolBornmTdAng6tDpjw,452
|
|
7
|
+
scez/utils.py,sha256=lPmt01JILiKoBUhhxZeyhl2CfAzailTp4dIefP4FqGQ,1955
|
|
8
|
+
scez-0.0.1.dist-info/LICENSE,sha256=59TS1D5RmGh66RJikXvFoq_ZW9pGirB4zUOUXLfptJM,1071
|
|
9
|
+
scez-0.0.1.dist-info/METADATA,sha256=H5YLkU2mzh80sL9WrXsNX740nA7tTt4NWQFIHi93Kio,1647
|
|
10
|
+
scez-0.0.1.dist-info/WHEEL,sha256=IrRNNNJ-uuL1ggO5qMvT1GGhQVdQU54d6ZpYqEZfEWo,92
|
|
11
|
+
scez-0.0.1.dist-info/RECORD,,
|