首页 文章

如何将dplyr与变量用于列名

提问于
浏览
1

我想通过使用变量传递列名来动态地使用dplyr变异数据帧的一列 . 例如,我有以下数据框:

DF <- data.frame(A = 1:10, 
                 B = 11:20, 
                 C = c(23:30, 21:22), 
                 D = c(39:40, 31:38), 
                 E = c(TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE))

DF
    A  B  C  D    E
1   1 11 23 39 TRUE
2   2 12 24 40 TRUE
3   3 13 25 31 TRUE
4   4 14 26 32 TRUE
5   5 15 27 33 TRUE
6   6 16 28 34 TRUE
7   7 17 29 35 TRUE
8   8 18 30 36 TRUE
9   9 19 21 37 TRUE
10 10 20 22 38 TRUE

现在我想将那些行的列E的值更改为FALSE

  • 列B中的值小于0.1分位数或大于B列中所有值的0.9分位数

  • 如果相同的条件适用于C或OR列

  • 如果相同条件适用于D列

因此,结果数据框应如下所示:

A  B  C  D    E
1   1 11 23 39 FALSE
2   2 12 24 40 FALSE
3   3 13 25 31 FALSE
4   4 14 26 32 TRUE
5   5 15 27 33 TRUE
6   6 16 28 34 TRUE
7   7 17 29 35 TRUE
8   8 18 30 36 FALSE
9   9 19 21 37 FALSE
10 10 20 22 38 FALSE

我编写了一个脚本,它只根据一行来改变数据框,并且效果很好:

DF <- DF %>%
    dplyr::mutate(E = if_else(B < quantile(B, 0.9), E, FALSE)) %>%
    dplyr::mutate(E = if_else(B > quantile(B, 0.1), E, FALSE))

DF
    A  B  C  D     E
1   1 11 23 39 FALSE
2   2 12 24 40  TRUE
3   3 13 25 31  TRUE
4   4 14 26 32  TRUE
5   5 15 27 33  TRUE
6   6 16 28 34  TRUE
7   7 17 29 35  TRUE
8   8 18 30 36  TRUE
9   9 19 21 37  TRUE
10 10 20 22 38 FALSE

但是,当我尝试使其动态化时,它不起作用:

for (col in cols) {
  DF <- DF %>%
      dplyr::mutate_(E = if_else(col < quantile(col, 0.9), E, FALSE)) %>%
      dplyr::mutate_(E = if_else(col > quantile(col, 0.1), E, FALSE))
}

(1 - h)* qs [i]中的错误:二元运算符的非数字参数

我怎么解决这个问题?

2 回答

  • 1

    我们可以用 interp

    library(dplyr)
    library(lazyeval)
    for (col in cols) {
      DF <- DF %>%
                mutate_(E = interp(~if_else(Col<quantile(Col, 0.9), E, FALSE),
                                            Col=as.name(col))) %>%
                mutate_(E = interp(~if_else(Col>quantile(Col, 0.1), E, FALSE),
                                            Col = as.name(col)))
            } 
    
    DF
    #    A  B  C  D     E
    #1   1 11 23 39 FALSE
    #2   2 12 24 40 FALSE
    #3   3 13 25 31 FALSE
    #4   4 14 26 32  TRUE
    #5   5 15 27 33  TRUE
    #6   6 16 28 34  TRUE
    #7   7 17 29 35  TRUE
    #8   8 18 30 36 FALSE
    #9   9 19 21 37 FALSE
    #10 10 20 22 38 FALSE
    

    哪里

    cols <- names(DF)[2:4]
    

    更新

    如果我们还需要通过'E'列

    for (col in cols) {
        DF <- DF %>%
            mutate_(.dots = setNames(list(interp(~if_else(Col < quantile(Col, 0.9), Col2, FALSE), 
                        .values = list(Col= as.name(col), Col2 = as.name(names(DF)[5])))), names(DF)[5])) %>%
            mutate_(.dots = setNames(list(interp(~if_else(Col > quantile(Col, 0.1), Col2, FALSE), 
                        .values = list(Col= as.name(col), Col2 = as.name(names(DF)[5])))), names(DF)[5]))
    }
     DF
    #   A  B  C  D     E
    #1   1 11 23 39 FALSE
    #2   2 12 24 40 FALSE
    #3   3 13 25 31 FALSE
    #4   4 14 26 32  TRUE
    #5   5 15 27 33  TRUE
    #6   6 16 28 34  TRUE
    #7   7 17 29 35  TRUE
    #8   8 18 30 36 FALSE
    #9   9 19 21 37 FALSE
    

    Update2

    使用 dplyr 的devel版本(很快将被发布 0.6.0 ),我们也可以将变量作为quosures传递,并通过不引用 mutate 进行评估

    varN <- quo(E)
     cols <- rlang::parse_quosures(paste(names(DF)[2:4], collapse=";"))
     varN1 <- quo_name(varN)
    
     for(i in seq_along(cols)) {
        DF <- DF %>%
             mutate(!!varN1 := if_else((!!cols[[i]]) < quantile((!!cols[[i]]), 0.9),
                          (!!varN), FALSE),
                    !!varN1 := if_else((!!cols[[i]]) > quantile((!!cols[[i]]), 0.1),
                          (!!varN), FALSE))  
    
    
     }  
    DF
    #    A  B  C  D     E
    #1   1 11 23 39 FALSE
    #2   2 12 24 40 FALSE
    #3   3 13 25 31 FALSE
    #4   4 14 26 32  TRUE
    #5   5 15 27 33  TRUE
    #6   6 16 28 34  TRUE
    #7   7 17 29 35  TRUE
    #8   8 18 30 36 FALSE
    #9   9 19 21 37 FALSE
    #10 10 20 22 38 FALSE
    

    或另一种选择是 data.table

    library(data.table) 
    setDT(DF)[,  E := Reduce(`&`, lapply(.SD, function(x) x < quantile(x, 0.9) & 
                 x > quantile(x, .1))), .SDcols = 2:4]
    
     DF
     #    A  B  C  D     E
     #1:  1 11 23 39 FALSE
     #2:  2 12 24 40 FALSE
     #3:  3 13 25 31 FALSE
     #4:  4 14 26 32  TRUE
     #5:  5 15 27 33  TRUE
     #6:  6 16 28 34  TRUE
     #7:  7 17 29 35  TRUE
     #8:  8 18 30 36 FALSE
     #9:  9 19 21 37 FALSE
     #10:10 20 22 38 FALSE
    

    或仅使用 base R 功能

    DF$E <- Reduce(`&`, lapply(DF[2:4], function(x) x < quantile(x, 0.9) & x > quantile(x, .1)))
    
    DF
    #    A  B  C  D     E
    #1   1 11 23 39 FALSE
    #2   2 12 24 40 FALSE
    #3   3 13 25 31 FALSE
    #4   4 14 26 32  TRUE
    #5   5 15 27 33  TRUE
    #6   6 16 28 34  TRUE
    #7   7 17 29 35  TRUE
    #8   8 18 30 36 FALSE
    #9   9 19 21 37 FALSE
    #10 10 20 22 38 FALSE
    

    注意:不使用外部包

    注2:所有选项都返回相同的输出

  • 0

    您可以直接在 mutate 内迭代:

    DF %>% mutate(E = apply(sapply(list(B, C, D), 
                                   function(x){x < quantile(x, .9) & x > quantile(x, .1)}), 
                            1, all))
    ##     A  B  C  D     E
    ## 1   1 11 23 39 FALSE
    ## 2   2 12 24 40 FALSE
    ## 3   3 13 25 31 FALSE
    ## 4   4 14 26 32  TRUE
    ## 5   5 15 27 33  TRUE
    ## 6   6 16 28 34  TRUE
    ## 7   7 17 29 35  TRUE
    ## 8   8 18 30 36 FALSE
    ## 9   9 19 21 37 FALSE
    ## 10 10 20 22 38 FALSE
    

    或者用purrr,

    library(tidyverse)
    
    DF %>% mutate(E = list(B, C, D) %>%
                          map(~.x < quantile(.x, .9) & .x > quantile(.x, .1)) %>% 
                          pmap_lgl(all))
    

    或全力以赴的矩阵:

    DF %>% mutate(E = cbind(B, C, D) %>% 
                          apply(2, function(x){x < quantile(x, .9) & x > quantile(x, .1)}) %>% 
                          apply(1, all))
    

    所有回报都是一样的 .

    如果您愿意,可以用 between 代替不等式,例如: between(x, quantile(x, .1), quantile(x, .9)) ,但因为它定义为 x >= left & x <= right ,当边界重要时它可能会有所不同 .

相关问题