问题
我正在尝试创建一个使用dplyr
语法和[]
的函数,但是却错误地使用了quosures。这个问题源于一个充满基调和提提耶瓦尔的坚硬基础。我希望有人可以解释为什么我的功能无法正常工作。
背景
我发现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列时,我不确定如何合并这些数量。
我尝试过的事情
使用此数据:
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
> 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
答案 0 :(得分:2)
我认为这以更友好的方式满足您的需求。
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)