uniprotpy 0.0.1__tar.gz

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,40 @@
1
+ Metadata-Version: 2.1
2
+ Name: uniprotpy
3
+ Version: 0.0.1
4
+ Summary: A Python library that interfaces with UniProt data.
5
+ Home-page: https://github.com/danielmarrama/uniprotpy
6
+ Author: Daniel Marrama
7
+ Author-email: danielmarrama@gmail.com
8
+ Description-Content-Type: text/markdown
9
+
10
+ # UniProtPy
11
+
12
+ Python library that interfaces with UniProt API.
13
+
14
+ For something like [openvax/pyensembl](https://github.com/openvax/pyensembl) with UniProt.
15
+
16
+ The REST API has changed as of 2022. Many of the ways to extract data from UniProt is now different and there isn't a clean way to interface with it.
17
+
18
+
19
+ ### Goals
20
+ 1. Allow users to pull any kind of data from UniProt.
21
+ 2. Store and query large data using a local database.
22
+ 3. Manipulate and output data in many standard formats.
23
+
24
+ ### Installation
25
+
26
+ ```bash
27
+ pip install uniprotpy
28
+ ```
29
+
30
+ ### Getting a proteome for a species
31
+ ```bash
32
+ uniprotpy get-best-proteome --taxon-id 9606
33
+ ```
34
+
35
+ ### TODO
36
+
37
+ - Retrieve individual entries in all supported formats.
38
+ - Get metadata (protein ID, name, gene, # of isoforms, etc.) for entries.
39
+ - Retrieve proteomes via proteome ID or select "best" proteome based on taxon ID.
40
+ - Query proteomes for a protein by ID, name, seq, or peptide unit.
@@ -0,0 +1,31 @@
1
+ # UniProtPy
2
+
3
+ Python library that interfaces with UniProt API.
4
+
5
+ For something like [openvax/pyensembl](https://github.com/openvax/pyensembl) with UniProt.
6
+
7
+ The REST API has changed as of 2022. Many of the ways to extract data from UniProt is now different and there isn't a clean way to interface with it.
8
+
9
+
10
+ ### Goals
11
+ 1. Allow users to pull any kind of data from UniProt.
12
+ 2. Store and query large data using a local database.
13
+ 3. Manipulate and output data in many standard formats.
14
+
15
+ ### Installation
16
+
17
+ ```bash
18
+ pip install uniprotpy
19
+ ```
20
+
21
+ ### Getting a proteome for a species
22
+ ```bash
23
+ uniprotpy get-best-proteome --taxon-id 9606
24
+ ```
25
+
26
+ ### TODO
27
+
28
+ - Retrieve individual entries in all supported formats.
29
+ - Get metadata (protein ID, name, gene, # of isoforms, etc.) for entries.
30
+ - Retrieve proteomes via proteome ID or select "best" proteome based on taxon ID.
31
+ - Query proteomes for a protein by ID, name, seq, or peptide unit.
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,33 @@
1
+ from setuptools import setup
2
+ import re
3
+ import os
4
+
5
+
6
+ with open('uniprotpy/version.py', 'r') as f:
7
+ version = re.search(
8
+ r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
9
+ f.read(),
10
+ re.MULTILINE).group(1)
11
+
12
+ with open('README.md', 'r', encoding='utf-8') as f:
13
+ long_description = f.read()
14
+
15
+ setup(
16
+ name='uniprotpy',
17
+ version=version,
18
+ description='A Python library that interfaces with UniProt data.',
19
+ long_description=long_description,
20
+ long_description_content_type='text/markdown',
21
+ url='https://github.com/danielmarrama/uniprotpy',
22
+ author='Daniel Marrama',
23
+ author_email='danielmarrama@gmail.com',
24
+ packages=['uniprotpy'],
25
+ install_requires=['pandas>=1.1',
26
+ 'biopython>=1.5'],
27
+ zip_safe=False,
28
+ entry_points={
29
+ 'console_scripts': [
30
+ 'uniprotpy = uniprotpy.shell:run'
31
+ ],
32
+ },
33
+ )
@@ -0,0 +1,3 @@
1
+ from .select_best_proteome import ProteomeSelector
2
+
3
+ from .version import __version__
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import pandas as pd
4
+ import json
5
+ import requests
6
+ import gzip
7
+
8
+ directory = ''
9
+
10
+ def get_proteomes(taxon_dict):
11
+ '''
12
+ Extracts taxons with proteomes from the UniProt FTP site.
13
+ If get_canonical: get 1 protein per gene proteome as well as total proteome.
14
+ '''
15
+
16
+ url = 'https://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes'
17
+
18
+ for taxon_id, proteome_id in taxon_dict.items():
19
+
20
+ # if file exists in directory already, skip it
21
+ if taxon_id + '.fasta' in os.listdir(directory):
22
+ continue
23
+
24
+ # check Eukaryota first
25
+ r = requests.get(url + '/Eukaryota/' + proteome_id + '/' + proteome_id + '_' + taxon_id + '.fasta.gz', stream=True)
26
+
27
+ # then Archaea
28
+ if r.status_code == 404:
29
+ r = requests.get(url + '/Archaea/' + proteome_id + '/' + proteome_id + '_' + taxon_id + '.fasta.gz', stream=True)
30
+
31
+ # then Bacteria
32
+ if r.status_code == 404:
33
+ r = requests.get(url + '/Bacteria/' + proteome_id + '/' + proteome_id + '_' + taxon_id + '.fasta.gz', stream=True)
34
+
35
+ # then Viruses
36
+ if r.status_code == 404:
37
+ r = requests.get(url + '/Viruses/' + proteome_id + '/' + proteome_id + '_' + taxon_id + '.fasta.gz', stream=True)
38
+
39
+ # if found nowhere, move on
40
+ if r.status_code == 404:
41
+ get_protein_entries([taxon_id])
42
+
43
+ try:
44
+ with open(directory + taxon_id + '.fasta', 'wb') as f1:
45
+ f1.write(gzip.open(r.raw, 'rb').read())
46
+ except gzip.BadGzipFile:
47
+ get_protein_entries([taxon_id])
48
+
49
+ return 0
50
+
51
+
52
+ def get_protein_entries(taxons):
53
+ '''
54
+ Get collection of proteins for taxons with no proteome in UniProt.
55
+ '''
56
+ for taxon in taxons:
57
+
58
+ # if file exists in directory already, skip it
59
+ if taxon + '.fasta' in os.listdir(directory):
60
+ continue
61
+
62
+ r = requests.get('https://rest.uniprot.org/uniprotkb/stream?format=fasta&query=%28taxonomy_id%3A' + taxon + '%29')
63
+ with open(directory + taxon + '.fasta', 'w') as f2:
64
+ f2.write(r.text)
65
+
66
+
67
+ if __name__ == '__main__':
68
+
69
+ # list taxons you want to retrieve from UniProt in text file
70
+ with open('taxons.txt', 'r') as f:
71
+ lines = f.readlines()
72
+ taxons = [i.rstrip() for i in lines]
73
+ taxons = pd.Series(taxons)
74
+
75
+ # read in the table from UniProt (https://www.uniprot.org/proteomes?query=*)
76
+ df = pd.read_csv('proteome_ids.tsv', sep='\t')
77
+
78
+ # get taxons with proteomes (those that have IDs)
79
+ taxons_with_proteomes = list(taxons[taxons.isin(df['Organism Id'].astype(str))])
80
+
81
+ # get collection of proteins for taxons that have no proteome
82
+ taxons_without_proteomes = list(taxons[~taxons.isin(df['Organism Id'].astype(str))])
83
+ get_protein_entries(taxons_without_proteomes)
84
+
85
+ # get proteome ID with most proteins
86
+ df.drop_duplicates(subset=['Organism Id'], inplace=True)
87
+ df = df[df['Organism Id'].astype(str).isin(taxons_with_proteomes)]
88
+ taxons_with_proteomes_dict = dict(zip(df['Organism Id'].astype(str), df['Proteome Id'].astype(str)))
89
+
90
+ # now get those proteomes from UniProt FTP server
91
+ get_proteomes(taxons_with_proteomes_dict)
92
+
93
+
@@ -0,0 +1,55 @@
1
+ import re
2
+ import pandas as pd
3
+
4
+ from Bio import SeqIO
5
+ from anytree import Node, RenderTree
6
+
7
+
8
+ def add_nodes(nodes, parent, child):
9
+ if parent not in nodes:
10
+ nodes[parent] = Node(parent)
11
+ if child not in nodes:
12
+ nodes[child] = Node(child)
13
+ nodes[child].parent = nodes[parent]
14
+
15
+ def create_protein_tree(proteome):
16
+ proteins = list(SeqIO.parse(proteome, 'fasta'))
17
+
18
+ # get UniProt IDs for one protein per gene proteome
19
+ gp_ids = [str(x.id.split('|')[1]) for x in list(SeqIO.parse(gp_proteome, 'fasta'))]
20
+
21
+ data = []
22
+ for protein in proteins:
23
+
24
+ # get gene symbol from FASTA file
25
+ try:
26
+ gene = re.search('GN=(.*?) ', protein.description).group(1)
27
+ except AttributeError:
28
+
29
+ # sometimes the gene symbol is at the end of the FASTA description
30
+ try:
31
+ gene = re.search('GN=(.*?)$', protein.description).group(1)
32
+ except AttributeError:
33
+ gene = ''
34
+
35
+ data.append([protein.id.split('|')[0], gene, protein.id.split('|')[1], str(protein.seq)])
36
+
37
+ # put protein tree data into dataframe
38
+ df = pd.DataFrame(data, columns=['db', 'gene', 'id', 'seq'])
39
+
40
+ # start tree with nodes - genes as root and UniProt IDs as children
41
+ nodes = {}
42
+ for parent, child in zip(df['gene'],df['id']):
43
+ add_nodes(nodes, parent, child)
44
+
45
+ # write the tree into a text file
46
+ with open('protein_tree.txt', 'w') as f:
47
+ roots = list(df[~df['gene'].isin(df['id'])]['gene'].unique())
48
+ for root in roots: # you can skip this for roots[0], if there is no forest and just 1 tree
49
+ for pre, _, node in RenderTree(nodes[root]):
50
+ if node.name in gp_ids:
51
+ f.write("%s%s*" % (pre, node.name))
52
+ f.write('\n')
53
+ else:
54
+ f.write("%s%s" % (pre, node.name))
55
+ f.write('\n')
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import warnings
4
+ warnings.filterwarnings('ignore')
5
+
6
+ import re
7
+ import os
8
+ import pandas as pd
9
+ import requests
10
+
11
+
12
+ class ProteomeSelector:
13
+ def __init__(self, taxon_id):
14
+ self.taxon_id = taxon_id
15
+
16
+ # get proteome list for species and count number of proteomes
17
+ self.proteome_list = self._get_proteome_list()
18
+ self.num_of_proteomes = len(self.proteome_list) + 1 # +1 because "all proteins" is also a candidate proteome
19
+
20
+ def select_proteome(self):
21
+ """
22
+ Select the best proteome to use for a species. Return the proteome ID,
23
+ proteome taxon, and proteome type.
24
+
25
+ Check UniProt for all candidate proteomes associated with that
26
+ taxon. Then do the following checks:
27
+
28
+ 1. Are there any representative proteomes?
29
+ 2. Are there any reference proteomes?
30
+ 3. Are there any non-redudant proteomes?
31
+ 4. Are there any other proteomes?
32
+
33
+ If yes to any of the above, check if there are ties. If there are ties,
34
+ then select the proteome with the most proteins.
35
+
36
+ If no to all of the above, then get every protein associated with
37
+ the taxon ID using the get_all_proteins method.
38
+ """
39
+ # if species_dir already exists then return the already selected proteome, else create dir
40
+ if os.path.exists(f'./data/{self.taxon_id}'):
41
+ print(f'Proteome already selected for {self.taxon_id}.')
42
+ return []
43
+ else:
44
+ os.makedirs(f'./data/{self.taxon_id}')
45
+
46
+ # if there is no proteome_list, get all proteins associated with that taxon ID
47
+ if self.proteome_list.empty:
48
+ self._get_all_proteins()
49
+ return 'None', self.taxon_id, 'All-proteins'
50
+
51
+ if self.proteome_list['isRepresentativeProteome'].any():
52
+ proteome_type = 'Representative'
53
+ self.proteome_list = self.proteome_list[self.proteome_list['isRepresentativeProteome']]
54
+ proteome_id, proteome_taxon = self._get_proteome_with_most_proteins()
55
+ # self._get_gp_proteome_to_fasta(proteome_id, proteome_taxon)
56
+
57
+ elif self.proteome_list['isReferenceProteome'].any():
58
+ proteome_type = 'Reference'
59
+ self.proteome_list = self.proteome_list[self.proteome_list['isReferenceProteome']]
60
+ proteome_id, proteome_taxon = self._get_proteome_with_most_proteins()
61
+ # self._get_gp_proteome_to_fasta(proteome_id, proteome_taxon)
62
+
63
+ elif 'redundantTo' not in self.proteome_list.columns:
64
+ proteome_type = 'Other'
65
+ proteome_id, proteome_taxon = self._get_proteome_with_most_proteins()
66
+
67
+ elif self.proteome_list['redundantTo'].isna().any():
68
+ proteome_type = 'Non-redundant'
69
+ self.proteome_list = self.proteome_list[self.proteome_list['redundantTo'].isna()]
70
+ proteome_id, proteome_taxon = self._get_proteome_with_most_proteins()
71
+
72
+ else:
73
+ proteome_type = 'Other'
74
+ proteome_id, proteome_taxon = self._get_proteome_with_most_proteins()
75
+
76
+ # sanity check to make sure proteome.fasta is not empty
77
+ if os.stat(f'./data/{self.taxon_id}/proteome.fasta').st_size == 0:
78
+ proteome_id = 'None'
79
+ proteome_taxon = self.taxon_id
80
+ proteome_type = 'All-proteins'
81
+ self._get_all_proteins()
82
+
83
+ proteome_data = [proteome_id, proteome_taxon, proteome_type]
84
+ return proteome_data
85
+
86
+ def proteome_to_csv(self):
87
+ """
88
+ Write the proteome data for a species to a CSV file for later use.
89
+ """
90
+ from Bio import SeqIO
91
+
92
+ # read in the FASTA file and then get the gene priority IDs if they exist
93
+ proteins = list(SeqIO.parse(f'data/{self.taxon_id}/proteome.fasta', 'fasta'))
94
+
95
+ gp_proteome_path = f'data/{self.taxon_id}/gp_proteome.fasta'
96
+ if os.path.isfile(gp_proteome_path):
97
+ gp_ids = [str(protein.id.split('|')[1]) for protein in list(SeqIO.parse(gp_proteome_path, 'fasta'))]
98
+ else:
99
+ gp_ids = []
100
+
101
+ # start collecting proteome data
102
+ proteome_data = []
103
+ for protein in proteins:
104
+ uniprot_id = protein.id.split('|')[1]
105
+ gp = 1 if uniprot_id in gp_ids else 0
106
+
107
+ # TODO: look into using HUGO to map old gene names to new ones
108
+
109
+ try:
110
+ gene = re.search('GN=(.*?) ', protein.description).group(1)
111
+ except AttributeError:
112
+ try: # some gene names are at the end of the header
113
+ gene = re.search('GN=(.*?)$', protein.description).group(1)
114
+ except AttributeError:
115
+ gene = ''
116
+ try: # protein existence level
117
+ pe_level = int(re.search('PE=(.*?) ', protein.description).group(1))
118
+ except AttributeError:
119
+ pe_level = 0
120
+
121
+ proteome_data.append([protein.id.split('|')[0], gene, uniprot_id, gp, pe_level, str(protein.seq)])
122
+
123
+ columns = ['Database', 'Gene Symbol', 'UniProt ID', 'Gene Priority', 'Protein Existence Level', 'Sequence']
124
+ pd.DataFrame(proteome_data, columns=columns).to_csv(f'data/{self.taxon_id}/proteome.csv', index=False)
125
+
126
+ def _get_proteome_list(self):
127
+ """
128
+ Get a list of proteomes for a species from the UniProt API.
129
+ Check for proteome_type:1 first, which are the representative or
130
+ reference proteomes.
131
+
132
+ If there are no proteomes, return empty DataFrame.
133
+ """
134
+ # URL to get proteome list for a species - use proteome_type:1 first
135
+ url = f'https://rest.uniprot.org/proteomes/stream?format=xml&query=(proteome_type:1)AND(taxonomy_id:{self.taxon_id})'
136
+
137
+ try:
138
+ proteome_list = pd.read_xml(requests.get(url).text)
139
+ except ValueError:
140
+ try: # delete proteome_type:1 from URL and try again
141
+ url = url.replace('(proteome_type:1)AND', '')
142
+ proteome_list = pd.read_xml(requests.get(url).text)
143
+ except ValueError: # if there are no proteomes, return empty DataFrame
144
+ return pd.DataFrame()
145
+
146
+ # remove the namespace from the columns
147
+ proteome_list.columns = [x.replace('{http://uniprot.org/proteome}', '') for x in proteome_list.columns]
148
+ return proteome_list
149
+
150
+ def _get_all_proteins(self):
151
+ """
152
+ Get every protein associated with a taxon ID on UniProt.
153
+ Species on UniProt will have a proteome, but not every protein is
154
+ stored within those proteomes. There is a way to get every protein
155
+ using the taxonomy part of UniProt.
156
+ """
157
+ # URL link to all proteins for a species - size = 500 proteins at a time
158
+ url = f'https://rest.uniprot.org/uniprotkb/search?format=fasta&'\
159
+ f'query=taxonomy_id:{self.taxon_id}&size=500'
160
+
161
+ # loop through all protein batches and write proteins to FASTA file
162
+ for batch in self._get_protein_batches(url):
163
+ with open(f'data/{self.taxon_id}/proteome.fasta', 'a') as f:
164
+ f.write(batch.text)
165
+
166
+ def _get_protein_batches(self, batch_url):
167
+ """
168
+ Get a batch of proteins from UniProt API because it limits the
169
+ number of proteins you can get at once. Yield each batch until the
170
+ URL link is empty.
171
+
172
+ Args:
173
+ batch_url (str): URL to get all proteins for a species.
174
+ """
175
+ while batch_url:
176
+ r = requests.get(batch_url)
177
+ r.raise_for_status()
178
+ yield r
179
+ batch_url = self._get_next_link(r.headers)
180
+
181
+ def _get_next_link(self, headers):
182
+ """
183
+ UniProt will provide a link to the next batch of proteins when getting
184
+ all proteins for a species' taxon ID.
185
+ We can use a regular expression to extract the URL from the header.
186
+
187
+ Args:
188
+ headers (dict): Headers from UniProt API response.
189
+ """
190
+ re_next_link = re.compile(r'<(.+)>; rel="next"') # regex to extract URL
191
+ if 'Link' in headers:
192
+ match = re_next_link.match(headers['Link'])
193
+ if match:
194
+ return match.group(1)
195
+
196
+ def _get_gp_proteome_to_fasta(self, proteome_id, proteome_taxon):
197
+ """
198
+ Write the gene priority proteome to a file.
199
+ This is only for representative and reference proteomes.
200
+ Depending on the species group, the FTP URL will be different.
201
+
202
+ Args:
203
+ proteome_id (str): Proteome ID.
204
+ proteome_taxon (str): Taxon ID for the proteome.
205
+ """
206
+ import gzip
207
+
208
+ group = self.species_df[self.species_df['Taxon ID'].astype(str) == self.taxon_id]['Group'].iloc[0]
209
+ ftp_url = f'https://ftp.uniprot.org/pub/databases/uniprot/knowledgebase/reference_proteomes/'
210
+
211
+ if group == 'archeobacterium':
212
+ ftp_url += f'Archaea/{proteome_id}/{proteome_id}_{proteome_taxon}.fasta.gz'
213
+ elif group == 'bacterium':
214
+ ftp_url += f'Bacteria/{proteome_id}/{proteome_id}_{proteome_taxon}.fasta.gz'
215
+ elif group in ['vertebrate', 'other-eukaryote']:
216
+ ftp_url += f'Eukaryota/{proteome_id}/{proteome_id}_{proteome_taxon}.fasta.gz'
217
+ elif group in ['virus', 'small-virus', 'large-virus']:
218
+ ftp_url += f'Viruses/{proteome_id}/{proteome_id}_{proteome_taxon}.fasta.gz'
219
+
220
+ r = requests.get(ftp_url, stream=True)
221
+ try:
222
+ r.raise_for_status()
223
+ except:
224
+ return
225
+
226
+ # unzip the request and write the gene priority proteome to a file
227
+ with open(f'data/{self.taxon_id}/gp_proteome.fasta', 'wb') as f:
228
+ f.write(gzip.open(r.raw, 'rb').read())
229
+
230
+ def _get_proteome_to_fasta(self, proteome_id):
231
+ """
232
+ Get the FASTA file for a proteome from UniProt API.
233
+ Include all isoforms and do not compress the file.
234
+ """
235
+ url = f'https://rest.uniprot.org/uniprotkb/stream?compressed=false&format=fasta&includeIsoform=true&query=(proteome:{proteome_id})'
236
+ r = requests.get(url)
237
+ r.raise_for_status()
238
+ with open(f'data/{self.taxon_id}/proteome.fasta', 'w') as f:
239
+ f.write(r.text)
240
+
241
+ def _get_proteome_with_most_proteins(self):
242
+ """
243
+ Between UniProt proteome ties, get the proteome with the most proteins.
244
+ """
245
+ # get the index of the row with the most proteins using proteinCount
246
+ proteome_id = self.proteome_list.iloc[self.proteome_list['proteinCount'].idxmax()]['upid']
247
+ proteome_taxon = self.proteome_list.iloc[self.proteome_list['proteinCount'].idxmax()]['taxonomy']
248
+
249
+ # write the proteome to a file
250
+ self._get_proteome_to_fasta(proteome_id)
251
+
252
+ return proteome_id, proteome_taxon
253
+
254
+
255
+ def main():
256
+ import argparse
257
+
258
+ parser = argparse.ArgumentParser()
259
+
260
+ parser.add_argument('-t', '--taxon_id', required=True, help='Taxon ID for the species to pull data for.')
261
+
262
+ args = parser.parse_args()
263
+ taxon_id = args.taxon_id
264
+
265
+ Selector = ProteomeSelector(taxon_id)
266
+ print(f'Number of candidate proteomes: {Selector.num_of_proteomes}')
267
+ proteome_data = Selector.select_proteome()
268
+ Selector.proteome_to_csv()
269
+
270
+ print(f'Proteome ID: {proteome_data[0]}')
271
+ print(f'Proteome taxon: {int(proteome_data[1])}')
272
+ print(f'Proteome type: {proteome_data[2]}')
273
+
274
+ if __name__ == '__main__':
275
+ main()
@@ -0,0 +1,26 @@
1
+ import argparse
2
+
3
+ from .select_best_proteome import ProteomeSelector
4
+
5
+ def parse_arguments():
6
+ parser = argparse.ArgumentParser(description='UniProtPy')
7
+ parser.add_argument('-t', '--taxon_id', required=True)
8
+ parser.add_argument(
9
+ 'action',
10
+ choices=(
11
+ 'get-best-proteome',
12
+ ),
13
+ help='\"get-best-proteome\" will download the best proteome from UniProt for a given '
14
+ 'taxon ID of a species.'
15
+ )
16
+ args = parser.parse_args()
17
+
18
+ return args
19
+
20
+ def run():
21
+ args = parse_arguments()
22
+ taxon_id = args.taxon_id
23
+ if args.action == 'get-best-proteome':
24
+ ProteomeSelector(taxon_id).select_proteome()
25
+ else:
26
+ raise ValueError('Invalid action.')
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import requests
4
+ import pandas as pd
5
+ import io
6
+
7
+ BASE_URL = 'https://rest.uniprot.org/taxonomy/stream'
8
+
9
+
10
+ def get_taxon_children(taxon_id):
11
+ """Retrives all children taxa for a given taxon ID."""
12
+ url = f'{BASE_URL}?fields=id%2Cscientific_name&format=tsv&query=%28parent%3A{taxon_id}%29'
13
+ response = requests.get(url)
14
+
15
+ try:
16
+ taxon_children_df = pd.read_csv(io.StringIO(response.text), sep='\t')
17
+ children = taxon_children_df.to_dict(orient='records')
18
+ except ValueError:
19
+ children = []
20
+
21
+ return children
22
+
23
+ def traverse_tree(taxon_id):
24
+ """
25
+ Recursively traverse the taxonomy tree for a given taxon ID.
26
+
27
+ It goes down to each leaf node and returns a dict of the form:
28
+ {taxon_id: [child1, {child2: subchild1}, ...]}
29
+
30
+ Taxon IDs that have no children are standalone integers; anything
31
+ else is a list of dicts.
32
+ """
33
+ children = get_taxon_children(taxon_id)
34
+
35
+ if not children:
36
+ return taxon_id
37
+
38
+ child_results = []
39
+ for child in children:
40
+ child_id = child['Taxon Id']
41
+ child_result = traverse_tree(child_id)
42
+
43
+ if isinstance(child_result, int):
44
+ child_results.append(child_result)
45
+ else:
46
+ child_results.append(child_result)
47
+
48
+ return {taxon_id: child_results}
49
+
50
+ def create_taxon_tree(tree, prefix="", file=None):
51
+ """
52
+ Create a taxonomy tree structure from the output of traverse_tree.
53
+
54
+ It looks like this for the homo genus (9605):
55
+ └──9605
56
+ ├──9606
57
+ │ ├──63221
58
+ │ └──741158
59
+ ├──1425170
60
+ ├──2665952
61
+ │ └──2665953
62
+ └──2813598
63
+ └──2813599
64
+ """
65
+ if isinstance(tree, list):
66
+ for i, item in enumerate(tree):
67
+ is_last = i == len(tree) - 1
68
+ if isinstance(item, dict):
69
+ for k, v in item.items():
70
+ print(f"{prefix}{'└──' if is_last else '├──'}{k}", file=file)
71
+ create_taxon_tree(v, f"{prefix}{' ' if is_last else '│ '}", file)
72
+ else:
73
+ print(f"{prefix}{'└──' if is_last else '├──'}{item}", file=file)
74
+
75
+ elif isinstance(tree, dict):
76
+ for i, (key, value) in enumerate(tree.items()):
77
+ is_last = i == len(tree) - 1
78
+ print(f"{prefix}{'└──' if is_last else '├──'}{key}", file=file)
79
+ create_taxon_tree(value, f"{prefix}{' ' if is_last else '│ '}", file)
80
+
81
+ def main():
82
+ import argparse
83
+
84
+ parser = argparse.ArgumentParser()
85
+ parser.add_argument('-t', '--taxon_id', required=True, type=int, help='Taxon ID')
86
+ parser.add_argument('-p', '--pickle', action='store_true', help='Pickle tree output.')
87
+ parser.add_argument('-o', '--output', action='store_true', help='Output tree file.')
88
+
89
+ args = parser.parse_args()
90
+ taxon_id = args.taxon_id
91
+
92
+ taxonomy_tree = traverse_tree(taxon_id)
93
+
94
+ if args.pickle:
95
+ import pickle
96
+ with open('taxonomy_tree.pkl', 'wb') as f:
97
+ pickle.dump(taxonomy_tree, f)
98
+
99
+ if args.output:
100
+ with open(f'{taxon_id}_taxonomy_tree.txt', 'w') as f:
101
+ create_taxon_tree(taxonomy_tree, file=f)
102
+
103
+ print(f'Taxon tree for {taxon_id}:')
104
+ create_taxon_tree(taxonomy_tree)
105
+
106
+ if __name__ == '__main__':
107
+ main()
@@ -0,0 +1 @@
1
+ __version__ = "0.0.1"
@@ -0,0 +1,40 @@
1
+ Metadata-Version: 2.1
2
+ Name: uniprotpy
3
+ Version: 0.0.1
4
+ Summary: A Python library that interfaces with UniProt data.
5
+ Home-page: https://github.com/danielmarrama/uniprotpy
6
+ Author: Daniel Marrama
7
+ Author-email: danielmarrama@gmail.com
8
+ Description-Content-Type: text/markdown
9
+
10
+ # UniProtPy
11
+
12
+ Python library that interfaces with UniProt API.
13
+
14
+ For something like [openvax/pyensembl](https://github.com/openvax/pyensembl) with UniProt.
15
+
16
+ The REST API has changed as of 2022. Many of the ways to extract data from UniProt is now different and there isn't a clean way to interface with it.
17
+
18
+
19
+ ### Goals
20
+ 1. Allow users to pull any kind of data from UniProt.
21
+ 2. Store and query large data using a local database.
22
+ 3. Manipulate and output data in many standard formats.
23
+
24
+ ### Installation
25
+
26
+ ```bash
27
+ pip install uniprotpy
28
+ ```
29
+
30
+ ### Getting a proteome for a species
31
+ ```bash
32
+ uniprotpy get-best-proteome --taxon-id 9606
33
+ ```
34
+
35
+ ### TODO
36
+
37
+ - Retrieve individual entries in all supported formats.
38
+ - Get metadata (protein ID, name, gene, # of isoforms, etc.) for entries.
39
+ - Retrieve proteomes via proteome ID or select "best" proteome based on taxon ID.
40
+ - Query proteomes for a protein by ID, name, seq, or peptide unit.
@@ -0,0 +1,16 @@
1
+ README.md
2
+ setup.py
3
+ uniprotpy/__init__.py
4
+ uniprotpy/extract_proteomes.py
5
+ uniprotpy/protein_tree.py
6
+ uniprotpy/select_best_proteome.py
7
+ uniprotpy/shell.py
8
+ uniprotpy/taxon_tree.py
9
+ uniprotpy/version.py
10
+ uniprotpy.egg-info/PKG-INFO
11
+ uniprotpy.egg-info/SOURCES.txt
12
+ uniprotpy.egg-info/dependency_links.txt
13
+ uniprotpy.egg-info/entry_points.txt
14
+ uniprotpy.egg-info/not-zip-safe
15
+ uniprotpy.egg-info/requires.txt
16
+ uniprotpy.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ uniprotpy = uniprotpy.shell:run
@@ -0,0 +1,2 @@
1
+ pandas>=1.1
2
+ biopython>=1.5
@@ -0,0 +1 @@
1
+ uniprotpy