{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Binder link to this notebook:\n", "\n", "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ciakovx/ciakovx.github.io/master?filepath=rcrossref.ipynb)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# `rcrossref` and `roadoi`\n", "\n", "# Licensing\n", "\n", "This walkthrough is distributed under a [Creative Commons Attribution 4.0 International (CC BY 4.0) License](https://creativecommons.org/licenses/by/4.0/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Load packages\n", "When you download R it already has a number of functions built in: these encompass what is called **Base R**. However, many R users write their own libraries of functions, package them together in R **packages**, and provide them to the R community at no charge. This extends the capacity of R and allows us to do much more. In many cases, they improve on the Base R functions by making them easier and more straight-forward to use. In addition to `rcrossref`, we will also be using the `dplyr`, `purrr`, `stringr` and `tidyr` packages. These packages are part of the [tidyverse](https://www.tidyverse.org/), a collection of R packages designed for data science.\n", "\n", "If you are using R and R Studio, you will need to use `install.packages()` function to install the packages first. I have already installed the packages for you in this Binder repository, so we will simply load them by calling `library()`. Let's also set an option to see a max number of 100 columns and max 20 rows in our Jupyter Notebooks environment, to make printed tables easier to look at." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# load packages\n", "library(rcrossref)\n", "library(roadoi)\n", "library(dplyr)\n", "library(purrr)\n", "library(stringr)\n", "library(tidyr)\n", "\n", "# increase number of columns and rows displayed when we print a table\n", "options(repr.matrix.max.cols=100, repr.matrix.max.rows=20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can ignore the warning message in pink stating `The following objects are masked from ‘package:stats’: filter, lag`. This simply means that there are functions from other packages with the name `filter()` and `lag()` and that the `dplyr` functions will `mask` those (i.e. assume predominance).\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Crossref & `rcrossref`\n", "\n", "## Crossref\n", "\n", "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", "\n", "\n", "Crossref is a a not-for-profit membership organization dedicated to interlinking scholarly metadata, including journals, books, conference proceedings, working papers, technical reports, data sets, authors, funders, and more. The [Crossref REST API](https://github.com/CrossRef/rest-api-doc) allows anybody to search and reuse members' metadata in a variety of ways. Read [examples of user stories](https://www.crossref.org/services/metadata-delivery/user-stories/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `rcrossref`\n", "\n", "`rcrossref` is a package developed by [Scott Chamberlain](https://scottchamberlain.info/), Hao Zhu, Najko Jahn, Carl Boettiger, and Karthik Ram, part of the [rOpenSci](https://ropensci.org/) set of packages. rOpenSci is an incredible organization dedicated to open and reproducible research using shared data and reusable software. I strongly recommend you browse their set of packages at https://ropensci.org/packages/.\n", "\n", "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", "\n", "`rcrossref` serves as an interface to the Crossref API.\n", "\n", "**Key links**\n", "\n", "* [rcrossref documentation](https://cran.r-project.org/web/packages/rcrossref/rcrossref.pdf)\n", "* [Crossref REST API documentation](https://github.com/ropensci/rcrossref)\n", "* [Crossref Metadata API JSON Format](https://github.com/CrossRef/rest-api-doc/blob/master/api_format.md)\n", "* Crossref and rcrossref tutorials\n", " - [my `rcrossref` tutorial](https://ciakovx.github.io/rcrossref.html)\n", " - rOpenSci [rcrossref tutorial](https://ropensci.org/tutorials/rcrossref_tutorial/)\n", " - Paul Oldham: [\"Accessing the Scientific Literature with Crossref\"](https://poldham.github.io/abs/crossref.html)\n", " - [rcrossref vignette](https://cran.r-project.org/web/packages/rcrossref/vignettes/crossref_vignette.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setting up `rcrossref`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As described in the documentation, the Crossref team encourages users to send requests with their email address, and will forward you to a dedicated API cluster for improved performance when you share your email with them. Learn more at . \n", "\n", "To do this in R, replace `name@example.com` below with your own email address. It will now be shared with Crossref whenever you send them an API request. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Sys.setenv(crossref_email='name@example.com')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Getting publications from journals with `cr_journals`\n", "\n", "`cr_journals()` takes either an ISSN or a general keyword query, and returns metadata for articles published in the journal, including DOI, title, volume, issue, pages, publisher, authors, etc. A full list of publications in Crossref is [available on their website](https://support.crossref.org/hc/en-us/articles/213197226-Browsable-title-list).\n", "\n", "## Getting journal details\n", "\n", "Crossref is entirely dependent on publishers to supply the metadata. Some fields are required, while others are optional. You may therefore first be interested in what metadata publishers have submitted to Crossref for a given journal. By using `cr_journals` with `works = FALSE`, you can determine who publishes the journal, the total number of articles for the journal in Crossref, whether abstracts are included, if the full text of articles is deposited, if author ORCIDs are provided, and if the publisher supplies author affiliations, author ORCID iDs, article licensing data, funders for the article, article references, and a few other items. \n", "\n", "Crossref displays some of this data on a publisheropenly on the web at .\n", "\n", "First we will create a new vector `plosone_issn` with the ISSN for the journal *PLoS ONE*. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign the PLoS ISSN\n", "plosone_issn <- '1932-6203'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will then run `rcrossref::cr_journals()`, setting the ISSN equal to the `plosone_issn` we just created, and print the results." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [ "# get information about the journal\n", "plosone_details <- cr_journals(issn = plosone_issn, works = FALSE)\n", "plosone_details" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This actually comes back as a list of three items: `meta`, `data`, and `facets`. The good stuff is in `data`. \n", "\n", "We use the `pluck()` function from the `purrr` package to pull that data only. We will be using `pluck` throughout this tutorial; it's an easy way of indexing deeply and flexibly into lists to extract information.\n", "\n", "We don't have time in this tutorial to discuss list items and purr. For an excellent in-depth tutorial, see Jenny Bryan's [\n", "Introduction to map(): extract elements](https://ciakovx.github.io/jennybc_lists_lesson.html), reproduced on the course website under the terms of a Creative Commons license." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# get information about the journal and pluck the data at the same time\n", "plosone_details <- rcrossref::cr_journals(issn = plosone_issn, works = FALSE) %>%\n", " purrr::pluck(\"data\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is precisely the same thing as passing the ISSN directly to `cr_journals()`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cr_journals(\"1932-6203\", works = FALSE) %>% \n", " pluck(\"data\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `purrr::pluck()` function is connected to `plosone_details` with something called a [Pipe Operator](https://www.datacamp.com/community/tutorials/pipe-r-tutorial) `%>%`, which we will be using throughout the tutorial. A pipe takes the output of one statement and immediately makes it the input of the next statement. It helps so that you don't have to write every intermediate, processing data to your R environment. You can think of it as \"then\" in natural language. So the above script first makes the API call with `cr_journals()`, then it applies `pluck()` to extract only the list element called `\"data\"`, and returns it to the `plosone_details` value.\n", "\n", "We now have a **data frame** including the details Croassref has on file about PLoS ONE. Scroll to the right to see all the columns." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "plosone_details" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are a number of ways to explore this data frame:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#display information about the data frame\n", "str(plosone_details)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Type `?str` into the console to read the description of the `str` function. You can call `str()` on an R object to compactly display information about it, including the data type, the number of elements, and a printout of the first few elements." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# dimensions: 1 row, 53 columns\n", "dim(plosone_details)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# number of rows\n", "nrow(plosone_details)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# number of columns\n", "ncol(plosone_details)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# column names\n", "names(plosone_details)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see this data frame includes one observation of 53 different variables. This includes the total number of DOIs, whether the abstracts, orcids, article references are current; and other information.\n", "\n", "You can use the $ symbol to work with particular variables. For example, the `publisher` column:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# print the publisher variable\n", "plosone_details$publisher\n", "\n", "# return the publisher variable to a new value `myPLOSONEpublisher`\n", "myPLOSONEpublisher <- plosone_details$publisher" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The total number of DOIs on file:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print the total number of DOIs\n", "plosone_details$total_dois" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whether publisher provides data on funders for articles in the current file (as opposed to the backfile) in Crossref (a TRUE/FALSE value–called “logical” in R):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# is funder data current on deposits?\n", "plosone_details$deposits_funders_current" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What percentage of articles in Crossref's current file contains at least one funding award number? (i.e., a number assigned by the funding organization to identify the specific piece of funding (the award or grant))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plosone_details$award_numbers_current * 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "**TRY IT YOURSELF**\n", "\n", "1. Assign an ISSN for a well-known journal to a new variable in R. Name it whatever you like. You can use the [Scimago Journal Rank](https://www.scimagojr.com/journalrank.php) to look up the ISSN. If you need a couple examples, try [RUSA](https://www.scimagojr.com/journalsearch.php?q=16004&tip=sid&clean=0) or [Library Hi Tech](https://www.scimagojr.com/journalsearch.php?q=144908&tip=sid&clean=0). Make sure to put the ISSN in quotes to create a character vector.\n", "2. Look up the journal details using `cr_journals`. Make sure to pass the argument `works = FALSE`.\n", "3. Print the data to your console by typing in the value name.\n", "\n", "\n", "**Does it matter if the ISSN has a hyphen or not? Try both methods.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign an ISSN to a value. Call the value what you want (e.g. plosone_issn)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# look up journal details using the cr_journals function and assign it to a new value (e.g. plosone_details). \n", "# Remember to include a %>% pipe and call purrr::pluck(\"data\")\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print info about the journal details to the console by typing in the value name inside str()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# how many total DOIs does it have on file?\n", "\n", "\n", "# what percent of articles in the current file have orcid iDs?\n", "\n", "\n", "# does this journal provide open references in its current file?\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting journal publications by ISSN\n", "\n", "To get metadata for the publications themselves rather than data about the journal, we will again use the `plosone_issn` value in the `issn =` argument to `cr_journals`, but we now set `works = TRUE`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# get metadata on articles by setting works = TRUE\n", "plosone_publications <- cr_journals(issn = plosone_issn, works = TRUE, limit = 25) %>%\n", " pluck(\"data\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's walk through this step by step:\n", "\n", "* First, we are creating a new value called `plosone_publications`\n", "* We are using the assignment operator `<-` to assign the results of an operation to this new value\n", "* We are running the function `cr_journals()`. It is not necessary to add `rcrossref::` to the beginning of the function.\n", "* We **pass** three arguments to the function:\n", " * `issn = plosone_issn` : We defined `plosone_issn` earlier in the session as '1932-6203'. We are reusing that value here to tell the `cr_journals()` function what journal we want information on\n", " * `works = TRUE` : When we earlier specified `works = FALSE`, we got back information on the publication. When `works = TRUE`, we will get back article level metadata\n", " * `limit = 25` : We will get back 25 articles. The default number of articles returned is 20, but you can increase or decrease that with the `limit` argument. The max limit is 1000, but you can get more using the `cursor` argument (see below).\n", "* `%>%` : Pipe operator says to R to take the results of this function and use it as the input for what follows\n", "* `pluck(\"data\")` : This will grab only the contents of the list item \"data\" and return it to `plosone_publications`.\n", "\n", "Let's explore the data frame:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print dimensions of this data frame\n", "dim(plosone_publications)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we run `dim()` (dimensions) on this result, we now see a different number of rows and columns: 25 rows and 28 columns. This is therefore a different dataset than `plosone_details`. Let's call `names()` to see what the column names are:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print column names\n", "names(plosone_publications)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We view the entire data frame below. Because there are some nested lists within the data, we will use the `select()` function from the `dplyr` package to select only a few columns. This will make it easier for us to view here in the Azure Notebook environment. You can also use the `select()` function to rearrange the columns." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print select columns from the data frame\n", "plosone_publications %>%\n", " dplyr::select(title, doi, volume, issue, page, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we are just getting back the last 25 articles that have been indexed in Crossref by PLoS ONE. However, this gives you a taste of how rich the metadata is. We have the dates the article was deposited and published online, the title, DOI, the ISSN, the volume, issue, and page numbers, the number of references, the URL, and for some items, the subjects. The omitted columns include information on licensing, authors, and more. We will deal with those columns further down.\n", "\n", "## Getting multiple publications by ISSN\n", "\n", "You can also pass multiple ISSNs to `cr_journals`. Here we create 2 new values, `jama_issn` and `jah_issn`. These are ISSNs for the *Journal of American History* and *JAMA: The Journal of the American Medical Association*. We then pass them to `cr_journals` by passing them to the `c()` function, which will combine them (it's like CONCATENATE in Excel). We set `works` to `TRUE` so we'll get the publications metadata, and we set the `limit` to 50, so we'll get 50 publications per journal." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign the JAMA and JAH ISSNs\n", "jama_issn <- '1538-3598'\n", "jah_issn <- '0021-8723'\n", "\n", "# get the last 10 publications on deposit from each journal. For multiple ISSNs, use c() to combine them\n", "jah_jama_publications <- rcrossref::cr_journals(issn = c(jama_issn, jah_issn), \n", " works = TRUE, \n", " limit = 10) %>%\n", " purrr::pluck(\"data\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "c(jama_issn, jah_issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we used `c()` to combine `jama_issn` and `jah_issn`. `c()` is used to create a **vector** in R. A vector is a sequence of elements of the same type. In this case, even though the ISSNs are numbers, we created them as `character` vectors by surrounding them in quotation marks. You can use single or double quotes. Above, when we assigned 5 to `y`, we created a `numeric` vector. \n", "\n", "Vectors can only contain “homogenous” data–in other words, all data must be of the same type. The type of a vector determines what kind of analysis you can do on it. For example, you can perform mathematical operations on `numeric` objects, but not on `character` objects. You can think of vectors as columns in an Excel spreadsheet: for example, in a name column, you want every value to be a character; in a date column, you want every value to be a date; etc.\n", "\n", "Going back to our `jah_jama_publications` object, we have a dataframe composed of 20 observations of 24 variables. This is a rich set of metadata for the articles in the given publications. The fields are detailed in the [Crossref documentation](https://github.com/CrossRef/rest-api-doc/blob/master/api_format.md#work), including the field name, type, description, and whether or not it's required. Some of these fields are title, DOI, DOI prefix identifer, ISSN, volume, issue, publisher, abstract (if provided), reference count (if provided--i.e., the number of references *in* the given article), link (if provided), subject (if provided), and other information. The number of citations *to* the article are not pulled, but these can be gathered separately with `cr_citation_count()` (see below)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print column names\n", "names(jah_jama_publications)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print data frame with select columns\n", "jah_jama_publications %>%\n", " dplyr::select(title, container.title, doi, volume, issue, page, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Filtering the `cr_journals` query with the `filter` argument\n", "\n", "You can use the `filter` argument within `cr_journals` to specify some parameters as the query is executing. This filter is built into the Crossref API query. See the available filters by calling `rcrossref::filter_names()`, and details by calling `rcrossref::filter_details`. It's also in [the API documentation](https://github.com/CrossRef/rest-api-doc#filter-names). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| filter | possible values | description|\n", "|:-----------|:----------------|:-----------|\n", "| `has-funder` | | metadata which includes one or more funder entry |\n", "| `funder` | `{funder_id}` | metadata which include the `{funder_id}` in FundRef data |\n", "| `location` |`{country_name}` | funder records where location = `{country name}`. Only works on `/funders` route |\n", "| `prefix` | `{owner_prefix}` | metadata belonging to a DOI owner prefix `{owner_prefix}` (e.g. `10.1016` ) |\n", "| `member` | `{member_id}` | metadata belonging to a Crossref member |\n", "| `from-index-date` | `{date}` | metadata indexed since (inclusive) `{date}` |\n", "| `until-index-date` | `{date}` | metadata indexed before (inclusive) `{date}` |\n", "| `from-deposit-date` | `{date}` | metadata last (re)deposited since (inclusive) `{date}` |\n", "| `until-deposit-date` | `{date}` | metadata last (re)deposited before (inclusive) `{date}` |\n", "| `from-update-date` | `{date}` | Metadata updated since (inclusive) `{date}`. Currently the same as `from-deposit-date`. |\n", "| `until-update-date` | `{date}` | Metadata updated before (inclusive) `{date}`. Currently the same as `until-deposit-date`. |\n", "| `from-created-date` | `{date}` | metadata first deposited since (inclusive) `{date}` |\n", "| `until-created-date` | `{date}` | metadata first deposited before (inclusive) `{date}` |\n", "| `from-pub-date` | `{date}` | metadata where published date is since (inclusive) `{date}` |\n", "| `until-pub-date` | `{date}` | metadata where published date is before (inclusive) `{date}` |\n", "| `from-online-pub-date` | `{date}` | metadata where online published date is since (inclusive) `{date}` |\n", "| `until-online-pub-date` | `{date}` | metadata where online published date is before (inclusive) `{date}` |\n", "| `from-print-pub-date` | `{date}` | metadata where print published date is since (inclusive) `{date}` |\n", "| `until-print-pub-date` | `{date}` | metadata where print published date is before (inclusive) `{date}` |\n", "| `from-posted-date` | `{date}` | metadata where posted date is since (inclusive) `{date}` |\n", "| `until-posted-date` | `{date}` | metadata where posted date is before (inclusive) `{date}` |\n", "| `from-accepted-date` | `{date}` | metadata where accepted date is since (inclusive) `{date}` |\n", "| `until-accepted-date` | `{date}` | metadata where accepted date is before (inclusive) `{date}` |\n", "| `has-license` | | metadata that includes any `` elements. |\n", "| `license.url` | `{url}` | metadata where `` value equals `{url}` |\n", "| `license.version` | `{string}` | metadata where the ``'s `applies_to` attribute is `{string}`|\n", "| `license.delay` | `{integer}` | metadata where difference between publication date and the ``'s `start_date` attribute is <= `{integer}` (in days)|\n", "| `has-full-text` | | metadata that includes any full text `` elements. |\n", "| `full-text.version` | `{string}` | metadata where `` element's `content_version` attribute is `{string}`. |\n", "| `full-text.type` | `{mime_type}` | metadata where `` element's `content_type` attribute is `{mime_type}` (e.g. `application/pdf`). |\n", "| `full-text.application` | `{string}` | metadata where `` link has one of the following intended applications: `text-mining`, `similarity-checking` or `unspecified` |\n", "| `has-references` | | metadata for works that have a list of references |\n", "| `reference-visibility` | `[open, limited, closed]` | metadata for works where references are either `open`, `limited` (to [Metadata Plus subscribers](https://www.crossref.org/services/metadata-delivery/plus-service/)) or `closed` |\n", "| `has-archive` | | metadata which include name of archive partner |\n", "| `archive` | `{string}` | metadata which where value of archive partner is `{string}` |\n", "| `has-orcid` | | metadata which includes one or more ORCIDs |\n", "| `has-authenticated-orcid` | | metadata which includes one or more ORCIDs where the depositing publisher claims to have witness the ORCID owner authenticate with ORCID |\n", "| `orcid` | `{orcid}` | metadata where `` element's value = `{orcid}` |\n", "| `issn` | `{issn}` | metadata where record has an ISSN = `{issn}`. Format is `xxxx-xxxx`. |\n", "| `isbn` | `{isbn}` | metadata where record has an ISBN = `{issn}`. |\n", "| `type` | `{type}` | metadata records whose type = `{type}`. Type must be an ID value from the list of types returned by the `/types` resource |\n", "| `directory` | `{directory}` | metadata records whose article or serial are mentioned in the given `{directory}`. Currently the only supported value is `doaj`. |\n", "| `doi` | `{doi}` | metadata describing the DOI `{doi}` |\n", "| `updates` | `{doi}` | metadata for records that represent editorial updates to the DOI `{doi}` |\n", "| `is-update` | | metadata for records that represent editorial updates |\n", "| `has-update-policy` | | metadata for records that include a link to an editorial update policy |\n", "| `container-title` | | metadata for records with a publication title exactly with an exact match |\n", "| `category-name` | | metadata for records with an exact matching category label. Category labels come from [this list](https://www.elsevier.com/solutions/scopus/content) published by Scopus |\n", "| `type` | | metadata for records with type matching a type identifier (e.g. `journal-article`) |\n", "| `type-name` | | metadata for records with an exactly matching type label |\n", "| `award.number` | `{award_number}` | metadata for records with a matching award nunber. Optionally combine with `award.funder` |\n", "| `award.funder` | `{funder doi or id}` | metadata for records with an award with matching funder. Optionally combine with `award.number` |\n", "| `has-assertion` | | metadata for records with any assertions |\n", "| `assertion-group` | | metadata for records with an assertion in a particular group |\n", "| `assertion` | | metadata for records with a particular named assertion |\n", "| `has-affiliation` | | metadata for records that have any affiliation information |\n", "| `alternative-id` | | metadata for records with the given alternative ID, which may be a publisher-specific ID, or any other identifier a publisher may have provided |\n", "| `article-number` | | metadata for records with a given article number |\n", "| `has-abstract` | | metadata for records which include an abstract |\n", "| `has-clinical-trial-number` | | metadata for records which include a clinical trial number |\n", "| `content-domain` | | metadata where the publisher records a particular domain name as the location Crossmark content will appear |\n", "| `has-content-domain` | | metadata where the publisher records a domain name location for Crossmark content |\n", "| `has-domain-restriction` | | metadata where the publisher restricts Crossmark usage to content domains |\n", "| `has-relation` | | metadata for records that either assert or are the object of a relation |\n", "| `relation.type` | | One of the relation types from the Crossref relations schema (e.g. `is-referenced-by`, `is-parent-of`, `is-preprint-of`) |\n", "| `relation.object` | | Relations where the object identifier matches the identifier provided |\n", "| `relation.object-type` | | One of the identifier types from the Crossref relations schema (e.g. `doi`, `issn`) |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Filtering by publication date with `from_pub_date` and `until_pub_date`\n", "For example, you may only want to pull publications from a given year, or within a date range. Remember to increase the limit or use `cursor` if you need to. Also notice three things about the `filter` argument:\n", "\n", "* The query parameter is in backticks (the key next to the 1 on the keyboard)\n", "* The query itself is in single quotes\n", "* The whole thing is wrapped in `c()`\n", "\n", "Here, we will get all articles from the *Journal of Librarianship and Scholarly Communication* published after January 1, 2019:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign the JLSC ISSN\n", "jlsc_issn <- \"2162-3309\"\n", "\n", "# get articles published since January 1, 2019\n", "jlsc_publications_2019 <- rcrossref::cr_journals(issn = jlsc_issn, works = TRUE, \n", " filter = c(from_pub_date='2019-01-01')) %>%\n", " purrr::pluck(\"data\")\n", "\n", "# print the dataframe with select column\n", "jlsc_publications_2019 %>%\n", " dplyr::select(title, container.title, doi, volume, issue, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Filtering by funder with `award.funder`\n", "\n", "You can also return all articles funded by a specific funder. See the [Crossref Funder Registry](https://gitlab.com/crossref/open_funder_registry) for a list of funders and their DOIs (download the CSV at the bottom of the page). \n", "\n", "Here, we will combine two filters: `award.funder` and `from_pub_date` to return all articles published in PLoS ONE where a) at least one funder is the National Institutes of Health, and b) the article was published after July 1, 2020. Note that we set a `limit` here of 25 because we are doing a teaching activity and we don't want to send heavy queries. If you were doing this on your own, you would likely want to remove the limit." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign the PLoS ONE ISSN and the NIH Funder DOI\n", "plosone_issn <- '1932-6203'\n", "nih_funder_doi <- '10.13039/100000002'\n", "\n", "# get articles published in PLoS since 3/1 funded by NIH\n", "plosone_publications_nih <- rcrossref::cr_journals(issn = plosone_issn, works = T, limit = 25,\n", " filter = c(award.funder = nih_funder_doi,\n", " from_pub_date = '2020-07-01')) %>%\n", " purrr::pluck(\"data\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will use `unnest()` from the `tidyr` package to view the data frame here. This is described below in [Unnesting List Columns](https://rcrossref2-clarkeiakovakis.notebooks.azure.com/j/notebooks/rcrossref_20200305.ipynb#Unnesting-list-columns)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print the dataframe, first unnesting the funder column\n", "plosone_publications_nih %>%\n", " tidyr::unnest(funder)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you scroll all the way to the right, you can see the funder information. Look at the `title` column and you will notice that some article titles are now duplicated, however you will see different funders in the `name` column. This is because a single article may have multiple funders, and a new row is created for each funder, with data including the `award` number." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "**TRY IT YOURSELF**\n", "\n", "1. Run a `cr_journals` query with `works = FALSE` to get information on the journal *Scientometrics* (ISSN 1588-2861). Remember to set `works` to `FALSE` and include `%>% pluck(\"data\")`. Assign it to a symbol of your choosing.\n", "2. Call `str()` on it to view information on it. Does the current Crossref file contain funder information? What percent of articles in the current file have at least one funder listed? What percent of articles in the current file have at least one ORCID iD?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# get data on Scientometrics\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# call str() to view information about it\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# does Crossref contain information about funders?\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# What percent of articles in the current file have at least one funder listed?\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# What percent of articles in the current file have at least one ORCID iD?\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. Find out if the NIH has funded any publications in the journal *Scientometrics* since 2015.\n", "\n", "Remember if you are printing the data frame out, include `%>% dplyr::select(title, container.title, doi, volume, issue, issued, url, publisher, reference.count, type, issn)`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# how many publications in Scientometrics funded by the NIH since 2015?\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. Look at the above table of filters to see what the filter argument for ORCID is. Check to see if *Scientometrics* has any articles authored by Dr. Anne-Wil Harzing, whose ORCID iD is `0000-0003-1509-3003`. How many articles does she have?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# does Scientometrics have any articles authored by the professor with ORCID iD 0000-0003-1509-3003?\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Filtering by license with `has_license`\n", "\n", "You may be interested in licensing information for articles; for instance, gathering publications in a given journal that are licensed under Creative Commons. First run `cr_journals` with `works` set to `FALSE` in order to return journal details so you can check if the publisher even sends article licensing information to Crossref--it's not required. We will use PLOS ONE again as an example.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign the PLoS ONE ISSN and get journal details by setting works = FALSE\n", "plosone_issn <- '1932-6203'\n", "plosone_details <- rcrossref::cr_journals(issn = plosone_issn, works = FALSE) %>%\n", " purrr::pluck(\"data\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can check the `deposits_licenses_current` variables to see if license data on file is current. If it is `TRUE`, PLoS ONE does send licensing information and it is current. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# is article licensing data on file current?\n", "plosone_details$deposits_licenses_current" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now rerun the query but set `works = TRUE`, and set the `has_license` to `TRUE`. This will therefore return only articles that have license information. We will set our `limit` to 25." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# get last 25 articles on file where has_license is TRUE\n", "plosone_license <- rcrossref::cr_journals(issn = plosone_issn, works = T, limit = 25, \n", " filter = c(`has_license` = TRUE)) %>% \n", " pluck(\"data\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print the data with select columns\n", "plosone_license %>%\n", " dplyr::select(title, doi, volume, issue, page, issued, url, publisher, reference.count, type, issn, license)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The license data comes in as a nested column. We can unnest it using `tidyr::unnest`, which we used above with funders and will be discussed more below. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print the data frame with license unnested. The .drop argument will drop all other list columns.\n", "plosone_license %>%\n", " tidyr::unnest(license, .drop = TRUE)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This adds four columns all the way to the right: \n", "* **date** (Date on which this license begins to take effect) \n", "* **URL** (Link to a web page describing this license--in this case, Creative Commons)\n", "* **delay in days** (Number of days between the publication date of the work and the start date of this license), and \n", "* **content.version**, which specifies the version of the article the licensing data pertains to (VOR = Version of Record, AM = Accepted Manuscript, TDM = Text and Data Mining). \n", "\n", "Browsing the rows, we see all are CC BY 4.0, which stands to reason given *PLOS ONE* is an open access publisher and [applies the CC BY license](https://journals.plos.org/plosone/s/licenses-and-copyright) to the articles they publish. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Filtering rows and selecting columns with `dplyr`\n", "\n", "You can use the `filter()` and `select()` functions from the `dplyr` package if you want to get subsets of this data after you have made the query. **Note that this is a completely different `filter` function** than the one used above inside the `cr_journals()` function. That one was an argument we sent with the API call that filtered the results before they were returned. This is a separate function that is part of `dplyr` to help you filter a data frame in R. \n", "\n", "To learn more about the `dplyr` package, read the [\"Data Transformation\" chapter in *R For Data Science*](https://r4ds.had.co.nz/transform.html).\n", "\n", "Above, we retrieved all articles from the *Journal of Librarianship & Scholarly Communication* published after January 1, 2019. Let's say you want only volume 8, issue 1:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# assign the JLSC ISSN and get all publications after January 1, 2019\n", "jlsc_issn <- \"2162-3309\"\n", "jlsc_publications_2019 <- rcrossref::cr_journals(issn = jlsc_issn, works = T, limit = 25,\n", " filter = c(from_pub_date='2019-01-01')) %>%\n", " purrr::pluck(\"data\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print the data frame with select columns\n", "jlsc_publications_2019 %>%\n", " dplyr::select(title, doi, volume, issue, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use filter from dplyr to get only volume 8, issue 1\n", "jlsc_8_1 <- jlsc_publications_2019 %>%\n", " dplyr::filter(volume == \"8\",\n", " issue == \"1\") \n", "\n", "# print the data frame with select columns\n", "jlsc_8_1 %>%\n", " dplyr::select(title, doi, volume, issue, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`filter()` will go through each row of your existing `jlsc_publications_2019` data frame, and keep only those rows with values matching the filters you input. **Note:** be careful of filtering by ISSN. If a journal has multiple ISSNs they'll be combined in a single cell with a comma and the `filter()` will fail, as with JAMA above. In this case it may be wiser to use `str_detect()`, as described a couple code chunks down." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "jah_jama_publications$issn[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use `filter()` to get a single article from within this data frame if we need, either by DOI:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# filter to get \"The Five Laws of OER\" article by DOI\n", "jlsc_article <- jlsc_publications_2019 %>%\n", " dplyr::filter(doi == \"10.7710/2162-3309.2299\") \n", "\n", "# print data frame with select columns\n", "jlsc_article %>%\n", " dplyr::select(title, doi, volume, issue, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or by title:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use str_detect to search the title column for articles that include the term OER\n", "jlsc_article <- jlsc_publications_2019 %>%\n", " dplyr::filter(stringr::str_detect(title, \"OER\"))\n", "\n", "# print the data frame with select column\n", "jlsc_article %>%\n", " dplyr::select(title, doi, volume, issue, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we use the `str_detect()` function from the `stringr` package, which is loaded as part of the `tidyverse`, in order to find a single term (OER) in the title.\n", "\n", "Remember that these `dplyr` and `stringr` functions are searching through our existing data frame `jlsc_publications_2019`, not issuing new API calls." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Field queries\n", "\n", "There is yet another way of making your query more precise, and that is to use a field query (`flq`) argument to `cr_journals()`. This allows you to search in specific bibliographic fields such as author, editor, titles, ISSNs, and author affiliation (not widely available). These are listed in the [Crossref documentation](https://github.com/CrossRef/rest-api-doc#field-queries) and reproduced below. You *must* provide an ISSN--in other words, you can't run a field query for authors across all journals. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| Field query parameter | Description |\n", "|-----------------------|-------------|\n", "| `query.container-title` | Query `container-title` aka. publication name |\n", "| `query.author` | Query author given and family names |\n", "| `query.editor` | Query editor given and family names |\n", "| `query.chair` | Query chair given and family names |\n", "| `query.translator` | Query translator given and family names |\n", "| `query.contributor` | Query author, editor, chair and translator given and family names |\n", "| `query.bibliographic` | Query bibliographic information, useful for citation look up. Includes titles, authors, ISSNs and publication years |\n", "| `query.affiliation` | Query contributor affiliations |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Field query by title\n", "\n", "Here, we get all publications from the Journal of Librarianship and Scholarly Communication with the term \"open access\" in the title. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign JLSC ISSN and query the bibliographic field for terms mentioning open access. \n", "jlsc_issn <- \"2162-3309\"\n", "jlsc_publications_oa <- rcrossref::cr_journals(issn = jlsc_issn, works = T, limit = 25,\n", " flq = c(`query.bibliographic` = 'open access')) %>%\n", " purrr::pluck(\"data\")\n", "\n", "# print the data frame with select columns\n", "jlsc_publications_oa %>%\n", " dplyr::select(title, doi, volume, issue, issued, issn, author)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Field query by author, contributor, or editor\n", "\n", "The `flq` argument can also be used for authors, contributors, or editors. Here we search the same journal for authors with the name Salo (looking for all articles written by Dorothea Salo).\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Use the query.author field query to find JLSC articles with author name Salo\n", "jlsc_publications_auth <- rcrossref::cr_journals(issn = jlsc_issn, works = T, limit = 25,\n", " flq = c(`query.author` = 'salo')) %>%\n", " purrr::pluck(\"data\")\n", "\n", "# print the data frame with select columns\n", "jlsc_publications_auth %>%\n", " dplyr::select(title, doi, volume, issue, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "**TRY IT YOURSELF**\n", "\n", "1. Assign the ISSN for *College & Research Libraries* to a value - 2150-6701\n", "2. Use the `query.author` field query (`flq`) to find all articles written by Lisa Janicke Hinchliffe \n", "3. Print the tibble using `select` and the specified columns" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign the C&RL ISSN 2150-6701\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use the query.author field query to search for articles written by Lisa Hinchliffe.\n", "# separate her name with a plus: Lisa+Janicke+Hinchliffe\n", "# make sure to use backticks around query.author (next to the 1 key) `\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print the data using select and the specified columns: title, doi, volume, issue, issued, url, publisher, reference.count, type, issn\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Viewing the JSON file\n", "\n", "You can view these files in a JSON viewer using the `toJSON()` function from the `jsonlite` package." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign the PLOS ISSN and get the last 25 articles on deposit\n", "plosone_issn <- '1932-6203'\n", "plosone_publications <- cr_journals(issn = plosone_issn, works = TRUE, limit = 5) %>%\n", " pluck(\"data\")\n", "\n", "# use the toJSON function to convert the output to JSON\n", "plosone_publications_json <- jsonlite::toJSON(plosone_publications)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Print the JSON, triple click inside the box to highlight the text, and copy it to the clipboard. Watch out! This will look like a jumbled mess of text!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [ "# print the JSON\n", "plosone_publications_json" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Go to [Code Beautify](https://codebeautify.org/jsonviewer) and paste the JSON on the left side. Click **Tree Viewer** to view the data. Open the first item to view the metadata. Note especially the last few variables. These are nested lists, as a single article can have multiple authors, and each author has a given name, family name, and sequence of authorship.\n", "\n", "To write to JSON, use `jsonlite::write_json()`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# write a JSON file\n", "jsonlite::write_json(plosone_publications_json, \"data/plosone_publications.json\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Saving files in Binder\n", "\n", "You can save files while in a Binder session, but you will need to download them before you close the session down. The JSON file we just saved is now available if you click **File > Open** and navigate to the **data** folder. There, you can check the box and click the **Download** button at the top of the page. Note that this file will disappear when you close down your Binder session." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Using `cr_works()` to get data on articles\n", "\n", "`cr_works()` allows you to search by DOI or a general query in order to return the Crossref metadata.\n", "\n", "It is important to note, as Crossref does [in the documentation](https://github.com/CrossRef/rest-api-doc/blob/master/demos/crossref-api-demo.ipynb):\n", "\n", "> Crossref does not use \"works\" in the FRBR sense of the word. In Crossref parlance, a \"work\" is just a thing identified by a DOI. In practice, Crossref DOIs are used as citation identifiers. So, in FRBR terms, this means, that a Crossref DOI tends to refer to one *expression* which might include multiple *manifestations*. So, for example, the ePub, HTML and PDF version of an article will share a Crossref DOI because the differences between them should not effect the interpretation or crediting of the content. In short, they can be cited interchangeably. The same is true of the \"accepted manuscript\" and the \"version-of-record\" of that accepted manuscript.\n", "\n", "\n", "## Searching by DOI\n", "\n", "You can pass a DOI directly to `cr_works()` using the `dois` argument:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# Get metadata for a single article by DOI\n", "jlsc_ku_oa <- cr_works(dois = '10.7710/2162-3309.1252') %>%\n", " purrr::pluck(\"data\")\n", "\n", "# print the data frame with select columns\n", "jlsc_ku_oa %>%\n", " dplyr::select(title, doi, volume, issue, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also pass more than one DOI. Here we start by assigning our DOIs to a variable `my_dois`, then pass it to `cr_works()` in the `doi` argument:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Use c() to create a vector of DOIs\n", "my_dois <- c(\"10.2139/ssrn.2697412\", \n", " \"10.1016/j.joi.2016.08.002\", \n", " \"10.1371/journal.pone.0020961\", \n", " \"10.3389/fpsyg.2018.01487\", \n", " \"10.1038/d41586-018-00104-7\", \n", " \"10.12688/f1000research.8460.2\", \n", " \"10.7551/mitpress/9286.001.0001\")\n", "\n", "# pass the my_dois vector to cr_works()\n", "my_dois_works <- rcrossref::cr_works(dois = my_dois) %>%\n", " pluck(\"data\")\n", "\n", "# print the data frame with select columns\n", "my_dois_works %>%\n", " dplyr::select(title, doi, volume, issue, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Unnesting list columns\n", "\n", "Authors, links, licenses, funders, and some other values can appear in nested lists when you call `cr_journals` because there can be, and often are, multiple of each of these items per article. You can check the data classes on all variables by running `typeof()` across all columns using the `map_chr()` function from `purrr`:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# query to get data on a specific PLOS article\n", "plos_article <- cr_works(dois = '10.1371/journal.pone.0228782') %>%\n", " purrr::pluck(\"data\")\n", "\n", "# print the type of each column (e.g. character, numeric, logical, list)\n", "purrr::map_chr(plos_article, typeof)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our `plos_article` data frame has a nested list for **author**. We can unnest this column using `unnest()` from the `tidyr` package. The `.drop = TRUE` argument will drop any other list columns." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# unnest author column\n", "plos_article %>%\n", " tidyr::unnest(author, .drop = TRUE) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see this has added 5 rows and 5 new columns: **ORCID** (the URL to the author's ORCID iD), and **authenticated.orcid** (a TRUE/FALSE value indicating whether that ORCID has been authenticated), **given** (first name), **family** (last name), and **sequence** (order in which they appeared). It has dropped the other 4 list columns: funder, link, license, and reference.\n", "\n", "See https://ciakovx.github.io/rcrossref.html#unnesting_list_columns for more detailed strategies in unnesting nested lists in Crossref. For more details, call `?unnest` and read the [R for Data Science section on Unnesting](https://r4ds.had.co.nz/many-models.html#unnesting)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "**TRY IT YOURSELF**\n", "\n", "1. Do a quick search in [Google Scholar](https://scholar.google.com/) for an article you are interested in, and create an object below with the DOI. Remember to use quotation marks. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. Do a search with `cr_works` to get the article metadata. Assign it to a new symbol. Remember to `pluck()` the data. Print it with `dplyr::select(title, doi, url, publisher, reference.count, type)`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Print the column types using `purrr::map_chr(my_article, typeof)`. Replace `my_article` with whatever you called the symbol containing your article data." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Try unnesting one of list columns using `unnest()`. Set `.drop = TRUE`. Scroll all the way to the right. What new columns have appeared? Have any rows been duplicated?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting more than 1000 results with the `cursor` argument to `cr_journals`\n", "\n", "If our result will have more than 1000 results, we have to use the `cursor` argument. We will not be covering this in this class, but see for instructions on how to do it." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running general queries on `cr_works()`\n", "\n", "You can also use `cr_works()` to run a query based on very simple text keywords. For example, you can run `oa_works <- rcrossref::cr_works(query = \"open+access\")`.\n", "Paul Oldham [gives a great example of this](https://poldham.github.io/abs/crossref.html#searching_crossref), but does make the comment:\n", "\n", "> CrossRef is not a text based search engine and the ability to conduct text based searches is presently crude. Furthermore, we can only search a very limited number of fields and this will inevitably result in lower returns than commercial databases such as Web of Science (where abstracts and author keywords are available). \n", "Unfortunately there is no boolean AND for Crossref queries (see https://github.com/CrossRef/rest-api-doc/issues/135 and https://twitter.com/CrossrefSupport/status/1073601263659610113). However, as discussed above, the Crossref API assigns a score to each item returned giving a measure of the API's confidence in the match, and if you connect words using `+` the Crossref API will give items with those terms a higher score.\n", "\n", "## Specifying field queries to `cr_works()` with `flq`\n", "\n", "As with `cr_journals`, you can use `flq` to pass field queries on to `cr_works()`, such as author.\n", "\n", "Here we search for the book *Open Access* by Peter Suber by doing a general keyword search for \"open access\" and an author search for \"suber\":\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# do a general query for the term open access and a field query to return results where the author name includes Suber\n", "suber_oa <- cr_works(query = 'open+access', flq = c(`query.author` = 'suber')) %>%\n", " pluck(\"data\")\n", "\n", "# print the data frame with select columns\n", "suber_oa %>%\n", " dplyr::select(title, doi, volume, issue, page, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dr. Suber has written lots of materials that includes the term \"open access.\" We can use the `filter()` function from `dplyr` to look only at books, from the **type** column:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# use filter() from dplyr to filter that result to include only books\n", "suber_oa_books <- suber_oa %>%\n", " filter(type == \"book\")\n", "\n", "# print the data frame with select columns\n", "suber_oa_books %>%\n", " dplyr::select(title, doi, volume, issue, page, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One is the book from MIT Press that we're looking for; the other is *Knowledge Unbound*, which is a collection of his writings.\n", "\n", "We could be more specific from the outset by adding bibliographic information in `query.bibliographic`, such as ISBN (or ISSN, if it's a journal):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# run a different cr_works() query with author set to Suber and his book's ISBN passed to query.bibliographic\n", "suber_isbn <- cr_works(flq = c(`query.author` = 'suber',\n", " `query.bibliographic` = '9780262301732')) %>%\n", " pluck(\"data\")\n", "\n", "# print the data frame with select columns\n", "suber_isbn %>%\n", " dplyr::select(title, doi, issued, url, publisher, type, author)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can combine the `filter` argument with `flq` to return only items of **type** `book` published in 2012." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting formatted references in a text file\n", "\n", "We can use the `cr_cn()` function from the `rcrossref` package to get the citations to those articles in text form in the style you specify. We'll put it into Chicago. The `cr_cn()` function returns each citation into a list element. We can use the `map_chr` and the `pluck` functions from `purrr` to instead assign them to a character vector. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# Use c() to create a vector of DOIs\n", "my_dois <- c(\"10.2139/ssrn.2697412\", \n", " \"10.1016/j.joi.2016.08.002\", \n", " \"10.1371/journal.pone.0020961\", \n", " \"10.3389/fpsyg.2018.01487\", \n", " \"10.1038/d41586-018-00104-7\", \n", " \"10.12688/f1000research.8460.2\", \n", " \"10.7551/mitpress/9286.001.0001\")\n", "\n", "# Use cr_cn to get back citations formatted in Chicago for those DOIs\n", "my_citations <- rcrossref::cr_cn(my_dois,\n", " format = \"text\",\n", " style = \"chicago-note-bibliography\") %>% \n", " purrr::map_chr(., purrr::pluck, 1)\n", "\n", "# print the formatted citations\n", "my_citations" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "?cr_cn" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Beautiful formatted citations from simply a list of DOIs! You can then write this to a text file using `writeLines`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# write the formatted citations to a text file\n", "writeLines(my_citations, \"data/my_citations_text.txt\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The above is helpful if you need to paste the references somewhere, and there are loads of other citation styles included in `rcrossref`--view them by calling `rcrossref::get_styles()` and it will print a vector of these styles to your console. I'll just print the first 15 below:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# look at the first 15 styles Crossref offers\n", "rcrossref::get_styles()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting formatted references in a BibTeX or RIS file\n", "\n", "In addition to a text file, you can also write it to BibTeX or RIS:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Use cr_cn() to get BibTeX files for my DOIs\n", "my_citations_bibtex <- rcrossref::cr_cn(my_dois, format = \"bibtex\") %>%\n", " purrr::map_chr(., purrr::pluck, 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Write it to a .bib file using `writeLines()`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# write to bibtex file\n", "writeLines(my_citations_bibtex, \"data/my_citations_bibtex.bib\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Same with RIS files. EndNote has a hard time reading BibTeX, so do this if you use that as your reference management software. Instead, set the format to RIS. For this to work, we must first make it into a `tibble`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_citations_ris <- rcrossref::cr_cn(my_references_dois, format = \"ris\") %>%\n", " purrr::map_chr(., purrr::pluck, 1) %>%\n", " dplyr::tibble()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use `write_csv()` from `readr` to write the RIS file.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "readr::write_csv(my_citations_ris, \"./data/my_citations_ris.ris\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting works from a typed citation in a Word document/text file\n", "\n", "This can be helpful if you have a bibliography in a Word document or text file that you want to get into a reference management tool like Zotero. For instance, you may have written the citations in APA style and need to change to Chicago, but don't want to rekey it all out. Or perhaps you jotted down your citations hastily and left out volume, issue, or page numbers, and you need a nice, fully-formatted citation.\n", "\n", "If each citation is on its own line in your document's bibliography, then you can probably paste the whole bibliography into an Excel spreadsheet. If it goes as planned, each citation will be in its own cell. You can then save it to a CSV file, which can then be read into R. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# read in a CSV file of citations\n", "my_references <- readr::read_csv(\"data/references.txt\", locale = readr::locale(encoding = \"iso-8859-1\"))\n", "\n", "# print the file\n", "my_references" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, these are just raw citations, not divided into variables by their metadata elements (that is, with title in one column, author in another, etc.). But, we can now run a query to get precisely that from Crossref using `cr_works`. Because `cr_works` is not vectorized, we will need to build a loop using `map()` from the `purrr` package. \n", "\n", "Don't mind the technical details--it is basically saying to take each row and look it up in the Crossref search engine. Basically, this is the equivalent of copy/pasting the whole reference into the Crossref search engine. The loop will `print()` the citation before searching for it so we can keep track of where it is. We set the `limit` to 5. If it didn't find it in the first 3 results, it's not likely to be there at all." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# loop through the references column, using cr_works() to look the item up and return the top 5 hits\n", "my_references_works_list <- purrr::map(\n", " my_references$reference,\n", " function(x) {\n", " print(x)\n", " my_works <- rcrossref::cr_works(query = x, limit = 5) %>%\n", " purrr::pluck(\"data\")\n", " })" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Crossref API assigns a score to each item returned within each query, giving a measure of the API's confidence in the match. The item with the highest score is returned first in the datasets. We can return the first result in each item in the `my_references_works_list` by using `map_dfr()`, which is like `map()` except it returns the results into a data frame rather than a list. This will take a minute to run." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# for each reference looked up, get back the first result\n", "my_references_works_df <- my_references_works_list %>%\n", " purrr::map_dfr(., function(x) {\n", " x[1, ]\n", " })\n", "\n", "# print the data frame with select columns\n", "my_references_works_df %>%\n", " dplyr::select(title, doi, volume, issue, page, issued, url, publisher, reference.count, type, issn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can print just the titles to quickly see how well they match with the titles of the works we requested:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print the title column\n", "my_references_works_df$title" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not bad! Looks like we got 6 out of 8, with problems on number 5 and 7. Let's deal with 5 first. This was the result for \"The Ascent of Open Access\", which was a report by Digital Science posted to figshare, didn't come back. Even though this report does have a DOI (https://doi.org/10.6084/m9.figshare.7618751.v2) assigned via figshare, the `cr_works()` function searches only for Crossref DOIs. We should check to see if it came back in any of the 5 items we pulled. We do this by calling `pluck()` on the titles of the fifth item in the list:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_references_works_list %>%\n", " purrr::pluck(5, \"title\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nope, unfortunately none of these are \"The Ascent of Open Access\", so we're out of luck. We can just throw this row out entirely using `slice()` from `dplyr`. We'll overwrite our existing `my_references_works_df` because we have no future use for it in this R session.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_references_works_df <- my_references_works_df %>%\n", " dplyr::slice(-5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For row 7, it's giving us the full citation for Peter Suber's book when we asked for the title only, so something is fishy. \n", "\n", "When we look at it more closely (we can call `View(my_references_works_df)`), we see the author of this item is not Peter Suber, but Rob Harle, and, checking the **type** column, it's a journal article, not a book. This is a book review published in the journal *Leonardo*, not Peter Suber's book. So let's go back to `my_references_works_list` and pull data from all 5 items that came back with the API call and see if Suber's book is in there somewhere:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "suber <- my_references_works_list %>%\n", " purrr::pluck(7)\n", "suber" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It looks like it is the second item, confirming by seeing the **author** is Peter Suber, the **publisher** is MIT Press, the **type** is book, and the **ISBN** is \"9780262301732\". \n", "\n", "We do the following to correct it:\n", "\n", "* use `filter()` with the isbn to assign the correct row from `suber` to a variable called `suber_correct` \n", "* remove the incorrect row with `slice` (double checking that it is the 6th row)\n", "* use `bind_rows()` to add the correct one to our `my_references_works_df` data frame. We can just overwrite the existing `my_references_works_df` again\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "suber_correct <- suber %>%\n", " dplyr::filter(isbn == \"9780262301732\")\n", "my_references_works_df <- my_references_works_df %>%\n", " dplyr::slice(-6) %>%\n", " bind_rows(suber_correct)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Writing publications to CSV\n", "\n", "We will use the `write.csv()` function to write our data to disk as a CSV file. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Unfortunately, you cannot simply write the `plosone_publications` data frame to a CSV, due to the nested lists. It will throw an error: `\"Error in stream_delim_(df, path, ...) : Don't know how to handle vector of type list.\"`\n", "\n", "I run through three solutions at https://ciakovx.github.io/rcrossref.html#writing_publications_to_disk\n", "\n", "Here, we will use solution 3: You can use `mutate()` from `dplyr` to coerce the list columns into character vectors with `as.character()`.\n", "\n", "First, identify the list vectors:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dois <- c(\"10.2139/ssrn.2697412\", \n", " \"10.1016/j.joi.2016.08.002\", \n", " \"10.1371/journal.pone.0020961\", \n", " \"10.3389/fpsyg.2018.01487\", \n", " \"10.1038/d41586-018-00104-7\", \n", " \"10.12688/f1000research.8460.2\", \n", " \"10.7551/mitpress/9286.001.0001\")\n", "\n", "# pass the my_dois vector to cr_works()\n", "my_dois_works <- rcrossref::cr_works(dois = my_dois) %>%\n", " pluck(\"data\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use map_chr to print the column types\n", "purrr::map_chr(my_dois_works, typeof)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For any variables that are type `list`, coerce those columns to character:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use mutate() to coerce list columns to character vectors\n", "my_dois_mutated <- my_dois_works %>%\n", " dplyr::mutate(author = as.character(author)) %>%\n", " dplyr::mutate(assertion = as.character(assertion)) %>%\n", " dplyr::mutate(link = as.character(link)) %>%\n", " dplyr::mutate(license = as.character(license)) %>%\n", " dplyr::mutate(reference = as.character(reference))\n", "write.csv(my_dois_mutated, \"data/my_dois_mutated.csv\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, this is not an ideal solution, but if you need to move the data into CSV to view in Excel, it can do the trick." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Using `roadoi` to check for open access\n", "\n", "`roadoi` was developed by Najko Jahn, with reviews from Tuija Sonkkila and Ross Mounce. It interfaces with [Unpaywall](https://unpaywall.org) (which used to be called oaDOI), an important tool developed by [ImpactStory](http://unpaywall.org/team) (Heather Piwowar and Jason Priem) for locating open access versions of scholarship--read more in this [*Nature* article](https://www.nature.com/articles/d41586-018-05968-3). See here for [the `roadoi` documentation](https://cran.r-project.org/web/packages/roadoi/roadoi.pdf).\n", "\n", "This incredible [Introduction to `roadoi`](https://cran.r-project.org/web/packages/roadoi/vignettes/intro.html) by Najko Jahn provides much of what you need to know to use the tool, as well as an interesting use case. Also see his recently published article [Open Access Evidence in Unpaywall](https://subugoe.github.io/scholcomm_analytics/posts/unpaywall_evidence/), running deep analysis on Unpaywall data.\n", "\n", "We loaded the package at the beginning of this notebook with `library(roadoi`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setting up `roadoi`\n", "\n", "Your API calls to Unpaywall must include a valid email address where you can be reached in order to keep the service open and free for everyone. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Checking OA status with `oadoi_fetch`\n", "\n", "We then create DOI vector and use the `oadoi_fetch()` function from `roadoi`. \n", "\n", "**Be sure to replace the email below with your own**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign your email address to a vector\n", "my_email <- \"name@example.com\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Use c() to create a vector of DOIs\n", "my_dois <- c(\"10.2139/ssrn.2697412\", \n", " \"10.1016/j.joi.2016.08.002\", \n", " \"10.1371/journal.pone.0020961\", \n", " \"10.3389/fpsyg.2018.01487\", \n", " \"10.1038/d41586-018-00104-7\", \n", " \"10.12688/f1000research.8460.2\", \n", " \"10.7551/mitpress/9286.001.0001\")\n", "\n", "# use oadoi_fetch() to get Unpaywall data on those DOIs\n", "my_dois_oa <- roadoi::oadoi_fetch(dois = my_dois,\n", " email = my_email)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Look at the column names." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print column names\n", "names(my_dois_oa)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dois_oa" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The returned variables are described on the [Unpaywall Data Format](http://unpaywall.org/data-format) page.\n", "\n", "We can see that Unpaywall could not find OA versions for one of the seven of these:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dois_oa$is_oa" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we will filter it out with `filter()` from the `dplyr` package:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use filter() to overwrite the data frame and keep only items that are available OA\n", "my_dois_oa <- my_dois_oa %>%\n", " dplyr::filter(is_oa == TRUE)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As above, it is easier to use `unnest()` to more closely view one of the variables:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print the data frame with best open access location unnested\n", "my_dois_oa %>%\n", " tidyr::unnest(best_oa_location, names_repair = \"unique\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "**TRY IT YOURSELF**\n", "\n", "Use the same article you found in the above `cr_works` exercise, or something different. Go through the above steps to check if it is open access. If not, find an article that is OA (you can search on [DOAJ](https://doaj.org/), just click the \"Articles\" button." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# assign your article DOI to a new object my_doi2 or another name of your choosing\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use roadoi::oadoi_fetch() to get OA information about the article\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use mydoi2$is_oa to find out if the article has an open access version. If not, find an open access article and try again.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use `unnest()` to find out the OA locations. What is the URL to the best OA location? Is the journal in DOAJ? Explore the data." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# use unnest(best_oa_location) to find the OA locations. What is the URL to the best OA location?\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Next steps\n", "\n", "There are several other excellent R packages that interface with publication metadata that can be used in conjunction with this package. Examples:\n", "\n", "* `rorcid` is a wrapper for the ORCID API. Functions included for searching for people, searching by 'DOI',and searching by ORCID iD. https://cran.r-project.org/web/packages/rorcid/rorcid.pdf. See my walkthrough at https://ciakovx.github.io/rorcid.html.\n", "* `bibliometrix` \"is an open-source tool for quantitative research in scientometrics and bibliometrics that includes all the main bibliometric methods of analysis.\" See more information at https://bibliometrix.org/.\n", "* `rromeo` is a wrapper for the SHERPA-RoMEO API. You can retrieve a set of publications metadata from `rcrossref`, then use the ISSN to look up the policies of the journal regarding the archival of preprints, postprints, and publisher versions. https://cran.r-project.org/web/packages/rromeo/rromeo.pdf\n", "* `crminer` \"includes functions for getting getting links to full text of articles, fetching full text articles from those links or Digital Object Identifiers ('DOIs'), and text extraction from 'PDFs'.\" https://cran.r-project.org/web/packages/crminer/crminer.pdf" ] } ], "metadata": { "kernelspec": { "display_name": "R", "language": "R", "name": "ir" }, "language_info": { "codemirror_mode": "r", "file_extension": ".r", "mimetype": "text/x-r-source", "name": "R", "pygments_lexer": "r", "version": "4.1.0" } }, "nbformat": 4, "nbformat_minor": 2 }