PubMed is an online repository of references and abstracts of publications in the fields of medicine and life sciences. PubMed is a free resource that is developed and maintained by the National Center for Biotechnology Information (NCBI), at the U.S. National Library of Medicine (NLM), located at the National Institutes of Health (NIH). PubMed homepage is located at the following URL: https://www.ncbi.nlm.nih.gov/pubmed/. Alternatively, PubMed can be programmatically queried via the NCBI Entrez E-utilities interface.

easyPubMed is an R interface to the Entrez Programming Utilities aimed at allowing an easy and smooth programmatic access to PubMed. The package is suitable for batch downloading large volumes of records (via the batch_pubmed_download() function), and comes with a set of functions to perform basic processing of the PubMed query output. easyPubMed can request and handle PubMed records in either XML or TXT format. This vignette covers the key functions of the package and provides informative examples.

Installation

To install easyPubMed from CRAN, you can run the following line of code

install.packages("easyPubMed")

Before using the functions included in easyPubMed, make sure to load the library.

library("easyPubMed")

Retrieving data from PubMed

The first section of this tutorial covers how to use easyPubMed for querying Entrez and how to retrieve or download PubMed records from the Entrez History Server.

A simple PubMed query via easyPubMed

Performing a standard PubMed search via easyPubMed is a two-step process:

  • the PubMed query step

  • the data retrieval step

PubMed is queried via the get_pubmed_ids() function, which takes a Query string as argument. The standard PubMed synthax applies, i.e. you can use the same tags-filters as in the web search engine. A PubMed query by the get_pubmed_ids() function results in:

  • the query results are posted on the Entrez History Server ready for retrieval

  • the function returns a list containing all information to access and download resuts from the server

Data can be retrieved from the History Server via the fetch_pubmed_data() function. The records can be requested in either XML or TXT format. Here following you can find a very simple example.

my_query <- "Damiano Fantini[AU]"
my_entrez_id <- get_pubmed_ids(my_query)
my_abstracts_txt <- fetch_pubmed_data(my_entrez_id, format = "abstract")
print(my_abstracts_txt[1:16])
##  [1] ""                                                                                 
##  [2] "1. Urol Oncol. 2018 Nov 13. pii: S1078-1439(18)30381-8. doi:"                     
##  [3] "10.1016/j.urolonc.2018.09.017. [Epub ahead of print]"                             
##  [4] ""                                                                                 
##  [5] "Molecular footprints of muscle-invasive bladder cancer in smoking and nonsmoking "
##  [6] "patients."                                                                        
##  [7] ""                                                                                 
##  [8] "Fantini D(1), Seiler R(2), Meeks JJ(3)."                                          
##  [9] ""                                                                                 
## [10] "Author information: "                                                             
## [11] "(1)Department of Urology, Northwestern University, Feinberg School of Medicine,"  
## [12] "Chicago, IL; Robert H. Lurie Comprehensive Cancer Center, Northwestern"           
## [13] "University, Chicago, IL."                                                         
## [14] "(2)Department of Urology, University Hospital Bern, Bern, Switzerland."           
## [15] "(3)Department of Urology, Northwestern University, Feinberg School of Medicine,"  
## [16] "Chicago, IL; Robert H. Lurie Comprehensive Cancer Center, Northwestern"

Here, the PubMed records were retrieved in the Abstract format. The formats supported by Entrez and easyPubMed are the following: “asn.1”, “xml”, “medline”, “uilist”, “abstract”. The following example shows how to retrieve PubMed records in XML format. In this case, the resulting output will be a XMLInternalDocument and XMLAbstractDocument class object. To access such XML object, we recommend using the functions included in the XML package. For example, it is possible to extract the title of each Article as follows.

my_abstracts_xml <- fetch_pubmed_data(my_entrez_id)
class(my_abstracts_xml) 
## [1] "XMLInternalDocument" "XMLAbstractDocument"
# apply "saveXML" to each //ArticleTitle tag via XML::xpathApply()
my_titles <- unlist(xpathApply(my_abstracts_xml, "//ArticleTitle", saveXML))

# use gsub to remove the tag, also trim long titles
my_titles <- gsub("(^.{5,10}Title>)|(<\\/.*$)", "", my_titles)
my_titles[nchar(my_titles)>75] <- paste(substr(my_titles[nchar(my_titles)>75], 1, 70), 
                                        "...", sep = "")
print(my_titles)
##  [1] "Molecular footprints of muscle-invasive bladder cancer in smoking and ..."
##  [2] "Genomic classification and risk stratification of bladder cancer."        
##  [3] "The BBN model: a mouse bladder cancer model featuring basal-subtype ge..."
##  [4] "Resetting the epigenetic balance of Polycomb and COMPASS function at e..."
##  [5] "APOBEC-mediated mutagenesis in urothelial carcinoma is associated with..."
##  [6] "A Carcinogen-induced mouse model recapitulates the molecular alteratio..."
##  [7] "DDB2 Is a Novel Regulator of Wnt Signaling in Colon Cancer."              
##  [8] "The evolving genomic landscape of urothelial carcinoma."                  
##  [9] "Chromatin association of XRCC5/6 in the absence of DNA damage depends ..."
## [10] "The prion protein is critical for DNA repair and cell survival after g..."
## [11] "Rapid inactivation and proteasome-mediated degradation of OGG1 contrib..."
## [12] "Understanding different functions of mammalian AP endonuclease (APE1) ..."
## [13] "Critical lysine residues within the overlooked N-terminal domain of hu..."
## [14] "APE1/Ref-1 interacts with NPM1 within nucleoli and plays a role in the..."
## [15] "APE1/Ref-1 regulates PTEN expression mediated by Egr-1."

Downloading and saving records as XML or TXT files

Instead of retrieving PubMed records as character- or XML-class objects, it is also possible to download all records returned by a PubMed query, and save them as txt or xml files on the local machine. Downloaded records will be saved locally as one or more files with a common user-defined prefix followed by a sequential number and the txt or xml extension. If a destination folder is not specified, the current directory will be used as target directory for the download. The batch_pubmed_download() function is suitable for downloading very large volumes of PubMed records.

new_query <- "(APE1[TI] OR OGG1[TI]) AND (2012[PDAT]:2016[PDAT])"
out.A <- batch_pubmed_download(pubmed_query_string = new_query, 
                               format = "xml", 
                               batch_size = 150,
                               dest_file_prefix = "easyPM_example")
## [1] "PubMed data batch 1 / 2 downloaded..."
## [1] "PubMed data batch 2 / 2 downloaded..."
out.A # this variable stores the name of the output files
## [1] "easyPM_example01.xml" "easyPM_example02.xml"

Analyzing PubMed records

The second section of this tutorial covers those easyPubMed functionalities aimed at transforming and analyzing PubMed records. While using the functions from the XML package is usually the recommended approach to deal with data stored in XML format, there are some exceptions where it may be convenient to coerce these records to Strings. easyPubMed comes with a set of dedicated functions that perform this task and manipulate the results. These functions will be covered in the following section.

Extracting Affiliation data from a single PubMed record

To convert XML PubMed records to strings, the articles_to_list() function is used. This function converts an an XML object containing PubMed records (identified by the \\PubmedArticle tag) into a list of individual records from or an XML file obtained as shown above. Each record in the list is a string (character-class vector of length 1) that still includes XML tags.

my_PM_list <- articles_to_list(my_abstracts_xml)
class(my_PM_list[[4]])
## [1] "character"
cat(substr(my_PM_list[[4]], 1, 984))
## <PubmedArticle>
##   <MedlineCitation Status="In-Data-Review" Owner="NLM">
##     <PMID Version="1">29785026</PMID>
##     <DateRevised>
##       <Year>2019</Year>
##       <Month>01</Month>
##       <Day>07</Day>
##     </DateRevised>
##     <Article PubModel="Print-Electronic">
##       <Journal>
##         <ISSN IssnType="Electronic">1546-170X</ISSN>
##         <JournalIssue CitedMedium="Internet">
##           <Volume>24</Volume>
##           <Issue>6</Issue>
##           <PubDate>
##             <Year>2018</Year>
##             <Month>Jun</Month>
##           </PubDate>
##         </JournalIssue>
##         <Title>Nature medicine</Title>
##         <ISOAbbreviation>Nat. Med.</ISOAbbreviation>
##       </Journal>
##       <ArticleTitle>Resetting the epigenetic balance of Polycomb and COMPASS function at enhancers for cancer therapy.</ArticleTitle>
##       <Pagination>
##         <MedlinePgn>758-769</MedlinePgn>
##       </Pagination>
##       <ELocationID EIdType="doi" ValidYN="Y">10.1038/s41591-018-0034-6</ELocationID>
##       <Abstract>
## 

Affiliations or other fields of interest can be extracted from a specific record using the custom_grep() function, that combines regular expressions (regexpr, gsub) and substring extraction (substr). The fields extracted from the record will be returned as elements of a list or a character vector.

curr_PM_record <- my_PM_list[[(length(my_PM_list) - 3)]]
custom_grep(curr_PM_record, tag = "DateCompleted")
## [[1]]
## [1] "\n      <Year>2010</Year>\n      <Month>11</Month>\n      <Day>16</Day>\n    "
custom_grep(curr_PM_record, tag = "LastName", format = "char")
## [1] "Tell"         "Fantini"      "Quadrifoglio"

easyPubMed implements out of the box a tool for extracting data from a PubMed record: the article_to_df() function. This function accepts a string as input (typically, an element of the list returned by an articles_to_list() call) and returns a data.frame. Each row corresponds to a different author; columns include values extracted from the following fields: c("pmid", "doi", "title", "abstract", "year", "month", "day", "jabbrv", "journal", "lastname", "firstname", "address", "email"). One of these fields corresponds to the Article Abstract text (column n. 2). If the full text Abstract is not required, it is possible to limit the number of chars retrieved from this field by setting the max_chars argument.

my.df <- article_to_df(curr_PM_record, max_chars = 18)

# Fields extracted from the PubMed record
colnames(my.df)
##  [1] "pmid"      "doi"       "title"     "abstract"  "year"     
##  [6] "month"     "day"       "jabbrv"    "journal"   "keywords" 
## [11] "lastname"  "firstname" "address"   "email"
# Trim long strings and then display some content: each row corresponds to one author
my.df$title <- substr(my.df$title, 1, 15)
my.df$address <- substr(my.df$address, 1, 19)
my.df$jabbrv <- substr(my.df$jabbrv, 1, 10)
my.df[,c("pmid", "title", "jabbrv", "firstname", "address")] %>% kable() %>% 
  kable_styling(bootstrap_options = 'striped')
pmid title jabbrv firstname address
20706766 Understanding d Cell. Mol. Gianluca Department of Biome
20706766 Understanding d Cell. Mol. Damiano NA
20706766 Understanding d Cell. Mol. Franco NA

When affiliation info are identical for multiple authors, they are usually omitted as in the example above. Addresses may be imputed for all authors in the data.frame by setting the autofill argument to TRUE.

my.df2 <- article_to_df(curr_PM_record, autofill = TRUE)
my.df2$title <- substr(my.df2$title, 1, 15)
my.df2$jabbrv <- substr(my.df2$jabbrv, 1, 10)
my.df2$address <- substr(my.df2$address, 1, 19)
my.df2[,c("pmid", "title", "jabbrv", "firstname", "address")] %>%
  kable() %>% kable_styling(bootstrap_options = 'striped')
pmid title jabbrv firstname address
20706766 Understanding d Cell. Mol. Gianluca Department of Biome
20706766 Understanding d Cell. Mol. Damiano Department of Biome
20706766 Understanding d Cell. Mol. Franco Department of Biome

Automatic Data Extraction from XML PubMed Records

To retrieve author information and publication data from multiple XML records at once, it is possible to use the table_articles_byAuth() function. This function relies on the funcions discussed above and returns a data.frame including all the fields extracted in the previous example. The function accepts five arguments.

  • pubmed_data: an XML file or an XML object with PubMed records

  • max_chars and autofill: same as discussed in the previous example

  • included_authors: one of the following options c(“first”, “last”, “all”). The function can return data corresponding to the first, the last or all the authors for each PubMed record.

  • dest_file: if not NULL, the function attempts writing its output to the selected file. Existing files will be overwritten.

new_PM_query <- "(APEX1[TI] OR OGG1[TI]) AND (2010[PDAT]:2013[PDAT])"
out.B <- batch_pubmed_download(pubmed_query_string = new_PM_query, dest_file_prefix = "apex1_sample")
## [1] "PubMed data batch 1 / 1 downloaded..."
# Retrieve the full name of the XML file downloaded in the previous step
new_PM_file <- out.B[1]
new_PM_df <- table_articles_byAuth(pubmed_data = new_PM_file, included_authors = "first", max_chars = 0)

# Alternatively, the output of a fetch_pubmed_data() could have been used

# Printing a sample of the resulting data frame
new_PM_df$address <- substr(new_PM_df$address, 1, 28)
new_PM_df$jabbrv <- substr(new_PM_df$jabbrv, 1, 9)
new_PM_df[1:10, c("pmid", "year", "jabbrv", "lastname", "address")] %>%
  kable() %>% kable_styling(bootstrap_options = 'striped')
pmid year jabbrv lastname address
24190502 2015 Arch. Tox Bach Grup de Mutagènesi, Departam
24186001 2014 Tumour Bi Yan Department of Clinical Labor
24175791 2014 Asian Pac Li Cancer Center, Daping Hospit
24121118 2014 Mech. Age Lillenes Centre for Molecular Biology
24101388 2014 J. Physio Antushevich The Kielanowski Institute of
24075420 2014 DNA Repai Gu State Key Laboratory of Repr
23999824 2014 Tumour Bi Chen Department of Hepatobiliary
23959014 2014 Biol. Res Alanazi Genome Research Chair, Depar
23909557 2014 Genet Tes Wang Department of Oncology, Shan
23892003 2014 Exp. Cell Yan State Key Laboratory of Repr

References

Feedback and Citation

Thank you very much for using easyPubMed and/or reading this vignette. Please, feel free to contact me (author/maintainer) for feedback, questions and suggestions: my email is <damiano.fantini(at)gmail(dot)com>. More info about easyPubMed are available at the following URL: www.data-pulse.com.

easyPubMed Copyright (C) 2017-2019 Damiano Fantini. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

!!Note!! If you are using easyPubMed for a scientific publication, please name the package in the Materials and Methods section of the paper. Thanks! Also, I am always open to collaborations. If you have an idea you would like to discuss or develop based on what you read in this Vignette, feel free to contact me via email. Thank you.

SessionInfo

sessionInfo()
## R version 3.4.4 (2018-03-15)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 18.04.1 LTS
## 
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/libopenblasp-r0.2.20.so
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] kableExtra_0.9.0 dplyr_0.7.8      easyPubMed_2.11  XML_3.98-1.16   
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.0        rstudioapi_0.8    xml2_1.2.0       
##  [4] knitr_1.20        bindr_0.1.1       magrittr_1.5     
##  [7] hms_0.4.2         munsell_0.5.0     rvest_0.3.2      
## [10] tidyselect_0.2.5  viridisLite_0.3.0 colorspace_1.3-2 
## [13] R6_2.3.0          rlang_0.3.0.1     highr_0.7        
## [16] httr_1.3.1        stringr_1.3.1     tools_3.4.4      
## [19] htmltools_0.3.6   yaml_2.2.0        rprojroot_1.3-2  
## [22] digest_0.6.15     assertthat_0.2.0  tibble_1.4.2     
## [25] crayon_1.3.4      bindrcpp_0.2.2    readr_1.2.1      
## [28] purrr_0.2.5       glue_1.3.0        evaluate_0.10.1  
## [31] rmarkdown_1.10    stringi_1.2.4     compiler_3.4.4   
## [34] pillar_1.3.0      scales_1.0.0      backports_1.1.2  
## [37] pkgconfig_2.0.2