.funs

时间:2019-08-23 21:33:38

标签: r dplyr

我不明白在传递dplyr::arrange_at参数时.funs在做什么。

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

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

,并假设我们要按A(先按NA )对其进行排序,然后按B对其进行排序。然后我们可以在下面尝试targetcurrent

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的参数。

1 个答案:

答案 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