dplyr mutate new dynamic variables with case_when

  • A+
Category:Languages

I'm aware of similar questions here and here, but I haven't been able to figure out the right solution for my specific situation. Some of what I'm finding are solutions which use mutate_, etc but I understand these are now obsolete. I'm new to dynamic usages of dplyr.

I have a dataframe which includes some variables with two different prefixes, alpha and beta:

df <- data.frame(alpha.num = c(1, 3, 5, 7),              alpha.char = c("a", "c", "e", "g"),              beta.num = c(2, 4, 6, 8),              beta.char = c("b", "d", "f", "h"),              which.to.use = c("alpha", "alpha", "beta", "beta")) 

I want to create new variables with the prefix "chosen." which are copies of either the "alpha" or "beta" columns depending on which is named for that row in the "which.to.use" column. The desired output would be:

desired.df <- data.frame(alpha.num = c(1, 3, 5, 7),                      alpha.char = c("a", "c", "e", "g"),                      beta.num = c(2, 4, 6, 8),                      beta.char = c("b", "d", "f", "h"),                      which.to.use = c("alpha", "alpha", "beta", "beta"),                      chosen.num = c(1, 3, 6, 8),                      chosen.char = c("a", "c", "f", "h")) 

My failed attempt:

varnames <- c("num", "char") df %<>%   mutate(as.name(paste0("chosen.", varnames)) := case_when(     which.to.use == "alpha" ~ paste0("alpha.", varnames),     which.to.use == "beta" ~ pasteo("beta.", varnames)   )) 

I'd prefer a pure dplyr solution, and even better would be one which could be included in a longer pipe modifying the df (i.e. no need to stop to create "varnames"). Thanks for your help.

 


This is a nest()/map() strategy that should be pretty fast. It stays in the tidyverse, but doesn't go into rlang land.

library(tidyverse)  df %>%      nest(-which.to.use) %>%     mutate(new_data = map2(data, which.to.use,                        ~ select(..1, matches(..2)) %>%                            rename_all(funs(gsub(".*//.", "choosen.", .) )))) %>%     unnest()    which.to.use alpha.num alpha.char beta.num beta.char choosen.num choosen.char 1        alpha         1          a        2         b           1            a 2        alpha         3          c        4         d           3            c 3         beta         5          e        6         f           6            f 4         beta         7          g        8         h           8            h 

It grabs all columns, not just num and char, that are not which.to.use. But that seems like what you (I) would want IRL. You could add a select(matches('(var1|var2|etc')) line before you call nest() if you wanted to pull only specific variables.

EDIT: My original suggestion of using select() to drop unneeded columns would result in doing a join to bring them back later. If instead you adjust the nest parameters, you can acheive this on only certain columns.

I added new bool columns here, but they will be ignored for the "choosen" selection:

new_df <- data.frame(alpha.num = c(1, 3, 5, 7),                  alpha.char = c("a", "c", "e", "g"),                  alpha.bool = FALSE,                  beta.num = c(2, 4, 6, 8),                  beta.char = c("b", "d", "f", "h"),                  beta.bool = TRUE,                  which.to.use = c("alpha", "alpha", "beta", "beta"),                  stringsAsFactors = FALSE)  new_df %>%      nest(matches("num|char")) %>% # only columns that match this pattern get nested, allows you to save others     mutate(new_data = map2(data, which.to.use,                            ~ select(..1, matches(..2)) %>%                                rename_all(funs(gsub(".*//.", "choosen.", .) )))) %>%     unnest()    alpha.bool beta.bool which.to.use alpha.num alpha.char beta.num beta.char choosen.num choosen.char 1      FALSE      TRUE        alpha         1          a        2         b           1            a 2      FALSE      TRUE        alpha         3          c        4         d           3            c 3      FALSE      TRUE         beta         5          e        6         f           6            f 4      FALSE      TRUE         beta         7          g        8         h           8            h 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: