我不明白在传递dplyr::arrange_at
参数时.funs
在做什么。
例如,假设我们创建一个数据帧Z
:
library(dplyr)
Z <- expand.grid(A = c(1:2, NA), B = c(1:2, NA))
,并假设我们要按A
(先按NA
)对其进行排序,然后按B
对其进行排序。然后我们可以在下面尝试target
或current
:
all_equal(
target = Z %>% arrange(!is.na(A), A, B),
current = Z %>% arrange_at(.vars = c("A", "A", "B"),
.funs = list(function(x)!is.na(x), identity, identity)),
ignore_row_order = FALSE)
返回“相同的行值,但顺序不同”。我期望的是第一个版本(target
),但是第二个版本(current
)令人费解。我期望的是,.funs
中的每个函数都将应用于.var
中的相应列,然后将其排序为arrange()
。
最终,我想以一种非常动态的方式进行排序,因此需要arrange_at
的全部功能。
正如@akrun在评论中所说,_at
函数族dplyr
创建所有.vars
和所有.funs
的笛卡尔积。因此,我需要一个arrange_parallel_at
函数,该函数期望.vars
和.funs
具有相同的长度,并且在名称为{{1的第k个条目的列上}}(仅限该列)。然后,所有这些按相同顺序排列的列将成为.vars
的参数。
答案 0 :(得分:0)
下面是一个对我自己问题的回答。尽管它可以正常工作(尤其是我在更新中提出的要求),但由于我怀疑有基于How to open multiple hrefs within a webtable to scrape through selenium的更好的解决方案,因此几乎肯定不是最佳的。 因此,我不愿意接受它。
library(tidyverse)
arrange_parallel_at <- function(.data, .vars, .funs) {
stopifnot(length(.vars) == length(.funs), is.character(.vars), is.list(.funs))
tmp_cols <- paste0('.tmp', seq_along(.vars))
for (i in seq_along(.vars)) {
.data[[tmp_cols[i]]] <- sort_trans[[i]](.data[[.vars[i]]])
}
.data <- arrange_at(.data, tmp_cols)
.data[tmp_cols] <- NULL
.data
}
下面是一些测试代码。
tibble(A = c(1:2, NA)) %>%
crossing(B = c(1:2, NA)) ->
Z
na_first <- function(x) !is.na(x)
all_equal(
Z %>% arrange(!is.na(A), A, !is.na(B), desc(B)),
Z %>% arrange_parallel_at( c( 'A', 'A', 'B', 'B'),
list(na_first, identity, na_first, desc)),
ignore_row_order = FALSE) # returns TRUE