Entrez és un motor de cerca de l'institució pública NCBI que integra diverses bases de dades de ciències de la salut. Ofereix de manera gratuïta molta informació genòmica d'alt rendiment, a través de la API anonimitzada d'Entrez.
NCBI
El Centre Nacional d’Informació Biotecnològica (NCBI)
és una institució pública del govern dels Estats Units que ofereix de manera gratuïta molta informació biomèdica i genòmica.
Pots accedir al seu lloc web a http://www.ncbi.nlm.nih.gov/ i obtenir qualsevol dada de seqüències biològiques en diverses formes: des de genomes complets fins a proteïnes, polimorfismes de nucleòtids
únics (SNP
) o dades de seqüències d'alt rendiment carregades.
L’NCBI i altres bancs de dades publiquen mostres anònimes autoritzades
per tal de prevenir MTS
.
Entrez
Entrez
és un motor de cerca del NCBI que integra diverses bases de dades de ciències de la salut.
Des d'una única pàgina web pots cercar en diferents conjunts de dades com ara literatura científica, bases de dades de seqüències d'ADN i proteïnes, dades d'estructura de proteïnes en 3D
i domini de proteïnes, dades d'expressió gènica, conjunts de genomes complets
i informació taxonòmica
.
Aquest motor de cerca està disponible a http://www.ncbi.nlm.nih.gov/sites/gquery com aplicació web.
eUtils
L'NCBI també ha creat les eUtils
("Entrez Programming Utilities"), un conjunt de 9 eines del costat del servidor per consultar la base de dades Entrez sense un navegador web:
- EGQuery
- ECitMatch
- EInfo
- ELink
- EPost
- ESearch
- ESpell
- EFetch
- ESummary
Aqueste eines porten molt anys en funcionament i s’accedeixen mitjançant una API web de manera flexible.
L’objectiu és crear una interfície comuna per accedir a bases de dades de característiques diferents: les dades de publicacions mèdiques són diferents a les de les proteïnes o de les de seqüències genètiques.
Per a consultar una base de dades has de crear una URL
específica amb el nom del programa que s'utilitzarà al servidor web del NCBI i tots els paràmetres necessaris (com el nom de la base de dades i els termes de cerca), tal com s’explica en aquest document:
La resposta serà un document amb les dades en el format corresponent.
Totes les URLs E-utility
comparteixen el mateix URL base:
Pots fer una cerca básica amb aquesta eina i aquests paràmetres:
esearch.fcgi?db=<database>&term=<query>
Per exemple, pots buscar tots els identificadors de PubMed (PMID) per als articles sobre càncer de mama publicats a Science el 2008 amb aquesta URL:
Pots veure que el resultat és un document XML
, que era el format estàndard d’intercanvi de dades fins l’aparició de JSON
, i és el format per defecte que encara fa servir eUtils per compatibilitat.
Si vols la mateixa informació amb JSON pots utilitzar l’argument retmode=json
.
https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=science[journal]+AND+breast+cancer+AND+2008[pdat]&retmode=json
term
Els documents que estan en la base de dades de PubMed
estan marcats amb tags
que tenen metadata dels documents.
Cada base de dades té uns tags específics, encara que en molts casos són coincidents amb altres bases de dades.
En la consulta anterior pots veure que es fan servir tags per restringir el camp de búsqueda en el paràmetre term.
Per exemple science[journal]
per restringir la paraula science
al tag journal
.
En aquest enllaç tens més informació del camps de cerca i els tags:
PubMed també ofereix "cerca de proximitat" per a diversos termes que apareixen en qualsevol ordre dins d'un nombre determinat de paraules entre si als camps [Title]
o [Title/Abstract]
.
Python
En el seu moment Perl era el llenguatge d’Internet i el NCBI té scripts en aquest llenguatge.
Però avui en dia el llenguatge que es fa servir és Python.
Com que l’accés a eUtils
és mitjançant una API Web, pots crear un programa en Python per consultar dades a Entrez mitjançant una sol.licitud HTTP
:
./scripts/entrez.py
db = "pubmed"
term = "science[journal] AND breast cancer AND 2008[pdat]"
# https://www.ncbi.nlm.nih.gov/books/NBK25500/
url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
term = urllib.parse.quote_plus(term)
response = urllib3.request(
"GET",
url,
fields={"db": db, "term": term})
data = response.data.decode("utf-8")
print(data)
Però com pots saber quina informació tens disponible?
NCBI utilitza fitxers DTD
per descriure l'estructura de la informació continguda en fitxers XML
tal com pots veure en el document de resposta:
Amb el paràmetre retmode pots escollir el format de tornada del document. El valor per defecte és xml
, però pots utilitzar json
perquè és més ràpid de processar.
Modifica el programa perquè torni el document en JSON.
./scripts/entrez-json.py
import json
import urllib.parse
import urllib3
db = "pubmed"
term = "science[journal] AND breast cancer AND 2008[pdat]"
url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
term = urllib.parse.quote_plus(term)
response = urllib3.request(
"GET",
url,
fields={"db": db, "term": term, "retmode": "json"}
)
data = response.json()
print(json.dumps(data["esearchresult"], indent=2))
Pots veure el resultat formatejat perquè els puguis veure bé:
{
"count": "14597",
"retmax": "20",
"retstart": "0",
"idlist": [
"39825989",
"39825944",
"39824498",
A diferència de XML, fer un “parsing” de JSON és més ràpid que en XML i les respostes en JSON es poden utilitzar directament per les aplicacions web.
En aquest enllaç tens totes les opcions disponibles: https://www.ncbi.nlm.nih.gov/books/NBK25499/#_chapter4_ESearch_
Limitacions
Pots realitzar com a màxim 3 consultes per segon i per IP, sense importar el temps que tardi la consulta en completar-se.
Les altres consultes dins del mateix segon tornen error.
Et pots registrar al NCBI per obtenir una API key tal com s’explica a l'article New API Keys for the E-utilities, i llavors pots realitzar 10 consultes per segon, encara que comparteixis IP.
Biopython
Enlloc de crear la teva llibreria en Python, pots utilitzar el mòdul Bio.Entrez
de BioPython que proporciona funcions per utilitzar tots els programes eUtils.
Al capítol 12 del tutorial de Biopython podem veure com usar tots els programes: https://biopython.org/DIST/docs/tutorial/Tutorial.html#sec19
ESearch
- https://pubmed.ncbi.nlm.nih.gov/ és un motor de cerca per a MEDLINE, una base de dades bibliogràfica de ciències de la vida i informació biomèdica.
JSON
Aquest script consulta la base de dades PubMed
mitjançant Entrez.
Fixa't que al fer consultes amb Entrez un dels requeriments necessaris és que informis un camp anomenat email (pex email = "david@xtec.dev"
) per tal que l'NCBI pugui avisar als usuaris si de forma accidental fan un mal ús de la API (pex no tancar bé un bucle).
./scripts/pubmed.py
from Bio import Entrez
import json
db = "pubmed"
term = "python and bioinformatics"
email = "david@xtec.dev"
def with_xml():
record = Entrez.read(Entrez.esearch(db, term, email=email))
for id in record["IdList"]:
summary = Entrez.read(Entrez.esummary(db=db, id=id, email=email))
summary = summary[0]
print("Title = {}".format(summary["Title"]))
print("DOI = {}".format(summary["DOI"]))
print("==============================================")
quit()
def with_json():
record = json.loads(Entrez.esearch(db, term, email=email, retmode="json").read())
for id in record["esearchresult"]["idlist"]:
summary = json.loads(Entrez.esummary(db=db, id=id, email=email, retmode="json").read())
summary = summary["result"][id]
print("Title = {}".format(summary["title"]))
print("DOI = {}".format(summary["elocationid"]))
print("==============================================")
quit()
with_json()
Com que saps que per defecte la base de dades “pubmed” torna el resulta amb XML, fas servir el mètode Entrez.read
per fer un parse del fitxer XML i convertir-lo en un diccionari Python.
El resultat és una llista de publicacions:
$ python entrez-pubmed.py
Title = MetAssimulo 2.0: a web app for simulating realistic 1D & 2D Metabolomic 1H NMR spectra.
DOI = pii: btaf045. doi: 10.1093/bioinformatics/btaf045
Tots els arguments que tens disponibles al crear la URL els pots utilitzar en la funció esearch
.
Si poses el ratolí sobre la funció esearch apareixerà el menú d’ajuda, i dins el menú l’enllaç al document que hem vist abans:
Podem modificar el mètode per a què ens mostri directament la resposta en JSON amb l'atribut retmode="json"
i el mètode read()
i transformem el text “json” en un diccionari.
XML+DTD
Un dels motius pels qual JSON
és més ràpid que XML és perquè el parser del Bio.Entrez
utilitza els fitxers DTD
quan analitza un fitxer XML
retornat per NCBI Entrez.
Per evitar haver de descarregar el fitxer DTD la distribució de Biopython incorpora fitxers dtd
, entre d'altres:
EInfo
En aquest enllaç tens una descripció de les bases de dades disponibles:
Amb einfo
pots obtenir:
- Una llista de totes les bases de dades disponibles.
- Una descripció, estadístiques, etc. de cada base dades.
A més, pots utilitzar EInfo per obtenir una llista de tots els noms de bases de dades disponibles mitjançant les utilitats Entrez.
./scripts/einfo.py
from Bio import Entrez
import json
email = "david@xtec.dev"
def with_xml():
# Return a list of all Entrez database names:
with Entrez.einfo(email=email) as response:
record = Entrez.read(response)
assert ("pubmed" in record["DbList"])
# Return statistics for Entrez PubMed:
with Entrez.einfo(db="pubmed", email=email) as response:
record = Entrez.read(response)["DbInfo"]
assert (record["DbName"] == "pubmed")
print(record["Count"])
Com amb la funció anterior també podem obtenir la informació amb JSON:
./scripts/einfo.py (cont.)
def with_json():
with Entrez.einfo(retmode="json", email=email) as response:
record = json.load(response)
assert ("pubmed" in record["einforesult"]["dblist"])
with Entrez.einfo(db = "pubmed", retmode="json", email=email) as response:
record = json.load(response)
#print(json.dumps(record, indent= 2))
info = record["einforesult"]["dbinfo"][0]
assert(info["dbname"] == "pubmed")
print(info["count"])
Fields
Una de les entrades més interessant és FieldList
que et mostra possibles camps de cerca que pots utilitzar amb ESearch
en aquesta base de dades:
./scripts/einfo.py (cont.)
def list_fields():
with Entrez.einfo(db="pubmed", email=email) as response:
record = Entrez.read(response)["DbInfo"]
print("Name\tFullName\tDescription")
print("-----------------------------------------------------------------------")
for field in record["FieldList"]:
print("%(Name)s\t'%(FullName)s'\t %(Description)s" % field)
list_fields()
Pots veure que és una llista bastant llarga:
$ python einfo.py
Name FullName Description
-----------------------------------------------------------------------
ALL 'All Fields' All terms from all searchable fields
UID 'UID' Unique number assigned to publication
FILT 'Filter' Limits the records
TITL 'Title' Words in title of publication
MESH 'MeSH Terms' Medical Subject Headings assigned to publication
MAJR 'MeSH Major Topic' MeSH terms of major importance to publication
JOUR 'Journal' Journal abbreviation of publication
AFFL 'Affiliation' Author's institutional affiliation and address
Si no estàs familiaritzat amb una base de dades concreta pots obtenir informació molt interessant.
Per exemple, si fas una consulta a la base de dades Pubmed pots buscar per autor amb Jones[AUTH]
o restringir els autors al Centre Sanger amb Sanger[AFFL]
.
Llista bases de dades i la seva descripció.
Si només volem recordar la llista de bases de dades, usem aquest codi:
from Bio import Entrez
Entrez.email = "mamoro10@xtec.cat"
# Obté la informació de totes les bases de dades amb una sola consulta
handle = Entrez.einfo(email=Entrez.email)
rec = Entrez.read(handle)
print(rec['DbList'])
handle.close()
Resultat:
['pubmed', 'protein', 'nuccore', 'ipg', 'nucleotide', 'structure', 'genome', 'annotinfo', 'assembly', 'bioproject', 'biosample', 'blastdbinfo', 'books', 'cdd', 'clinvar', 'gap', 'gapplus', 'grasp', 'dbvar', 'gene', 'gds', 'geoprofiles', 'medgen', 'mesh', 'nlmcatalog', 'omim', 'orgtrack', 'pmc', 'popset', 'proteinclusters', 'pcassay', 'protfam', 'pccompound', 'pcsubstance', 'seqannot', 'snp', 'sra', 'taxonomy', 'biocollections', 'gtr']
Per obtenir la descripció necessitem fer una nova cerca einfo
a cada base de dades per separat.
ESummary
Gene és una base de dades de gens, centrada en genomes que s'han seqüenciat completament i que tenen una comunitat de recerca activa per aportar dades específiques de gens.
La informació dels registres genètics inclou la nomenclatura, la localització cromosòmica, els productes gènics i els seus atributs (per exemple, les interaccions de proteïnes), marcadors associats, fenotips, interaccions i enllaços a citacions, seqüències, detalls de variacions, mapes, informes d'expressió, homòlegs, contingut de domini proteic, i bases de dades externes.
Com que eUtils és una interfície per a diverses bases de dades, pots utilitzar la mateixa estructura del programa d’abans per obtenir informació gènica.
Aquest script consulta la base de dades Gene mitjançant Entrez:
from Bio import Entrez
db = "gene"
term = "cobalamin synthase homo sapiens"
email = "david@xtec.dev"
with Entrez.esearch(db, term, email=email) as response:
record = Entrez.read(response)
for id in record["IdList"]:
with Entrez.esummary(db=db, id=id, email=email) as response:
summary = Entrez.read(response)
summary = summary["DocumentSummarySet"]["DocumentSummary"][0]
#print(summary.keys())
print("Id = {}".format(id))
print("Name = {}".format(summary["Name"]))
print("Description = {}".format(summary["Description"]))
print("Summary = {}".format(summary["Summary"]))
print("==============================================")
quit()
La principal diferència és el document de resposta és diferent:
Id = 1956
Name = EGFR
Description = epidermal growth factor receptor
Summary = The protein encoded by this gene is a transmembrane glycoprotein that is a member of the protein kinase superfamily. This protein is a receptor for members of the epidermal growth factor family. EGFR is a cell surface protein that binds to epidermal growth factor, thus inducing receptor dimerization and tyrosine autophosphorylation leading to cell proliferation. Mutations in this gene are associated with lung cancer. EGFR is a component of the cytokine storm which contributes to a severe form of Coronavirus Disease 2019 (COVID-19) resulting from infection with severe acute respiratory syndrome coronavirus-2 (SARS-CoV-2). [provided by RefSeq, Jul 2020]
FTP
Encara que és útil utilitzar Entrez per buscar informació, si necessites tenir accés a totes les dades pots baixar tot el contingut amb ftp desde aquesta adreça:
Encara que el lloc digui FTP també et pots descarregar el contingut am HTTP:
També et pots subscriure al Gene News RSS feed per rebre notificacions de quan s’afegeixen o modifiquen fitxers:
Descarrega FTP amb Python
Escriurem un script que baixi el fitxer gene_orthologs
.
Els gens ortòlegs són aquells gens en espècies diferents que provenen d'un antecessor comú i que mantenen una funció similar. Aquesta informació és essencial per a l'estudi de l'evolució genètica i per comprendre com els gens s'han adaptat a diferents espècies al llarg del temps.
./scripts/gene-orthologs.py
import gzip, os, shutil, urllib3
file ="gene_orthologs"
#####
dir = "data/gene"
if not os.path.isdir(dir):
os.makedirs(dir)
file_gz = "{}/{}.gz".format(dir,file)
file_tsv = "{}/{}.tsv".format(dir,file)
url = "https://ftp.ncbi.nih.gov/gene/DATA/{}.gz".format(file)
if not os.path.exists(file_tsv):
print("Downloading file {} ...".format(file_gz))
with urllib3.request("GET", url, preload_content= False) as response:
with open(file_gz, "wb") as writer:
for chunk in response.stream(10*1024):
writer.write(chunk)
print("Extracting file ...")
with gzip.open(file_gz,"rb") as f_in, open(file_tsv,"wb") as f_out:
shutil.copyfileobj(f_in, f_out)
os.remove(file_gz)
Tingueu paciència, la primera vegada sol fallar; a més a més, ell fitxer és gran un cop descomprimit.
El resultat és un fitxer en format TSV
, com el CSV
, però enlloc de separar el valors amb comes és fa amb Tabs
.
A continuació podries carregar el fitxer amb Polars… però ara no toca.
EFetch
https://www.ncbi.nlm.nih.gov/nucleotide/ és una col·lecció de seqüències de diverses fonts, com GenBank, RefSeq, TPA i PDB.
Les dades del genoma, el gen i la seqüència de transcripció proporcionen la base per a la investigació i el descobriment biomèdic.
Amb efetch
pots recuperar un registre complet Entrez de diverses bases de dades:
Per a la majoria de les seves bases de dades, l'NCBI admet diversos formats de fitxer diferents tal com es descriu en aquesta taula:
Per sol·licitar un format de fitxer específic has de fer servir els arguments opcionals rettype
i/o retmode
.
genbank
Un ús comú és descarregar seqüències en els formats de text senzill FASTA
o GenBank/GenPept
, que després es poden analitzar amb Bio.SeqIO
.
Baixa el fitxer Genbank de l’orquidea “Cypripedioideae” :
from Bio import Entrez
id = "186972394"
email = "david@xtec.dev"
with Entrez.efetch("nuccore", id=id, rettype="gb", retmode="text", email=email) as response:
print(response.read())
El resultat és un fitxer genbank en format text:
LOCUS EU490707 1302 bp DNA linear PLN 26-JUL-2016
DEFINITION Selenipedium aequinoctiale maturase K (matK) gene, partial cds;
chloroplast.
ACCESSION EU490707
VERSION EU490707.1
KEYWORDS .
SOURCE chloroplast Selenipedium aequinoctiale
ORGANISM Selenipedium aequinoctiale
Eukaryota; Viridiplantae; Streptophyta; Embryophyta; Tracheophyta;
Spermatophyta; Magnoliopsida; Liliopsida; Asparagales; Orchidaceae;
Cypripedioideae; Selenipedium.
Per defecte efetch retorna els documents en format XML, per això és necessari passar l’argument retmode = “text”
.
Enlloc d’imprimir el fitxer per pantalla, el més normal és desar les dades de la seqüència en un fitxer local i després analitzar-les amb Bio.SeqIO.
./scripts/nuccore.py
from Bio import Entrez, SeqIO
import os
id = "186972394"
email = "david@xtec.dev"
dir = "data/nuccore"
if not os.path.isdir(dir):
os.makedirs(dir)
filename = "{}/gi_{}.gbk".format(dir, id)
if not os.path.isfile(filename):
print("Downloading file ...")
with Entrez.efetch("nuccore", id=id, rettype="gb", retmode="text", email=email) as response:
with open(filename, "w") as out_handle:
out_handle.write(response.read())
print("Saved file.")
record = SeqIO.read(filename, "genbank")
print(record.seq)
El resultat és la seqüència:
Downloading file ...
Saved file.
ATTTTTTACGAACCTGTGGAAATTTTTGGTTATGACAATAAATCTAGTTTAGTACTTGTGAAACGTTTAATTACTCGAATGTATCAACAGAATTTTTTGATTTCTTCGGTTAATGATTCTAACCAAAAAGGATTTTGGGGGCACAAGCATTTTTTTTCTTCTCATTTTTCTTCTCAAATGGTATCAGAAGGTTTTGGAGTCATTCTGGAAATTCCATTCTCGTCGCAATTAGTATCTTCTCTTGAAGAAAAAAAAATACCAAAATATCAGAATTTACGATCTATTCATTCAATATTTCCCTTTTTAGAAGACAAATTTTTACATTTGAATTATGTGTCAGATCTACTAATACCCCATCCCATCCATCTGGAAATCTTGGTTCAAATCCTTCAATGCCGGATCAAGGATGTTCCTTCTTTGCATTTATTGCGATTGCTTTTCCACGAATATCATAATTTGAATAGTCTCATTACTTCAAAGAAATTCATTTACGCCTTTTCAAAAAGAAAGAAAAGATTCCTTTGGTTACTATATAATTCTTATGTATATGAATGCGAATATCTATTCCAGTTTCTTCGTAAACAGTCTTCTTATTTACGATCAACATCTTCTGGAGTCTTTCTTGAGCGAACACATTTATATGTAAAAATAGAACATCTTCTAGTAGTGTGTTGTAATTCTTTTCAGAGGATCCTATGCTTTCTCAAGGATCCTTTCATGCATTATGTTCGATATCAAGGAAAAGCAATTCTGGCTTCAAAGGGAACTCTTATTCTGATGAAGAAATGGAAATTTCATCTTGTGAATTTTTGGCAATCTTATTTTCACTTTTGGTCTCAACCGTATAGGATTCATATAAAGCAATTATCCAACTATTCCTTCTCTTTTCTGGGGTATTTTTCAAGTGTACTAGAAAATCATTTGGTAGTAAGAAATCAAATGCTAGAGAATTCATTTATAATAAATCTTCTGACTAAGAAATTCGATACCATAGCCCCAGTTATTTCTCTTATTGGATCATTGTCGAAAGCTCAATTTTGTACTGTATTGGGTCATCCTATTAGTAAACCGATCTGGACCGATTTCTCGGATTCTGATATTCTTGATCGATTTTGCCGGATATGTAGAAATCTTTGTCGTTATCACAGCGGATCCTCAAAAAAACAGGTTTTGTATCGTATAAAATATATACTTCGACTTTCGTGTGCTAGAACTTTGGCACGGAAACATAAAAGTACAGTACGCACTTTTATGCGAAGATTAGGTTCGGGATTATTAGAAGAATTCTTTATGGAAGAAGAA
fasta
Si volem el fitxer en format fasta només hem de canviar l’argument rettype
de Entrez.fetch
i l’argument format de SeqIO.read
:
filename = "{}/gi_{}.fasta".format(dir, id)
if not os.path.isfile(filename):
print("Downloading file ...")
with Entrez.efetch("nuccore", id=id, rettype="fasta", retmode="text", email=email) as response:
with open(filename, "w") as out_handle:
out_handle.write(response.read())
print("Saved file.")
record = SeqIO.read(filename, "fasta")
print(record.seq)
ELink
Les diferents bases de dades de Entrez estan relacionades i pots utilitzar la funció ELink per trobar aquestes relacions tal com s’explica a The E-utilities In-Depth: ELink
Per exemple, podem buscar a PubMed tots els articles a PubMed que fan referència a aquest article “Protein measurement with the Folin phenol reagent” que té id 14907713:
./scripts/link.py
from Bio import Entrez
from tabulate import tabulate
pmid = "14907713"
Entrez.email = "david@xtc.dev"
with Entrez.esummary(db="pubmed", id=pmid) as response:
links = Entrez.read(response)[0]
#print(record.keys())
print(f'{links["LastAuthor"]} ({links["PubDate"]}). {links["Title"]} {links["ELocationID"]}')
print("")
with Entrez.elink(dbfrom="pubmed", id=pmid) as response:
links = Entrez.read(response)[0]
links = [[link["DbTo"], link["LinkName"], len(link["Link"])] for link in links["LinkSetDb"]]
print(tabulate(links, headers=["DbTo", "LinkName", "len(Link)"]))
La variable record consta d'una llista de Python amb una element per a cada base de dades en què hem cercat.
Com que només hem especificat un identificador de PubMed per cercar, el registre només conté un element.
Aquest element és un diccionari que conté informació sobre el nostre terme de cerca, així com tots els elements relacionats que s'han trobat.
La clau "LinkSetDb" conté els resultats de la cerca, emmagatzemats com una llista que consta d'un element per a cada base de dades de destinació.
El resultat de la nostra cerca són enllaços a PubMed dividits en categories:
RANDALL RJ (1951 Nov). Protein measurement with the Folin phenol reagent.
DbTo LinkName len(Link)
------ -------------------------- -----------
pubmed pubmed_pubmed 91
pubmed pubmed_pubmed_alsoviewed 194
pubmed pubmed_pubmed_citedin 56100
pubmed pubmed_pubmed_combined 5
pubmed pubmed_pubmed_five 5
pubmed pubmed_pubmed_reviews 5
pubmed pubmed_pubmed_reviews_five 5
Els resultats de la cerca reals s'emmagatzemen a la clau "Link".
En total s'han trobat 91 articles semblants i l’article està citat en 56.100 articles.
Per obtenir ajuda sobre ELink consulta ELink help page.
Hi ha una subpàgina sencera només per als link names, que descriu com es poden fer referència creuades a diferents bases de dades.
A continuació podem obtenir un llistat de tots els articles semblants:
./scripts/link-similar.py
from Bio import Entrez
pmid = "14907713"
Entrez.email = "david@xtc.dev"
def print_summary(summary):
print(
f'{summary["LastAuthor"]} ({summary["PubDate"]}). {summary["Title"]} {summary["ELocationID"]}')
with Entrez.esummary(db="pubmed", id=pmid) as response:
summary = Entrez.read(response)[0]
print_summary(summary)
with Entrez.elink(dbfrom="pubmed", id=pmid) as response:
record = Entrez.read(response)[0]
similar = [ls for ls in record["LinkSetDb"]
if ls["LinkName"] == "pubmed_pubmed"][0]
ids = [link["Id"] for link in similar["Link"]]
with Entrez.esummary(db="pubmed", id=",".join(ids)) as response:
record = Entrez.read(response)
print("\nSimilar articles\n=============================================================")
for summary in record[0:3]:
print_summary(summary)
EGQuery (deprecated)
EGQuery
proporciona recomptes per a un terme de cerca a cadascuna de les bases de dades d'Entrez (és a dir, una consulta global).
D’aquesta manera podies esbrinar quants resultats trobarien a cada base de dades sense realitzar moltes cerques separades amb ESearch.
Tanmateix, des de finals del 2024 pots seguir usant-lo però adverteixen que està deprecated (obsolet, o desaconsellat) perquè amb ESearch ja pots fer el mateix.
En aquest exemple, utilitzavem egquery()
per obtenir els recomptes de SARS-CoV-2
, però ja no podem usar-lo:
BiopythonDeprecationWarning: The Bio.Entrez.egquery function is deprecated and will be removed in a future release of Biopython because the underlying NCBI EGQuery API is no longer maintained.
./scripts/gcquery.py
from Bio import Entrez
from tabulate import tabulate
Entrez.email = "david@xtec.dev"
with Entrez.egquery(term="SARS-CoV-2") as response:
record = Entrez.read(response)
result = [[row["DbName"], row["Count"]] for row in record["eGQueryResult"] ]
print(tabulate(result, headers=["DbName","Count"]))
ESpell
ESpell et permet consultar suggeriments ortogràfics de paraules de búsqueda que poden estar mal escrites.
Això és molt útil en la interfície d’usuari per proporcionar de manera automàtica suggeriments a l’usuari respecte els termes de búsqueda.
En aquest document pots trobar més informació: ESpell
En aquest exemple, utilitzem espell() per obtenir l'ortografia correcta de “AXN”:
./scripts/spell.py
from Bio import Entrez
term = "adenin"
Entrez.email = "david@xtec.dev"
with Entrez.espell(term = term) as response:
record = Entrez.read(response)
print(record["CorrectedQuery"])
El terme correcte és “adenine”:
$ python3 ./scripts/spell.py
adenine
History Server
Una característica potent del sistema Entrez és que pot emmagatzemar conjunts recuperats d'UID temporalment als servidors perquè es puguin combinar posteriorment o proporcionar-los com a entrada per a altres ordres EUtils.
En aquest enllaç pots veure diferents fluxos d’ordres: Combining E-utility Calls to Create Entrez Applications
search
A continuació buscarem i baixarem totes les seqüències de nucleòtids de Opuntia rpl16 i les guardarem en un fitxer FASTA.
ESearch
publica (post
) el seu conjunt de sortida d'UID al servidor d'historial si el paràmetre &usehistory=1
:
./scripts/history.py
from Bio import Entrez
from tqdm import tqdm
term = "Opuntia[orgn] and rpl16"
file = "data/orchid_rpl16.fasta"
batch_size = 3
Entrez.email = "david@xtec.dev"
with Entrez.esearch("nuccore", term, usehistory="y") as response:
record = Entrez.read(response)
count = int(record["Count"])
with open(file, "w") as writer:
for start in tqdm(range(0, count, batch_size), desc=f"Fetch {count} (BS={batch_size}) ..."):
end = min(count, start + batch_size)
with Entrez.efetch(db="nuccore", rettype="fasta", retmode="text", retstart=start, retmax=batch_size, webenv=record["WebEnv"], query_key=record["QueryKey"]) as response:
writer.write(response.read())
Pots veure que només et torna 20 Ids de 97, perquè per defecte el valor retmax és 20 Com has passat l’argument usehistory=”yes” tens dos valor nous en la resposta: WebEnv i QueryKey.
A continuació utilitzem les variables WebEnv
i QueryKey
com a arguments de la funció efetch()
.
Encara que podries solicitar baixar tot a la vegada és millor descarregar en lots amb els paràmetres retstart
i retmax
que permet especificar quin rang de resultats de cerca vols que es tornin.
Pots veure com els fitxers es van baixant en lots de 3, i que es baixen els 97 encara que en el resultat de la consulta només havia tornat 20 dels 97 identificadors.
search (retry)
De vegades quan estem descarrent tota una seqüència de fitxers podem tenir alguns errors intermitents en la connexió: HTTPError 5xx
.
La lògica és la mateixa que abans (ara fem una consulta a pubmed):
./scripts/history-retry.py
from Bio import Entrez
from time import time
from tqdm import tqdm
from urllib.error import HTTPError
term = "Opuntia[ORGN]"
file = "data/orchid_papers.txt"
batch_size = 10
Entrez.email = "david@xtec.dev"
with Entrez.esearch(db="pubmed", term=term, reldate=365, datetype="pdat", usehistory="y") as response:
search = Entrez.read(response)
count = int(search["Count"])
with open(file, "w") as writer:
for start in tqdm(range(0, count, batch_size), desc=f"Fetch {count} (BS={batch_size}) ..."):
end = min(count, start+batch_size)
attempts = 0
while attempts < 3:
try:
with Entrez.efetch(db="pubmed", rettype="medline", retmode="text", retstart=start, retmax=batch_size, webenv=search["WebEnv"], query_key=search["QueryKey"]) as response:
data = response.read()
writer.write(data)
break
except HTTPError as err:
if 500 <= err.code <= 599:
attempts += 1
time.sleep(15)
else:
raise
La diferència és que per tal d’evitar tornar a començar podem ara inclous una lògica de reintent amb pausa per poder continuar.
Fixa't com se t'han baixat els 2 fitxers quan ha acabat el procediment.
sequence-py3.10miquel@LAPTOP-NBA651HM:/mnt/c/Users/miquel/entrez$ ls -lh data/
total 9.2M
-rwxrwxrwx 1 miquel miquel 653K Jan 27 13:30 orchid_papers.txt
-rwxrwxrwx 1 miquel miquel 8.6M Jan 27 13:28 orchid_rpl16.fasta
EPost
A vegades no vols fer una cerca sinó baixar per lots tot un conjunt de dades de les quals ja tens els Ids.
Si tens una llista molt llarga d'identificadors que vols descarregar amb EFetch (potser seqüències, potser cites, qualsevol cosa), la llista d'identificadors es converteixen en un URL molt llarga que s’envia al servidor i alguns servidors intermedis poden trencar les URLs massa llargues.
També has de tenir en compte que les bases de dades tenen un límit de registres que pots descarregar a la vegada: E-utilities In-Depth: EPost.
Per exemple, en bases de dades de seqüències el límit d'EPost és 500.
En aquest cas utilitzes EPost per carregar una llista d'identificadors (s'inicia una nova sessió d'historial), i continuació baixes els registres amb EFetch fent referència a la sessió (en lloc dels identificadors) tal com hem fet abans:
./scripts/history-post.py
from Bio import Entrez
from tqdm import tqdm
ids = list(range(19000000, 19002000))
file = "data/pubmed.txt"
batch_size = 500
Entrez.email = "david@xtec.dev"
id = ",".join(list(map(str, ids)))
with Entrez.epost("pubmed", id=id) as response:
record = Entrez.read(response)
count = len(ids)
with open(file, "w") as writer:
for start in tqdm(range(0, count, batch_size), desc=f"Fetch {count} (BS={batch_size}) ..."):
end = min(count, start + batch_size)
with Entrez.efetch(db="pubmed", rettype="medline", retmode="text", retstart=start, retmax=batch_size, webenv=record["WebEnv"], query_key=record["QueryKey"]) as response:
writer.write(response.read())
Pots veure que es baixen lots de 500 en 500 documents.
Resum de Mètodes d'Entrez
Els mètodes següents són utilitzats per interactuar amb les bases de dades de NCBI. Aquests permeten cercar, carregar i descarregar dades de manera eficient, amb diverses opcions i limitacions.
EInfo, ESearch, EPost, EFetch
-
EInfo (Mètode GET)
Proporciona informació global sobre les bases de dades disponibles a NCBI, com ara el nombre total de registres i els camps d'índex. -
ESearch (Mètode GET)
Realitza cerques dins dels índexs de les bases de dades de NCBI i retorna una llista d'Accession Numbers (identificadors únics) que coincideixen amb els criteris de cerca. -
EPost (Mètode POST)
Permet carregar a l'NCBI una llista d'Accession Numbers i retorna un Id de Sessió, necessari per a posteriors operacions com la descàrrega massiva. -
EFetch (Mètode POST)
Facilita la descàrrega de dades utilitzant l'idSessió generat amb l'EPost.- Sense un idSessió, es pot descarregar fins a 3 fitxers (o 10 si es disposa d'una clau API).
- Amb l'idSessió, es poden descarregar fins a 500 fitxers alhora.
Altres Consideracions
- usehistory: Una funcionalitat clau per gestionar l'historial i millorar l'eficiència en consultes i descàrregues massives.
Aquest resum et dona una visió clara dels mètodes i funcionalitats més importants de les eines NCBI. 😊
Exemple Entrez Post 1 - Resum molts articles.
Crea un script amb Biopython que cerqui articles a PubMed relacionats amb human mitochondrial DNA
mitjançant ESearch
, carregui els identificadors temporalment amb EPost
i recuperi els resums dels articles amb EFetch
.
Mostra els resultats per pantalla.
from Bio import Entrez
# Configuració del correu electrònic per a Entrez
Entrez.email = "mamoro10@xtec.com"
# 1. Cerquem articles relacionats amb "human mitochondrial DNA"
query = "human mitochondrial DNA"
db = "pubmed"
# ESearch per obtenir els identificadors
print(f"Buscant articles relacionats amb: '{query}'...")
with Entrez.esearch(db=db, term=query, retmax=10) as handle: # Limitem a 10 resultats
search_results = Entrez.read(handle)
ids = search_results["IdList"]
print(f"IDs trobats: {ids}")
# 2. Utilitzem EPost per carregar els identificadors temporalment
print("Carregant els identificadors amb EPost...")
with Entrez.epost(db=db, id=",".join(ids)) as handle:
post_results = Entrez.read(handle)
# Obtenim el WebEnv i QueryKey necessaris per a consultes posteriors
web_env = post_results["WebEnv"]
query_key = post_results["QueryKey"]
print(f"WebEnv: {web_env}")
print(f"QueryKey: {query_key}")
# 3. Exemple d'ús posterior (opcional): obtenir informació detallada amb efetch
print("Obtenint informació detallada dels articles...")
with Entrez.efetch(db=db, query_key=query_key, WebEnv=web_env, rettype="abstract", retmode="text") as handle:
abstracts = handle.read()
print("\n=== Resums obtinguts ===")
print(abstracts)
Exemple Entrez Post 2 - Concatenar seqüències.
Cerca el gen chloroquine resistance transporter (CRT) (KM288867) fins de l'organisme Plasmodium falciparum (el paràsit responsable de la forma més mortal de malària) utilitzant la base de dades de nucleòtids.
Quan utilitzem una cerca estàndard, cal tenir en compte que el nombre de referències de registres està limitat a 20 per defecte.
Si necessitem obtenir més referències, podem modificar el paràmetre retmax
per indicar la quantitat desitjada de registres.
from Bio import Entrez, SeqIO
import os
# Exemple inspirat en el repositori de "12 days of Biopython".
# He incorporat el mètode EPost per si hi ha problemes amb el límit de baixada d'NCBI-Entrez.
# https://github.com/lanadominkovic/12-days-of-biopython/blob/main/12_days_of_biopython/day_02/day_02-accessing-ncbi-databases.ipynb
Entrez.email = "mamoro10@xtec.cat"
save_folder: str = "./data/epost"
term: str = 'CRT[Gene Name] AND "Plasmodium falciparum"[Organism]'
output_filename : str = f'{term}.fasta'
output_path = os.path.join(save_folder, output_filename)
if not os.path.exists(save_folder):
os.makedirs(save_folder)
if os.path.exists(output_path):
print(f"Ja teníem el fitxer {output_filename}. No cal tornar a descarregar les seqüències.")
else:
print("Step 1.a eSearch term.")
handle = Entrez.esearch(db="nucleotide", term='CRT[Gene Name] AND "Plasmodium falciparum"[Organism]', retmax="30")
rec_list = Entrez.read(handle)
handle.close()
accession_list = rec_list['IdList']
# EPost necessari perquè demanem molts fitxers a la vegada.
print("Step 2. ePost Accession ID's.")
search_handle = Entrez.epost(db="nucleotide", id=accession_list)
search_results = Entrez.read(search_handle)
webenv = search_results["WebEnv"]
query_key = search_results["QueryKey"]
print("Step 3. eFetch sequences info.")
handle = Entrez.efetch(db="nucleotide", webenv=webenv, query_key=query_key, rettype="fasta", retmode="text")
fasta_records = list(SeqIO.parse(handle, "fasta"))
handle.close()
print("Step 4. Write records in a same fasta file.")
with open(output_path, "w") as out_handle:
SeqIO.write(fasta_records, out_handle, "fasta")
print("All records written to", output_path)
Exercicis
Primer de tot, descarrega't el projecte de referència:
git clone https://gitlab.com/xtec/bio/entrez-24.git
La part de client s'ha d'adaptar a les tecnologies que usem el 2024-2025 (usem Vite i no Next, usem bun i no pas npm...)
La part del servidor és molt semblant; però si pel que sigui no funciones copia i enganxa els scripts en un altre projecte; ja que contenen tots els exercicis.
1.- Retorna un resum dels nucleotids relatius als accession numbers NC_010611.1 i EU477409.1.
El terme de consulta és: 'NC_010611.1[accn] OR EU477409.1[accn]'
.
import entrez as ez
for line in ez.on_search(term='NC_010611.1[accn] OR EU477409.1[accn]',
db='nucleotide', tool='summary'):
print(line)
2.- Retorna la descripció i la seqüència del fitxer .fasta
del gen GAPDH en éssers humans
, que sabem que té l'accession number NM_002046.
from Bio import Entrez, SeqIO
import os
id = "NM_002046"
email = "mamoro10@xtec.dev"
dir = "data/nuccore"
if not os.path.isdir(dir):
os.makedirs(dir)
filename = "{}/gi_{}.fasta".format(dir, id)
if not os.path.isfile(filename):
print("Downloading file ...")
with Entrez.efetch("nuccore", id=id, rettype="fasta", retmode="text", email=email) as response:
with open(filename, "w") as out_handle:
out_handle.write(response.read())
print("Saved file.")
record = SeqIO.read(filename, "fasta")
print(record)
print(record.seq)
3.- Cercar SNP’s ens proporciona informació sobre la variabilitat genètica en una població. A més a més, pot ser que el cromosoma 20 contingui gens que estan associats amb certes malalties o característiques fenotípiques específiques.
Per exemple, el SNP rs7903146
en el gen TCF7L2
ha estat identificat com un factor de risc per al desenvolupament de diabetis tipus 2.
Les persones que porten una variant específica d'aquest SNP poden tenir un augment del risc de desenvolupar la malaltia.
Per tant, usant el terme term = "human[orgn] AND 20[chr] AND alive[prop]"
cerca enllaços (amb ELink) que apuntin a SNP
.
En genètica, el Polimorfisme de nucleòtids simples (SNP
, sigles de single nucleotide polymorphisms, polimorfismes d'un sol nucleòtid), són polimorfismes d'un gen que ocorren per variació en un sol nucleòtid de la seqüència d'ADN. Perquè un canvi en un nucleòtid es consideri com un SNP, s'ha de donar almenys en l'1% de la població. Si no arriba a aquest percentatge, el canvi es considera una mutació puntual. Els SNP contribueixen a les diferències entre els individus.
from Bio import Entrez
import json
from time import time
from tqdm import tqdm
from tabulate import tabulate
term = "human[orgn] AND 20[chr] AND alive[prop]"
file = "data/snp_table.txt"
batch_size = 2
Entrez.email = "david@xtec.dev"
with Entrez.esearch("gene", term, retmode="json") as response:
search = json.loads(response.read())["esearchresult"]
id = search["idlist"][0]
with Entrez.elink(db="snp", dbfrom="gene", linkname="gene_snp", id=id, retmode="json") as response:
links = json.loads(response.read())
print(json.dumps(links, indent=2))
4.- Troba i descarrega les seqüències biològiques utilitzades en aquest article del PMC
per a resoldre un cas criminal, en format genbank
:
- Obrint l’article podem trobar molts genbank al final de tot.
- Per provar, en seleccionem 30 (n’hi ha més de 100)
gb_accesion_ids = [range(156734, 156764, 1)]
from Bio import Entrez, SeqIO
import os
gb_accession_prefix = 'AY' # First genbank is AY156734
gb_accession_ids = list(range(156734, 156764, 1))
save_folder = "./data/criminal-case"
Entrez.email = "mamoro10@xtec.cat"
print("Step 1. ePost Accession ID's.")
# Convert id numbers strings y agregar el prefijo
accession_ids_str = [f"{gb_accession_prefix}{id}" for id in gb_accession_ids]
search_handle = Entrez.epost(db="nucleotide", id=",".join(accession_ids_str))
search_results = Entrez.read(search_handle)
webenv = search_results["WebEnv"]
query_key = search_results["QueryKey"]
print("Step 2. eFetch sequences info.")
handle = Entrez.efetch(db="nucleotide", webenv=webenv, query_key=query_key, rettype="gb", retmode="text")
genbank_records = list(SeqIO.parse(handle, "genbank"))
for i, record in enumerate(genbank_records):
filename = os.path.join(save_folder, f"{gb_accession_prefix}{gb_accession_ids[i]}.gb")
print(filename)
#if not os.path.exists(filename):
SeqIO.write(record, filename, "genbank")
print('Genbank files downloaded in ', save_folder)
Font: