ScreenPro2 0.5.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.
- pyproject.toml +30 -0
- screenpro/__init__.py +31 -0
- screenpro/__main__.py +8 -0
- screenpro/assays/__init__.py +465 -0
- screenpro/dashboard/__init__.py +293 -0
- screenpro/load.py +239 -0
- screenpro/main.py +227 -0
- screenpro/ngs/__init__.py +390 -0
- screenpro/ngs/cas12.py +206 -0
- screenpro/ngs/cas9.py +276 -0
- screenpro/phenoscore/__init__.py +148 -0
- screenpro/phenoscore/_annotate.py +122 -0
- screenpro/phenoscore/delta.py +375 -0
- screenpro/phenoscore/deseq.py +57 -0
- screenpro/phenoscore/evaluate.py +66 -0
- screenpro/phenoscore/phenostat.py +84 -0
- screenpro/plotting/__init__.py +9 -0
- screenpro/plotting/_rank.py +88 -0
- screenpro/plotting/_utils.py +81 -0
- screenpro/plotting/pheno_plots.py +196 -0
- screenpro/plotting/qc_plots.py +45 -0
- screenpro/preprocessing.py +98 -0
- screenpro2-0.5.0.dist-info/LICENSE +25 -0
- screenpro2-0.5.0.dist-info/METADATA +366 -0
- screenpro2-0.5.0.dist-info/RECORD +27 -0
- screenpro2-0.5.0.dist-info/WHEEL +4 -0
- screenpro2-0.5.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
## Copyright (c) 2022-2025 ScreenPro2 Development Team.
|
|
2
|
+
## All rights reserved.
|
|
3
|
+
## Gilbart Lab, UCSF / Arc Institute.
|
|
4
|
+
## Multi-Omics Tech Center, Arc Insititue.
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import scanpy as sc
|
|
8
|
+
from .qc_plots import plotReplicateScatter, plotCountDistribution
|
|
9
|
+
from .pheno_plots import volcano_plot, label_by_color, label_resistance_hit, label_sensitivity_hit
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
|
|
4
|
+
from adjustText import adjust_text
|
|
5
|
+
from ._utils import yellow_blue
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def rank_plot(df, rank_col, color_col=None, name_col='target', highlight_values_dict=None, xlabel='Rank', ylabel='Values', title='Rank Plot', ax=None, dot_size=1.5, highlight_size_factor=100, txt_font_size=8, **args):
|
|
9
|
+
"""
|
|
10
|
+
Plot the ranks against their values with specified color.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
df (DataFrame): The input DataFrame.
|
|
14
|
+
rank_col (str): The column name containing the values to be ranked.
|
|
15
|
+
color_col (str): The column name containing the values to be used for color coding. Default is None.
|
|
16
|
+
name_col (str, optional): The column name containing the names of the values. Default is 'target'.
|
|
17
|
+
highlight_values_dict (dict, optional): A dictionary specifying the values to be highlighted.
|
|
18
|
+
The keys are the highlight colors and the values are dictionaries with 'genes' and 'text' keys.
|
|
19
|
+
'genes' is a list of values to be highlighted and 'text' is a boolean indicating whether to display
|
|
20
|
+
the names of the highlighted values. Default is None.
|
|
21
|
+
xlabel (str, optional): The label for the x-axis. Default is 'Rank'.
|
|
22
|
+
ylabel (str, optional): The label for the y-axis. Default is 'Values'.
|
|
23
|
+
title (str, optional): The title of the plot. Default is 'Rank Plot'.
|
|
24
|
+
ax (matplotlib.axes.Axes, optional): The axis object to plot on. If not provided, a new axis will be created.
|
|
25
|
+
dot_size (float, optional): The size of the dots in the scatter plot. Default is 1.5.
|
|
26
|
+
highlight_size_factor (int, optional): The size factor for the highlighted dots. Default is 100.
|
|
27
|
+
txt_font_size (int, optional): The font size for the text labels. Default is 8.
|
|
28
|
+
**args: Additional keyword arguments to be passed to the scatter plot.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
matplotlib.axes.Axes: The axis object containing the plot.
|
|
32
|
+
"""
|
|
33
|
+
# Create a new DataFrame with the values and their corresponding ranks
|
|
34
|
+
rank_df = df.copy()
|
|
35
|
+
rank_df['Rank'] = rank_df[rank_col].rank()
|
|
36
|
+
rank_df.sort_values('Rank', inplace=True)
|
|
37
|
+
|
|
38
|
+
# Use a color that is suitable for publications
|
|
39
|
+
if color_col is None:
|
|
40
|
+
color_col = 'darkgray'
|
|
41
|
+
|
|
42
|
+
# If no axis is provided, create one
|
|
43
|
+
if ax is None:
|
|
44
|
+
_, ax = plt.subplots()
|
|
45
|
+
|
|
46
|
+
# Plot the ranks against their values with specified color
|
|
47
|
+
rank_df.plot.scatter(
|
|
48
|
+
'Rank', rank_col, marker='o',
|
|
49
|
+
colormap=yellow_blue,
|
|
50
|
+
s=dot_size,
|
|
51
|
+
c=color_col, ax=ax,
|
|
52
|
+
colorbar=False,
|
|
53
|
+
**args
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if highlight_values_dict is not None:
|
|
57
|
+
for highlight_color, highlight_values in highlight_values_dict.items():
|
|
58
|
+
highlight_ranks = rank_df[rank_df[name_col].isin(highlight_values['genes'])]
|
|
59
|
+
ax.plot(highlight_ranks['Rank'], highlight_ranks[rank_col], 'o', color=highlight_color, markersize=dot_size * highlight_size_factor)
|
|
60
|
+
|
|
61
|
+
if highlight_values['text'] is not False:
|
|
62
|
+
texts = []
|
|
63
|
+
for i, row in highlight_ranks.iterrows():
|
|
64
|
+
t = ax.text(
|
|
65
|
+
row['Rank'] + .01,
|
|
66
|
+
row[rank_col] + .001,
|
|
67
|
+
row[name_col],
|
|
68
|
+
fontsize=txt_font_size,
|
|
69
|
+
color=highlight_color,
|
|
70
|
+
ha='right'
|
|
71
|
+
)
|
|
72
|
+
texts.append(t)
|
|
73
|
+
|
|
74
|
+
adjust_text(
|
|
75
|
+
texts,
|
|
76
|
+
arrowprops=dict(arrowstyle='-', color=highlight_color, lw=0.5),
|
|
77
|
+
ax=ax
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Add labels and title
|
|
81
|
+
ax.set_xlabel(xlabel)
|
|
82
|
+
ax.set_ylabel(ylabel)
|
|
83
|
+
ax.set_title(title)
|
|
84
|
+
|
|
85
|
+
# Customize the grid lines for a clean look
|
|
86
|
+
ax.grid(False)
|
|
87
|
+
|
|
88
|
+
return rank_df, ax
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import matplotlib
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
# variables
|
|
7
|
+
almost_black = '#111111'
|
|
8
|
+
dark2 = ['#1b9e77',
|
|
9
|
+
'#d95f02',
|
|
10
|
+
'#7570b3',
|
|
11
|
+
'#e7298a',
|
|
12
|
+
'#66a61e',
|
|
13
|
+
'#e6ab02',
|
|
14
|
+
'#a6761d',
|
|
15
|
+
'#666666']
|
|
16
|
+
blue_yellow = matplotlib.colors.LinearSegmentedColormap.from_list(
|
|
17
|
+
'BuYl', [(0, '#ffff00'), (.49, '#000000'), (.51, '#000000'), (1, '#0000ff')])
|
|
18
|
+
blue_yellow.set_bad('#999999', 1)
|
|
19
|
+
yellow_blue = matplotlib.colors.LinearSegmentedColormap.from_list(
|
|
20
|
+
'YlBu', [(0, '#0000ff'), (.49, '#000000'), (.51, '#000000'), (1, '#ffff00')])
|
|
21
|
+
yellow_blue.set_bad('#999999', 1)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# plt.rcParams['font.sans-serif'] = [
|
|
25
|
+
# 'Helvetica', 'Arial', 'Verdana', 'Bitstream Vera Sans'
|
|
26
|
+
# ]
|
|
27
|
+
# plt.rcParams['font.size'] = 8
|
|
28
|
+
# plt.rcParams['font.weight'] = 'regular'
|
|
29
|
+
# plt.rcParams['text.color'] = almost_black
|
|
30
|
+
#
|
|
31
|
+
# axisLineWidth = .5
|
|
32
|
+
# plt.rcParams['axes.linewidth'] = axisLineWidth
|
|
33
|
+
# plt.rcParams['lines.linewidth'] = 1.5
|
|
34
|
+
#
|
|
35
|
+
# plt.rcParams['axes.facecolor'] = 'white'
|
|
36
|
+
# plt.rcParams['axes.edgecolor'] = almost_black
|
|
37
|
+
# plt.rcParams['axes.labelcolor'] = almost_black
|
|
38
|
+
# # plt.rcParams['axes.color_cycle'] = dark2_all
|
|
39
|
+
#
|
|
40
|
+
# plt.rcParams['patch.edgecolor'] = 'none'
|
|
41
|
+
# plt.rcParams['patch.linewidth'] = .25
|
|
42
|
+
# # plt.rcParams['patch.facecolor'] = dark2_all[0]
|
|
43
|
+
#
|
|
44
|
+
# plt.rcParams['savefig.dpi'] = 1000
|
|
45
|
+
# plt.rcParams['savefig.format'] = 'svg'
|
|
46
|
+
#
|
|
47
|
+
# plt.rcParams['legend.frameon'] = False
|
|
48
|
+
# plt.rcParams['legend.handletextpad'] = .25
|
|
49
|
+
# plt.rcParams['legend.fontsize'] = 8
|
|
50
|
+
# plt.rcParams['legend.numpoints'] = 1
|
|
51
|
+
# plt.rcParams['legend.scatterpoints'] = 1
|
|
52
|
+
#
|
|
53
|
+
# plt.rcParams['ytick.direction'] = 'out'
|
|
54
|
+
# plt.rcParams['ytick.color'] = almost_black
|
|
55
|
+
# plt.rcParams['ytick.major.width'] = axisLineWidth
|
|
56
|
+
# plt.rcParams['xtick.direction'] = 'out'
|
|
57
|
+
# plt.rcParams['xtick.color'] = almost_black
|
|
58
|
+
# plt.rcParams['xtick.major.width'] = axisLineWidth
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def cleanAxes(ax, top=False, right=False, bottom=True, left=True):
|
|
62
|
+
ax.grid('off')
|
|
63
|
+
ax.spines['top'].set_visible(top)
|
|
64
|
+
ax.spines['right'].set_visible(right)
|
|
65
|
+
ax.spines['left'].set_visible(left)
|
|
66
|
+
ax.spines['bottom'].set_visible(bottom)
|
|
67
|
+
|
|
68
|
+
#turn off all ticks
|
|
69
|
+
ax.yaxis.set_ticks_position('none')
|
|
70
|
+
ax.xaxis.set_ticks_position('none')
|
|
71
|
+
|
|
72
|
+
#now re-enable visibles
|
|
73
|
+
if top:
|
|
74
|
+
ax.xaxis.tick_top()
|
|
75
|
+
if bottom:
|
|
76
|
+
ax.xaxis.tick_bottom()
|
|
77
|
+
if left:
|
|
78
|
+
ax.yaxis.tick_left()
|
|
79
|
+
if right:
|
|
80
|
+
ax.yaxis.tick_right()
|
|
81
|
+
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from ._utils import yellow_blue
|
|
5
|
+
from ._rank import rank_plot
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def volcano_plot(
|
|
9
|
+
ax, df, up_hit, down_hit,
|
|
10
|
+
score_col='score', pvalue_col='pvalue',
|
|
11
|
+
xlabel='phenotype score',
|
|
12
|
+
ylabel='-log10(pvalue)',
|
|
13
|
+
dot_size=1,
|
|
14
|
+
xlims='auto',
|
|
15
|
+
ylims='auto',
|
|
16
|
+
ctrl_label='negative_control',
|
|
17
|
+
**args
|
|
18
|
+
):
|
|
19
|
+
|
|
20
|
+
if f'-log10({pvalue_col})' not in df.columns:
|
|
21
|
+
df[f'-log10({pvalue_col})'] = np.log10(df[pvalue_col]) * -1
|
|
22
|
+
|
|
23
|
+
if xlims == 'auto':
|
|
24
|
+
xlims = (df[score_col].min() - 0.1, df[score_col].max() + 0.1)
|
|
25
|
+
if ylims == 'auto':
|
|
26
|
+
ylims = (df[f'-log10({pvalue_col})'].min() - 0.1, df[f'-log10({pvalue_col})'].max() + 0.1)
|
|
27
|
+
|
|
28
|
+
# Scatter plot for each category
|
|
29
|
+
ax.scatter( df.loc[df['label'] == 'target_non_hit', score_col],
|
|
30
|
+
df.loc[df['label'] == 'target_non_hit', f'-log10({pvalue_col})'],
|
|
31
|
+
alpha=0.1, s=dot_size, c='black', label='target_non_hit',
|
|
32
|
+
**args)
|
|
33
|
+
|
|
34
|
+
ax.scatter( df.loc[df['label'] == up_hit, score_col],
|
|
35
|
+
df.loc[df['label'] == up_hit, f'-log10({pvalue_col})'],
|
|
36
|
+
alpha=0.9, s=dot_size, c='#fcae91', label=up_hit,
|
|
37
|
+
**args)
|
|
38
|
+
|
|
39
|
+
ax.scatter( df.loc[df['label'] == down_hit, score_col],
|
|
40
|
+
df.loc[df['label'] == down_hit, f'-log10({pvalue_col})'],
|
|
41
|
+
alpha=0.9, s=dot_size, c='#bdd7e7', label=down_hit,
|
|
42
|
+
**args)
|
|
43
|
+
|
|
44
|
+
ax.scatter( df.loc[df['label'] == ctrl_label, score_col],
|
|
45
|
+
df.loc[df['label'] == ctrl_label, f'-log10({pvalue_col})'],
|
|
46
|
+
alpha=0.1, s=dot_size, c='gray', label=ctrl_label,
|
|
47
|
+
**args)
|
|
48
|
+
|
|
49
|
+
# Set x-axis and y-axis labels
|
|
50
|
+
ax.set_xlabel(xlabel)
|
|
51
|
+
ax.set_ylabel(ylabel)
|
|
52
|
+
|
|
53
|
+
# Set x-axis limits
|
|
54
|
+
ax.set_xlim(xlims)
|
|
55
|
+
|
|
56
|
+
# Set y-axis limits
|
|
57
|
+
ax.set_ylim(ylims)
|
|
58
|
+
|
|
59
|
+
# Add legend
|
|
60
|
+
ax.legend()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def label_by_color(ax, df_in, label,
|
|
64
|
+
x_col, y_col,
|
|
65
|
+
size=2, size_txt="auto",
|
|
66
|
+
edgecolors='black', facecolors='black',
|
|
67
|
+
textcolor='black',
|
|
68
|
+
t_x=.5, t_y=-0.1, **args):
|
|
69
|
+
|
|
70
|
+
if size_txt == 'auto': size_txt = size * 2
|
|
71
|
+
|
|
72
|
+
df = df_in.copy()
|
|
73
|
+
target_data = df[df['target'] == label]
|
|
74
|
+
|
|
75
|
+
# Scatter plot for labeled data
|
|
76
|
+
ax.scatter(
|
|
77
|
+
target_data[x_col], target_data[y_col],
|
|
78
|
+
s=size, linewidth=0.5,
|
|
79
|
+
edgecolors=edgecolors,
|
|
80
|
+
facecolors=facecolors, label='target',
|
|
81
|
+
**args
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if size_txt == None:
|
|
85
|
+
pass
|
|
86
|
+
else:
|
|
87
|
+
# Annotate the points
|
|
88
|
+
for i, _ in enumerate(target_data['target']):
|
|
89
|
+
txt = target_data['target'].iloc[i]
|
|
90
|
+
ax.annotate(txt, (target_data[x_col].iloc[i] + t_x, target_data[y_col].iloc[i] + t_y),
|
|
91
|
+
color=textcolor, size=size_txt)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def label_as_black(ax, df_in, label,
|
|
95
|
+
x_col='score', y_col='-log10(pvalue)',
|
|
96
|
+
size=2, size_txt="auto",
|
|
97
|
+
t_x=.5, t_y=-0.1,
|
|
98
|
+
**args):
|
|
99
|
+
label_by_color(
|
|
100
|
+
ax, df_in, label,
|
|
101
|
+
x_col=x_col, y_col=y_col,
|
|
102
|
+
size=size, size_txt=size_txt,
|
|
103
|
+
edgecolors='black', facecolors='black',
|
|
104
|
+
textcolor='black',
|
|
105
|
+
t_x=t_x, t_y=t_y,
|
|
106
|
+
**args)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def label_sensitivity_hit(ax, df_in, label,
|
|
110
|
+
x_col='score', y_col='-log10(pvalue)',
|
|
111
|
+
size=2, size_txt="auto",
|
|
112
|
+
t_x=.5, t_y=-0.1,
|
|
113
|
+
**args):
|
|
114
|
+
label_by_color(
|
|
115
|
+
ax, df_in, label,
|
|
116
|
+
x_col=x_col, y_col=y_col,
|
|
117
|
+
size=size, size_txt=size_txt,
|
|
118
|
+
edgecolors='black', facecolors='#3182bd',
|
|
119
|
+
textcolor='black',
|
|
120
|
+
t_x=t_x, t_y=t_y,
|
|
121
|
+
**args)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def label_resistance_hit(ax, df_in, label,
|
|
125
|
+
x_col='score', y_col='-log10(pvalue)',
|
|
126
|
+
size=2, size_txt="auto",
|
|
127
|
+
t_x=.5, t_y=-0.1,
|
|
128
|
+
**args):
|
|
129
|
+
label_by_color(
|
|
130
|
+
ax, df_in, label,
|
|
131
|
+
x_col=x_col, y_col=y_col,
|
|
132
|
+
size=size, size_txt=size_txt,
|
|
133
|
+
edgecolors='black', facecolors='#de2d26',
|
|
134
|
+
textcolor='black',
|
|
135
|
+
t_x=t_x, t_y=t_y,
|
|
136
|
+
**args)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# def drawRhoGammaScatter(
|
|
140
|
+
# self, ax,
|
|
141
|
+
# rho_df=None, gamma_df=None,
|
|
142
|
+
# dot_size=1,
|
|
143
|
+
# score_col='score',
|
|
144
|
+
# xlabel='auto',
|
|
145
|
+
# ylabel='auto',
|
|
146
|
+
# xlims='auto',
|
|
147
|
+
# ylims='auto',
|
|
148
|
+
# **args
|
|
149
|
+
# ):
|
|
150
|
+
# #TODO: fix by making a single dataframe with both rho and gamma scores
|
|
151
|
+
# if rho_df is None:
|
|
152
|
+
# _, _, rho_df = self._prep_data()
|
|
153
|
+
# if gamma_df is None:
|
|
154
|
+
# gamma_df, _, _ = self._prep_data()
|
|
155
|
+
|
|
156
|
+
# if xlabel == 'auto':
|
|
157
|
+
# xlabel = self.rho_score_name.replace(':', ': ').replace('_', ' ')
|
|
158
|
+
# if ylabel == 'auto':
|
|
159
|
+
# ylabel = self.gamma_score_name.replace(':', ': ').replace('_', ' ')
|
|
160
|
+
|
|
161
|
+
# # color by rho score labels
|
|
162
|
+
# up_hit = 'resistance_hit'
|
|
163
|
+
# down_hit = 'sensitivity_hit'
|
|
164
|
+
|
|
165
|
+
# # Scatter plot for each category
|
|
166
|
+
# ax.scatter( rho_df.loc[rho_df['label'] == 'target_non_hit', score_col],
|
|
167
|
+
# gamma_df.loc[rho_df['label'] == 'target_non_hit', score_col],
|
|
168
|
+
# alpha=0.1, s=dot_size, c='black', label='target_non_hit',
|
|
169
|
+
# **args)
|
|
170
|
+
|
|
171
|
+
# ax.scatter( rho_df.loc[rho_df['label'] == up_hit, score_col],
|
|
172
|
+
# gamma_df.loc[rho_df['label'] == up_hit, score_col],
|
|
173
|
+
# alpha=0.9, s=dot_size, c='#fcae91', label=up_hit,
|
|
174
|
+
# **args)
|
|
175
|
+
|
|
176
|
+
# ax.scatter( rho_df.loc[rho_df['label'] == down_hit, score_col],
|
|
177
|
+
# gamma_df.loc[rho_df['label'] == down_hit, score_col],
|
|
178
|
+
# alpha=0.9, s=dot_size, c='#bdd7e7', label=down_hit,
|
|
179
|
+
# **args)
|
|
180
|
+
|
|
181
|
+
# ax.scatter( rho_df.loc[rho_df['label'] == self.ctrl_label, score_col],
|
|
182
|
+
# gamma_df.loc[rho_df['label'] == self.ctrl_label, score_col],
|
|
183
|
+
# alpha=0.1, s=dot_size, c='gray', label=self.ctrl_label,
|
|
184
|
+
# **args)
|
|
185
|
+
|
|
186
|
+
# # Set x-axis and y-axis labels
|
|
187
|
+
# ax.set_xlabel(xlabel)
|
|
188
|
+
# ax.set_ylabel(ylabel)
|
|
189
|
+
|
|
190
|
+
# # Set x-axis limits
|
|
191
|
+
# ax.set_xlim(xlims)
|
|
192
|
+
# ax.set_ylim(ylims)
|
|
193
|
+
|
|
194
|
+
# # Add legend
|
|
195
|
+
# ax.legend()
|
|
196
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import scanpy as sc
|
|
3
|
+
from ._utils import almost_black
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Histogram of guide counts distribution
|
|
7
|
+
def plotCountDistribution(ax, adat, title, **args):
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Scatter plot of replicates
|
|
12
|
+
def plotReplicateScatter(ax, adat_in, x, y, title, min_val=None, max_val=None, log_transform=True, **args):
|
|
13
|
+
adat = adat_in[[x, y], :].copy()
|
|
14
|
+
|
|
15
|
+
adat.obs.index = [f'Replicate {str(r)}' for r in adat.obs.replicate.to_list()]
|
|
16
|
+
x_lab, y_lab = [f'Replicate {str(r)}' for r in adat.obs.replicate.to_list()]
|
|
17
|
+
|
|
18
|
+
if log_transform:
|
|
19
|
+
adat.X = np.log10(adat.X+1)
|
|
20
|
+
|
|
21
|
+
if min_val is None:
|
|
22
|
+
min_val = min([adat.to_df().loc[x_lab,:].min(), adat.to_df().loc[y_lab,:].min()])
|
|
23
|
+
min_val = min_val * 1.1
|
|
24
|
+
if max_val is None:
|
|
25
|
+
max_val = max([adat.to_df().loc[x_lab,:].max(), adat.to_df().loc[y_lab,:].max()])
|
|
26
|
+
max_val = max_val * 1.1
|
|
27
|
+
|
|
28
|
+
sc.pl.scatter(
|
|
29
|
+
adat,
|
|
30
|
+
x_lab, y_lab,
|
|
31
|
+
legend_fontsize='xx-large',
|
|
32
|
+
palette=[almost_black, '#BFBFBF'],
|
|
33
|
+
color='targetType',
|
|
34
|
+
title=title,
|
|
35
|
+
size=5,
|
|
36
|
+
show=False,
|
|
37
|
+
ax=ax,
|
|
38
|
+
**args
|
|
39
|
+
)
|
|
40
|
+
ax.set_ylim(min_val, max_val)
|
|
41
|
+
ax.set_xlim(min_val, max_val)
|
|
42
|
+
ax.tick_params(axis='both', labelsize=10)
|
|
43
|
+
ax.get_legend().remove()
|
|
44
|
+
|
|
45
|
+
ax.grid(False)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
from pydeseq2 import preprocessing
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def findLowCounts(adata, filter_type, minimum_reads, verbose=True):
|
|
7
|
+
"""
|
|
8
|
+
Label variables with low counts in either or all samples.
|
|
9
|
+
|
|
10
|
+
Parameters:
|
|
11
|
+
adata (AnnData): AnnData object containing the counts to be filtered.
|
|
12
|
+
filter_type (str): specify the filter type. Possible values are: 'all', or 'sum'.
|
|
13
|
+
minimum_reads (int): minimum number of reads.
|
|
14
|
+
verbose (bool): print the number of removed variables. Default is True.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
None
|
|
18
|
+
"""
|
|
19
|
+
count_bin = adata.X >= minimum_reads
|
|
20
|
+
|
|
21
|
+
if filter_type == 'all':
|
|
22
|
+
out = adata[:, count_bin.all(axis=0)].copy()
|
|
23
|
+
elif filter_type == 'sum':
|
|
24
|
+
out = adata[:, adata.to_df().sum(axis=0) >= minimum_reads].copy()
|
|
25
|
+
else:
|
|
26
|
+
raise ValueError(f'filter_type "{filter_type}" not recognized. Use "all", or "sum".')
|
|
27
|
+
|
|
28
|
+
if verbose:
|
|
29
|
+
n_removed = adata.shape[1] - out.shape[1]
|
|
30
|
+
# print the number of removed variables
|
|
31
|
+
print(
|
|
32
|
+
f"{n_removed} variables with less than {minimum_reads} reads (filter_type: '{filter_type}')"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
adata.var['low_count'] = ~adata.var.index.isin(out.var.index.to_list())
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def addPseudoCount(adata, behavior, value, inplace=True):
|
|
39
|
+
"""
|
|
40
|
+
Add pseudocounts to the given counts based on the specified behavior.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
adata (AnnData): AnnData object containing the counts to which pseudocounts will be added.
|
|
44
|
+
behavior (str): The behavior for adding pseudocounts. Possible values are:
|
|
45
|
+
- 'default' or 'zeros_only': Add pseudocounts only to rows with at least one zero value.
|
|
46
|
+
- 'all_values': Add pseudocounts to all rows.
|
|
47
|
+
- 'filter_out': Set rows with at least one zero value to NaN.
|
|
48
|
+
value (float): The value of the pseudocount to be added.
|
|
49
|
+
inplace (bool): If True, the pseudocounts will replace the original counts in the AnnData object.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
DataFrame: The counts DataFrame with pseudocounts added based on the specified behavior.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
ValueError: If the pseudocount behavior is not recognized or not implemented.
|
|
56
|
+
"""
|
|
57
|
+
## possible pseudocount behaviors
|
|
58
|
+
# 1. remove 0
|
|
59
|
+
# 2. add pseudocount
|
|
60
|
+
# 3. impute 0 (it's hard)
|
|
61
|
+
|
|
62
|
+
# Source:
|
|
63
|
+
# https://github.com/mhorlbeck/ScreenProcessing/blob/0ee5192ecc17348665bd1387ddfa9037efb7964f/process_experiments.py#L485
|
|
64
|
+
|
|
65
|
+
counts = adata.to_df()
|
|
66
|
+
|
|
67
|
+
# pseudocount
|
|
68
|
+
if behavior == 'default' or behavior == 'zeros_only':
|
|
69
|
+
counts_pseudo = counts.replace(0, value)
|
|
70
|
+
|
|
71
|
+
elif behavior == 'all_values':
|
|
72
|
+
counts_pseudo = counts + value
|
|
73
|
+
elif behavior == 'filter_out':
|
|
74
|
+
counts_pseudo = counts.replace(0, np.nan)
|
|
75
|
+
else:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
'Pseudocount behavior not recognized or not implemented')
|
|
78
|
+
|
|
79
|
+
if inplace:
|
|
80
|
+
adata.X = counts_pseudo.to_numpy()
|
|
81
|
+
else:
|
|
82
|
+
return counts_pseudo
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def normalizeSeqDepth(adata):
|
|
86
|
+
"""
|
|
87
|
+
Normalize counts by sequencing depth and update the adata object.
|
|
88
|
+
This function uses the PyDESeq2 normalization method.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
adata (AnnData): AnnData object containing the counts to be normalized.
|
|
92
|
+
"""
|
|
93
|
+
# normalize counts by sequencing depth
|
|
94
|
+
norm_counts, size_factors = preprocessing.deseq2_norm(adata.X)
|
|
95
|
+
# update adata object
|
|
96
|
+
adata.obs['size_factors'] = size_factors
|
|
97
|
+
adata.layers['seq_depth_norm'] = norm_counts
|
|
98
|
+
adata.X = adata.layers['seq_depth_norm']
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2024 ScreenPro2 Development Team.
|
|
4
|
+
All rights reserved.
|
|
5
|
+
Gilbart Lab, UCSF / Arc Institute.
|
|
6
|
+
Multi-Omics Tech Center, Arc Insititue.
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
|
25
|
+
|