Parse text variables into binary variables with function

Viewed 46

I have the following data frame with character values separated by spaces. I would like to create new columns for each unique value, thus turning the values into binary variables.

df <- data.frame("letter" = c("a", "b c a", "b a", "c b"))

This is the output I want to get:

df$a <- grepl("a", df$letter)
df$b <- grepl("b", df$letter)
df$c <- grepl("c", df$letter)

This is what I have tried:

list <- c("a", "b", "c")

replace_func <- function(x, df) {
  df[[x]] <- grepl("[x]", df$letter)
  return(df[[x]])
}
lapply(1:list, function(x) replace_func(x, df))

and this:

lapply(list, function(x) df[[x]] <- grepl("[x]", df$letter))
4 Answers

We can change the code to do the assigment on the output of the list of logical vectors

df[letters[1:3]] <- lapply(letters[1:3], function(x) grepl(x, df$letter))

-output

df
  letter     a     b     c
1      a  TRUE FALSE FALSE
2  b c a  TRUE  TRUE  TRUE
3    b a  TRUE  TRUE FALSE
4    c b FALSE  TRUE  TRUE

If we need to construct a function,

replace_func <- function(dat, colnm, pat) {
       grepl(pat, dat[[colnm]])
}

and use the function as

df[letters[1:3]] <- lapply(letters[1:3], replace_func, dat = df, colnm = 'letter')

Another perspective from me.

df <- data.frame("letter" = c("a", "b c a", "b a", "c b"))
replace_func <- function(df, col) {
  stopifnot(inherits(df, "data.frame"))
  stopifnot(col %in% colnames(df))
  vals <- stringr::str_split(df[[col]], " ")
  uu <- sort(unique(unlist(vals)))
  new_df <- do.call(rbind, lapply(vals, function(i) uu %in% i))
  colnames(new_df) <- uu
  cbind(df, new_df)
}
replace_func(df, "letter")
#>   letter     a     b     c
#> 1      a  TRUE FALSE FALSE
#> 2  b c a  TRUE  TRUE  TRUE
#> 3    b a  TRUE  TRUE FALSE
#> 4    c b FALSE  TRUE  TRUE

Created on 2021-06-08 by the reprex package (v2.0.0)

tidyverse

df <- data.frame("letter" = c("a", "b c a", "b a", "c b"))

library(tidyverse)

bind_cols(df, map_dfc(letters[1:3], ~transmute(df, !!.x := grepl(.x, letter))))
#>   letter     a     b     c
#> 1      a  TRUE FALSE FALSE
#> 2  b c a  TRUE  TRUE  TRUE
#> 3    b a  TRUE  TRUE FALSE
#> 4    c b FALSE  TRUE  TRUE

Created on 2021-06-09 by the reprex package (v2.0.0)

Another base R option that does not pre-define the letters and orders them:

l <- sort(unique(unlist(strsplit(paste(df$letter, collapse=" ")," "))))
df[,l] <- t(sapply(unique(strsplit(df$letter, " ")), \(x) !is.na(match(l, x))))
Related