hogprof 0.3.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.
@@ -0,0 +1,229 @@
1
+
2
+ from __future__ import print_function
3
+
4
+ from goatools import semantic
5
+ from pyoma.browser.models import ProteinEntry
6
+ from goatools.obo_parser import GODag
7
+ from . import hashutils
8
+ import pickle
9
+ from goatools.go_enrichment import GOEnrichmentStudy
10
+ from pyoma.browser import db
11
+ import multiprocessing as mp
12
+ from tables import *
13
+ import time
14
+ ##############enrichment##############################################
15
+
16
+ def return_enrichment_study_obj(gaf_taxfiltered, obo = None):
17
+ '''
18
+ Generate go enrichment study object with a background dataset.
19
+ '''
20
+ if obo is None:
21
+ obodag = GODag(config_utils.datadir+"/GOData/go-basic.obo")
22
+ else:
23
+ obodag = GODag(obo)
24
+
25
+ goeaobj = GOEnrichmentStudy(
26
+ gaf_taxfiltered.keys(), #
27
+ gaf_taxfiltered, # geneid/GO associations possible with tree used for DB
28
+ obodag, # Ontologies
29
+ propagate_counts = False,
30
+ alpha = 0.15, # default significance cut-off
31
+ methods = ['fdr_bh']) # defult multipletest correction method
32
+ return goeaobj
33
+
34
+
35
+ def buildGAF(gaf_file , universe= None):
36
+
37
+ gaf_filtered = {}
38
+ with open(gaf_file, mode='r') as gafin:
39
+ for line in gafin:
40
+ words = line.split()
41
+ if words[0] not in gaf_filtered:
42
+ gaf_filtered[words[0]]=set([words[1]])
43
+ else:
44
+ gaf_filtered[words[0]].add(words[1])
45
+
46
+ if universe:
47
+ gaf_filtered = { prot:gaf_filtered[prot] for prot in universe}
48
+
49
+
50
+ return gaf_filtered
51
+
52
+
53
+ def return_hogs_timeout( result, retq):
54
+ print('started')
55
+ with open_file(config_utils.config['dir']['omadir']+'OmaServer.h5' , mode="r") as h5_oma:
56
+ db_obj = db.Database(h5_oma)
57
+ res = [ ProteinEntry(db_obj, e).omaid for e in db_obj.member_of_fam(int(result)) ]
58
+ retq.put(res)
59
+
60
+ def run_GOEA_onresults(results, db_obj, goeaobj, outname = None):
61
+ '''
62
+ Perform enrichment analysis on returned results
63
+ grabs all member protein of all hogs in result
64
+ returns goe results and HOG composition
65
+ '''
66
+ hogids =[ "HOG:" + (7-len(str(fam_id))) * '0' + str(fam_id) for fam_id in results ]
67
+ HOGS={}
68
+ print('compiling hogs')
69
+ prots = []
70
+
71
+ print('mod13')
72
+ retq = mp.Queue()
73
+
74
+ for i,result in enumerate(results):
75
+ if i %10 ==0:
76
+ print(i)
77
+ print(result)
78
+ HOGS[result]=[]
79
+ p = mp.Process( target= return_hogs_timeout , args= (result, retq))
80
+ p.start()
81
+
82
+ t0 = time.time()
83
+ timeout = False
84
+
85
+ while time.time()-t0 < 10 :
86
+ time.sleep(.1)
87
+ if p.is_alive() == False:
88
+ print('done')
89
+ break
90
+ if time.time()-t0 > 10:
91
+ timeout = True
92
+ print('Dead')
93
+ p.terminate()
94
+ del p
95
+
96
+
97
+ if retq.empty() == False:
98
+ iterobj = retq.get(10)
99
+ #retq get
100
+ for k,member in enumerate(iterobj):
101
+ if k < 1:
102
+ print(member)
103
+ if k > 500:
104
+ break
105
+ HOGS[result].append(member)
106
+ prots.append(member)
107
+
108
+ print('done')
109
+ print('running GO enrichment study')
110
+ goea_results_all = goeaobj.run_study(prots )
111
+ print('done')
112
+ if outname:
113
+ with open( config_utils.datadir + outname + 'Hogs2Prots.pkl' , 'wb' ) as save:
114
+ save.write(pickle.dumps(HOGS,2))
115
+ goeaobj.wr_txt(config_utils.datadir+ str(outname)+"enrichment.txt", goea_results_all)
116
+ print('DONE!')
117
+ return goea_results_all, HOGS
118
+
119
+
120
+ def run_GOEA_onresults_tar(results, tar, goeaobj, outname = None):
121
+ '''
122
+ Perform enrichment analysis on returned results
123
+ grabs all member protein of all hogs in result
124
+ returns goe results and HOG composition
125
+ '''
126
+ ## TODO: finish this function with tar hog to list of prot IDS
127
+ #print(db_obj.member_of_hog_id(int(results[0])))
128
+ #hogids =[ "HOG:" + (7-len(fam_id)) * '0' + fam_id for fam_id in results ]
129
+ #print( db_obj.member_of_hog_id(hogids[0]) )
130
+
131
+
132
+ HOGS={}
133
+ print('compiling hogs')
134
+ prots = []
135
+ for i,result in enumerate(hogids):
136
+ if i %10 ==0:
137
+ print(i)
138
+ HOGS[result]=[]
139
+ for member in db_obj.iter_members_of_hog_id(result):
140
+ HOGS[result].append(member.omaid)
141
+ prots.append(member.omaid)
142
+ print('done')
143
+ print('running GO enrichment study')
144
+
145
+ goea_results_all = goeaobj.run_study(prots )
146
+ print('done')
147
+ with open( config_utils.datadir + outname + 'Hogs2Prots.pkl' , 'wb' ) as save:
148
+ save.write(pickle.dumps(HOGS,2))
149
+
150
+ goeaobj.wr_txt(config_utils.datadir+ str(outname)+"enrichment.txt", goea_results_all)
151
+ print('DONE!')
152
+ return goea_results_all, HOGS
153
+
154
+
155
+ ######################resnik semsim ###################################################
156
+
157
+ def resnik_sim_hdf5(go_id1, go_id2, godag, termcounts, hdf5):
158
+ '''
159
+ Computes Resnik's similarity measure.
160
+ '''
161
+ try:
162
+ msca_goid = deepest_common_ancestor_hdf5([goterm2id(go_id1), goterm2id(go_id2)], godag, hdf5)
163
+ score = semantic.get_info_content(msca_goid, termcounts)
164
+ except:
165
+ score = -1
166
+ return score
167
+
168
+
169
+ def deepest_common_ancestor_hdf5(go_ids, godag, hdf5):
170
+ '''
171
+ Gets the nearest common ancestor
172
+ using the above function.
173
+ Only returns single most specific - assumes unique exists.
174
+ '''
175
+ # Take the element at maximum depth.
176
+ return max(common_parent_go_ids_hdf5(go_ids, hdf5), key=lambda t: godag[t].depth)
177
+
178
+ def common_parent_go_ids_hdf5(go_ids, hdf5_set):
179
+ '''
180
+ Finds the common ancestors in the GO
181
+ tree of the list of goids in the input.
182
+ '''
183
+ candidates = set(hdf5_set[go_ids[0]].tolist())
184
+ for go_id in go_ids[1:]:
185
+ candidates_to_add = set(hdf5_set[go_id].tolist())
186
+ candidates.intersection_update(candidates_to_add)
187
+ corrected_candidates = [id2goterm(c) for c in candidates]
188
+ return corrected_candidates
189
+
190
+ def resnik_sim_pandas(tup, df , termcounts):
191
+ '''
192
+ Computes Resnik's similarity measure.
193
+ '''
194
+ go_id1, go_id2 = tup
195
+ #print(df.head())
196
+ if go_id1 == go_id2:
197
+ return semantic.get_info_content(go_id1, termcounts)
198
+
199
+ elif go_id2 in df.index and go_id1 in df.index:
200
+
201
+ ancestors = df.loc[str(go_id2)].parents
202
+ ancestors += df.loc[str(go_id1)].parents
203
+ terms = df.loc[ancestors]
204
+ ancestors_set = terms.parents.tolist()
205
+ intersection = set(ancestors_set[0]).intersection(* ancestors_set[1:])
206
+ common_ancestors = df.loc[list(intersection)]
207
+ common_ancestors = common_ancestors.sort_values('depth', ascending= False)
208
+ msca_goid = common_ancestors.index.tolist()[0]
209
+ return semantic.get_info_content(msca_goid, termcounts)
210
+
211
+ else:
212
+ return -1
213
+
214
+
215
+ def get_go_terms_gaf(hog_id, pyoma_dbobj, gaf , genomes = None):
216
+ '''
217
+ iterate over hog members and get the go information from a gaf in memory
218
+ '''
219
+ fam = hashutils.hogid2fam(hog_id)
220
+ go_terms = { mr.omaid:gaf[mr.omaid] for mr in pyoma_dbobj.iter_members_of_hog_id(hog_id) if mr.omaid in gaf }
221
+ return go_terms
222
+
223
+
224
+ def goterm2id(go_term_to_modif):
225
+
226
+ return int(go_term_to_modif.split(':')[1])
227
+
228
+ def id2goterm(go_term_to_modif):
229
+ return 'GO:{:07d}'.format(go_term_to_modif)
@@ -0,0 +1,190 @@
1
+ import datasketch
2
+ import copy
3
+ import logging
4
+ import numpy as np
5
+ import pandas as pd
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def generate_treeweights( mastertree, taxaIndex , taxfilter= None, taxmask=None ):
11
+ #weighing function for tax level, masking levels etc. sets all weights to 1 if they are in taxmask or filter
12
+ #custom weights can also be used here
13
+ """
14
+ Generate the weights of each taxonomic level to be applied during the
15
+ constructin of weighted minhashes
16
+ :param mastertree: full corrected ncbi taxonomy
17
+ :param taxaIndex: dict mapping taxa to columns
18
+ :param taxfilter: list of branches to delete
19
+ :param taxmask: if this is not NONE taxmask, the DB is constructed with this subtree
20
+ :return: weights: a vector of weights for each tax level
21
+ """
22
+ #get max of taxa index
23
+ taxmax = max(taxaIndex.values())+1
24
+ weights = np.zeros((3*taxmax,1))
25
+ logger.info('making tree weights with %d taxa', len(taxaIndex))
26
+ newtree = copy.deepcopy(mastertree)
27
+ if taxmask:
28
+ for node in newtree.traverse():
29
+ if taxmask:
30
+ if str(node.name) == str(taxmask):
31
+ logger.info('Masking tree to %s', taxmask)
32
+ newtree = node
33
+ break
34
+ logger.info(
35
+ 'Masked tree contains %d taxa',
36
+ len([n.name for n in newtree.traverse()]),
37
+ )
38
+
39
+ if taxfilter:
40
+ for n in newtree.traverse():
41
+ if n.name in taxfilter:
42
+ n.delete()
43
+
44
+ for i in range(3):
45
+ for n in newtree.traverse():
46
+ weights[taxmax*i + taxaIndex[n.name] ] = 1
47
+ return weights
48
+
49
+ def hash_tree(tp , taxaIndex , treeweights , wmg , lossonly = False , duplonly = False ):
50
+ """
51
+ Generate a weighted minhash and binary matrix row for a tree profile
52
+
53
+ :param tp: a pyham tree profile
54
+ :param taxaIndex: dict mapping taxa to columns
55
+ :param treeweights: a vector of weights for each tax levels
56
+ :param wmg: Datasketch weighted minhash generator
57
+ :return hog_matrix: a vector of weights for each tax level
58
+ :return weighted_hash: a weighted minhash of a HOG
59
+
60
+ """
61
+ if not tp:
62
+ return None, None
63
+
64
+ taxaIndex_max = max(taxaIndex.values())+1
65
+ hog_matrix_weighted = np.zeros((1, 3*taxaIndex_max))
66
+ hog_matrix_binary = np.zeros((1, 3*taxaIndex_max))
67
+
68
+ losses = [ taxaIndex[n.name] for n in tp.traverse() if n.lost and n.name in taxaIndex ]
69
+ dupl = [ taxaIndex[n.name] for n in tp.traverse() if n.dupl and n.name in taxaIndex ]
70
+ presence = [ taxaIndex[n.name] for n in tp.traverse() if n.nbr_genes > 0 and n.name in taxaIndex ]
71
+ indices = dict(zip (['presence', 'loss', 'dup'],[presence,losses,dupl] ) )
72
+ ### Athina comment:
73
+ ### change things here to do permutation test!!!!
74
+ for i,event in enumerate(indices):
75
+ if len(indices[event])>0:
76
+ try:
77
+ hogindex = np.asarray(indices[event])+i*taxaIndex_max
78
+
79
+ hog_matrix_weighted[:,hogindex] = treeweights[hogindex , : ].ravel()
80
+
81
+ if lossonly == True and event != 'loss':
82
+ hog_matrix_weighted[:,hogindex] = 0
83
+ if duplonly == True and event != 'dup':
84
+ hog_matrix_weighted[:,hogindex] = 0
85
+ if lossonly == False and duplonly == False:
86
+ hog_matrix_binary[:,hogindex] = 1
87
+
88
+ except Exception:
89
+ logger.exception(
90
+ 'Error hashing event %s (indices=%s, hogindex=%s)',
91
+ event,
92
+ indices[event],
93
+ hogindex,
94
+ )
95
+
96
+ input_vec = list(hog_matrix_weighted.flatten())
97
+
98
+ if wmg.dim == len(input_vec) and sum(input_vec) > 0:
99
+ weighted_hash = wmg.minhash(input_vec)
100
+ return hog_matrix_binary , weighted_hash
101
+
102
+ else:
103
+ return None, None
104
+
105
+
106
+
107
+
108
+ def hash_trees_subhogs(hog_tps , taxaIndex , treeweights , wmg , lossonly = False , duplonly = False ):
109
+ """
110
+ Generate a weighted minhash and binary matrix row for multiple tree profiles within a HOG
111
+
112
+ :param tp: a pyham tree profile
113
+ :param taxaIndex: dict mapping taxa to columns
114
+ :param treeweights: a vector of weights for each tax levels
115
+ :param wmg: Datasketch weighted minhash generator
116
+ :return hog_matrix: a vector of weights for each tax level
117
+ :return weighted_hash: a weighted minhash of a HOG
118
+
119
+ """
120
+ #print(hog_tps.keys())
121
+ #print(hog_tps[['tree_dicts']].to_dict())
122
+ if hog_tps is None or 'tree_dicts' not in hog_tps or hog_tps['tree_dicts'] is None:
123
+ return {}
124
+
125
+ hashes_subhogs = {}
126
+ for key, tp in hog_tps['tree_dicts'].items():
127
+ matrix, hashed = hash_tree(tp, taxaIndex, treeweights, wmg,
128
+ lossonly=lossonly, duplonly=duplonly)
129
+ if hashed is not None:
130
+ hashes_subhogs[key] = (matrix, hashed)
131
+ return hashes_subhogs
132
+
133
+
134
+ def tree2str_DCA(tp , taxaIndex ):
135
+ """
136
+ Generate a string where each column is a tax level
137
+ each letter code corresponds to an event type
138
+ each row is a protein family. for use with DCA pipelines
139
+
140
+ :param tp: a pyham tree profile
141
+ :param taxaIndex: dict mapping taxa to columns
142
+ :return dcaMat: a weighted minhash of a HOG
143
+ """
144
+ #convert a tree profile to a weighted minhash
145
+ losses = [ taxaIndex[n.name] for n in tp.traverse() if n.lost and n.name in taxaIndex ]
146
+ dupl = [ taxaIndex[n.name] for n in tp.traverse() if n.dupl and n.name in taxaIndex ]
147
+ presence = [ taxaIndex[n.name] for n in tp.traverse() if n.nbr_genes > 0 and n.name in taxaIndex ]
148
+ Ds = list( set(dupl).intersection(set(presence)))
149
+ Ps= list(set(presence).difference(set(dupl)))
150
+ Ls= list(set(losses))
151
+ charar = np.chararray(len(taxaIndex) )
152
+ #set to absent
153
+ charar.fill( 'A')
154
+ charar[Ds] = 'D'
155
+ charar[Ls] = 'L'
156
+ charar[Ps] = 'P'
157
+ return charar
158
+
159
+ def row2hash(row , taxaIndex , treeweights , wmg , lossonly = False , duplonly = False):
160
+ """
161
+ turn a dataframe row with an orthoxml file to hash and matrix row
162
+ :param row: lsh builder dataframe row
163
+ :param taxaIndex: dict mapping taxa to columnsfam
164
+ :param treeweights: a vector of weights for each tax levels
165
+ :param wmg: Datasketch weighted minhash generator
166
+ :return: hog_matrix: a vector of weights for each tax level
167
+ :return: weighted_hash: a weighted minhash of a HOG
168
+ """
169
+ #convert a dataframe row to a weighted minhash
170
+ fam, treemap = row.tolist()
171
+ #print(f'Processing row in row2hash: fam={fam}, treemap={treemap}')
172
+ hog_matrix,weighted_hash = hash_tree(treemap , taxaIndex , treeweights , wmg , lossonly = lossonly , duplonly = duplonly)
173
+ #print(f'Generated hog_matrix: {hog_matrix}, weighted_hash: {weighted_hash}')
174
+
175
+ return pd.Series([weighted_hash,hog_matrix], index=['hash','rows'])
176
+
177
+ def fam2hash_hdf5(fam, hdf5, dataset=None, nsamples=128):
178
+ """
179
+ Read the stored hash values and return a weighted minhash
180
+ :param fam: hog id
181
+ :param hdf5: h5py object of the hashvalues
182
+ :param dataset: which dataset to use when constructing the hash
183
+ :return: minhash1: the weighted hash of your HOG
184
+ """
185
+ if dataset is None:
186
+ dataset = next(iter(hdf5.keys()))
187
+
188
+ hash_values = np.asarray(hdf5[dataset][fam, :].reshape(nsamples,2 )).astype('int64')
189
+ minhash1 = datasketch.WeightedMinHash(seed = 1, hashvalues=hash_values)
190
+ return minhash1
@@ -0,0 +1,83 @@
1
+ import collections
2
+ import xml.etree.ElementTree as ET
3
+ from io import StringIO
4
+
5
+
6
+ class OrthoXMLBuilder:
7
+ """
8
+ This is a function for adding subHOG IDs to the OMA orthoxml files (from Adrian).
9
+ Will become redundant with the next version of OMA
10
+ """
11
+
12
+ def __init__(self):
13
+ self.NS = "http://orthoXML.org/2011/"
14
+ ET.register_namespace('', self.NS) # Register default namespace
15
+
16
+ def add_loft_ids(self, orthoxml: str | ET.Element) -> str:
17
+ """Annotate XML text or an already parsed root, modifying it in place."""
18
+
19
+ def encode_paralog_cluster_id(prefix, nr):
20
+ letters = []
21
+ while nr // 26 > 0:
22
+ letters.append(chr(97 + (nr % 26)))
23
+ nr = nr // 26 - 1
24
+ letters.append(chr(97 + (nr % 26)))
25
+ return prefix + ''.join(letters[::-1])
26
+
27
+ def next_sub_hog_id(idx):
28
+ dups[idx] += 1
29
+ return dups[idx]
30
+
31
+ def rec_annotate(node, og, idx=0):
32
+ if node.tag == f"{{{self.NS}}}orthologGroup":
33
+ taxon_id = node.get('taxonId')
34
+ if taxon_id is not None:
35
+ node.set('id', og + "_" + taxon_id)
36
+ else:
37
+ # If no taxonId, just set the id to the og
38
+ node.set('id', og )
39
+ for child in list(node):
40
+ rec_annotate(child, og, idx)
41
+ elif node.tag == f"{{{self.NS}}}paralogGroup":
42
+ idx += 1
43
+ next_og = f"{og}.{next_sub_hog_id(idx)}"
44
+ for i, child in enumerate(list(node)):
45
+ rec_annotate(child, encode_paralog_cluster_id(next_og, i), idx)
46
+ elif node.tag == f"{{{self.NS}}}geneRef":
47
+ # Set the id to the geneRef id
48
+ node.set('LOFT', og)
49
+
50
+ xml = ET.fromstring(orthoxml) if isinstance(orthoxml, str) else orthoxml
51
+ doc = ET.ElementTree(xml)
52
+ for i, el in enumerate(xml.findall(".//groups/orthologGroup", {"": self.NS})):
53
+ og = el.get('id', f"HOG:{i+1:08d}")
54
+ dups = collections.defaultdict(int)
55
+ rec_annotate(el, og)
56
+ tree_str = StringIO()
57
+ ### addition by Athina:
58
+ tree_str.write('<?xml version="1.0" encoding="UTF-8"?>\n')
59
+ doc.write(tree_str, encoding='unicode')
60
+ return tree_str.getvalue()
61
+
62
+
63
+ def get_root_hog_id(orthoxml: str | ET.Element) -> str:
64
+ xml = ET.fromstring(orthoxml) if isinstance(orthoxml, str) else orthoxml
65
+ groups = xml.findall(
66
+ '{http://orthoXML.org/2011/}groups/{http://orthoXML.org/2011/}orthologGroup'
67
+ )
68
+ if len(groups) != 1:
69
+ raise ValueError(f'Expected one root HOG, found {len(groups)}')
70
+ return groups[0].attrib['id']
71
+
72
+
73
+ def count_species(ortho_fam: str | ET.Element) -> int:
74
+ if isinstance(ortho_fam, ET.Element):
75
+ return len(ortho_fam.findall('{http://orthoXML.org/2011/}species'))
76
+ return ortho_fam.count('<species name=')
77
+
78
+
79
+ def iter_species(filename):
80
+ root = ET.parse(filename).getroot()
81
+ for child in root:
82
+ if child.tag.rsplit("}", 1)[-1] == "species":
83
+ yield child