How to get a list of genes corresponding to the list of SNPs (rs ids)? Is there a way for me to get a list of genes given a list of SNP rs ids? I found several questions asked with a similar goal years ago, and the answers are always about using multiple online tools, and/or R programming. 

I wonder if there is a simpler solution recently? (and hopefully, I can solve it within the scope of python. )

The relevant question I found (that are asked and answered more than five years ago.)


https://support.bioconductor.org/p/39278/
https://www.biostars.org/p/10437/ 
https://www.biostars.org/p/413/
I wonder if there is a simpler solution recently? (and hopefully, I can solve it within the scope of python. )


A simpler solution, I don't know... but this is at least one Python solution using Biopython's ELink method via NCBI's Entrez E-utils.

The Biopython library is flexible enough (they have an in-depth tutorial worth reading) to modify the code below to to fit project-specific needs.  For example, the mapping between snp id and gene id is lost with this bulk request method.

from Bio import Entrez
Entrez.email = "youremail@gmail.com"

snp_ids = ["rs8179065", 
           "rs12899845", 
           "rs8179066", 
           "rs80357332", 
           "rs80356920"]

record = Entrez.read(Entrez.elink(dbfrom="snp", 
                                  id=",".join(snp_ids).replace('rs', ''), 
                                  db="gene"))


This returns a record object

[{'LinkSetDb': [{'DbTo': 'gene', 'LinkName': 'snp_gene', 'Link': [{'Id': '374654'}, {'Id': '90381'}, {'Id': '672'}]}], 'ERROR': [], 'LinkSetDbHistory': [], 'DbFrom': 'snp', 'IdList': ['8179065', '8179066', '12899845', '80356920', '80357332']}]


which contains several iterable fields -- 'Link' has a list of the Gene UID's that can be converted to names using ESummary.

for gene_id in record[0]['LinkSetDb'][0]['Link']:
    handle = Entrez.esummary(db="gene", id=gene_id['Id'])
    uid_record = Entrez.read(handle)
    handle.close()

    uid_summary = uid_record["DocumentSummarySet"]['DocumentSummary'][0]
    print (gene_id['Id'], uid_summary['Name'])
    # ...or something more interesting than print


374654 KIF7
90381 TICRR
672 BRCA1


An option to preserve discrete mapping, is to loop over each snp id and extract the gene(s):

although I don't endorse the method below, especially for long lists of snps, you may flood the server with too many requests, maybe someone more familiar can comment on request limits or best practices for Entrez

for snp_id in snp_ids:
    record = Entrez.read(Entrez.elink(dbfrom="snp", 
                                  id=snp_id.replace('rs',''), 
                                  db="gene"))
    results = record[0]['LinkSetDb'][0]['Link']
    for result in results:
        uid = result['Id']
        handle = Entrez.esummary(db="gene", id=uid)
        uid_record = Entrez.read(handle)
        handle.close()
        uid_summary = uid_record["DocumentSummarySet"]['DocumentSummary'][0]
        gene_name = uid_summary['Name']

        print(snp_id, uid, gene_name)

rs8179065 374654 KIF7
rs12899845 374654 KIF7
rs12899845 90381 TICRR
rs8179066 374654 KIF7
rs80357332 672 BRCA1
rs80356920 672 BRCA1