<p>I can show you a simple way in R, using biomaRt. Let's say you have two snp ids you want to exmine.</p>

<pre><code>SNPids &lt;- c("rs431905509", "rs431905511")

library("biomaRt")

# To show which marts are available
listMarts()

# You need the SNP mart
mart &lt;- useMart("ENSEMBL_MART_SNP")

# Find homo sapiens
listDatasets(mart)

# This will be the dataset we want to use
dataset &lt;- useDataset("hsapiens_snp", mart=mart)

# Show available filters
listFilters(dataset)

# Now list all available attributes
listAttributes(dataset)

# To get the ensembl gene id belonging to the SNPs
getBM(attributes=c('refsnp_id', 'ensembl_gene_stable_id'), 
      filters = 'snp_filter', 
      values = SNPids, 
      mart = dataset)

    refsnp_id ensembl_gene_stable_id
1 rs431905509        ENSG00000070371
2 rs431905509        ENSG00000100075
3 rs431905509        ENSG00000260924
4 rs431905511        ENSG00000145335
</code></pre>

CC BY-SA 3.0
2017-09-10T08:34:51.797Z
2017-09-10T09:43:53.693Z
2017-09-10T09:43:53.693Z
benn
<p>If you want a fast (binary) search with a locally-stored dataset, where you know exactly what SNPs and gene annotations are being queried, here's a way to set up files to do your own querying, which should scale up nicely if you have a lot of SNPs.</p>

<p>1) Get annotations of interest and write them into a sorted BED file.</p>

<p>For example, here's a way to get Gencode v26 gene annotations:</p>

<pre><code>$ wget -qO- ftp://ftp.sanger.ac.uk/pub/gencode/Gencode_human/release_26/gencode.v26.basic.annotation.gff3.gz \
    | gunzip -c \
    | convert2bed --input=gff - \
    &gt; gencode.v26.bed
</code></pre>

<p>2) Get SNPs and write them into a text file sorted by SNP ID. </p>

<p>For <code>hg38</code>, for instance:</p>

<pre><code>$ LC_ALL=C
$ wget -qO- http://hgdownload.cse.ucsc.edu/goldenpath/hg38/database/snp147.txt.gz \
   | gunzip -c \
   | awk -v OFS="\t" '{ print $5,$2,$3,($3+1) }' \
   | sort -k1,1 \
   &gt; hg38.snp147.sortedByName.txt
</code></pre>

<p>3) Install <a href="https://github.com/pts/pts-line-bisect" rel="nofollow noreferrer">pts-line-bisect</a>:</p>

<pre><code>$ git clone https://github.com/pts/pts-line-bisect.git
$ cd pts-line-bisect &amp;&amp; make &amp;&amp; cd ..
</code></pre>

<p>4) Run a query:</p>

<pre><code>$ rs_of_interest=rs2814778
$ ./pts-line-bisect/pts_lbsearch -p hg38.snp147.sortedByName.txt ${rs_of_interest} \
   | head -1 \
   | cut -f2- \
   | bedmap --echo --echo-map-id-uniq --range 1000 - gencode.v26.bed \
   &gt; answer.bed
</code></pre>

<p>The file <code>answer.bed</code> contains the genomic interval of the SNP of interest, along with the IDs of any Gencode v26 gene annotations within 1000 nt upstream and downstream of the SNP.</p>

<p>To repeat step 4 within a Python script, you can use <code>subprocess.check_output()</code>:</p>

<pre><code>...
pts_lbsearch = os.path.join(project_dir, "pts-line-bisect", "pts_lbsearch")
if not os.path.exists(pts_lbsearch):
  print "Content-type:application/json\r\n\r\n"
  print json.dumps({'error' : 'could not locate pts_lbsearch [%s]' % (pts_lbsearch)})
  sys.exit(errno.ENOENT)

if term.startswith('rs'):
  query_fn = os.path.join(annotations_dir, "hg38.snp147.sortedByName.txt")
  cmd = "%s -p %s %s" % (pts_lbsearch, query_fn, term)
  try:
    res = subprocess.check_output(cmd, shell=True)
    if res and len(res) &gt; 0:
      (chr, start, stop) = res.strip().split('\n')[0].split('\t')
    else:
      pass
  except subprocess.CalledProcessError as err:
    sys.stderr.write("Query failed!\n")
    sys.exit(errno.EIO)

#
# map 'chr', 'start', and 'stop' variables to annotations with 
# another subprocess call (not shown -- use example above)
#
...
</code></pre>

CC BY-SA 3.0
2017-09-10T21:57:31.637Z
2017-09-10T22:08:41.117Z
2017-09-10T22:08:41.117Z
Alex Reynolds
<p>Have you tried using SnpEff and SnpShift (<a href="http://snpeff.sourceforge.net" rel="nofollow noreferrer">http://snpeff.sourceforge.net</a>) for annotation of the SNPs?</p>

<p>Even if your desired organism available in their annotation database you can create them easily with the gene bank files. Other than annotation the tools features a bunch of other amazing functionalities.</p>

CC BY-SA 3.0
2017-09-18T12:15:40.003Z
2017-09-18T12:15:40.003Z
arup
<pre><code>BiocManager::install('biomaRt')
require(biomaRt)
ensembl &lt;- useMart(&quot;ENSEMBL_MART_SNP&quot;, dataset = &quot;hsapiens_snp&quot;)
getBM(attributes=c(&quot;refsnp_id&quot;, &quot;chr_name&quot;, &quot;chrom_start&quot;, &quot;chrom_end&quot;,
                   &quot;allele&quot;, &quot;mapweight&quot;, &quot;validated&quot;, &quot;allele_1&quot;, &quot;minor_allele&quot;,
                   &quot;minor_allele_freq&quot;, &quot;minor_allele_count&quot;, &quot;clinical_significance&quot;,
                   &quot;synonym_name&quot;, &quot;ensembl_gene_stable_id&quot;),
      filters=&quot;snp_filter&quot;, values=c(&quot;rs6025&quot;,&quot;rs431905509&quot;),
      mart=ensembl, uniqueRows=TRUE)
</code></pre>

CC BY-SA 4.0
2020-07-20T16:38:44.867Z
2020-07-20T16:38:44.867Z
Shicheng Guo
<p>using ncbi-efetch and <strong>XSLT</strong> with the following stylesheet:</p>

<pre><code>&lt;?xml version='1.0' encoding="UTF-8"?&gt;
&lt;xsl:stylesheet
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
    xmlns:r="https://www.ncbi.nlm.nih.gov/SNP/docsum"
    version='1.1'
    &gt;
&lt;xsl:output method="text"  encoding="UTF-8"/&gt;

&lt;xsl:template match="/"&gt;
&lt;xsl:apply-templates select="r:ExchangeSet"/&gt;
&lt;/xsl:template&gt;
&lt;xsl:template match="r:ExchangeSet"&gt;
&lt;xsl:apply-templates select="r:Rs"/&gt;
&lt;/xsl:template&gt;
&lt;xsl:template match="r:Rs"&gt;
&lt;xsl:apply-templates select="r:Assembly/r:Component/r:MapLoc/r:FxnSet"/&gt;
&lt;/xsl:template&gt;


&lt;xsl:template match="r:FxnSet"&gt;
&lt;xsl:text&gt;rs&lt;/xsl:text&gt;
&lt;xsl:value-of select="../../../../@rsId"/&gt;
&lt;xsl:text&gt;  ncbi:&lt;/xsl:text&gt;
&lt;xsl:value-of select="@geneId"/&gt;
&lt;xsl:text&gt;  &lt;/xsl:text&gt;
&lt;xsl:value-of select="@symbol"/&gt;
&lt;xsl:text&gt;
&lt;/xsl:text&gt;
&lt;/xsl:template&gt;

&lt;/xsl:stylesheet&gt;
</code></pre>

<p>usage (group the rs# by 3):</p>

<pre><code>$ echo -e "rs8179065\nrs12899845\nrs8179066\nrs80357332\nrs80356920\n" | xargs  -L 3 echo  | tr " " "," | while read R; do curl -sL "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=snp&amp;id=${R}&amp;retmode=xml" | xsltproc --novalid  transform.xsl -; done | uniq
rs8179065   ncbi:374654 KIF7
rs12899845  ncbi:90381  TICRR
rs8179066   ncbi:374654 KIF7
rs80357332  ncbi:672    BRCA1
rs80356920  ncbi:672    BRCA1
</code></pre>

CC BY-SA 3.0
2017-09-11T08:50:28.680Z
2017-09-11T09:03:01.633Z
2017-09-11T09:03:01.633Z
Pierre
<blockquote>
  <p>I wonder if there is a simpler solution recently? (and hopefully, I can solve it within the scope of python. )</p>
</blockquote>

<p>A <em>simpler</em> solution, I don't know... but this is at least <em>one</em> Python solution using <a href="http://biopython.org/DIST/docs/tutorial/Tutorial.html#htoc118" rel="noreferrer">Biopython's ELink method</a> via <a href="https://www.ncbi.nlm.nih.gov/books/NBK25499/" rel="noreferrer">NCBI's Entrez E-utils</a>.</p>

<p>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.</p>

<pre><code>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"))
</code></pre>

<p>This returns a <code>record</code> object</p>

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

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

<pre><code>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
</code></pre>

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

<p><em>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</em></p>

<pre><code>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
</code></pre>

CC BY-SA 3.0
2017-09-10T14:38:44.270Z
2017-09-10T14:38:44.270Z
Kevin
<p>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. </p>

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

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

<ul>
<li><a href="https://support.bioconductor.org/p/39278/" rel="noreferrer">https://support.bioconductor.org/p/39278/</a></li>
<li><a href="https://www.biostars.org/p/10437/" rel="noreferrer">https://www.biostars.org/p/10437/</a> </li>
<li><a href="https://www.biostars.org/p/413/" rel="noreferrer">https://www.biostars.org/p/413/</a> </li>
</ul>

CC BY-SA 3.0
2017-10-03T06:10:44.397Z
Actually functional annotation is a key step in SNP calling pipelines. I have used SnpEff and it worked perfectly. Even if you have to build your own data base given the reference genome and the gene models against the SNPs were called. I have a script if you want to use it as guide.
Erik
<database><gene><snp>
How to get a list of genes corresponding to the list of SNPs (rs ids)?
# How to get a list of genes corresponding to the list of SNPs (rs ids)?

(Asked by: username_0 on 2017-09-09)

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/>

**Tags:** database, gene, snp
> **Comments:**
> **username_1**: Actually functional annotation is a key step in SNP calling pipelines. I have used SnpEff and it worked perfectly. Even if you have to build your own data base given the reference genome and the gene models against the SNPs were called. I have a script if you want to use it as guide.  

---

## 6 answers

### Answer by username_7 on 2017-09-10. Score: 8 | Accepted answer

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


---

### Answer by username_2 on 2017-09-10. Score: 7

I can show you a simple way in R, using biomaRt. Let's say you have two snp ids you want to exmine.
SNPids <- c("rs431905509", "rs431905511")

library("biomaRt")

# To show which marts are available
listMarts()

# You need the SNP mart
mart <- useMart("ENSEMBL_MART_SNP")

# Find homo sapiens
listDatasets(mart)

# This will be the dataset we want to use
dataset <- useDataset("hsapiens_snp", mart=mart)

# Show available filters
listFilters(dataset)

# Now list all available attributes
listAttributes(dataset)

# To get the ensembl gene id belonging to the SNPs
getBM(attributes=c('refsnp_id', 'ensembl_gene_stable_id'), 
      filters = 'snp_filter', 
      values = SNPids, 
      mart = dataset)

    refsnp_id ensembl_gene_stable_id
1 rs431905509        ENSG00000070371
2 rs431905509        ENSG00000100075
3 rs431905509        ENSG00000260924
4 rs431905511        ENSG00000145335


---

### Answer by username_3 on 2017-09-10. Score: 2

If you want a fast (binary) search with a locally-stored dataset, where you know exactly what SNPs and gene annotations are being queried, here's a way to set up files to do your own querying, which should scale up nicely if you have a lot of SNPs.
1) Get annotations of interest and write them into a sorted BED file.
For example, here's a way to get Gencode v26 gene annotations:
$ wget -qO- ftp://ftp.sanger.ac.uk/pub/gencode/Gencode_human/release_26/gencode.v26.basic.annotation.gff3.gz \
    | gunzip -c \
    | convert2bed --input=gff - \
    > gencode.v26.bed

2) Get SNPs and write them into a text file sorted by SNP ID. 
For hg38, for instance:
$ LC_ALL=C
$ wget -qO- http://hgdownload.cse.ucsc.edu/goldenpath/hg38/database/snp147.txt.gz \
   | gunzip -c \
   | awk -v OFS="\t" '{ print $5,$2,$3,($3+1) }' \
   | sort -k1,1 \
   > hg38.snp147.sortedByName.txt

3) Install pts-line-bisect:
$ git clone https://github.com/pts/pts-line-bisect.git
$ cd pts-line-bisect && make && cd ..

4) Run a query:
$ rs_of_interest=rs2814778
$ ./pts-line-bisect/pts_lbsearch -p hg38.snp147.sortedByName.txt ${rs_of_interest} \
   | head -1 \
   | cut -f2- \
   | bedmap --echo --echo-map-id-uniq --range 1000 - gencode.v26.bed \
   > answer.bed

The file answer.bed contains the genomic interval of the SNP of interest, along with the IDs of any Gencode v26 gene annotations within 1000 nt upstream and downstream of the SNP.
To repeat step 4 within a Python script, you can use subprocess.check_output():
...
pts_lbsearch = os.path.join(project_dir, "pts-line-bisect", "pts_lbsearch")
if not os.path.exists(pts_lbsearch):
  print "Content-type:application/json\r\n\r\n"
  print json.dumps({'error' : 'could not locate pts_lbsearch [%s]' % (pts_lbsearch)})
  sys.exit(errno.ENOENT)

if term.startswith('rs'):
  query_fn = os.path.join(annotations_dir, "hg38.snp147.sortedByName.txt")
  cmd = "%s -p %s %s" % (pts_lbsearch, query_fn, term)
  try:
    res = subprocess.check_output(cmd, shell=True)
    if res and len(res) > 0:
      (chr, start, stop) = res.strip().split('\n')[0].split('\t')
    else:
      pass
  except subprocess.CalledProcessError as err:
    sys.stderr.write("Query failed!\n")
    sys.exit(errno.EIO)

#
# map 'chr', 'start', and 'stop' variables to annotations with 
# another subprocess call (not shown -- use example above)
#
...


---

### Answer by username_4 on 2017-09-18. Score: 1

Have you tried using SnpEff and SnpShift (<http://snpeff.sourceforge.net>) for annotation of the SNPs?

Even if your desired organism available in their annotation database you can create them easily with the gene bank files. Other than annotation the tools features a bunch of other amazing functionalities.


---

### Answer by username_5 on 2020-07-20. Score: 0

BiocManager::install('biomaRt')
require(biomaRt)
ensembl <- useMart("ENSEMBL_MART_SNP", dataset = "hsapiens_snp")
getBM(attributes=c("refsnp_id", "chr_name", "chrom_start", "chrom_end",
                   "allele", "mapweight", "validated", "allele_1", "minor_allele",
                   "minor_allele_freq", "minor_allele_count", "clinical_significance",
                   "synonym_name", "ensembl_gene_stable_id"),
      filters="snp_filter", values=c("rs6025","rs431905509"),
      mart=ensembl, uniqueRows=TRUE)


---

### Answer by username_6 on 2017-09-11. Score: 0

using ncbi-efetch and XSLT with the following stylesheet:
















rs

  ncbi:

  







usage (group the rs# by 3):
$ echo -e "rs8179065\nrs12899845\nrs8179066\nrs80357332\nrs80356920\n" | xargs  -L 3 echo  | tr " " "," | while read R; do curl -sL "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=snp&id=${R}&retmode=xml" | xsltproc --novalid  transform.xsl -; done | uniq
rs8179065   ncbi:374654 KIF7
rs12899845  ncbi:90381  TICRR
rs8179066   ncbi:374654 KIF7
rs80357332  ncbi:672    BRCA1
rs80356920  ncbi:672    BRCA1


---

