Source-only snapshot of the cluster branch for WES execution. Large reference files (HPA/MANE/ensemble FASTA, model weights, ~597 MB) are omitted: they are baked into the container images at build time and mounted from the dreamdock-data PVC at runtime, and exceed the Gitea request size limit. Pipeline entry point is main.nf, which orchestrates the biotransformer, conplex and tissue modules as a single workflow. Ligand inputs are read from the eureka workspace; protein_zarr and chembl_db come from the dreamdock-data PVC.
239 lines
12 KiB
Python
239 lines
12 KiB
Python
import pandas as pd
|
|
import numpy as np
|
|
import argparse
|
|
|
|
#get data framw with ENST ids and tissue specific expression
|
|
human_protein_atlas = '/home/omic/HPA_normal_ihc_data.tsv'
|
|
mane = '/home/omic/MANE_all_transcipts.csv'
|
|
|
|
#extrect enst with hight expression per tissue and match to enst ids
|
|
HPA_normal_ihc_data = pd.read_csv(human_protein_atlas, sep = '\t')
|
|
MANE_all_transcipts = pd.read_csv(mane)
|
|
|
|
#change name to merge
|
|
HPA_normal_ihc_data = HPA_normal_ihc_data.rename({'Gene':'Ensembl_Gene'}, axis=1)
|
|
|
|
#merge dfs
|
|
HPA_normal_ihc_data = MANE_all_transcipts.merge(HPA_normal_ihc_data, left_on='Ensembl_Gene', right_on='Ensembl_Gene')
|
|
|
|
#drop not needed colums
|
|
HPA_normal_ihc_data = HPA_normal_ihc_data.drop(['Unnamed: 0', 'seq', 'Ensembl_Gene'], axis=1)
|
|
|
|
#get only hight expressed proteins
|
|
HPA_normal_hight = HPA_normal_ihc_data[HPA_normal_ihc_data['Level'] == 'High']
|
|
|
|
def get_n_metabolites(file):
|
|
"""
|
|
Get number of metabolites for a drug
|
|
input metabolite file from GET_FINAL_METABOLITES
|
|
"""
|
|
with open(file, 'r') as f:
|
|
first_line = f.readline().strip()
|
|
if first_line == 'NO_METABOLITES' or first_line == '':
|
|
return 0
|
|
metabol = pd.read_csv(file, sep = '\t', header=None)
|
|
return metabol.shape[0]
|
|
|
|
def get_conplex_metric(file_drug, file_all_interactions, target):
|
|
"""
|
|
Get merrics of interest in dataframe and a list of ENTSs with durg/metabolite interactions
|
|
with high expression based on Human Protein Atlas
|
|
input files from CONPLEX (first for drug interaction only, second for all interaction above threshold)
|
|
and traget ids from pipeline input csv file
|
|
"""
|
|
#drug target interaction
|
|
drug_interactions = pd.read_csv(file_drug, sep = '\t')
|
|
|
|
#drug target protein ents
|
|
ensts = target
|
|
scors = []
|
|
for enst in ensts:
|
|
scor = drug_interactions[drug_interactions['Transcript'] == enst]['Score']
|
|
scors.append(scor)
|
|
|
|
# mean_drug_scor = avg(scors)
|
|
mean_drug_scor = 0
|
|
max_drug_scor = 0
|
|
if len(scors) > 0:
|
|
mean_drug_scor = pd.concat(scors).mean() # 0 if empty
|
|
max_drug_scor = pd.concat(scors).max() # 0 if empty
|
|
|
|
#drug/metabolite target interaction — only 'transcipt' and 'conplex_score' are
|
|
# referenced below (the 'smile' and 'drug/metabolite' columns are the bulk of the
|
|
# 5+ GB file). Skipping them cuts memory ~10x and prevents OOM in 5 GB pods.
|
|
drug_interactions = pd.read_csv(file_all_interactions, sep = '\t', usecols=['transcipt', 'conplex_score'])
|
|
n_significant_int = len(drug_interactions)
|
|
|
|
# Handle empty DataFrame case
|
|
if n_significant_int == 0:
|
|
df = pd.DataFrame([max_drug_scor, mean_drug_scor, np.nan, np.nan, 0, 0, 0, 0, np.nan]).T.rename(
|
|
{0:'max_drug_scor',1:'mean_drug_scor',2:'max_target_scor',3:'mean_target_scor',
|
|
4:'n_significant_int',5:'n_unique_proteins_above_target_interaction',
|
|
6:'n_interactions_above_target_interaction',7:'n_enst_with_hight_expression',
|
|
8:'mean_scor_with_hight_expression'}, axis = 1)
|
|
return df, [], drug_interactions
|
|
|
|
#drug target protein ents
|
|
ensts = target
|
|
scors = []
|
|
for enst in ensts:
|
|
scor = drug_interactions[drug_interactions['transcipt'] == enst]['conplex_score']
|
|
scors.append(scor)
|
|
|
|
mean_target_scor = 0
|
|
max_target_scor = 0
|
|
if len(scors) > 0:
|
|
mean_target_scor = pd.concat(scors).mean() # 0 if empty
|
|
max_target_scor = pd.concat(scors).max() # 0 if empty
|
|
|
|
if np.isnan(max_target_scor):
|
|
pass
|
|
else:
|
|
drug_interactions = drug_interactions[drug_interactions['conplex_score'] >= max_target_scor].sort_values('conplex_score',ascending=False)
|
|
|
|
n_unique_proteins_above_target_interaction = pd.unique(drug_interactions['transcipt']).shape[0]
|
|
n_interactions_above_target_interaction = drug_interactions.shape[0]
|
|
|
|
uniq_enst = pd.unique(drug_interactions['transcipt'])
|
|
|
|
#get only ensts with hight expression
|
|
uniq_enst_hight = list(set(pd.unique(HPA_normal_hight['transcipt'])).intersection(uniq_enst))
|
|
enst_with_hight_expression = len(uniq_enst_hight)
|
|
|
|
#sort based on a conplex score
|
|
drug_interactions = drug_interactions[[i in uniq_enst_hight for i in drug_interactions['transcipt']]]
|
|
|
|
if len(drug_interactions) > 0:
|
|
mean_scor_with_hight_expression = drug_interactions['conplex_score'].mean()
|
|
hight_expression_enst_per_drug = pd.unique(drug_interactions['transcipt'])
|
|
else:
|
|
mean_scor_with_hight_expression = np.nan
|
|
hight_expression_enst_per_drug = []
|
|
|
|
df = pd.DataFrame([max_drug_scor, mean_drug_scor, max_target_scor, mean_target_scor, n_significant_int,n_unique_proteins_above_target_interaction,n_interactions_above_target_interaction,enst_with_hight_expression, mean_scor_with_hight_expression]).T.rename({0:'max_drug_scor',1:'mean_drug_scor',2:'max_target_scor',3:'mean_target_scor',4:'n_significant_int',5:'n_unique_proteins_above_target_interaction',6:'n_interactions_above_target_interaction',7:'n_enst_with_hight_expression',8:'mean_scor_with_hight_expression'}, axis = 1)
|
|
|
|
return df, hight_expression_enst_per_drug, drug_interactions
|
|
|
|
#look at tissue interactions specificly
|
|
def get_number_of_interacting_prot_per_tissue(names, hight_expression_enst_per_drug, drug_interactions, n_of_interactions = None):
|
|
'''
|
|
Tissue specific interactions, only for high expressed proteins
|
|
input:
|
|
names; drug id, list
|
|
hight_expression_enst_per_drug; ensts with interaction of interest, make sure if follows names order and enst orderd by importance based on highest conplex score, lisf of list
|
|
drug_interactions; significant interactions filtered
|
|
n_of_interactions; n proteins to analyse ,default None use all proteins, use when there is no drug target interaction, interger
|
|
output:
|
|
number and min_max of tissue highly exprest proteins in interaction with a drug/metabolites, pandas df
|
|
'''
|
|
n_interactions = []
|
|
min_max_df = []
|
|
|
|
list_of_tissue_ids = [['Heart muscle'],\
|
|
['Rectum', 'Gallbladder', 'Esophagus', 'Stomach', 'Small intestine', 'Duodenum', 'Colon', 'Salivary gland'],\
|
|
['Breast', 'Lactating breast', 'Cervix', 'Fallopian tube', 'Ovary', 'Placenta', 'Endometrium', 'Vagina'],\
|
|
['Testis', 'Prostate', 'Epididymis', 'Seminal vesicle'],\
|
|
['Liver'],\
|
|
['Kidney', 'Urinary bladder'],\
|
|
['Cerebral cortex', 'Cerebellum', 'Caudate', 'Hippocampus', 'Hypothalamus', 'Dorsal raphe', 'Choroid plexus', 'Pituitary gland', 'Substantia nigra'],\
|
|
['Adrenal gland', 'Pancreas', 'Parathyroid gland', 'Thyroid gland'],\
|
|
['Appendix', 'Lymph node', 'Spleen', 'Thymus', 'Tonsil'],\
|
|
['Bone marrow'],\
|
|
['Bronchus', 'Lung', 'Nasopharynx', 'Oral mucosa'],\
|
|
['Eye', 'Retina'],\
|
|
['Skeletal muscle', 'Cartilage'],\
|
|
['Adipose tissue', 'Hair', 'Skin', 'Smooth muscle', 'Soft tissue', 'Sole of foot']]
|
|
|
|
list_of_tissue_names = ['heart', 'gasrto_intestin', 'female_tissues', 'male_tissues', 'liver', 'kidney', 'brain', 'endocrine', 'immune',\
|
|
'hematopoietic', 'respiratory', 'sensory', 'musculoskeletal', 'others']
|
|
|
|
for t_id, t_name in zip(list_of_tissue_ids, list_of_tissue_names):
|
|
HPA_normal_hight_tis = HPA_normal_hight[[i in t_id for i in HPA_normal_hight['Tissue']]]
|
|
|
|
tis = [HPA_normal_hight_tis[[i in j[:n_of_interactions] for i in HPA_normal_hight_tis['transcipt']]] for j in hight_expression_enst_per_drug]
|
|
tis = [i.drop_duplicates('transcipt') for i in tis]
|
|
|
|
# Calculate highly expressed proteins once to use in both branches
|
|
num_highly_expressed = len(HPA_normal_hight_tis.drop_duplicates('transcipt'))
|
|
|
|
# Handle empty drug_interactions case
|
|
if len(drug_interactions) == 0 or len(tis[0]) == 0:
|
|
min_max_df.append(pd.DataFrame([names, [np.nan], [np.nan], [np.nan], [num_highly_expressed], [0.0], [np.nan]])\
|
|
.T.rename({0:'Drug_ID',
|
|
1:f'max_{t_name}_interaction',
|
|
2:f'min_{t_name}_interaction',
|
|
3:f'mean_{t_name}_interaction',
|
|
4:f'n_high_protein_in_{t_name}',
|
|
5:f'share_affected_{t_name}',
|
|
6:f'weighted_score_{t_name}'}, axis = 1))
|
|
else:
|
|
#get max and min conplex sores for tissue specific ents !!! only for one drug ate a time not for a list (n_interactions is for a list)
|
|
inter = drug_interactions[drug_interactions['transcipt'].isin(tis[0]['transcipt'])].sort_values('conplex_score', ascending=False)
|
|
|
|
# Calculate new metrics based on `inter`
|
|
num_affected = inter.shape[0]
|
|
share_affected = num_affected / num_highly_expressed if num_highly_expressed > 0 else 0.0
|
|
mean_score = inter['conplex_score'].mean()
|
|
weighted_score = share_affected * mean_score
|
|
|
|
min_max_df.append(pd.DataFrame([names, [inter['conplex_score'].max()], [inter['conplex_score'].min()], [mean_score], [num_highly_expressed], [share_affected], [weighted_score]])\
|
|
.T.rename({0:'Drug_ID',
|
|
1:f'max_{t_name}_interaction',
|
|
2:f'min_{t_name}_interaction',
|
|
3:f'mean_{t_name}_interaction',
|
|
4:f'n_high_protein_in_{t_name}',
|
|
5:f'share_affected_{t_name}',
|
|
6:f'weighted_score_{t_name}'}, axis = 1))
|
|
|
|
#get number of interactions for tissue specific ents
|
|
n_interactions.append(pd.DataFrame([names, [len(i) for i in tis]]).T.rename({0:'Drug_ID',1:f'n_enst_{t_name}_above_target_interaction'}, axis = 1))
|
|
|
|
#merge interaction data
|
|
df_n_interactions = n_interactions[0]
|
|
for d in n_interactions[1:]:
|
|
df_n_interactions = df_n_interactions.merge(d, on='Drug_ID')
|
|
|
|
#merge min max data
|
|
df_min_max_df = min_max_df[0]
|
|
for d in min_max_df[1:]:
|
|
df_min_max_df = df_min_max_df.merge(d, on='Drug_ID')
|
|
|
|
n_interactions = df_n_interactions.merge(df_min_max_df, left_on='Drug_ID', right_on='Drug_ID')
|
|
|
|
return n_interactions
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Calculate biological metrics.")
|
|
parser.add_argument("--metabolites", required=True, help="Path to the metabolites file.")
|
|
parser.add_argument("--conplex_all_interactions", required=True, help="Path to the conplex_significant_interaction file.")
|
|
parser.add_argument("--conplex_drug", required=True, help="Path to the conplex_significant_interaction_drug_only file.")
|
|
parser.add_argument("--target", required=True, help="PATH to file csv with drug targets in ENST formet separated by space.")
|
|
|
|
args = parser.parse_args()
|
|
|
|
drug_name = args.metabolites.split('/')[-1].split('_out.txt')[0]
|
|
n_metabolites = get_n_metabolites(args.metabolites)
|
|
|
|
#get target
|
|
target = pd.read_csv(args.target, header=None)[2:]
|
|
if len(target) == 2:
|
|
target = list(target.iloc[-1])
|
|
target = target[0].split(' ')
|
|
else:
|
|
target = []
|
|
|
|
df_1, target_ents, drug_interactions = get_conplex_metric(args.conplex_drug, args.conplex_all_interactions, target)
|
|
|
|
#if no target interaction use only 200 unique proteins with highest interactions per tissue
|
|
if df_1['max_target_scor'].isna()[0]:
|
|
target_interaction = 0
|
|
df_2 = get_number_of_interacting_prot_per_tissue([drug_name], [target_ents], drug_interactions, n_of_interactions = 200)
|
|
else:
|
|
target_interaction = 1
|
|
df_2 = get_number_of_interacting_prot_per_tissue([drug_name], [target_ents], drug_interactions)
|
|
|
|
df = pd.concat([df_2, df_1], axis=1)
|
|
df['target_interaction_found'] = target_interaction
|
|
df['n_metabolites'] = n_metabolites
|
|
|
|
df.to_csv(f'{drug_name}_biological_properties.tsv', sep = '\t', index=False) |