我今天必须做这样的事情

data1 %>%
  summarise(
   ab1 = fn(a1, b1),
   ab2 = fn(a2, b2), 
   ab3 = fn(a3, b3) 
  )
# imagine if there are 100 of them

如果fn是单参数函数我可以这样做

data1 %>%
  summarise(across(starts_with("a", fn)))

但不幸的是,我的函数需要两列作为输入。有没有更优雅的方法来实现这一点?

0


最佳答案
3

您可以使用map2*函数来传递两组列。

library(dplyr)
library(purrr)

data1 %>%
  summarise(map2_df(pick(starts_with("a")), pick(starts_with("b")), fn))

#  a1 a2 a3
#1 21 57 93

使用来自@ThomasIsCoding 的数据,但由于您的代码使用了不同的函数,summarise这意味着它在最后将只有一行。

fn <- function(a, b) {
  sum(a, b)
}

3

  • 好的,我想我可以将其推广到 3 个论点pmap


    – 

  • map2_df已弃用。只需使用map2后跟unlist


    – 

  • 我必须假设这些列的顺序正确吗?我可以安排


    – 

另一种方法是使用重塑数据。如果您能克服从较长格式来回重塑的障碍,那么计算将变得微不足道。

这种方法的一个好处是它对列顺序具有很强的鲁棒性,并且您不需要预先指定列前缀,只要您可以使用正则表达式指定一些常规模式。

library(tidyverse)
data1 |>

  # reshape long, in this case assuming the columns are all (letters)(numbers).
  mutate(row = row_number()) |>
  pivot_longer(cols = -row,
               names_to = c(".value", "Pair"), 
               names_pattern = "(\\D+)(\\d+)") |>

  # do the calculation with the two or more involved columns
  mutate(ab = a*b, .by = c(row, Pair)) |>

  # reshape wider again
  pivot_wider(names_from = Pair, names_glue = "{.value}{Pair}", names_vary = "slowest",
              values_from = a:ab)

使用来自@ThomasIsCoding 的数据输出:

    row    a1    b1   ab1    a2    b2   ab2    a3    b3   ab3
  <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1     1     1     4     4     7    10    70    13    16   208
2     2     2     5    10     8    11    88    14    17   238
3     3     3     6    18     9    12   108    15    18   270

也许您可以尝试split.default按列名称将其分成几组,例如,

data1 %>%
  split.default(sub("\\D+", "ab", names(.))) %>%
  map_dfr(\(...) do.call(fn, unname(...)))

给出

# A tibble: 3 × 3
    ab1   ab2   ab3
  <dbl> <dbl> <dbl>
1     4    70   208
2    10    88   238
3    18   108   270

数据示例

data1 <- data.frame(
  a1 = c(1, 2, 3),
  b1 = c(4, 5, 6),
  a2 = c(7, 8, 9),
  b2 = c(10, 11, 12),
  a3 = c(13, 14, 15),
  b3 = c(16, 17, 18)
)

fn <- function(a, b) {
  a * b
}