Introduction to parse and the lookup_ functions

library(DOPE)
library(dplyr)
library(stringr)

This package aims to parse out identifiable drug names given a corpus of text. By corpus of text, we assume that the data has already been imported into R.

Data: drug_df


Throughout this vignette, we will employ a sample dataset - drug_df - that is intended to represent data collected from a clinical trial. The dataset contains 3 variables and 500 observations.

dim(drug_df)
   [1] 500   3

str(drug_df)
   tibble [500 x 3] (S3: tbl_df/tbl/data.frame)
    $ textdrug: chr [1:500] "Remeron" "Remeron" "Soma" "Ectacy" ...
    $ sex     : chr [1:500] "male" "female" "female" "female" ...
    $ race    : chr [1:500] "black" "ai/an" "ai/an" "hn/pi" ...

class(drug_df)
   [1] "tbl_df"     "tbl"        "data.frame"

Note: drug_df is a simulated dataset and does not reflect any true clinical observations.

parse()


The parse() function is intended to extract identifiable drug names from a corpus of text such as, clinical trial data, social media, survey or interview transcription. parse() takes in one argument, the vector that contains the strings to be parsed.

Here is an example of some problematic records in the drug_df dataset that warrants the use of parse()


messy_data <- drug_df %>% 
  # select records that have problematic characters
  filter(str_detect(textdrug, ",|;|and|\\/|=|\\(")) %>% 
  distinct(textdrug)

knitr::kable(messy_data)
textdrug
Bup/Nx
Bup/Nx.
bup/nx
Percocets and Vicodin
Barbiturate (doesn’t know which)
heroin - “few days on, few days off”
heroin- "a few days on, few days off
Ambien = 2 pills
Ambien “a bunch” = 2 pills
promethazine (25mg), clonidine (0.1mg)

As you can see there are so many extraneous/problematic characters, multiple drugs in one record and several variations of the same drug (i.e. “bup/nx”). We assume that the user is solely interested in the drugs themselves, not information such as dosage and units.

This messy data is exactly what parse() was designed for.

drug_names <- DOPE::parse(messy_data$textdrug)

drug_names
    [1] "bup/nx"       "bup/nx"       "bup/nx"       "percocets"    "vicodin"     
    [6] "barbiturate"  "heroin"       "ambien"       "ambien"       "promethazine"
   [11] "clonidine"   
   attr(,"na.action")
   [1] 8 9
   attr(,"class")
   [1] "omit"

Notice parse() cleans up the capitalization and punctuation of ‘bup/nx’. parse() has special code to clean up cases of ‘bup/nx’ and also ‘speedball’. It also finds the distinction of the final row “promethazine (25mg), clonidine” and separates them. See the tidytext package.1

The resulting vector can then be passed on to the lookup_* functions to identify whether the input drug is a class, category or a synonym for other drugs in the same category.

lookup_*


lookup()

This function relies on a comprehensive lookup table lookup_df. This dataframe contains 3 variables:

These names were based on terms used by the DEA.2

dim(lookup_df)
   [1] 4766    3

str(lookup_df)
   'data.frame':    4766 obs. of  3 variables:
    $ class   : chr  "hallucinogen" "hallucinogen" "hallucinogen" "hallucinogen" ...
    $ category: chr  "2cb" "2cb" "2cb" "2cb" ...
    $ synonym : chr  "banana split" "bdmpea" "bromo" "mft" ...

class(lookup_df)
   [1] "data.frame"

The purpose of this function is to return any possible matches to the lookup_df, which is a comprehensive dataframe consisting of all drug classes, categories and synonyms. It serves as a source or helper function to many of the other more specific function in the package. The idea is to match any possible columns with a the single word, a list of separate words or a vector passed as an argument. The dataframe returned will consist of the lookup_df match as well as the original_word that was the source of the match.

Here is an example of a common search done using lookup.

results <- lookup(unique(drug_names))
head(results, 15) %>%
  knitr::kable()
original_word class category synonym
bup/nx treatment drug treatment drug bup/nx
percocets NA NA NA
vicodin narcotic (opioid) codeine combinations, non-injectable vicodin
barbiturate depressant barbiturate fiorina
barbiturate depressant barbiturate nembutal
barbiturate depressant barbiturate pentothal
barbiturate depressant barbiturate seconal
heroin heroin heroin a-bomb
heroin heroin heroin a-bomb (mixed with marijuana)
heroin heroin heroin achivia
heroin heroin heroin adormidera
heroin heroin heroin aip
heroin heroin heroin al capone
heroin heroin heroin antifreeze
heroin heroin heroin aries

You can see that the dataframe returned could be vast in its matches (heroin returns another few hundred matches alone), and that the other more specific functions, below, might be of more use depending on one’s needs.


compress_lookup()

This function takes in one argument: the table returned from a search using the lookup function. The purpose of this function is to narrow down the results to a more specific dataframe consisting of only relevant values, such as class and/or category depending on the user’s selection. compress_lookup returns, by default, original_word, class and category.

If a researcher wanted to determine the main classes of drugs being used by the patients of a clinical study, they might pass a large vector of substances from clinical notes taken in a study to the lookup function, then filter them down to only return the datafram of classes relevant to their needs.

Here is an example of a common search done using compress_lookup.

filtered_df <- compress_lookup(results)
head(filtered_df)
     original_word             class                             category
   1        bup/nx    treatment drug                       treatment drug
   2     percocets              <NA>                                 <NA>
   3       vicodin narcotic (opioid) codeine combinations, non-injectable
   4   barbiturate        depressant                          barbiturate
   5        heroin            heroin                               heroin
   6        ambien           Unknown                              Unknown

The resulting dataframe is a short list of only the relevant information needed.


lookup_syn()

The purpose of this function is to find all possible synonyms of, primarily, a slang/street name of a commonly abused drug. Though searching for a drug class or category with lookup() will also return common synonms, this function makes searching specifically for synonyms explicit by taking just one argument: drug_name. The function will then determine the category of the slang term (drug_name) and return all synonyms that share that category.

Here is an example of a common search done using lookup_syn.

results <- lookup_syn("shrooms")
head(results)
       category
   1  mushrooms
   2 psilocybin

The resulting dataframe contains a moderate list of terms that are synonyms of the drug_name given as determined by sources such as the DEA, FDA and other publicly available resources.


  1. https://juliasilge.github.io/tidytext/↩︎

  2. https://www.dea.gov/sites/default/files/2020-04/Drugs%20of%20Abuse%202020-Web%20Version-508%20compliant-4-24-20_0.pdf↩︎