dspin 1.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.
- dspin/__init__.py +21 -0
- dspin/annotate.py +461 -0
- dspin/compute.py +1690 -0
- dspin/dspin.py +869 -0
- dspin/gpt.py +65 -0
- dspin/metadata_drug.py +13 -0
- dspin/plot.py +776 -0
- dspin-1.5.0.dist-info/METADATA +49 -0
- dspin-1.5.0.dist-info/RECORD +12 -0
- dspin-1.5.0.dist-info/WHEEL +5 -0
- dspin-1.5.0.dist-info/licenses/LICENSE +201 -0
- dspin-1.5.0.dist-info/top_level.txt +1 -0
dspin/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
from . import annotate
|
|
3
|
+
from . import compute
|
|
4
|
+
from . import plot
|
|
5
|
+
from . import dspin
|
|
6
|
+
from importlib.metadata import version
|
|
7
|
+
|
|
8
|
+
# Import main classes for easy access
|
|
9
|
+
from .dspin import DSPIN, GeneDSPIN, ProgramDSPIN, AbstractDSPIN
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'DSPIN',
|
|
13
|
+
'GeneDSPIN',
|
|
14
|
+
'ProgramDSPIN',
|
|
15
|
+
'AbstractDSPIN',
|
|
16
|
+
'annotate',
|
|
17
|
+
'compute',
|
|
18
|
+
'plot'
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
__version__ = version("DSPIN")
|
dspin/annotate.py
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
# -*-coding:utf-8 -*-
|
|
2
|
+
'''
|
|
3
|
+
@Time : 2024/04/02 14:29
|
|
4
|
+
@Author : Jialong Jiang
|
|
5
|
+
'''
|
|
6
|
+
|
|
7
|
+
import csv
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from typing import List
|
|
10
|
+
import numpy as np
|
|
11
|
+
import os
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def automatic_gpt_annotation(data_folder, file_name, gpt_model="gpt-4-0125-preview", temp=0.0, max_gene=30):
|
|
16
|
+
"""
|
|
17
|
+
Main function to automate GPT annotation for a set of genes.
|
|
18
|
+
"""
|
|
19
|
+
# Validate input file extension
|
|
20
|
+
assert file_name.endswith('.csv'), 'Input file must be a CSV file'
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import openai
|
|
24
|
+
except ImportError:
|
|
25
|
+
raise ImportError('Please install the required packages: openai')
|
|
26
|
+
|
|
27
|
+
# Setup and read data
|
|
28
|
+
gene_df, all_gene = read_data(data_folder, file_name)
|
|
29
|
+
num_list = gene_df.shape[1]
|
|
30
|
+
|
|
31
|
+
output_dir = data_folder + file_name.replace('.csv', '_gpt/')
|
|
32
|
+
context = "You are an efficient and insightful assistant to a molecular biologist"
|
|
33
|
+
|
|
34
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
35
|
+
|
|
36
|
+
for ii in range(num_list):
|
|
37
|
+
|
|
38
|
+
cur_gene_list = gene_df[str(ii)]
|
|
39
|
+
cur_gene_list = cur_gene_list[~cur_gene_list.isna()][: max_gene]
|
|
40
|
+
cur_prompt = make_user_prompt_with_score(cur_gene_list)
|
|
41
|
+
|
|
42
|
+
response, fingerprint = openai_chat(
|
|
43
|
+
context, cur_prompt, gpt_model, temp)
|
|
44
|
+
|
|
45
|
+
output_file = output_dir + f'program_{ii}.txt'
|
|
46
|
+
|
|
47
|
+
with open(output_file, 'w') as f:
|
|
48
|
+
f.write(response)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def automatic_david_annotation(data_folder, file_name, token_name, species='human'):
|
|
52
|
+
"""
|
|
53
|
+
Main function to automate DAVID annotation for a set of genes.
|
|
54
|
+
"""
|
|
55
|
+
# Validate input file extension
|
|
56
|
+
assert file_name.endswith('.csv'), 'Input file must be a CSV file'
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
import mygene
|
|
60
|
+
from suds.client import Client
|
|
61
|
+
import xlsxwriter
|
|
62
|
+
except ImportError:
|
|
63
|
+
raise ImportError(
|
|
64
|
+
'Please install the required packages: mygene, suds, xlsxwriter')
|
|
65
|
+
|
|
66
|
+
# Setup and read data
|
|
67
|
+
all_onmf_df, all_gene = read_data(data_folder, file_name)
|
|
68
|
+
num_list = all_onmf_df.shape[1]
|
|
69
|
+
|
|
70
|
+
# Gene ID conversion
|
|
71
|
+
gene_id_map = convert_gene_ids(all_gene, species)
|
|
72
|
+
|
|
73
|
+
# Setup DAVID Web Service client
|
|
74
|
+
client = setup_david_client(token_name)
|
|
75
|
+
|
|
76
|
+
# Process each gene list
|
|
77
|
+
process_gene_lists(data_folder, file_name,
|
|
78
|
+
all_onmf_df, gene_id_map, client)
|
|
79
|
+
|
|
80
|
+
gene_id_map_reverse = {v: k for k, v in gene_id_map.items()}
|
|
81
|
+
|
|
82
|
+
# Save results to Excel
|
|
83
|
+
save_to_excel(data_folder, file_name,
|
|
84
|
+
gene_id_map_reverse, all_onmf_df.columns.tolist(), full=False)
|
|
85
|
+
save_to_excel(data_folder, file_name,
|
|
86
|
+
gene_id_map_reverse, all_onmf_df.columns.tolist(), full=True)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def make_user_prompt_with_score(genes, feature_df=[], direct=False, customized_prompt=None):
|
|
90
|
+
"""
|
|
91
|
+
This function is adapted from "Evaluation of large language models for discovery of gene set function"
|
|
92
|
+
Arxiv: arxiv.org/abs/2309.04019v2
|
|
93
|
+
GitHub: github.com/idekerlab/llm_evaluation_for_gene_set_interpretation
|
|
94
|
+
|
|
95
|
+
Create a "one shot" ChatGPT prompt based on the list of genes.
|
|
96
|
+
:return: A string containing the ChatGPT prompt text
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
general_analysis_instructions = """
|
|
100
|
+
Be concise, do not use unnecessary words.
|
|
101
|
+
Be factual, do not editorialize.
|
|
102
|
+
Be specific, avoid overly general statements such as 'the proteins are involved in various cellular processes'.
|
|
103
|
+
Avoid listing facts about individual proteins. Instead, try to group proteins with similar functions and discuss their interplay, synergistyc or antagonistic effects and functional integration within the system.
|
|
104
|
+
Also avoid choosing generic process names such as 'Cellular Signaling and Regulation'.
|
|
105
|
+
If you cannot identify a prominent biological process for the proteins in the system, I want you to communicate this in you analysis and name the process: “System of unrelated proteins”. Provide a score of 0.00 for a "System of unrelated proteins".
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
task_instructions = """
|
|
109
|
+
Write a critical analysis of the biological processes performed by this system of interacting proteins.
|
|
110
|
+
Base your analysis on prior knowledge available in your training data.
|
|
111
|
+
After completing your analysis, propose a brief and detailed name for the most prominent biological process performed by the system.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
score_instructions = """
|
|
115
|
+
After completing your analysis, please also assign a confidence score to the process name you selected.
|
|
116
|
+
This score should follow the name in parentheses and range from 0.00 to 1.00. A score of 0.00 indicates the lowest confidence,
|
|
117
|
+
while 1.00 reflects the highest confidence. This score helps gauge how accurately the chosen name represents the functions and activities
|
|
118
|
+
within the system of interacting proteins. When determining your score, consider the proportion of genes in the protein system that participate
|
|
119
|
+
in the identified biological process. For instance, if you select "Ribosome biogenesis" as the process name but only a few genes in the system
|
|
120
|
+
contribute to this process, the score should be lower compared to a scenario where a majority of the genes are involved in "Ribosome biogenesis".
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
direct_instructions = """
|
|
124
|
+
Propose a name and provide analysis for the following gene set.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
format_placeholder = """
|
|
128
|
+
Put your chosen name at the top of the analysis as 'Process: <name>’.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
example_analysis = """
|
|
132
|
+
To help you in your work, I am providing an example system of interacting proteins and the corresponding example analysis output.
|
|
133
|
+
|
|
134
|
+
The example system of interacting proteins is:
|
|
135
|
+
PDX1, SLC2A2, NKX6-1, GLP1, GCG.
|
|
136
|
+
|
|
137
|
+
The example analysis output is:
|
|
138
|
+
|
|
139
|
+
Process: Pancreatic development and glucose homeostasis (0.96)
|
|
140
|
+
|
|
141
|
+
1. PDX1 is a homeodomain transcription factor involved in the specification of the early pancreatic epithelium and its subsequent differentiation.
|
|
142
|
+
It activates the transcription of several genes including insulin, somatostatin, glucokinase and glucose transporter type 2.
|
|
143
|
+
It is essential for maintenance of the normal hormone-producing phenotype in the pancreatic beta-cell.
|
|
144
|
+
In pancreatic acinar cells, forms a complex with PBX1b and MEIS2b and mediates the activation of the ELA1 enhancer.
|
|
145
|
+
|
|
146
|
+
2. NKX6-1 is also a transcription factor involved in the development of pancreatic beta-cells during the secondary transition.
|
|
147
|
+
Together with NKX2-2 and IRX3, controls the generation of motor neurons in the neural tube and belongs to the neural progenitor
|
|
148
|
+
factors induced by Sonic Hedgehog (SHH) signals.
|
|
149
|
+
|
|
150
|
+
3.GCG and GLP1, respectively glucagon and glucagon-like peptide 1, are involved in glucose metabolism and homeostasis.
|
|
151
|
+
GCG raises blood glucose levels by promoting gluconeogenesis and is the counter regulatory hormone of Insulin.
|
|
152
|
+
GLP1 is a potent stimulator of Glucose-Induced Insulin Secretion (GSIS). Plays roles in gastric motility and suppresses blood glucagon levels.
|
|
153
|
+
Promotes growth of the intestinal epithelium and pancreatic islet mass both by islet neogenesis and islet cell proliferation.
|
|
154
|
+
|
|
155
|
+
4. SLC2A2, also known as GLUT2, is a facilitative hexose transporter. In hepatocytes, it mediates bi-directional transport of glucose accross the plasma membranes,
|
|
156
|
+
while in the pancreatic beta-cell, it is the main transporter responsible for glucose uptake and part of the cell's glucose-sensing mechanism.
|
|
157
|
+
It is involved in glucose transport in the small intestine and kidney too.
|
|
158
|
+
|
|
159
|
+
To summarize, the genes in this set are involved in the specification, differentiation, growth and functionality of the pancreas,
|
|
160
|
+
with a particular emphasis on the pancreatic beta-cell. Particularly, the architecture of the pancreatic islet ensures proper glucose sensing
|
|
161
|
+
and homeostasis via a number of different hormones and receptors that can elicit both synergistic and antagonistic effects in the pancreas itself and other peripheral tissues.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
if direct == True:
|
|
165
|
+
prompt_text = direct_instructions
|
|
166
|
+
prompt_text += format_placeholder
|
|
167
|
+
elif customized_prompt:
|
|
168
|
+
prompt_text = customized_prompt
|
|
169
|
+
prompt_text += format_placeholder
|
|
170
|
+
else:
|
|
171
|
+
prompt_text = task_instructions
|
|
172
|
+
prompt_text += score_instructions
|
|
173
|
+
prompt_text += format_placeholder
|
|
174
|
+
prompt_text += general_analysis_instructions
|
|
175
|
+
prompt_text += example_analysis
|
|
176
|
+
|
|
177
|
+
prompt_text += "\n\nHere are the interacting proteins:\n"
|
|
178
|
+
prompt_text += f'\nProteins: '
|
|
179
|
+
prompt_text += ", ".join(genes) + ".\n\n"
|
|
180
|
+
|
|
181
|
+
if feature_df:
|
|
182
|
+
prompt_text += "\n\nHere are the gene features:\n"
|
|
183
|
+
prompt_text = add_gene_feature_summary(prompt_text, feature_df)
|
|
184
|
+
|
|
185
|
+
return prompt_text
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def openai_chat(context, prompt, model, temperature, seed: int = None):
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
import openai
|
|
192
|
+
except ImportError:
|
|
193
|
+
raise ImportError('Please install the required packages: openai')
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
openai.api_key = os.environ['OPENAI_API_KEY']
|
|
197
|
+
except:
|
|
198
|
+
raise Exception(
|
|
199
|
+
"API key not found. Please set the OPENAI_API_KEY environment variable.")
|
|
200
|
+
|
|
201
|
+
backoff_time = 10 # Start backoff time at 10 second
|
|
202
|
+
max_tokens = 2000
|
|
203
|
+
|
|
204
|
+
response = openai.chat.completions.create(
|
|
205
|
+
model=model,
|
|
206
|
+
messages=[{"role": "system", "content": context},
|
|
207
|
+
{"role": "user", "content": prompt}],
|
|
208
|
+
max_tokens=max_tokens,
|
|
209
|
+
n=1,
|
|
210
|
+
stop=None,
|
|
211
|
+
seed=seed,
|
|
212
|
+
temperature=temperature,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# print(response)
|
|
216
|
+
# tokens_used = response["usage"]["total_tokens"]
|
|
217
|
+
tokens_used = response.usage.total_tokens
|
|
218
|
+
response_content = response.choices[0].message.content
|
|
219
|
+
system_fingerprint = response.system_fingerprint
|
|
220
|
+
|
|
221
|
+
return response_content, system_fingerprint
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def read_data(data_folder, file_name):
|
|
225
|
+
"""
|
|
226
|
+
Read gene data from a CSV file and return a unique list of gene names.
|
|
227
|
+
|
|
228
|
+
Parameters:
|
|
229
|
+
data_folder (str): The folder where the data file is located.
|
|
230
|
+
file_name (str): The name of the CSV file containing gene data.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
list: A unique list of gene names.
|
|
234
|
+
"""
|
|
235
|
+
program_file_name = os.path.join(data_folder, file_name)
|
|
236
|
+
gene_df = pd.read_csv(program_file_name)
|
|
237
|
+
gene_list = gene_df.values.flatten().astype('str')
|
|
238
|
+
unique_genes = list(np.unique(gene_list))
|
|
239
|
+
if 'nan' in unique_genes:
|
|
240
|
+
unique_genes.remove('nan')
|
|
241
|
+
return gene_df, unique_genes
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def convert_gene_ids(all_gene, species):
|
|
245
|
+
"""
|
|
246
|
+
Convert gene symbols to ENSEMBL IDs using the MyGeneInfo service.
|
|
247
|
+
|
|
248
|
+
Parameters:
|
|
249
|
+
all_gene (list): A list of gene symbols.
|
|
250
|
+
species (str): The species for gene ID conversion (default is 'human').
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
dict: A dictionary mapping gene symbols to ENSEMBL IDs.
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
import mygene
|
|
258
|
+
except ImportError:
|
|
259
|
+
raise ImportError('Please install the required packages: mygene')
|
|
260
|
+
|
|
261
|
+
mg = mygene.MyGeneInfo()
|
|
262
|
+
result = mg.querymany(all_gene, scopes="symbol",
|
|
263
|
+
fields="ensembl.gene", species=species)
|
|
264
|
+
gene_id_map = {}
|
|
265
|
+
for gene_info in result:
|
|
266
|
+
if 'ensembl' in gene_info:
|
|
267
|
+
ensembl_info = gene_info['ensembl']
|
|
268
|
+
ensembl_id = ensembl_info['gene'] if isinstance(
|
|
269
|
+
ensembl_info, dict) else ensembl_info[0]['gene']
|
|
270
|
+
gene_id_map[gene_info['query']] = ensembl_id
|
|
271
|
+
return gene_id_map
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def setup_david_client(token_name):
|
|
275
|
+
"""
|
|
276
|
+
Setup and authenticate with the DAVID Web Service.
|
|
277
|
+
|
|
278
|
+
Parameters:
|
|
279
|
+
token_name (str): The authentication token for DAVID Web Service.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Client: An authenticated suds client for DAVID Web Service.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
from suds.client import Client
|
|
286
|
+
|
|
287
|
+
url = 'https://davidbioinformatics.nih.gov/webservice/services/DAVIDWebService?wsdl'
|
|
288
|
+
client = Client(url)
|
|
289
|
+
client.wsdl.services[0].setlocation(
|
|
290
|
+
'https://davidbioinformatics.nih.gov/webservice/services/DAVIDWebService.DAVIDWebServiceHttpSoap11Endpoint/')
|
|
291
|
+
client.service.authenticate(token_name)
|
|
292
|
+
return client
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def parse_david_output(term_clustering_report, save_path):
|
|
296
|
+
"""
|
|
297
|
+
Parse the output from DAVID and save to a file.
|
|
298
|
+
|
|
299
|
+
Parameters:
|
|
300
|
+
term_clustering_report: The term clustering report from DAVID Web Service.
|
|
301
|
+
save_path (str): The file path to save the parsed output.
|
|
302
|
+
"""
|
|
303
|
+
total_clusters = len(term_clustering_report)
|
|
304
|
+
print('Total clusters:', total_clusters)
|
|
305
|
+
with open(save_path, 'w') as f_out:
|
|
306
|
+
for i, cluster_record in enumerate(term_clustering_report, start=1):
|
|
307
|
+
enrichment_score = cluster_record.score
|
|
308
|
+
f_out.write(
|
|
309
|
+
f'Annotation Cluster {i}\tEnrichmentScore:{enrichment_score}\n')
|
|
310
|
+
headers = ['Category', 'Term', 'Count', '%', 'Pvalue', 'Genes', 'List Total',
|
|
311
|
+
'Pop Hits', 'Pop Total', 'Fold Enrichment', 'Bonferroni', 'Benjamini', 'FDR']
|
|
312
|
+
f_out.write('\t'.join(headers) + '\n')
|
|
313
|
+
for chart_record in cluster_record.simpleChartRecords:
|
|
314
|
+
row = [
|
|
315
|
+
chart_record.categoryName,
|
|
316
|
+
chart_record.termName,
|
|
317
|
+
str(chart_record.listHits),
|
|
318
|
+
str(chart_record.percent),
|
|
319
|
+
str(chart_record.ease),
|
|
320
|
+
chart_record.geneIds,
|
|
321
|
+
str(chart_record.listTotals),
|
|
322
|
+
str(chart_record.popHits),
|
|
323
|
+
str(chart_record.popTotals),
|
|
324
|
+
str(chart_record.foldEnrichment),
|
|
325
|
+
str(chart_record.bonferroni),
|
|
326
|
+
str(chart_record.benjamini),
|
|
327
|
+
str(chart_record.afdr)
|
|
328
|
+
]
|
|
329
|
+
f_out.write('\t'.join(row) + '\n')
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def process_gene_lists(data_folder, file_name, all_onmf_df, gene_id_map, client):
|
|
333
|
+
"""
|
|
334
|
+
Process each gene list, communicate with DAVID, and parse the results.
|
|
335
|
+
|
|
336
|
+
Parameters:
|
|
337
|
+
data_folder (str): The folder where the data files are located.
|
|
338
|
+
file_name (str): The name of the original CSV file containing gene data.
|
|
339
|
+
all_onmf_df (pd.DataFrame): A DataFrame containing gene lists.
|
|
340
|
+
gene_id_map (dict): A dictionary mapping gene symbols to ENSEMBL IDs.
|
|
341
|
+
client (Client): An authenticated suds client for DAVID Web Service.
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
def map_gene_ids(gene_id_map, gene_list):
|
|
345
|
+
return gene_list.map(gene_id_map).fillna(gene_list)
|
|
346
|
+
|
|
347
|
+
num_lists = all_onmf_df.shape[1]
|
|
348
|
+
save_folder = os.path.join(
|
|
349
|
+
data_folder, file_name.replace('.csv', '_david_results/'))
|
|
350
|
+
os.makedirs(save_folder, exist_ok=True)
|
|
351
|
+
|
|
352
|
+
overlap = 3
|
|
353
|
+
initialSeed = 3
|
|
354
|
+
finalSeed = 3
|
|
355
|
+
linkage = 0.5
|
|
356
|
+
kappa = 50
|
|
357
|
+
|
|
358
|
+
for i in range(num_lists):
|
|
359
|
+
cur_gene_list = all_onmf_df.iloc[:, i].dropna()
|
|
360
|
+
cur_gene_list = map_gene_ids(gene_id_map, cur_gene_list)
|
|
361
|
+
save_path = os.path.join(save_folder, f'list_{all_onmf_df.columns[i]}.csv')
|
|
362
|
+
|
|
363
|
+
input_ids = ','.join(cur_gene_list)
|
|
364
|
+
id_type = 'ENSEMBL_GENE_ID'
|
|
365
|
+
list_name = f'List_{i}'
|
|
366
|
+
list_type = 0
|
|
367
|
+
|
|
368
|
+
client.service.addList(input_ids, id_type, list_name, list_type)
|
|
369
|
+
term_clustering_report = client.service.getTermClusterReport(
|
|
370
|
+
overlap, initialSeed, finalSeed, linkage, kappa)
|
|
371
|
+
|
|
372
|
+
print(f'Gene list {all_onmf_df.columns[i]} ', end='\t')
|
|
373
|
+
# print(term_clustering_report)
|
|
374
|
+
if term_clustering_report is None:
|
|
375
|
+
raise Exception(
|
|
376
|
+
'No results returned from DAVID, possibly wrong token name.')
|
|
377
|
+
|
|
378
|
+
parse_david_output(term_clustering_report, save_path)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def save_to_excel(data_folder, file_name, gene_id_map_reverse, list_name, full=True):
|
|
382
|
+
"""
|
|
383
|
+
Save the parsed DAVID output to an Excel file, with an option to include full or filtered results.
|
|
384
|
+
|
|
385
|
+
Parameters:
|
|
386
|
+
data_folder (str): The folder where the data files are located.
|
|
387
|
+
file_name (str): The name of the original CSV file containing gene data.
|
|
388
|
+
gene_id_map_reverse (dict): A dictionary mapping ENSEMBL IDs to gene symbols.
|
|
389
|
+
list_name (list): A list of gene list names.
|
|
390
|
+
full (bool): Whether to save the full results. If False, only results with Pvalue <= 0.05 are saved.
|
|
391
|
+
"""
|
|
392
|
+
|
|
393
|
+
def map_gene_id_reverse(gene_name):
|
|
394
|
+
try:
|
|
395
|
+
return gene_id_map_reverse[gene_name]
|
|
396
|
+
except:
|
|
397
|
+
return gene_name
|
|
398
|
+
|
|
399
|
+
def map_gene_id_reverse_list(gene_list):
|
|
400
|
+
try:
|
|
401
|
+
return ','.join([map_gene_id_reverse(gene) for gene in gene_list.split(', ')])
|
|
402
|
+
except:
|
|
403
|
+
return gene_list
|
|
404
|
+
|
|
405
|
+
def format_pvalue(pvalue):
|
|
406
|
+
try:
|
|
407
|
+
if isinstance(pvalue, str):
|
|
408
|
+
return '%.1e' % float(pvalue)
|
|
409
|
+
else:
|
|
410
|
+
return pvalue
|
|
411
|
+
except:
|
|
412
|
+
return pvalue
|
|
413
|
+
|
|
414
|
+
def format_percent(value):
|
|
415
|
+
try:
|
|
416
|
+
if isinstance(value, str):
|
|
417
|
+
return int(float(value))
|
|
418
|
+
else:
|
|
419
|
+
return value
|
|
420
|
+
except:
|
|
421
|
+
return value
|
|
422
|
+
|
|
423
|
+
def filt_fun(pvalue):
|
|
424
|
+
try:
|
|
425
|
+
return float(pvalue) > 0.05
|
|
426
|
+
except:
|
|
427
|
+
return False
|
|
428
|
+
|
|
429
|
+
save_folder = os.path.join(
|
|
430
|
+
data_folder, file_name.replace('.csv', '_david_results/'))
|
|
431
|
+
xlsx_name = file_name.replace(
|
|
432
|
+
'.csv', '_david_full.xlsx') if full else file_name.replace('.csv', '_david.xlsx')
|
|
433
|
+
|
|
434
|
+
with pd.ExcelWriter(data_folder + xlsx_name) as writer:
|
|
435
|
+
for lname in list_name:
|
|
436
|
+
cur_pd = pd.read_csv(save_folder + f'list_{lname}.csv', sep='\t', names=[
|
|
437
|
+
'Category', 'Term', 'Count', '%', 'Pvalue', 'Genes', 'List Total', 'Pop Hits', 'Pop Total', 'Fold Enrichment', 'Bonferroni', 'Benjamini', 'FDR'])
|
|
438
|
+
filt1 = cur_pd['Category'] == 'UP_SEQ_FEATURE'
|
|
439
|
+
filt2 = cur_pd['Pvalue'].apply(filt_fun)
|
|
440
|
+
if not full:
|
|
441
|
+
cur_pd = cur_pd[~(filt1 | filt2)]
|
|
442
|
+
|
|
443
|
+
cur_pd['Term'] = cur_pd['Term'].apply(lambda x: x.split('~')[- 1])
|
|
444
|
+
cur_pd['Genes'] = cur_pd['Genes'].apply(map_gene_id_reverse_list)
|
|
445
|
+
cur_pd['Pvalue'] = cur_pd['Pvalue'].apply(format_pvalue)
|
|
446
|
+
cur_pd['%'] = cur_pd['%'].apply(format_percent)
|
|
447
|
+
|
|
448
|
+
ind_insert = np.where(
|
|
449
|
+
cur_pd['Category'].str.startswith('Annotation Cluster'))[0]
|
|
450
|
+
if len(ind_insert) > 1:
|
|
451
|
+
for kk, ind in enumerate(ind_insert[1:]):
|
|
452
|
+
cur_pd = pd.DataFrame(np.insert(
|
|
453
|
+
cur_pd.values, ind + kk, values=[" "] * len(cur_pd.columns), axis=0), columns=cur_pd.columns)
|
|
454
|
+
|
|
455
|
+
sheet_name = lname
|
|
456
|
+
cur_pd.to_excel(writer, sheet_name=sheet_name, index=False)
|
|
457
|
+
writer.sheets[sheet_name].set_column(0, 0, 25)
|
|
458
|
+
writer.sheets[sheet_name].set_column(1, 1, 50)
|
|
459
|
+
writer.sheets[sheet_name].set_column(2, 2, 5)
|
|
460
|
+
writer.sheets[sheet_name].set_column(3, 3, 5)
|
|
461
|
+
writer.sheets[sheet_name].set_column(5, 5, 100)
|