【问题标题】:Problem with a dplyr filter inside a function in RR中函数内的dplyr过滤器问题
【发布时间】:2020-05-05 15:32:43
【问题描述】:

我有以下数据集:

dat<-structure(list(X1979 = c(1.26884, 0.75802, 0.35127, -0.0679517, 
-4.34841, -0.312289, -5.02931, -2.49339, -12.9065, -2.90853, 
-1.02833, 0.333109, 1.70236, -2.44456, -1.83307, -0.982637, -2.14197, 
-4.1294, -3.98545, -6.26205, -5.56162, 0.0789091, 1.63146, -0.214938 
), X1980 = c(-1.32651, -0.0199441, -1.08583, 3.25939, 0.0402712, 
-3.22174, -0.859756, -3.30898, 1.0128, 0.847161, 2.75866, 1.93117, 
1.05851, 1.83372, -0.811736, -0.992584, -0.110012, 0.132343, 
2.21745, -1.48902, 0.111302, -3.77058, -3.65044, -2.41263)), class = 
"data.frame", row.names = 50:73)

我想在上述数据的每列中应用以下函数:

  library(dplyr)
  library(tibble)
  library(zoo)


  test <- function(x){ 
  dat %>%
  rownames_to_column() %>%
  filter(V1 > 0 &
   rollsum(V1 > 0, 4, fill = NA, align = 
  "left") >= 3 &
   rollsum(V1, 4, fill = NA, align = 
  "left") > 1) %>%
  return(slice(1))
  }

 test(dat)

我遇到了一个错误,提示在数据中找不到 V1。所以我的问题是如何更正此函数,以便它可以读取列中的值而不管标题名称如何?

我将不胜感激。

【问题讨论】:

  • 这能回答你的问题吗? Programming with dplyr using string as input
  • 我猜你需要!!sym 或最近{{}}。但是,如果您想在一个数据集上执行此操作,是否需要一个函数(考虑到名称是从内部获取的)?
  • dat数据框中你的列名是X1979X1980,如果你想指定列选择,请尝试改变它并使用上面提到的NSE
  • @NelsonGon 我不确定我是否理解正确,你的意思是 !!V1?对于我正在应用的整个数据,列名是 X1979...X2019。

标签: r dplyr tidyeval nse


【解决方案1】:

您需要使用整洁的评估。更多信息在这里:

library(zoo)
library(rlang)
library(tidyverse)

dat <- structure(list(X1979 = c(1.26884, 0.75802, 0.35127, -0.0679517, 
                              -4.34841, -0.312289, -5.02931, -2.49339, -12.9065, -2.90853, 
                              -1.02833, 0.333109, 1.70236, -2.44456, -1.83307, -0.982637, -2.14197, 
                              -4.1294, -3.98545, -6.26205, -5.56162, 0.0789091, 1.63146, -0.214938 
), X1980 = c(-1.32651, -0.0199441, -1.08583, 3.25939, 0.0402712, 
             -3.22174, -0.859756, -3.30898, 1.0128, 0.847161, 2.75866, 1.93117, 
             1.05851, 1.83372, -0.811736, -0.992584, -0.110012, 0.132343, 
             2.21745, -1.48902, 0.111302, -3.77058, -3.65044, -2.41263)), class = 
  "data.frame", row.names = 50:73)

使用 curly-curly {{}}

test <- function(dat, column_name){ 
  dat %>%
    rownames_to_column() %>%
    filter({{column_name}} > 0 &
             rollsum({{column_name}} > 0, 4, fill = NA, align = 
                       "left") >= 3 &
             rollsum({{column_name}}, 4, fill = NA, align = 
                       "left") > 1) %>%
    slice(1) -> result
    return(result)
}

test(dat, X1979)
#>   rowname  X1979   X1980
#> 1      50 1.2688 -1.3265

使用.data[[]] 代词

test2 <- function(dat, column_name){ 
  dat %>%
    rownames_to_column() %>%
    filter(.data[[column_name]] > 0 &
             rollsum(.data[[column_name]] > 0, 4, fill = NA, align = 
                       "left") >= 3 &
             rollsum(.data[[column_name]], 4, fill = NA, align = 
                       "left") > 1) %>%
    slice(1) -> result
  return(result)
}

out <- colnames(dat) %>% 
  set_names %>% 
  map_dfr(~ test2(dat, .x), .id = 'Col_ID')
out
#>   Col_ID rowname    X1979   X1980
#> 1  X1979      50   1.2688 -1.3265
#> 2  X1980      58 -12.9065  1.0128

reprex package (v0.3.0) 于 2020-05-05 创建

【讨论】:

【解决方案2】:

只是为了演示@NelsonGon 在链接中提到的非标准评估,请参见下面的代码。

# if passing a string
test <- function(x) {
  my_v <- rlang::sym(x)

  out <- dat %>% 
    rownames_to_column() %>%
    filter(!!my_v > 0 &
             rollsum(!!my_v > 0, 4, fill = NA, align = 
                       "left") >= 3 &
             rollsum(!!my_v, 4, fill = NA, align = 
                       "left") > 1) %>% 
    slice(1)
    return(out)
}
test("X1979")

# if passing expression
test <- function(x) {
  my_v <- rlang::enquo(x)

  out <- dat %>% 
    rownames_to_column() %>%
    filter(!!my_v > 0 &
             rollsum(!!my_v > 0, 4, fill = NA, align = 
                       "left") >= 3 &
             rollsum(!!my_v, 4, fill = NA, align = 
                       "left") > 1) %>% 
    slice(1)
  return(out)
}
test(X1979)

但最好也将 data.frame 作为参数传递给函数,称为z

test3 <- function(z, x) {
  my_v <- rlang::sym(x)

  out <- z %>% 
    rownames_to_column() %>%
    filter(!!my_v > 0 &
             rollsum(!!my_v > 0, 4, fill = NA, align = 
                       "left") >= 3 &
             rollsum(!!my_v, 4, fill = NA, align = 
                       "left") > 1) %>% 
    slice(1)
  return(out)
}
test3(dat, "X1979")

【讨论】:

  • @EJJ..我明白了。也感谢您的帮助!
  • 请注意,通常最好使用.data[[string]] 而不是sym() / ensym()!!。当列不存在时,.data 代词总是会引发错误。使用 sym 创建的符号,如果该列不存在,R 将尝试在环境中找到匹配的符号,这可能会也可能不会失败。
  • 感谢您的来信!仍在尝试完全了解 NSE
猜你喜欢
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-28
  • 1970-01-01
  • 2020-03-29
  • 1970-01-01
  • 2021-11-30
相关资源
最近更新 更多