使用dplyr有条件地替换列中的值

f87krz0w  于 2023-03-05  发布在  其他
关注(0)|答案(7)|浏览(140)

我有一个示例数据集,其中一列的内容如下所示:

Candy
Sanitizer
Candy
Water
Cake
Candy
Ice Cream
Gum
Candy
Coffee

我想做的是把它替换成两个因子--“Candy”和“Non-Candy”。我可以用Python/Pandas来做这件事,但似乎找不到基于dplyr的解决方案。谢谢!

fjaof16o

fjaof16o1#

dplyrtidyr

dat %>% 
    mutate(var = replace(var, var != "Candy", "Not Candy"))

明显比ifelse方法快。创建初始 Dataframe 的代码如下所示:

library(dplyr)
dat <- as_data_frame(c("Candy","Sanitizer","Candy","Water","Cake","Candy","Ice Cream","Gum","Candy","Coffee"))
colnames(dat) <- "var"
h9vpoimq

h9vpoimq2#

假设数据框为dat,列为var

dat = dat %>% mutate(candy.flag = factor(ifelse(var == "Candy", "Candy", "Non-Candy")))
sulc1iza

sulc1iza3#

dplyr使用case_when的另一个解决方案:

dat %>%
    mutate(var = case_when(var == 'Candy' ~ 'Candy',
                           TRUE ~ 'Non-Candy'))

case_when的语法是condition ~ value to replace。文档here
可能比使用replace的解决方案效率更低,但优点是可以在单个命令中执行多个替换,同时仍然具有良好的可读性,即替换以产生三个级别:

dat %>%
    mutate(var = case_when(var == 'Candy' ~ 'Candy',
                           var == 'Water' ~ 'Water',
                           TRUE ~ 'Neither-Water-Nor-Candy'))
ux6nzvsh

ux6nzvsh4#

不需要dplyr。假设var已经存储为因子:

non_c <- setdiff(levels(dat$var), "Candy")
    
levels(dat$var) <- list(Candy = "Candy", "Non-Candy" = non_c)

参见?levels
这比ifelse方法(即bound to be slow)要"高效得多“:

library(microbenchmark)
set.seed(01239)
# resample data
smp <- data.frame(sample(dat$var, 1e6, TRUE))
names(smp) <- "var"
    
timings <- replicate(50, {
  # copy data to facilitate reuse
  cop <- smp
  t0 <- get_nanotime()
  levs <- setdiff(levels(cop$var), "Candy")
  levels(cop$var) <- list(Candy = "Candy", "Non-Candy" = levs)
  t1 <- get_nanotime() - t0

  cop <- smp
  t0 <- get_nanotime()
  cop = cop %>%
    mutate(candy.flag = factor(ifelse(var == "Candy", "Candy", "Non-Candy")))
  t2 <- get_nanotime() - t0

  cop <- smp
  t0 <- get_nanotime()
  cop$var <- 
    factor(cop$var == "Candy", labels = c("Non-Candy", "Candy"))
  t3 <- get_nanotime() - t0
  c(levels = t1, dplyr = t2, direct = t3)
})

x <- apply(times, 1, median)
x[2]/x[1]
#    dplyr   direct 
# 8.894303 4.962791

也就是说,这是9倍快。

new9mtju

new9mtju5#

我没有对此进行基准测试,但至少在某些情况下,如果有多个条件,mutate 和list的组合似乎提供了一个简单的解决方案:

# assuming that all sweet things fall in one category

dat <- data.frame(var = c("Candy", "Sanitizer", "Candy", "Water", "Cake", "Candy", "Ice Cream", "Gum", "Candy", "Coffee"))

conditions <- list("Candy" = TRUE, "Sanitizer" = FALSE, "Water" = FALSE, 
"Cake" = TRUE, "Ice Cream" = TRUE, "Gum" = TRUE, "Coffee" = FALSE)

dat %>% mutate(sweet = conditions[var])
jhiyze9q

jhiyze9q6#

当你只需要两个值时,我认为简单的ifelse()是很好的。
而且,嵌入式ifelses可以模拟与PhJ提出的case_when解决方案相同的情况(虽然我确实喜欢他的可读性)!

dat %>%
    mutate(
        var = ifelse(var == "Candy", "Candy", "Non-Candy")
    )
vwoqyblh

vwoqyblh7#

较新的解决方案是从dplyr使用case_match

library(dplyr)
dat %>% 
    mutate(var = case_match(var, "Candy" ~ var, .default ~ "Not Candy"))

相关问题