PubMed search Shiny App using RISmed

In part one of a series of tutorials, we will develop a Shiny App for performing analysis of academic text from PubMed. There’s no shortage of great tutorials for developing a Shiny App using R, including Shiny’s own tutorial. Here at DataScience+ we have a perfect introduction by Teja Kodali and a more in-depth development by J.S. Ramos. Here I will focus on the basics of making PubMed queries using the RISmed package, and to demonstrate how easily you can share any of your R functionality using Shiny. Click here to see the App in action and follow along (the App has recently been updated, to get the new code, visit my github page). In this introductory tutorial, we’ll get a taste of what we can accomplish, try to cover all the basics, and hopefully streamline some potential time-sinks.

About PubMed and RISmed

PubMed is a public query database of journal articles and other literature maintained and made available by the National Institutes of Health. The RISmed packages extracts content from the Entrez Programming Utilities (E-Utilities) interface to the PubMed query and database at NCBI into R. You will find great tutorials to get RISmed up and running by checking out this introduction or this very nice post by some dude named Dave Tang. You can find Stephanie Kovalchik’s terrific RISmed github page here. PubMed is a perfect place to search for scientific and health-related text, and coupled with Natural Language Processing tools in R, we can create powerful Meta-Analyses. Let’s get started!

Placeholder UI

Creating a shiny app requires the customary server.R and ui.R scripts in an empty directory. We will tackle the user interface head-on in the next post, so for now let’s get up and running with the following:

library(shiny)
library(shinythemes)
shinyUI(fluidPage(theme=shinytheme("united"),
        headerPanel("PubMed Search"),
        sidebarLayout(
        sidebarPanel(
        helpText("Type a word below and search PubMed to find documents that contain that word in the text. You can even type multiple words. You can search authors, topics, any acronym, etc."),
        textInput("text", label = h3("Keyord(s)"), value = "carson chow"),
        helpText("You can specify the start and end dates of your search, use the format YYYY/MM/DD"),
        textInput("date1", label = h3("From"),value="1990/01/01"),
        textInput("date2", label = h3("To"),  value = "2015/11/07"),
        helpText("Now select the output you'd like to see. You can see a barplot of articles per year, a wordcloud of the abstract texts, or a table of the top six authors"),

        actionButton("goButton","PLOT"),
        actionButton("wordButton","WORDS"),
        actionButton("authButton","AUTHORS")),

        mainPanel(
        plotOutput("distPlot"),
        plotOutput("wordPlot"),
        tableOutput("authList")

        )
        )))

We are designing an out-of-the-box Shiny UI with text entry boxes that allow the user to type their keyword(s), and specify a time frame using a start date and end date. We need to provide action buttons for users to select the output of their search instead of running it automatically, otherwise we run the risk of offending the E-Utilities servers with too many searches per second as the user types their query word (and looking glitchy in the meantime).

Act I

We will go through the server.R code in three parts, and demonstrate the RISmed package as we go. Basically we are developing three calls to E-Utilities, one for each action button on the UI. After loading all necessary packages, we activate the distPlot output expression for the user’s keyword when the user clicks the goButton action button, which the user sees on the UI as “PLOT”. RISmed then searches PubMed, and we construct a barplot of documents per year and add a line representing the sum of documents to date containing that keyword:

library(shiny)
library(SnowballC)
library(qdap)
library(ggplot2)
library(RISmed)
library(wordcloud)

shinyServer(function(input, output) {
word1<-eventReactive(input$goButton, {input$text})
  output$distPlot <- renderPlot({
  d1<-input$date1
  d2<-input$date2

  res <- EUtilsSummary(word1(), type="esearch", db="pubmed", datetype='pdat', mindate=d1, maxdate=d2, retmax=500)
  date()
  fetch <- EUtilsGet(res, type="efetch", db="pubmed")
  count<-table(YearPubmed(fetch))
  count<-as.data.frame(count)
  names(count)<-c("Year", "Counts")
  num <- data.frame(Year=count$Year, Counts=cumsum(count$Counts)) 
  num$g <- "g"
  names(num) <- c("Year", "Counts", "g")

  q <- qplot(x=Year, y=Counts, data=count, geom="bar", stat="identity")
  q <- q + geom_line(aes(x=Year, y=Counts, group=g), data=num) +
  ggtitle(paste("PubMed articles containing '", word1(), "' ", "= ", max(num$Counts), sep="")) +
  ylab("Number of articles") +
  xlab(paste("Year n Query date: ", Sys.time(), sep="")) +
  labs(colour="") + theme_bw()

  q
  })

Here is the plot we made:
CarsonChow

We have created an object res that contains the results of the EUtilsSummary function in RISmed, according to its arguments. In our case we are searching for the user’s keyword using an esearch, specifying the PubMed database (there are others), and we wish to search by date of publication to PubMed in the user’s date range (be sure to use YearPubmed to avoid NA’s). We will timestamp the user’s query on the plot using date(). Then we create another object called fetch, where we store the results of the EUtilsGet function that performs an efetch on our summary. This fetch object is sub-settable, and the year it was published to PubMed is accessed by YearPubmed. We use the table function to count the number of documents for each year to construct our plot. RISmed is more than just a wrapper for E-Utilities, it provides a seamless query and produces organized output ready for us to analyze.

Act II

In the second section of server.R, we produce a wordcloud of the most frequent terms in the abstracts of the documents that contain the user’s keyword. Again, we will wait to construct the wordcloud until the user clicks wordButton, which is the action button labeled “WORDS” on the UI:

word2<-eventReactive(input$wordButton, {input$text})
  output$wordPlot<-renderPlot({
  d1<-input$date1
  d2<-input$date2

  res <- EUtilsSummary(word2(), type="esearch", db="pubmed", datetype='pdat', mindate=d1, maxdate=d2, retmax=500)
  fetch <- EUtilsGet(res, type="efetch", db="pubmed")
  articles<-data.frame('Abstract'=AbstractText(fetch))
  abstracts<-as.character(articles$Abstract)
  abstracts<-paste(abstracts, sep="", collapse="") 
  wordcloud(abstracts, min.freq=10, max.words=70, colors=brewer.pal(7,"Dark2"))
  })

Here is the wordcloud:
WordCloud

Recall that in our first search, we accessed the publication years using YearPubmed(fetch), now we access the abstracts using AbstractText(fetch). This is the beauty of RISmed, we can access many fields of an EUtilsGet object in our R environment using the approach above. This documentation page, as well as googling RISmed is a good place to learn more. Shiny then easily allows us to share our findings and add interactivity.

Act III

Wordclouds are a fun way to visualize common words, and get a qualitative sense of the words associated with the user’s keyword. Future analyses will require that our outputs be in data frames, so in anticipation we will produce our third output in tabular format. When the user clicks the authButton action button titled “AUTHORS” on the UI, we again pass the user’s keyword to a new EUtilsSummary, and grab the author names using Author(fetch):

word3<-eventReactive(input$authButton, {input$text})
  output$authList<-renderTable({
  d1<-input$date1
  d2<-input$date2

  res <- EUtilsSummary(word3(), type="esearch", db="pubmed", 
datetype='pdat', mindate=d1, maxdate=d2, retmax=500)
  fetch <- EUtilsGet(res, type="efetch", db=" pubmed")
  AuthorList<-Author(fetch)
  Last<-sapply(AuthorList, function(x)paste(x$LastName))
  auths<-as.data.frame(sort(table(unlist(Last)), dec=TRUE))
  colnames(auths)<-"Count"
  auths <- cbind(Author = rownames(auths), auths)
  rownames(auths) <- NULL

  auths<-head(auths, 6)
  })
})

This is how the author table looks:
TableCarson

Note that RISmed stored the author information in a list (of documents) of data frames (of authors), so in this case we used sapply to extract each data frame (watch out for this occasional wrinkle).

Denouement

Remember that the most important component of our App is its benefit to the user. Here we have created a platform where a user can search a scientist’s name and quickly learn who that individual collaborates with, or collaborated with during a certain timeframe, how many publications that person has on PubMed, when they were added, as well as an idea of what type of research that scientist does. A user can also search drug names or molecule acronyms, and see the names of the researchers who are most associated with that keyword in PubMed. The user could then paste that name into the keyword box and see a wordcloud that might shed light on lab techniques or targets, or suggest that particular person’s field of interest.

Coming soon

In the next tutorial, we want to lay some groundwork for sharing future analyses by getting more control over our UI. We will see a few easy solutions provided by our good friends at Shiny, as well as walk through the process of creating a fully customized UI with our own html code. Also, check back soon for a link to a brief demonstration of this App for more ideas, and maybe some additional features.

Eventually we will be introducing other R packages that specialize in Natural Language Processing and sophisticated machine learning to peek at how data science is transforming the medical literature landscape. See you then!

11 Comments

  1. AG
    Aaron Gowins December 7, 2015

    Hi Peter,
    Check it out, the App calculates h-index and g- index.
    Thanks for the suggestion!

    Reply
  2. PH
    Peter Higgins December 5, 2015

    Hoping you will add facility to calculate h-index and g-index for authors in the near future.

    Reply
    1. AG
      Aaron Gowins December 5, 2015

      That’s a great suggestion, Peter, thanks.
      I will present an algorithm in the next tutorial and add it to the App.

      Reply
  3. R
    RafikMargaryan November 19, 2015

    Hi, excellent tutorial

    I have a problem with last word3() function which throws an error:

    cannot open: HTTP status was ‘400 Bad Request’

    Error in file(con, “r”) : cannot open the connection

    How can I solve this?
    Many thanks

    Reply
    1. AG
      Aaron Gowins November 19, 2015

      Hi, I’m glad you like the tutorial!

      I’m not sure what’s wrong, let’s figure it out .
      Are you at the site, or did you copy-paste code?

      Maybe try the link in the first paragraph, and if you want to copy-paste the code grab it from the bottom of the App display.

      If you fix it please let me know if there’s something wrong somewhere, otherwise shoot me a message and I’ll help you work it out. I want to know if I forgot to give some advice also.

      Thanks

      Reply
      1. R
        RafikMargaryan November 19, 2015

        Yes I’ve copy and pasted the code manually. First two modules are working perfectly, third one is throwing the errore above mentioned. I am not new to R, but still looking into code I can not find anything that makes me think of error.
        I will update If I will find other things.
        all the best.

        Reply
        1. AG
          Aaron Gowins November 19, 2015

          Do you get the error when you swipe the code from the App?
          Maybe try copying straight from: https://aarongowins.shinyapps.io/PubMedSearch
          Are you trying to just run it or publish also?

          Reply
          1. R
            RafikMargaryan November 19, 2015

            I think I found the problem. Git gave me the answer. Thanks a lot.

  4. A
    anarcho-chossid November 17, 2015

    the tools seems to be currently broken

    Reply
    1. AG
      Aaron Gowins November 17, 2015

      Hi anarcho-chossid,

      I have it running on my phone, not sure what’s wrong.

      https://aarongowins.shinyapp.io/PubMedSearch

      Reply
    2. K
      Klodian November 18, 2015

      Updated! I think it was a missing closing tag “})” in last code.

      Reply

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.