-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy path1_get_multiplier_LV_coverage.py
More file actions
374 lines (320 loc) · 13.2 KB
/
Copy path1_get_multiplier_LV_coverage.py
File metadata and controls
374 lines (320 loc) · 13.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# ---
# jupyter:
# jupytext:
# formats: ipynb,py
# text_representation:
# extension: .py
# format_name: light
# format_version: '1.5'
# jupytext_version: 1.9.1+dev
# kernelspec:
# display_name: Python [conda env:generic_expression] *
# language: python
# name: conda-env-generic_expression-py
# ---
# # Coverage of MultiPLIER LV
#
# The goal of this notebook is to examine why genes were found to be generic. Specifically, this notebook is trying to answer the question: Are generic genes found in more multiplier latent variables compared to specific genes?
#
# The PLIER model performs a matrix factorization of gene expression data to get two matrices: loadings (Z) and latent matrix (B). The loadings (Z) are constrained to aligned with curated pathways and gene sets specified by prior knowledge [Figure 1B of Taroni et. al.](https://www.cell.com/cell-systems/pdfExtended/S2405-4712(19)30119-X). This ensure that some but not all latent variables capture known biology. The way PLIER does this is by applying a penalty such that the individual latent variables represent a few gene sets in order to make the latent variables more interpretable. Ideally there would be one latent variable associated with one gene set unambiguously.
#
# While the PLIER model was trained on specific datasets, MultiPLIER extended this approach to all of recount2, where the latent variables should correspond to specific pathways or gene sets of interest. Therefore, we will look at the coverage of generic genes versus other genes across these MultiPLIER latent variables, which represent biological patterns.
#
# **Definitions:**
# * Generic genes: Are genes that are consistently differentially expressed across multiple simulated experiments.
#
# * Other genes: These are all other non-generic genes. These genes include those that are not consistently differentially expressed across simulated experiments - i.e. the genes are specifically changed in an experiment. It could also indicate genes that are consistently unchanged (i.e. housekeeping genes)
# +
# %load_ext autoreload
# %autoreload 2
import os
import random
import textwrap
import scipy
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
import rpy2.robjects as ro
from rpy2.robjects import pandas2ri
from rpy2.robjects.conversion import localconverter
from ponyo import utils
from generic_expression_patterns_modules import lv
# +
# Get data directory containing gene summary data
base_dir = os.path.abspath(os.path.join(os.getcwd(), "../"))
data_dir = os.path.join(base_dir, "human_general_analysis")
# Read in config variables
config_filename = os.path.abspath(
os.path.join(base_dir, "configs", "config_human_general.tsv")
)
params = utils.read_config(config_filename)
local_dir = params["local_dir"]
project_id = params["project_id"]
quantile_threshold = 0.98
# -
# Output file
nonzero_figure_filename = "nonzero_LV_coverage.svg"
highweight_figure_filename = "highweight_LV_coverage.svg"
# ## Load data
# Get gene summary file
summary_data_filename = os.path.join(data_dir, f"generic_gene_summary_{project_id}.tsv")
# +
# Load gene summary data
data = pd.read_csv(summary_data_filename, sep="\t", index_col=0, header=0)
# Check that genes are unique since we will be using them as dictionary keys below
assert data.shape[0] == len(data["Gene ID"].unique())
# -
# Load multiplier models
# Converted formatted pickle files (loaded using phenoplier environment) from
# https://github.com/greenelab/phenoplier/blob/master/nbs/01_preprocessing/005-multiplier_recount2_models.ipynb
# into .tsv files
multiplier_model_z = pd.read_csv(
"multiplier_model_z.tsv", sep="\t", index_col=0, header=0
)
# Get a rough sense for how many genes contribute to a given LV
# (i.e. how many genes have a value != 0 for a given LV)
# Notice that multiPLIER is a sparse model
(multiplier_model_z != 0).sum().sort_values(ascending=True)
# ## Get gene data
#
# Define generic genes based on simulated gene ranking. Refer to [figure](https://github.com/greenelab/generic-expression-patterns/blob/master/human_general_analysis/gene_ranking_log2FoldChange.svg) as a guide.
#
# **Definitions:**
# * Generic genes: `Percentile (simulated) >= 60`
#
# (Having a high rank indicates that these genes are consistently changed across simulated experiments.)
#
# * Other genes: `Percentile (simulated) < 60`
#
# (Having a lower rank indicates that these genes are not consistently changed across simulated experiments - i.e. the genes are specifically changed in an experiment. It could also indicate genes that are consistently unchanged.)
generic_threshold = 60
dict_genes = lv.get_generic_specific_genes(data, generic_threshold)
# +
# Check overlap between multiplier genes and our genes
multiplier_genes = list(multiplier_model_z.index)
our_genes = list(data.index)
shared_genes = set(our_genes).intersection(multiplier_genes)
print(len(our_genes))
print(len(shared_genes))
# -
# Drop gene ids not used in multiplier analysis
processed_dict_genes = lv.process_generic_specific_gene_lists(
dict_genes, multiplier_model_z
)
# Check numbers add up
assert len(shared_genes) == len(processed_dict_genes["generic"]) + len(
processed_dict_genes["other"]
)
# ## Get coverage of LVs
#
# For each gene (generic or other) we want to find:
# 1. The number of LVs that gene is present
# 2. The number of LVs that the gene contributes a lot to (i.e. the gene is highly weighted within that LV)
# ### Nonzero LV coverage
dict_nonzero_coverage = lv.get_nonzero_LV_coverage(
processed_dict_genes, multiplier_model_z
)
# Check genes mapped correctly
assert processed_dict_genes["generic"][0] in dict_nonzero_coverage["generic"].index
assert len(dict_nonzero_coverage["generic"]) == len(processed_dict_genes["generic"])
assert len(dict_nonzero_coverage["other"]) == len(processed_dict_genes["other"])
# ### High weight LV coverage
# Quick look at the distribution of gene weights per LV
sns.distplot(multiplier_model_z["LV3"], kde=False)
plt.yscale("log")
dict_highweight_coverage = lv.get_highweight_LV_coverage(
processed_dict_genes, multiplier_model_z, quantile_threshold
)
# Check genes mapped correctly
assert processed_dict_genes["generic"][0] in dict_highweight_coverage["generic"].index
assert len(dict_highweight_coverage["generic"]) == len(processed_dict_genes["generic"])
assert len(dict_highweight_coverage["other"]) == len(processed_dict_genes["other"])
# ### Assemble LV coverage and plot
# +
all_coverage = []
for gene_label in dict_genes.keys():
merged_df = pd.DataFrame(
dict_nonzero_coverage[gene_label], columns=["nonzero LV coverage"]
).merge(
pd.DataFrame(
dict_highweight_coverage[gene_label], columns=["highweight LV coverage"]
),
left_index=True,
right_index=True,
)
merged_df["gene type"] = gene_label
all_coverage.append(merged_df)
all_coverage_df = pd.concat(all_coverage)
# -
all_coverage_df = lv.assemble_coverage_df(
processed_dict_genes, dict_nonzero_coverage, dict_highweight_coverage
)
all_coverage_df.head()
# +
# Plot coverage distribution given list of generic coverage, specific coverage
nonzero_fig = sns.boxplot(
data=all_coverage_df,
x="gene type",
y="nonzero LV coverage",
notch=True,
palette=["#81448e", "lightgrey"],
)
# Manually add statistical annotations based on t-tests below
x1, x2 = 0, 1 # columns 'Sat' and 'Sun' (first column: 0, see plt.xticks())
y, h, col = all_coverage_df["nonzero LV coverage"].max() + 30, 30, "k"
plt.plot([x1, x1, x2, x2], [y, y + h, y + h, y], lw=1.5, c=col)
plt.text(
(x1 + x2) * 0.5,
y + h + 10,
"$P$-value = 0.239",
ha="center",
va="bottom",
color=col,
size=12,
)
nonzero_fig.set(ylim=(0, 800))
nonzero_fig.set_xlabel(None)
nonzero_fig.set_xticklabels(
["Common DEGs", "Other genes"], fontsize=14, fontname="Verdana"
)
nonzero_fig.set_ylabel(
textwrap.fill("No. of LVs (genes are present in)", width=30),
fontsize=14,
fontname="Verdana",
)
nonzero_fig.tick_params(labelsize=14)
nonzero_fig.set_title(
"Number of LVs genes are present in", fontsize=16, fontname="Verdana"
)
# +
# Plot coverage distribution given list of generic coverage, specific coverage
highweight_fig = sns.boxplot(
data=all_coverage_df,
x="gene type",
y="highweight LV coverage",
notch=True,
palette=["#81448e", "lightgrey"],
)
# Manually add statistical annotations based on t-tests below
x1, x2 = 0, 1 # columns 'Sat' and 'Sun' (first column: 0, see plt.xticks())
y, h, col = all_coverage_df["highweight LV coverage"].max() + 10, 10, "k"
plt.plot([x1, x1, x2, x2], [y, y + h, y + h, y], lw=1.5, c=col)
plt.text(
(x1 + x2) * 0.5,
y + h + 5,
"$P$-value = 6.31E-119",
ha="center",
va="bottom",
color=col,
size=12,
)
highweight_fig.set(ylim=(0, 150))
highweight_fig.set_xlabel(None)
highweight_fig.set_xticklabels(
["Common DEGs", "Other genes"], fontsize=14, fontname="Verdana"
)
highweight_fig.set_ylabel(
textwrap.fill(
"No. of LVs (genes contribute highly to)", width=30
),
fontsize=14,
fontname="Verdana",
)
highweight_fig.tick_params(labelsize=14)
highweight_fig.set_title(
"Number of LVs genes contribute highly to", fontsize=16, fontname="Verdana"
)
# -
# ## Calculate statistics
# * Is the reduction in generic coverage significant?
# * Is the difference between generic versus other genes signficant?
# +
# Test: mean number of LVs generic genes present in vs mean number of LVs that generic gene is high weight in
# (compare two blue boxes between plots)
generic_nonzero = all_coverage_df[all_coverage_df["gene type"] == "generic"][
"nonzero LV coverage"
].values
generic_highweight = all_coverage_df[all_coverage_df["gene type"] == "generic"][
"highweight LV coverage"
].values
(stats, pvalue) = scipy.stats.ttest_ind(generic_nonzero, generic_highweight)
print(pvalue)
# +
# Test: mean number of LVs generic genes present in vs mean number of LVs other genes high weight in
# (compare blue and grey boxes in high weight plot)
other_highweight = all_coverage_df[all_coverage_df["gene type"] == "other"][
"highweight LV coverage"
].values
generic_highweight = all_coverage_df[all_coverage_df["gene type"] == "generic"][
"highweight LV coverage"
].values
(stats, pvalue) = scipy.stats.ttest_ind(other_highweight, generic_highweight)
print(pvalue)
# +
# Check that coverage of other and generic genes across all LVs is NOT signficantly different
# (compare blue and grey boxes in nonzero weight plot)
other_nonzero = all_coverage_df[all_coverage_df["gene type"] == "other"][
"nonzero LV coverage"
].values
generic_nonzero = all_coverage_df[all_coverage_df["gene type"] == "generic"][
"nonzero LV coverage"
].values
(stats, pvalue) = scipy.stats.ttest_ind(other_nonzero, generic_nonzero)
print(pvalue)
# -
# ## Get LVs that generic genes are highly weighted in
#
# Since we are using quantiles to get high weight genes per LV, each LV has the same number of high weight genes. For each set of high weight genes, we will get the proportion of generic vs other genes. We will select the LVs that have a high proportion of generic genes to examine.
# Get proportion of generic genes per LV
prop_highweight_generic_dict = lv.get_prop_highweight_generic_genes(
processed_dict_genes, multiplier_model_z, quantile_threshold
)
# Return selected rows from summary matrix
multiplier_model_summary = pd.read_csv(
"multiplier_model_summary.tsv", sep="\t", index_col=0, header=0
)
lv.create_LV_df(
prop_highweight_generic_dict,
multiplier_model_summary,
0.5,
"Generic_LV_summary_table.tsv",
)
# Plot distribution of weights for these nodes
node = "LV61"
lv.plot_dist_weights(
node,
multiplier_model_z,
shared_genes,
20,
all_coverage_df,
f"weight_dist_{node}.svg",
)
# ## Save
# +
# Save plot
nonzero_fig.figure.savefig(
nonzero_figure_filename,
format="svg",
bbox_inches="tight",
transparent=True,
pad_inches=0,
dpi=300,
)
# Save plot
highweight_fig.figure.savefig(
highweight_figure_filename,
format="svg",
bbox_inches="tight",
transparent=True,
pad_inches=0,
dpi=300,
)
# -
# **Takeaway:**
# * In the first nonzero boxplot, generic and other genes are present in a similar number of LVs. This isn't surprising since the number of genes that contribute to each LV is <1000.
# * In the second highweight boxplot, other genes are highly weighted in more LVs compared to generic genes. This would indicate that generic genes contribute alot to few LVs.
#
# This is the opposite trend found using [_P. aeruginosa_ data](1_get_eADAGE_LV_coverage.ipynb). Perhaps this indicates that generic genes have different behavior/roles depending on the organism. In humans, perhaps these generic genes are related to a few hyper-responsive pathways, whereas in _P. aeruginosa_ perhaps generic genes are associated with many pathways, acting as *gene hubs*.
#
# * There are a number of LVs that contain a high proportion of generic genes can be found in [table](Generic_LV_summary_table.tsv). By quick visual inspection, it looks like many LVs are associated with immune response, signaling and metabolism. Which are consistent with the hypothesis that these generic genes are related to hyper-responsive pathways.