arrange_at 与 .funs 的令人困惑的行为

问题描述 投票:0回答:1

我不明白传递

dplyr::arrange_at
参数时
.funs
在做什么。

例如,假设我们创建一个数据框

Z

library(dplyr)
Z <- expand.grid(A = c(1:2, NA), B = c(1:2, NA))

并假设我们想按

A
(用
NA
first)排序,然后按
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
具有相同的长度,并且在名称为
.vars
中的第 k 个条目的列上计算第 k 个函数(并且仅该列) 。然后所有这些列以相同的顺序成为
arrange
的参数。

r dplyr
1个回答
0
投票

以下是对我自己的问题的an回答。虽然它有效(特别是我在更新中要求的),但它几乎肯定不是最佳的,因为我怀疑有基于tidy eval的更好的解决方案。 所以我不愿意接受。

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]]] <- .funs[[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
© www.soinside.com 2019 - 2024. All rights reserved.