Detect multiple strings in one df from strings in another df and, if detected, return the strings detected

Viewed 65

I'm learning to use R, so please bear with me.

I have a dataset of google play store apps (master_tib). Each row is a play store app. There's a column titled description that contains text on what the app does.

master_tib

App     Description
App1    Reduce your depression and anxiety
App2    Help your depression 
App3    This app helps with Anxiety 
App4    Dog walker app 3000 

I also have a df of tags (master_tags) that contains words of importance I've predefined. There is a single column titled tag and each row contains a single tag.

master_tag

Tag
Depression
Anxiety
Stress
Mood

My goal is to tag apps from the master_tib df with the tags in master_tags df based on the presence of the tag in the description. It will then print the tags in a new column. The final result would be a master_tib df that looks like this:

App     Description                            Tag
App1    Reduce your depression and anxiety     depression, anxiety
App2    Help your depression                   depression
App3    This app helps with anxiety            anxiety
App4    Dog walker app 3000                    FALSE

Below is what I've done so far using a combination of str_detect and mapply:

# define function to use in mapply

detect_tag <- function(description, tag){ 
  if(str_detect(description, tag, FALSE)) {
    return (tag)
  } else { 
    return (FALSE)
  }
}

index <-  mapply(FUN = detect_tag, description = master_tib$description, master_tags$tag)

master_tib[index,]

Unfortunately, only the first tag is being passed through.

App     Description                            Tag
App1    Reduce your depression and anxiety     depression

Instead of the desired:

App     Description                            Tag
App1    Reduce your depression and anxiety     depression, anxiety

I haven't gotten as far as printing the results into a new column. Would love to hear anyone's insight or thoughts and apologize in advance for my poor R skills.

3 Answers

You can combine the words from master_tag using str_c and use str_extract_all to get all the words that match the pattern.

library(stringr)
master_tib$Tag <- sapply(str_extract_all(tolower(master_tib$Description), 
              str_c('\\b', tolower(master_tag$Tag), '\\b', collapse = "|")), 
              function(x) toString(unique(x)))
master_tib$Tag
#[1] "depression, anxiety" "depression"          "anxiety"             "" 

data

master_tag <- structure(list(Tag = c("Depression", "Anxiety", "Stress", "Mood"
)), class = "data.frame", row.names = c(NA, -4L))

master_tib <- structure(list(App = c("App1  ", "App2  ", "App3  ", "App4  "
), Description = c("Reduce your depression and anxiety", "Help your depression", 
"This app helps with Anxiety", "Dog walker app 3000")), row.names = c(NA, 
-4L), class = "data.frame")

Using several packages from tidyverse(dplyr, stringr, tidyr) and the data shown in @Ronak Shah's answer. First transform the tags into a pattern:

pattern <- master_tags$Tag %>%
  tolower() %>%
  str_c(collapse="|")

Then find all matches and create the desired output:

master_tib %>%
  mutate(Tag = str_extract_all(tolower(Description), pattern)) %>%
  unnest(Tag, keep_empty = TRUE) %>%
  group_by(App, Description) %>% 
  summarise(Tag = str_c(Tag, collapse=", "))

This yields

# A tibble: 4 x 3
# Groups:   App [4]
  App   Description                        Tag                
  <chr> <chr>                              <chr>              
1 App1  Reduce your depression and anxiety depression, anxiety
2 App2  Help your depression               depression         
3 App3  This app helps with Anxiety        anxiety            
4 App4  Dog walker app 3000                NA 

Similar to @RonakShah's answer, but with base R:

apply(
  sapply(master_tag$Tag, grepl, master_tib$Description, ignore.case = TRUE),
  1, function(a) paste(master_tag$Tag[a], collapse = ","))
# [1] "Depression,Anxiety" "Depression"         "Anxiety"           
# [4] ""                  

(and without lower-casing or "comma-space" niceties, that is easily added if desired).

Related