【问题标题】:How to use dplyr with variables for column names如何将 dplyr 与列名变量一起使用
【发布时间】:2017-09-10 09:25:08
【问题描述】:

我想用 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))
}
Error in (1 - h) * qs[i] : non-numeric argument to binary operator

我该如何解决这个问题?

【问题讨论】:

  • 你试过mutate_each吗?
  • 我认为 mutate_each 在这种情况下不起作用,因为我只想改变 E 列,而不是所有列

标签: r dynamic dplyr


【解决方案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

更新2

使用dplyr 的开发版本(即将发布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:所有选项都返回相同的输出

【讨论】:

  • 我运行了您的代码,但出现了一个我无法解释的错误:` mutate_impl(.data, dots) 中的错误:找不到对象 '#A6CEE3' `
  • @KoenV 您正在使用哪个版本的dplyr。我正在使用dplyr_0.5.0。你也加载了plyr 包吗?在这种情况下,你需要dplyr::mutate_
  • 我使用dplyr_0.5.0。包plyr_1.8.4 是“通过命名空间加载(而不是附加)”。您建议使用dplyr::mutate_ 并不能解决问题。
  • @KoenV 无论如何,使用您的示例对我来说效果很好
  • 有没有办法将要变异的列的名称(在本例中为 E)也作为变量传递?
【解决方案2】:

你可以直接在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

或用咕噜声,

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 &gt;= left &amp; x &lt;= right,所以当边界很重要时可能会有所不同。

【讨论】:

  • 感谢您的回答。仍然困扰我的是我必须明确列出 B、C、D 列。我可以在 dplyr 部分之外的向量中定义这些列并在 dplyr 中使用这个向量吗?
  • 我想使用 cols &lt;- setdiff(names(DF), c("A", "E")) 这样的东西,因为我将有大约 60 列
  • 您实际上可以嵌套select,这使您可以使用它的任何选项进行列选择,例如DF %&gt;% mutate(E = select(., B:D) %&gt;% map(~.x &lt; quantile(.x, .9) &amp; .x &gt; quantile(.x, .1)) %&gt;% pmap_lgl(all))
【解决方案3】:

使用 get() 基本 R 函数 -

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

【讨论】:

    猜你喜欢
    • 2020-08-30
    • 2021-05-16
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 2021-02-22
    • 2021-09-07
    • 2018-02-03
    • 2015-04-23
    相关资源
    最近更新 更多