【问题标题】:Tidyeval Programming with Base R Indexing使用 Base R 索引的 Tidyeval 编程
【发布时间】:2018-12-05 14:41:52
【问题描述】:

问题

我正在尝试创建一个使用dplyr 语法和[] 的函数,但我错误地使用了quosures。问题源于具有 quosures 和 tidyeval 的岩石基础。我希望有人能解释为什么我的功能不起作用。

背景

我发现this code 非常有用,我想把它变成一个函数,通过它我可以在不使用字符串的情况下改变参数。使用Programming with dplyr Vignette,我能够得到这个功能。 (注:我改变了原来的代码以满足我的需要)

library(dplyr)    

persistence <- function(df, period, ...){
  period <- enquo(period)
  group_var <- quos(...)

  df %>% 
    group_by(!!! group_var, !! period) %>%
    summarise(persistence_rate = length(base::intersect(id, df$id[df$rank==(rank+1)]))/n_distinct(id))
}

使用我在下面提供的数据,使用这个函数可以得到我想要的输出:

persistence(data, period)

    # A tibble: 5 x 2
      period persistence_rate
      <chr>             <dbl>
    1 a                 0.500
    2 b                 1.00 
    3 c                 0.667
    4 d                 0.667
    5 e                 0. 

不幸的是,在尝试改变 id 和 rank 列时,我不确定如何合并 quosures。

我的尝试

使用这些数据:

   data <- structure(list(id = c("A", "B", "C", "D", "A", "C", "A", "B", "C", "A", "D", "C", "A", "B", "C"),
                   period = c("a", "a", "a", "a", "b", "b", "c", "c", "c", "d", "d", "d", "e", "e", "e"),
                   rank = c(1, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5),
                   group = c("g1", "g2", "g1", "g2", "g1", "g1", "g1", "g2", "g1", "g1", "g2", "g1", "g1", "g2", "g1")),
                   .Names = c("id", "period", "rank", "group"),
                   row.names = c(NA, -15L),
                   class = c("tbl_df", "tbl", "data.frame"))

我最终得到了这个功能:

persistence_new <- function(df, id, period, rank, ...){

  period <- enquo(period)
  id <- enquo(id)
  rank <- enquo(rank)
  group_var <- quos(...)

  df %>% 
    group_by(UQS(group_var), UQ(period)) %>%
    summarise(persistence_rate = length(base::intersect(UQ(id), UQ(id)[UQ(rank) == (UQ(rank) + 1)]))/n_distinct(UQ(id)))

}

这给了我这个结果:

persistence_new(data, id, period, rank)

    # A tibble: 5 x 2
  period persistence_rate
  <chr>             <dbl>
1 a                    0.
2 b                    0.
3 c                    0.
4 d                    0.
5 e                    0.

我花了很长时间才达到这一点。当我尝试不同的事情时,它经常会吐出一个错误。现在,它正在运行,但没有给我想要的结果。

我基本上尝试了我能想到的 ()UQ[][[]] 的每一次迭代。

谢谢

我希望了解更多关于 tidyeval 的信息,这样我以后就不会在这方面遇到困难了。话虽如此,并且鉴于问题是由于缺乏理解,我将不胜感激为什么我当前的功能不起作用的任何观点。任何使 tidyeval 更直观的见解都会很棒。

或者,请随时向我指出使用 dplyr Vignette 进行编程的特定部分。我已经完成了整个事情两次,但要关注的特定部分可能会有用。

感谢您的帮助。如果我可以提供任何其他信息,请告诉我。

会话信息

> sessionInfo()
R version 3.4.4 (2018-03-15)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252    LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                           LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] bindrcpp_0.2 dplyr_0.7.4 

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.16          utf8_1.1.3            crayon_1.3.4          assertthat_0.2.0      R6_2.2.2             
 [6] magrittr_1.5          pillar_1.2.1          cli_1.0.0             rlang_0.2.0.9001      rstudioapi_0.7.0-9000
[11] tools_3.4.4           glue_1.2.0            yaml_2.1.19           compiler_3.4.4        pkgconfig_2.0.1      
[16] bindr_0.1.1           tibble_1.4.2

【问题讨论】:

  • 你到底想在这里计算什么?您能用文字描述一下persistence_rate 列最后应该包含什么吗?
  • persistence_rate 是一个时期内出现在下一时期的 ID 的百分比。

标签: r dplyr


【解决方案1】:

我认为这会以对 dplyr 更友好的方式满足您的需求。

persistence_new <- function(df, id, period, rank, ...){

  period <- enquo(period)
  id <- enquo(id)
  rank <- enquo(rank)
  group_var <- quos(...)

  df %>% group_by(!!id) %>%   
    arrange(!!rank) %>% 
    mutate(nextrank = lead(!!rank)) %>% 
    group_by(!!!group_var, !!period) %>% 
    summarize(persistence_rate=sum(nextrank == !!rank + 1, na.rm=TRUE)/n())

}

persistence_new(data, id, period, rank)
#   period persistence_rate
#   <chr>             <dbl>
# 1 a                 0.5  
# 2 b                 1    
# 3 c                 0.667
# 4 d                 0.667
# 5 e                 0  

这里我们只使用lead()查看下一个排名列是否比最后一个多一个,并根据该信息进行汇总,而不是进行子查询类型连接。由于这使用了所有 dplyr 函数,因此它们很容易与 bang-bang 运算符一起使用。

此外,这里的句号和排名似乎基本相同。如果要在函数内计算排名,则不需要将排名作为参数。例如

persistence_new <- function(df, id, period, ...){

  period <- enquo(period)
  id <- enquo(id)
  group_var <- quos(...)

  data %>% 
    mutate(rank = group_indices(., period)) %>% 
    group_by(!!id) %>%   
    arrange(rank) %>% 
    mutate(nextrank = lead(rank)) %>% 
    group_by(!!!group_var, !!period) %>% 
    summarize(persistence_rate=sum(nextrank == rank + 1, na.rm=TRUE)/n())

}
persistence_new(data, id, period)

【讨论】:

  • 这段代码给出了我正在寻找的结果。感谢您及时的回复!但是您是否碰巧知道我如何将 tidyeval 与类似于我的示例的 Base R 索引结合使用?我认为了解未来的努力会很有用。我应该什么时候捕获值,什么时候应该捕获表达式等(如 Vignette 中所述)以及如何捕获?
  • 哦,我只能通过将您的 data %&gt;% 更改为 df %&gt;% 来实现上述操作。仅供参考。
  • Base R 函数不太可能支持 tidyeval。但真正可能搞砸的部分是新版本中没有的df$ 部分。在分组上下文之外再次调用 data.frame 对于该解决方案很重要(以及使它对 dplyr 不友好的原因)。但是由于我相信的解析器,取消引用不会与$ 配合得很好。如果您想使用变量,最好避免使用这些类型的表达式。
猜你喜欢
  • 1970-01-01
  • 2020-03-09
  • 2018-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
  • 2015-03-16
相关资源
最近更新 更多