return a data.frame 函数的分组应用(没有for循环)

Grouped application of function that return a data.frame (without a for loop)

我需要应用一个 return 一个 data.frame 跨(分组)tibble

的函数

部分数据:

df <- data.frame(start=1:10,end=21:30,g=sample(LETTERS[1:2],10,replace=TRUE))

ff <- function(start,end,... ) {
  out <- data.frame(T1=c(start,rev(start)),T2=c(end,rev(end)))
  return(out)
}

然后我想做这样的事情

library(dplyr)
library(purrr)

df %>%
  group_by(g) %>%
  pmap_dfr( ff,.keep=TRUE)

产生小标题/data.frame像这样:

   g start end
1  A     1  21
2  A     3  23
3  A     4  24
4  A     5  25
5  A     6  26
6  A     7  27
7  A     8  28
8  A     8  28
9  A     7  27
10 A     6  26
11 A     5  25
12 A     4  24
13 A     3  23
14 A     1  21
15 B     2  22
16 B     9  29
17 B    10  30
18 B    10  30
19 B     9  29
20 B     2  22

以便将输出连接在一起row-wise,并以某种方式标记它所属的组。

我想应用的函数需要从原始 data.frame 中的其他列获取参数(示例代码中的 df)所以我认为 pmap_dfr 是正确的函数.但我只是对输出感到困惑,所以我一定是用错了那个函数。

我将不胜感激能获得的所有帮助。

使用data.table和lapply可以达到预期的效果。

df <- data.frame(start=1:10,end=21:30,g=sample(LETTERS[1:2],10,replace=TRUE))

    start end g
 1:     1  21 B
 2:     2  22 A
 3:     3  23 A
 4:     4  24 A
 5:     5  25 A
 6:     6  26 B
 7:     7  27 A
 8:     8  28 B
 9:     9  29 B
10:    10  30 B

library(data.table)

setDT(df)

ff <- function(x) {
  x <- c(x, rev(x))
  return(x)
}

df[,lapply(.SD, ff), .SDcols = c('start', 'end'), by = .(g)]

    g start end
 1: B     1  21
 2: B     6  26
 3: B     8  28
 4: B     9  29
 5: B    10  30
 6: B    10  30
 7: B     9  29
 8: B     8  28
 9: B     6  26
10: B     1  21
11: A     2  22
12: A     3  23
13: A     4  24
14: A     5  25
15: A     7  27
16: A     7  27
17: A     5  25
18: A     4  24
19: A     3  23
20: A     2  22

一种选择是使用 dplyr::group_split()purrr::map_dfr()

工作原理:group_split() 会根据您提供的分组变量(例如,g).接下来,map_dfr() 可用于将函数应用于该列表的每个元素。因为您的自定义函数 ff() returns a data.frame 没有您的分组变量 g,您需要将该信息添加回 ff() 输出 - 这可以是使用 mutate() 完成,如下例所示:

library(dplyr)
library(purrr)

# set seed so that example is reproducible
set.seed(1)

# your example data and function
df <- data.frame(start=1:10,end=21:30,g=sample(LETTERS[1:2],10,replace=TRUE))

ff <- function(start,end,... ) {
  out <- data.frame(T1=c(start,rev(start)),T2=c(end,rev(end)))
  return(out)
}

# use group_split & map_dfr
df %>%
  # divide df into a list of data.frames based on supplied grouping variables
  group_split(g) %>%
  # for each element in the list, apply this function
  map_dfr(function(df.x) {
    with(df.x,
         # get the data.frame your function returns
         ff(start, end) %>%
           # add your grouping variables back-in (stripped by ff)
           mutate(g = g[1]))
  })

# a short-hand version of the above can be written as:
df %>%
  group_split(g) %>%
  map_dfr(~ff(.x$start, .x$end) %>% mutate(g = .x$g[1]))

可以这样使用dplyr::across()

library(tidyverse)

group_by(df, g) %>%
  summarise(across(all_of(c("start", "end"))) %>%
    {
      ff(.[[1]], .[[2]])
    })
#> `summarise()` has grouped output by 'g'. You can override using the `.groups` argument.
#> # A tibble: 20 × 3
#> # Groups:   g [2]
#>    g        T1    T2
#>    <chr> <int> <int>
#>  1 A         1    21
#>  2 A         3    23
#>  3 A         4    24
#>  4 A         9    29
#>  5 A        10    30
#>  6 A        10    30
#>  7 A         9    29
#>  8 A         4    24
#>  9 A         3    23
#> 10 A         1    21
#> 11 B         2    22
#> 12 B         5    25
#> 13 B         6    26
#> 14 B         7    27
#> 15 B         8    28
#> 16 B         8    28
#> 17 B         7    27
#> 18 B         6    26
#> 19 B         5    25
#> 20 B         2    22

reprex package (v2.0.1)

于 2021-12-21 创建