Serviços UniProt

Neste trabalho utilizamos dois serviços disponibilizados pelo site UniProt:

  • serviço de mapeamento de identificadores
  • serviço de download de informação em batch

A lista dos serviços disponibilizados pelo site pode ser encontrado aqui.

As funções apresentadas aqui podem ser encontradas no módulo util.www.

  1. www.gene_ids_to_uniprot_ids(gene_ids)
  2. www.fetch_uniprots(ids)
  3. www.extract_uniprot_info(entry)

A UniProt oferece um serviço de mapeamento de identificadores entre diferentes bases de dados (mais informação aqui). Neste caso, mapeamos de Entrez Gene (GeneID) (P_ENTREZGENEID) para UniProtKB ID (ID).

def gene_ids_to_uniprot_ids(ids):
    """
    Mapeia a lista de gene ids passada como argumento para uniprot ids.
    Retorna um dictionário em que as chaves são gene_id e os valores
    são uniprot_id.
    """

    url = "http://www.uniprot.org/mapping/"
    query = " ".join(ids)

    data = {
        "format": "tab",
        "from": "P_ENTREZGENEID",
        "to": "ACC",
        "query": query
    }

    # HTTP POST
    response = requests.post(
        url,
        data=data
    )

    result = {}

    # ignorar a primeira linha
    for row in response.text.splitlines()[1:]:
        gene_id, uniprot_id = row.split("\t")

        result[gene_id] = uniprot_id

    return result

Utilizamos ainda outro serviço de extração de informação em batch. Criámos uma função que dada uma lista de uniprot_id retorna um dicionário que mapeia estes id para um outro dicionário com a informação desejada.

def fetch_uniprots(ids):
    """
    Faz download da informação presente no site uniprot
    em formato xml dada uma lista de ids uniprot.
    """

    url = "http://www.uniprot.org/uploadlists/"
    max_retries = 100

    # Cada pedido à uniprot vai no máximo com 1000 ids.
    queries  = [ids[i:i+1000] for i in range(0, len(ids), 1000)]

    files = []

    for query in queries:
        query_all = " ".join(query)
        md5 = util.md5(query_all)

        data = {
            "format": "xml",
            "from": "ID",
            "to": "ACC",
            "uploadQuery": query_all,
            "jobId": md5
        }

        done = False
        attempt = 0

        # enquanto não acabar ou não esgotar as tentativas todas
        while not done and attempt < max_retries:

            # HTTP POST
            response = requests.post(
                url,
                data=data
            )

            if response.status_code == 200:
                # Gravar o xml
                file_path = "/tmp/" + md5 + ".xml"
                rw.write_file(response.text, file_path)
                files.append(file_path)
                done = True

            attempt += 1

        if not done:
            print("Não consegui fazer download de " + str(query))

    uniprots = {}

    for file_path in files:
        tree = parse_xml(file_path, add_root=True, start=2, end=1)
        entries = tree.findall(".//entry")

        for entry in entries:
            (uniprot_id, info) = extract_uniprot_info(entry)
            uniprots[uniprot_id] = info

    return uniprots

Esta função utiliza duas outras funções:

  • md5 definida no util.util (a descrição deste módulo pode ser encontrada aqui).
  • extract_uniprot_info definida no módulo util.www:
def extract_uniprot_info(entry):
    """
    Extrai a informação que necessitamos da uniprot.
    """
    # dataset
    ds = entry.get("dataset")

    if ds == "Swiss-Prot":
        status = "reviewed"
    elif ds == "TrEMBL":
        status = "unreviewed"
    else:
        print("Não conheço o dataset " + ds)

    # accessions
    accessions = [a.text for a in entry.findall(".//accession")]
    accession = accessions[0]

    # short name
    short_name = entry.find("name").text

    # full name
    full_name = entry.find(".//fullName").text

    # ec number
    ec_number = entry.find(".//ecNumber")
    if ec_number != None:
        ec_number = ec_number.text

    # organism
    organisms = entry.findall(".//name[@type='scientific']")
    if len(organisms) == 1:
        organism = organisms[0].text
    else:
        print("Encontrei um número de organismos diferente de 1: ", accession)
        organism = None

    # encontrar o texto função que costuma estar no início da
    # página da uniprot
    comment_functions = [c.find("text").text for c in entry.findall(".//comment[@type='function']")]

    # cofator
    cofactors = [c.find("cofactor").find("name").text for c in entry.findall(".//comment[@type='cofactor']")]

    # patologias
    pathologies = [c.find("disease").find("name").text for c in entry.findall(".//comment[@type='disease']")]

    # PDB
    pdbs = []
    PDB = entry.findall(".//dbReference[@type='PDB']")
    for pdb in PDB:
        id = pdb.get("id")
        method = pdb.find("property[@type='method']")
        chains = pdb.find("property[@type='chains']")

        if (not method == None) and (not chains == None):
            method = method.get("value")
            chains = chains.get("value")

            d = {}
            d["id"] = id
            d["method"] = method
            d["chains"] = chains
            pdbs.append(d)

    # encontrar GO - Molecular Function
    # e GO - Biological process
    molecular_functions = []
    biological_processes = []
    locations = []
    GO = entry.findall(".//dbReference[@type='GO']")
    for go in GO:
        value = go.find("property[@type='term']").get("value")
        is_molecular_function = value.startswith("F:")
        is_biological_process = value.startswith("P:")
        is_location = value.startswith("C:")

        value = value[2:]

        if is_molecular_function:
            molecular_functions.append(value)
        elif is_biological_process:
            biological_processes.append(value)
        elif is_location:
            locations.append(value)

    # sequência
    sequences = entry.findall(".//sequence[@length]")
    assert len(sequences) == 1
    sequence = sequences[0].text.replace("\n", "")
    length = int(sequences[0].get("length"))
    mass = int(sequences[0].get("mass"))

    # modified residue
    mrs = []
    res = entry.findall(".//feature[@type='modified residue']")
    for r in res:
        desc = r.get("description")
        position = r.find("location").find("position").get("position")
        mrs.append({"desc": desc, "position": position})

    dictionary = {}
    dictionary["status"] = status
    dictionary["accessions"] = accessions
    dictionary["short_name"] = short_name
    dictionary["product"] = full_name
    dictionary["EC_number"] = ec_number
    dictionary["organism"] = organism
    dictionary["comment_functions"] = comment_functions
    dictionary["cofactors"] = cofactors
    dictionary["pathologies"] = pathologies
    dictionary["pdbs"] = pdbs
    dictionary["molecular_functions"] = molecular_functions
    dictionary["biological_processes"] = biological_processes
    dictionary["locations"] = locations
    dictionary["translation"] = sequence
    dictionary["length"] = length
    dictionary["mass"] = mass
    dictionary["modified_residues"] = mrs

    return (accession, dictionary)