从一个数据框开始indf
,colname
作为它的一列,我想构建另一个outdf
有两列的数据框:第一列称为colname
,包含 的唯一值indf$colname
,而第二列称为n
,包含 中的重复项数indf$colname
(包括原始值,因此sum(duplicated())+1
)。
colname
应动态指定。
经过一些研究和反复试验后,看起来这样做的方法是:
indf <- data.frame(a=c("A","A","B"), b=c(1,2,3))
colname = "a"
outdf <- indf %>%
group_by(across(all_of(colname))) %>%
mutate(n = n()) %>%
select(!!colname, n)
我想知道为什么group_by
需要across(all_of(colname))
处理动态定义的列名,而select
我必须取消引用!!
。
如果我使用across(all_of())
我select
会收到此错误:
Error in `select()`:
ℹ In argument: `across(all_of(colname))`.
Caused by error in `across()`:
! Must only be used inside data-masking verbs like `mutate()`,
`filter()`, and `group_by()`.
如果我使用!!
它group_by
,则会创建一个名为"a"
(包括双引号)的新列。
编辑:
!!
里面arrange
也不起作用。
# This doesn't work
outdf %>%
arrange(desc(!!colname))
相反,你需要across(all_of())
:
outdf %>%
arrange(desc(across(all_of(colname))))
该函数
all_of
是一个选择辅助函数,类似于matches
和contains
。在底层,它只是将列名转换为数字列索引。但是,从tidyselect
v1.2.0 开始,选择辅助函数必须在选择上下文中使用(请参阅?`faq-selection-context`
)。这意味着选择助手可以在内部直接使用
select
而无需包装across
,因为这已经是一个选择上下文。对于其他动词(如
mutate
和 )group_by
,它们不构成选择上下文,并且需要函数across
将列索引转换为适合这些函数的输入。select(all_of(colname))
无需使用across
: