Skip to content

Commit cd89d5c

Browse files
Elarwei001claudelauraluebbert
authored
feat(g2p): add gget g2p module for the Genomics 2 Proteins portal (#220)
New module querying the Genomics 2 Proteins (G2P) portal (https://g2p.broadinstitute.org/) for residue-level protein structure/function annotations. The API serves TSV, parsed into a pandas DataFrame. - gget.g2p(gene, uniprot_id, resource='features'|'map'|'alignment', isoform=None, save=False, verbose=True) - 'features': per-residue table (AlphaFold pLDDT, UniProt sites, pockets, PTMs); 'map': gene->transcript->isoform->structure identifiers; 'alignment': residue-level isoform alignment - CLI parser + dispatch in main.py, export in __init__.py, G2P_API in constants.py - Tests (live integration + network-free validation) + docs (g2p.md, updates.md) Resolves #138 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Laura Luebbert, Ph.D. <56094636+lauraluebbert@users.noreply.github.com>
1 parent 64a2ce7 commit cd89d5c

7 files changed

Lines changed: 393 additions & 5 deletions

File tree

docs/src/en/g2p.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
[<kbd> View page source on GitHub </kbd>](https://github.com/scverse/gget/blob/main/docs/src/en/g2p.md)
2+
3+
> Python arguments are equivalent to long-option arguments (`--arg`), unless otherwise specified. Flags are True/False arguments in Python. The manual for any gget tool can be called from the command-line using the `-h` `--help` flag.
4+
# gget g2p 🧬➜🧪
5+
Query the [Genomics 2 Proteins (G2P) portal](https://g2p.broadinstitute.org/) to link genes/proteins to residue-level structural and functional annotations (e.g. AlphaFold pLDDT, UniProt sites, predicted pockets, PTMs), the gene–transcript–protein–isoform–structure map, and isoform alignments.
6+
7+
Returns: A data frame with the requested G2P information.
8+
9+
**Positional argument**
10+
`gene`
11+
Gene symbol, e.g. BRCA1.
12+
13+
**Other required arguments**
14+
`-u` `--uniprot_id`
15+
UniProt accession, e.g. P38398. For `--resource alignment` this is the canonical isoform (e.g. P01130-1).
16+
Tip: find a gene's UniProt ID with [`gget info`](info.md).
17+
18+
**Optional arguments**
19+
`-r` `--resource`
20+
Defines the type of information to return (default: 'features'):
21+
`features`: Per-residue protein feature table (AlphaFold pLDDT, UniProt sites, secondary structure, predicted pockets, PTMs, etc.).
22+
`map`: Gene → transcript → protein isoform → structure map (UniProt/Ensembl/RefSeq/PDB identifiers).
23+
`alignment`: Residue-level sequence alignment between two isoforms (requires `--isoform`; `--uniprot_id` is the canonical isoform).
24+
25+
`-i` `--isoform`
26+
Alternative isoform UniProt accession (e.g. P01130-2). Required when `--resource alignment`. Default: None.
27+
28+
`-o` `--out`
29+
Path to the file the results will be saved in, e.g. path/to/directory/results.json. Default: Standard out.
30+
Python: `save=True` will save the output in the current working directory.
31+
32+
**Flags**
33+
`-csv` `--csv`
34+
Command-line only. Returns results in CSV format instead of JSON.
35+
Python: Use `json=False` (default) to return a data frame.
36+
37+
`-q` `--quiet`
38+
Command-line only. Prevents progress information from being displayed.
39+
Python: Use `verbose=False` to prevent progress information from being displayed.
40+
41+
### Examples
42+
```bash
43+
# Per-residue protein features for BRCA1 (AlphaFold pLDDT, UniProt sites, ...)
44+
gget g2p BRCA1 -u P38398
45+
```
46+
```python
47+
# Python
48+
gget.g2p("BRCA1", uniprot_id="P38398", resource="features")
49+
```
50+
&rarr; Returns a data frame with one row per residue of the BRCA1 protein (UniProt P38398) and its structural/functional annotations.
51+
52+
<br/><br/>
53+
54+
```bash
55+
# Gene -> transcript -> isoform -> structure map (CSV)
56+
gget g2p BRCA1 -u P38398 -r map --csv
57+
```
58+
```python
59+
# Python
60+
gget.g2p("BRCA1", uniprot_id="P38398", resource="map")
61+
```
62+
&rarr; Returns the mapping of BRCA1 to its UniProt isoforms, Ensembl/RefSeq identifiers, and PDB structures.
63+
64+
<br/><br/>
65+
66+
```bash
67+
# Residue-level alignment between two LDLR isoforms
68+
gget g2p LDLR -u P01130-1 -r alignment -i P01130-2
69+
```
70+
```python
71+
# Python
72+
gget.g2p("LDLR", uniprot_id="P01130-1", resource="alignment", isoform="P01130-2")
73+
```
74+
&rarr; Returns the residue-level alignment between LDLR isoforms P01130-1 and P01130-2.
75+
76+
# References
77+
If you use `gget g2p` in a publication, please cite the following articles:
78+
79+
- Luebbert, L., & Pachter, L. (2023). Efficient querying of genomic reference databases with gget. Bioinformatics. [https://doi.org/10.1093/bioinformatics/btac836](https://doi.org/10.1093/bioinformatics/btac836)
80+
81+
- Kwon, S., Safer, J., Nguyen, D.T., et al. Genomics 2 Proteins portal: a resource and discovery tool for linking genetic screening outputs to protein sequences and structures. Nature Methods (2024). [https://doi.org/10.1038/s41592-024-02409-0](https://doi.org/10.1038/s41592-024-02409-0)

docs/src/en/updates.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
- Note: the new primate species require `census_version="2025-11-08"` (LTS) or newer.
99
- Docs/README: updated `gget` repository and manual URLs from `pachterlab` to `scverse` (`github.com/scverse/gget`, `scverse.org/gget`) to reflect the project's move under the scverse organization. Links to separate resources (`pachterlab/gget_examples`, `pachterlab/kvar`, `pachterlab/varseek`, and the Pachter Lab homepage) were left unchanged. Resolves [issue 217](https://github.com/scverse/gget/issues/217).
1010

11+
- [`gget g2p`](g2p.md): **New module** to query the [Genomics 2 Proteins (G2P) portal](https://g2p.broadinstitute.org/) for residue-level protein structure/function annotations — per-residue features (AlphaFold pLDDT, UniProt sites, predicted pockets, PTMs), the gene–transcript–protein–isoform–structure map, and isoform alignments. Resolves [issue 138](https://github.com/scverse/gget/issues/138).
12+
1113
**Version ≥ 0.30.6** (Jun 10, 2026):
1214
- [`gget blat`](blat.md): Improved resilience against UCSC BLAT endpoint failures (fixes intermittently failing tests).
1315
- Added retry-with-exponential-backoff for transient failures (HTTP 429/5xx, network errors, and non-JSON 200 responses caused by UCSC rate-limiting or HTML error pages). Up to 4 attempts with 1.5s → 3s → 6s backoff.

gget/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
from .gget_bgee import bgee
1010
from .gget_blast import blast
1111
from .gget_blat import blat
12+
from .gget_enrichr import enrichr
13+
from .gget_archs4 import archs4
14+
from .gget_alphafold import alphafold
15+
from .gget_setup import setup
16+
from .gget_pdb import pdb
17+
from .gget_g2p import g2p
18+
from .gget_gpt import gpt
1219
from .gget_cbio import cbio_plot, cbio_search
1320
from .gget_cellxgene import cellxgene
1421
from .gget_cosmic import cosmic

gget/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
# RCSB PDB API for gget pdb
3030
RCSB_PDB_API = "https://data.rcsb.org/rest/v1/core/"
3131

32+
# Genomics 2 Proteins (G2P) portal REST API for gget g2p
33+
G2P_API = "https://g2p.broadinstitute.org/api"
34+
3235
# API to get PDB entries from Ensembl IDs
3336
ENS_TO_PDB_API = "https://www.ebi.ac.uk/pdbe/aggregated-api/mappings/ensembl_to_pdb/"
3437

gget/gget_g2p.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import io
2+
import pandas as pd
3+
import requests
4+
5+
from .constants import G2P_API
6+
from .utils import set_up_logger, DEFAULT_REQUESTS_TIMEOUT
7+
8+
logger = set_up_logger()
9+
10+
11+
def g2p(
12+
gene,
13+
uniprot_id,
14+
resource="features",
15+
isoform=None,
16+
save=False,
17+
verbose=True,
18+
):
19+
"""
20+
Query the Genomics 2 Proteins (G2P) portal (https://g2p.broadinstitute.org/) to link
21+
genes/proteins to per-residue structural and functional annotations.
22+
23+
Args:
24+
- gene Gene symbol, e.g. "BRCA1" (str).
25+
- uniprot_id UniProt accession, e.g. "P38398" (str). For resource="alignment" this is the
26+
canonical isoform accession (e.g. "P01130-1").
27+
Tip: find a gene's UniProt ID with 'gget info'.
28+
- resource Type of information to return (default: "features"):
29+
"features": per-residue feature table (AlphaFold pLDDT, UniProt sites,
30+
secondary structure, predicted pockets, PTMs, etc.).
31+
"map": gene -> transcript -> protein isoform -> structure map
32+
(UniProt/Ensembl/RefSeq/PDB identifiers).
33+
"alignment": residue-level alignment between two isoforms (requires 'isoform';
34+
'uniprot_id' is the canonical isoform).
35+
- isoform Alternative isoform UniProt accession (e.g. "P01130-2"). Required when
36+
resource="alignment" (default: None).
37+
- save If True, save the result as a CSV in the current working directory (default: False).
38+
- verbose True/False whether to print progress information (default: True).
39+
40+
Returns a pandas DataFrame with the requested G2P information.
41+
"""
42+
resources = ["features", "map", "alignment"]
43+
if resource not in resources:
44+
raise ValueError(
45+
f"'resource' argument specified as {resource}. Expected one of: {', '.join(resources)}"
46+
)
47+
48+
if not uniprot_id:
49+
raise ValueError(
50+
"Please provide a UniProt accession as 'uniprot_id' (e.g. 'P38398'). "
51+
"You can find the UniProt ID for a gene using 'gget info'."
52+
)
53+
54+
if resource == "alignment" and not isoform:
55+
raise ValueError(
56+
"resource='alignment' requires an alternative isoform UniProt accession as 'isoform' "
57+
"(e.g. 'P01130-2'). 'uniprot_id' is the canonical isoform (e.g. 'P01130-1')."
58+
)
59+
60+
# The G2P REST API returns tab-separated values
61+
base = f"{G2P_API}/gene/{gene}/protein/{uniprot_id}"
62+
if resource == "features":
63+
url = f"{base}/protein-features"
64+
elif resource == "map":
65+
url = f"{base}/gene-transcript-protein-isoform-structure-map"
66+
else: # alignment
67+
url = f"{base}/{isoform}/alignment"
68+
69+
if verbose:
70+
logger.info(
71+
f"Querying the Genomics 2 Proteins portal ('{resource}') for {gene} / {uniprot_id}..."
72+
)
73+
74+
try:
75+
r = requests.get(
76+
url,
77+
timeout=DEFAULT_REQUESTS_TIMEOUT,
78+
headers={"Accept": "text/tab-separated-values"},
79+
)
80+
except requests.exceptions.RequestException as e:
81+
logger.error(f"Request to the Genomics 2 Proteins portal failed: {e}")
82+
return
83+
84+
if not r.ok:
85+
logger.error(
86+
f"G2P query (gene='{gene}', uniprot_id='{uniprot_id}', resource='{resource}') "
87+
f"returned status code {r.status_code}. Please double-check that the gene symbol "
88+
f"and UniProt accession match, then try again."
89+
)
90+
return
91+
92+
if not r.text.strip():
93+
logger.warning(
94+
"The Genomics 2 Proteins portal returned an empty result for this query."
95+
)
96+
return pd.DataFrame()
97+
98+
df = pd.read_csv(io.StringIO(r.text), sep="\t")
99+
100+
if save:
101+
out_name = f"gget_g2p_{gene}_{uniprot_id}_{resource}.csv"
102+
df.to_csv(out_name, index=False)
103+
if verbose:
104+
logger.info(f"Results saved as {out_name}.")
105+
106+
return df

gget/main.py

Lines changed: 130 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,30 @@
3535
from .gget_pdb import pdb # noqa: E402
3636

3737
# Module functions
38-
from .gget_ref import ref # noqa: E402
39-
from .gget_search import search # noqa: E402
40-
from .gget_seq import seq # noqa: E402
41-
from .gget_setup import setup # noqa: E402
42-
from .gget_virus import virus # noqa: E402
38+
from .gget_ref import ref
39+
from .gget_search import search
40+
from .gget_info import info
41+
from .gget_seq import seq
42+
from .gget_muscle import muscle
43+
from .gget_blast import blast
44+
from .gget_blat import blat
45+
from .gget_enrichr import enrichr
46+
from .gget_archs4 import archs4
47+
from .gget_alphafold import alphafold
48+
from .gget_setup import setup
49+
from .gget_pdb import pdb
50+
from .gget_gpt import gpt
51+
from .gget_cellxgene import cellxgene
52+
from .gget_elm import elm
53+
from .gget_diamond import diamond
54+
from .gget_cosmic import cosmic
55+
from .gget_mutate import mutate
56+
from .gget_opentargets import opentargets, OPENTARGETS_RESOURCES
57+
from .gget_cbio import cbio_plot, cbio_search
58+
from .gget_bgee import bgee
59+
from .gget_g2p import g2p
60+
from .gget_8cube import specificity, psi_block, gene_expression
61+
from .gget_virus import virus
4362

4463

4564
# Custom formatter for help messages that preserved the text formatting and adds the default value to the end of the help message
@@ -2324,6 +2343,77 @@ def main():
23242343
help="Does not print progress information.",
23252344
)
23262345

2346+
## g2p parser arguments
2347+
g2p_desc = "Query the Genomics 2 Proteins (G2P) portal for residue-level protein structure/function annotations."
2348+
parser_g2p = parent_subparsers.add_parser(
2349+
"g2p",
2350+
parents=[parent],
2351+
description=g2p_desc,
2352+
help=g2p_desc,
2353+
add_help=True,
2354+
formatter_class=CustomHelpFormatter,
2355+
)
2356+
parser_g2p.add_argument(
2357+
"gene",
2358+
type=str,
2359+
help="Gene symbol, e.g. BRCA1.",
2360+
)
2361+
parser_g2p.add_argument(
2362+
"-u",
2363+
"--uniprot_id",
2364+
type=str,
2365+
required=True,
2366+
help="UniProt accession, e.g. P38398. For '--resource alignment' this is the canonical isoform (e.g. P01130-1). Find it with `gget info`.",
2367+
)
2368+
parser_g2p.add_argument(
2369+
"-r",
2370+
"--resource",
2371+
type=str,
2372+
choices=["features", "map", "alignment"],
2373+
default="features",
2374+
required=False,
2375+
help=(
2376+
"Type of information to return (default: features):\n"
2377+
"'features': per-residue feature table (AlphaFold pLDDT, UniProt sites, etc.).\n"
2378+
"'map': gene -> transcript -> protein isoform -> structure map.\n"
2379+
"'alignment': residue-level alignment between two isoforms (requires --isoform)."
2380+
),
2381+
)
2382+
parser_g2p.add_argument(
2383+
"-i",
2384+
"--isoform",
2385+
type=str,
2386+
default=None,
2387+
required=False,
2388+
help="Alternative isoform UniProt accession (e.g. P01130-2). Required for '--resource alignment'.",
2389+
)
2390+
parser_g2p.add_argument(
2391+
"-o",
2392+
"--out",
2393+
type=str,
2394+
required=False,
2395+
help=(
2396+
"Path to the file the results will be saved in, e.g. path/to/directory/results.json.\n"
2397+
"Default: Standard out."
2398+
),
2399+
)
2400+
parser_g2p.add_argument(
2401+
"-csv",
2402+
"--csv",
2403+
default=False,
2404+
action="store_true",
2405+
required=False,
2406+
help="Returns results in csv format instead of json.",
2407+
)
2408+
parser_g2p.add_argument(
2409+
"-q",
2410+
"--quiet",
2411+
default=True,
2412+
action="store_false",
2413+
required=False,
2414+
help="Does not print progress information.",
2415+
)
2416+
23272417
## gget 8cube subparser
23282418
cube_desc = "Query 8cubeDB (https://eightcubedb.onrender.com/)."
23292419
parser_8cube = parent_subparsers.add_parser(
@@ -2849,6 +2939,7 @@ def main():
28492939
"setup": parser_setup,
28502940
"alphafold": parser_alphafold,
28512941
"pdb": parser_pdb,
2942+
"g2p": parser_g2p,
28522943
"gpt": parser_gpt,
28532944
"cellxgene": parser_cellxgene,
28542945
"elm": parser_elm,
@@ -3717,6 +3808,40 @@ def main():
37173808
else:
37183809
print(bgee_results.to_json(orient="records", force_ascii=False, indent=4))
37193810

3811+
## g2p return
3812+
if args.command == "g2p":
3813+
g2p_results: pd.DataFrame = g2p(
3814+
args.gene,
3815+
uniprot_id=args.uniprot_id,
3816+
resource=args.resource,
3817+
isoform=args.isoform,
3818+
verbose=args.quiet,
3819+
)
3820+
3821+
if g2p_results is None:
3822+
return
3823+
3824+
if args.out is not None and args.out != "":
3825+
# Make saving directory
3826+
directory = os.path.dirname(args.out)
3827+
if directory != "":
3828+
os.makedirs(directory, exist_ok=True)
3829+
3830+
with open(args.out, "w", encoding="utf-8") as f:
3831+
if args.csv:
3832+
g2p_results.to_csv(f, index=False)
3833+
else:
3834+
g2p_results.to_json(
3835+
f, orient="records", force_ascii=False, indent=4
3836+
)
3837+
else:
3838+
if args.csv:
3839+
g2p_results.to_csv(sys.stdout, index=False)
3840+
else:
3841+
print(
3842+
g2p_results.to_json(orient="records", force_ascii=False, indent=4)
3843+
)
3844+
37203845
## 8cube return
37213846
if args.command == "8cube":
37223847
from .gget_8cube import gene_expression, psi_block, specificity

0 commit comments

Comments
 (0)