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.
- uniprotpy-0.0.1/PKG-INFO +40 -0
- uniprotpy-0.0.1/README.md +31 -0
- uniprotpy-0.0.1/setup.cfg +4 -0
- uniprotpy-0.0.1/setup.py +33 -0
- uniprotpy-0.0.1/uniprotpy/__init__.py +3 -0
- uniprotpy-0.0.1/uniprotpy/extract_proteomes.py +93 -0
- uniprotpy-0.0.1/uniprotpy/protein_tree.py +55 -0
- uniprotpy-0.0.1/uniprotpy/select_best_proteome.py +275 -0
- uniprotpy-0.0.1/uniprotpy/shell.py +26 -0
- uniprotpy-0.0.1/uniprotpy/taxon_tree.py +107 -0
- uniprotpy-0.0.1/uniprotpy/version.py +1 -0
- uniprotpy-0.0.1/uniprotpy.egg-info/PKG-INFO +40 -0
- uniprotpy-0.0.1/uniprotpy.egg-info/SOURCES.txt +16 -0
- uniprotpy-0.0.1/uniprotpy.egg-info/dependency_links.txt +1 -0
- uniprotpy-0.0.1/uniprotpy.egg-info/entry_points.txt +2 -0
- uniprotpy-0.0.1/uniprotpy.egg-info/not-zip-safe +1 -0
- uniprotpy-0.0.1/uniprotpy.egg-info/requires.txt +2 -0
- uniprotpy-0.0.1/uniprotpy.egg-info/top_level.txt +1 -0
uniprotpy-0.0.1/PKG-INFO
ADDED
|
@@ -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.
|
uniprotpy-0.0.1/setup.py
ADDED
|
@@ -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,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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uniprotpy
|