【问题标题】:dplyr - ifelse within mutate using listdplyr - ifelse 在 mutate 中使用列表
【发布时间】:2018-04-18 11:30:27
【问题描述】:

给定一个数据框df,如下所示

text <- "
model,var,value
M1,a,12211
M1,b1,10.21
M1,b2,5.07
M1,c1,41.8
M1,c2,58.2
M1,d,1.6
M2,a,11922
M2,b1,15.6
M2,b2,8.9
M2,c1,38.1
M2,c2,61.9
M2,d,1.8
M2,a,13101
M2,b1,9.21
M2,b2,6.56
M2,c1,36.07
M2,c2,63.93
M2,d,1.75
"
df <- read.table(textConnection(text), sep=",", header = T)

我想通过 dplyr mutate 根据 var 的值添加一个 col var2

基于以下逻辑。

如果var == 'b1'var == 'b2' 那么All B 如果var == 'c1'var == 'c2' 然后All C 否则var

我想按如下方式存储映射,并用它来构建上述逻辑

mapping <- c("All B"= list(c('b1', 'b2')), "All C" = list(c('c1', 'c2')))
> mapping    
$`All B`
[1] "b1" "b2"

$`All C`
[1] "c1" "c2"

预期的输出是

   model var    value var2
1     M1   a 12211.00    a
2     M1  b1    10.21    All B
3     M1  b2     5.07    All B
4     M1  c1    41.80    All C
5     M1  c2    58.20    All C
6     M1   d     1.60    d
7     M2   a 11922.00    a
8     M2  b1    15.60    All B
9     M2  b2     8.90    All B
10    M2  c1    38.10    All C
11    M2  c2    61.90    All C
12    M2   d     1.80    d
13    M2   a 13101.00    a
14    M2  b1     9.21    All B
15    M2  b2     6.56    All B
16    M2  c1    36.07    All C
17    M2  c2    63.93    All C
18    M2   d     1.75    d

我打算将 dplyr 与 ifelse 一起使用,如下所示

df %>%
  mutate(var2 = ifelse(# what should go here )

【问题讨论】:

  • 您能否建议如何使用包含的mapping 列表来动态获取var2
  • @eipi10 我在case_when 的代码中收到以下错误:“错误:‘匹配’需要向量参数”。你以前见过这个吗?此外,mutate 函数缺少尾随“)”。

标签: r dplyr


【解决方案1】:

这是使用case_when 的示例解决方案(如 cmets 中所建议):

df %>%
    mutate(
        var = as.character(var),
        var2 = case_when(
            var == "b1" | var == "b2" ~ "All B",
            var == "c1" | var == "c2" ~ "All C",
            TRUE ~ var))
#   model var    value  var2
#1     M1   a 12211.00     a
#2     M1  b1    10.21 All B
#3     M1  b2     5.07 All B
#4     M1  c1    41.80 All C
#5     M1  c2    58.20 All C
#6     M1   d     1.60     d
#7     M2   a 11922.00     a
#8     M2  b1    15.60 All B
#9     M2  b2     8.90 All B
#10    M2  c1    38.10 All C
#11    M2  c2    61.90 All C
#12    M2   d     1.80     d
#13    M2   a 13101.00     a
#14    M2  b1     9.21 All B
#15    M2  b2     6.56 All B
#16    M2  c1    36.07 All C
#17    M2  c2    63.93 All C
#18    M2   d     1.75     d    

【讨论】:

  • var == "b1" | var == "b2" 也是var %in% c("b1","b2")
  • 真的,谢谢@thelatemail;顺便说一句,@steveb 遇到的错误源于varfactor,所以我们需要先将var 转换为character
【解决方案2】:

如果var 存在于mapping 中,我们可以创建一个返回列表名称的函数,否则返回var。我们可以使用rowwise()对每一行执行这个函数。

get_right_mapping <- function(var) {
   names(which(sapply(mapping, function(x) var %in% x)))
 }

library(dplyr)
df %>%
   rowwise() %>%
   mutate(var2 = ifelse(var %in% unlist(mapping), get_right_mapping(var), var))


#   model var      value  var2 
#   <fct> <chr>    <dbl> <chr>
# 1 M1    a        12211    a    
# 2 M1    b1       10.2  All B
# 3 M1    b2        5.07 All B
# 4 M1    c1       41.8  All C
# 5 M1    c2       58.2  All C
# 6 M1    d        1.60     d    
# 7 M2    a        11922    a    
# 8 M2    b1       15.6  All B
# 9 M2    b2        8.90 All B
#10 M2    c1       38.1  All C
#11 M2    c2       61.9  All C
#12 M2    d         1.80 d    
#13 M2    a         13101    a    
#14 M2    b1        9.21 All B
#15 M2    b2        6.56 All B
#16 M2    c1       36.1  All C
#17 M2    c2       63.9  All C
#18 M2    d         1.75 d    

数据

mapping <- c( "All A"= list(c('a1', 'a2')), "All B" = list(c('b1', 'b2')), 
              "All C" = list(c('c1','c2')))
df$var <- as.character(df$var)

【讨论】:

    【解决方案3】:

    顺便说一句,基本 R 中有一个很少使用的 levels&lt;- 函数,它的作用与所需结果几乎相同。如果您在右侧传递一个命名列表,则列表中的每个值都将替换为名称。因此,如果保留 mapping 作为来源很重要,请尝试:

    mapping <- c("B"= list(c('b1', 'b2')), "C" = list(c('c1', 'c2')))
    
    df$var2 <- df$var
    othval  <- setdiff(df$var, unlist(mapping))
    levels(df$var2) <- c(mapping, setNames(othval,othval))
    # [1] a B B C C d a B B C C d a B B C C d
    #Levels: B C a d
    

    (这里的大部分填充物是占mapping未涵盖的情况)

    【讨论】:

      【解决方案4】:

      我已经简化了你的逻辑 - 如果我错了,请纠正我。

      如果var = bc 后跟数字,则分别替换为All BAll C

      df %>% 
        mutate(var2 = gsub("^([bc])\\d+", 
                           paste("All", "\\U\\1"), 
                           var, 
                           perl = TRUE))
      
         model var    value  var2
      1     M1   a 12211.00     a
      2     M1  b1    10.21 All B
      3     M1  b2     5.07 All B
      4     M1  c1    41.80 All C
      5     M1  c2    58.20 All C
      6     M1   d     1.60     d
      7     M2   a 11922.00     a
      8     M2  b1    15.60 All B
      9     M2  b2     8.90 All B
      10    M2  c1    38.10 All C
      11    M2  c2    61.90 All C
      12    M2   d     1.80     d
      13    M2   a 13101.00     a
      14    M2  b1     9.21 All B
      15    M2  b2     6.56 All B
      16    M2  c1    36.07 All C
      17    M2  c2    63.93 All C
      18    M2   d     1.75     d
      

      【讨论】:

      • If var = b or c followed by digits, substitute All B or All C, respectively. 对于示例数据是可以的——但是这种模式在实际数据中可能不存在。所以var %in% mapping[['All A']]这种逻辑比较合适。
      • 所以最好看看var 可以在真实数据中取什么值。
      【解决方案5】:

      如果您将您的 mapping 转换为 data.framegather,那么您的值就是远离目标的正确连接。

      library(tidyverse)
      as_tibble(mapping) %>% rename_all(~paste0("All_",.x)) %>%
        gather(var2,var) %>% right_join(df) %>%   # main chunk
        mutate(var2=ifelse(is.na(var2),var,var2)) # replace nas
      
      # # A tibble: 18 x 4
      #     var2   var  model    value
      #    <chr> <chr> <fctr>    <dbl>
      #  1     a     a     M1 12211.00
      #  2 All_B    b1     M1    10.21
      #  3 All_B    b2     M1     5.07
      #  4 All_C    c1     M1    41.80
      #  5 All_C    c2     M1    58.20
      #  6     d     d     M1     1.60
      #  7     a     a     M2 11922.00
      #  8 All_B    b1     M2    15.60
      #  9 All_B    b2     M2     8.90
      # 10 All_C    c1     M2    38.10
      # 11 All_C    c2     M2    61.90
      # 12     d     d     M2     1.80
      # 13     a     a     M2 13101.00
      # 14 All_B    b1     M2     9.21
      # 15 All_B    b2     M2     6.56
      # 16 All_C    c1     M2    36.07
      # 17 All_C    c2     M2    63.93
      # 18     d     d     M2     1.75
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多