【问题标题】:R: Apply function over nested lists with varying lengthR:将函数应用于具有不同长度的嵌套列表
【发布时间】:2018-07-11 14:27:21
【问题描述】:

我有以下数据框:

df <- data.frame(id = paste0('id', sample(c(1:4),80000, replace = TRUE)), date = as.Date(rbeta(80000, 0.7, 10) * 100, origin = "2016-01-01"), 
             variant = sample(c(0:1), 80000, replace = TRUE), type = sample(paste0(LETTERS[1:3],LETTERS[1]), 80000, TRUE), code = sample(letters[1:2], 80000, TRUE), 
             level = sample(LETTERS[1:8], 80000, TRUE), number = sample(c(1:100), 80000, replace = TRUE) )

接下来,我多次拆分数据框并将它们(加上原始 df)组合到一个列表中:

dfs <- split(df,df$id)
df2 <- lapply(dfs, function(x) split(x,x$type))

df3 <- lapply(dfs, function(x) split(x,x$code))

df4 <- lapply(dfs, function(x) split(x,x$level))

df_all <- list(dfs,df2,df3,df4)

因此,我首先按 Id 拆分数据帧,然后在几个条件下拆分它们:无、类型、代码和级别。其中“none”意味着我不会进一步拆分它。

我的第一个问题:有没有更快/更清洁的方法来实现这一目标?

第二个问题:如何将函数应用于此列表的每个元素?它可能与 lapply 有关,但我不知道如何,因为嵌套列表的数量会有所不同。因此,为了更清楚,我想知道如何将我的功能应用于:

df_all[[1]]$id1
df_all[[1]]$id2
df_all[[1]]$id3
df_all[[1]]$id4
df_all[[2]]$id1$AA
df_all[[2]]$id1$BA
df_all[[2]]$id1$CA
df_all[[2]]$id2$AA
etc.

我的功能如下:

func <- function(x){
x <- x %>%
  group_by(variant) %>%
  summarise(H = sum(number)) %>%
  ungroup()

【问题讨论】:

  • “我如何将一个函数应用到这个列表的每个元素上?” 这真的取决于你应用什么函数以及你想用列表。这个问题的答案也可能首先影响您应该如何拆分列表元素。你能重申一下你想通过这个实现的目标吗?
  • @user 我用我想应用的函数编辑了这个问题。
  • 预期的输出是什么?一个列表列表,每个元素都有一个摘要 data.frame?

标签: r lapply nested-lists


【解决方案1】:

如果您只想按变量的不同组合进行分组并进行汇总,那么拆分组可能不是一个好主意,只需修改函数,以便您可以输入不同的分组变量组合,如下所示:

library(dplyr)

func2 <- function(x, ...){
  group_quo = quos(...)
  x %>%
    group_by(!!!group_quo) %>%
    summarize(H = sum(number)) 
}

结果:

> func2(df, id, variant)
# A tibble: 8 x 3
# Groups:   id [?]
  id    variant      H
  <fct>   <int>  <int>
1 id1         0 500192
2 id1         1 508282
3 id2         0 505829
4 id2         1 511855
5 id3         0 502280
6 id3         1 510854
7 id4         0 502621
8 id4         1 510372

> func2(df, id, type, variant)
# A tibble: 24 x 4
# Groups:   id, type [?]
   id    type  variant      H
   <fct> <fct>   <int>  <int>
 1 id1   AA          0 167757
 2 id1   AA          1 169025
 3 id1   BA          0 166225
 4 id1   BA          1 168208
 5 id1   CA          0 166210
 6 id1   CA          1 171049
 7 id2   AA          0 169277
 8 id2   AA          1 172240
 9 id2   BA          0 168596
10 id2   BA          1 169396
# ... with 14 more rows

etc.

如果你想应用更复杂的东西或者你想保持列表的层次结构,你可以尝试使用嵌套的 data.frames:

library(dplyr)
library(tidyr)
library(purrr)

func <- function(x){
  x %>%
    group_by(variant) %>%
    summarize(H = sum(number)) 
}

df_nested = df %>% 
  group_by(id) %>%
  nest() %>%
  mutate(df1 = data %>% map(func),
         df2 = data %>% map(~group_by(., type) %>% nest()),
         df3 = data %>% map(~group_by(., code) %>% nest()),
         df4 = data %>% map(~group_by(., level) %>% nest())) %>%
  mutate_at(vars(df2:df4), 
            funs(map(., function(x) mutate(x, data = map(data, func)) %>% unnest))) 

结果:

> df_nested
# A tibble: 4 x 6
  id    data                  df1              df2              df3              df4              
  <fct> <list>                <list>           <list>           <list>           <list>           
1 id1   <tibble [19,963 x 6]> <tibble [2 x 2]> <tibble [6 x 3]> <tibble [4 x 3]> <tibble [16 x 3]>
2 id3   <tibble [19,946 x 6]> <tibble [2 x 2]> <tibble [6 x 3]> <tibble [4 x 3]> <tibble [16 x 3]>
3 id2   <tibble [20,114 x 6]> <tibble [2 x 2]> <tibble [6 x 3]> <tibble [4 x 3]> <tibble [16 x 3]>
4 id4   <tibble [19,977 x 6]> <tibble [2 x 2]> <tibble [6 x 3]> <tibble [4 x 3]> <tibble [16 x 3]>

> df_nested %>% 
+   select(id, data) %>%
+   unnest()
# A tibble: 80,000 x 7
   id    date       variant type  code  level number
   <fct> <date>       <int> <fct> <fct> <fct>  <int>
 1 id1   2016-01-05       1 AA    b     H         71
 2 id1   2016-01-01       0 CA    a     G         85
 3 id1   2016-01-03       0 CA    a     E         98
 4 id1   2016-01-01       1 BA    b     E         78
 5 id1   2016-01-01       1 BA    b     G         64
 6 id1   2016-01-18       1 AA    a     E         69
 7 id1   2016-01-04       1 BA    b     E         12
 8 id1   2016-01-02       0 CA    b     B         32
 9 id1   2016-01-01       1 CA    a     B         44
10 id1   2016-01-02       0 BA    a     F         89
# ... with 79,990 more rows

> df_nested %>% 
+   select(id, df1) %>%
+   unnest()
# A tibble: 8 x 3
  id    variant      H
  <fct>   <int>  <int>
1 id1         0 500192
2 id1         1 508282
3 id3         0 502280
4 id3         1 510854
5 id2         0 505829
6 id2         1 511855
7 id4         0 502621
8 id4         1 510372

【讨论】:

  • 谢谢!这就是我一直在寻找的,尤其是您的第二个解决方案!
  • 我在应用您的解决方案时遇到了另一个问题。我的函数比示例中的函数稍微复杂一点,这意味着当嵌套列表元素仅包含一行时(例如 df3[[id3]]$CA 的类型为 list[1 x 10] (S3: data.frame) ),该功能不适用。你有什么建议我可以调整你的解决方案,以便在应用函数时跳过/排除只有一行的嵌套元素?
  • @MC09 我只想更改您的函数,以便它在看到只有一行的 data.frame 时返回输入。这可能比检查每个嵌套元素的行数更容易。
  • 是的,我刚刚发现这确实是一种更简单的方法。再次感谢您的帮助。
猜你喜欢
  • 2021-07-12
  • 2015-05-07
  • 2020-09-09
  • 1970-01-01
  • 2021-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多