迭代将新列连接到循环结构内的现有表

时间:2019-07-15 03:21:42

标签: r dplyr purrr

下面是问题的简化版本,其中涉及转换多个输入表并将转换后的输出连接到单个表中。

处理并汇总了三个输入表,产生了三个具有相同x列的输出表。 x因此可以用作索引变量,以将表与left_join组合在一起。

out_all是所需的最终输出表,具有索引列x和摘要列def

此代码可实现所需的输出,但对处理大量输入表而言效率不高。

我希望实现的目标,也许使用purr::map函数或循环 结构,是将新的摘要列迭代连接到输出表的当前版本。我不想暂停工作流以保存最新的输出,而是希望将输出反馈回循环的开头,以使其形成自身下一个版本的LHS,并在RHS上添加新的摘要列。

在此先感谢您的帮助!

library(tidyverse)
in1 <- tribble(
~x, ~a, 
1, 1, 
1, 2, 
1, 3, 
2, 4, 
3, 5 
)

in2 <- tribble(
~x, ~b, 
1, 1, 
2, 2, 
2, 3, 
2, 4, 
3, 5
)

in3 <- tribble(
~x, ~c, 
1, 1, 
2, 2, 
3, 3, 
3, 4, 
3, 5
)

out1 <- in1 %>% 
  group_by(x) %>% 
  summarize(d = mean(a))

out2 <- in2 %>% 
  group_by(x) %>% 
  summarize(e = mean(b))

out12 <- left_join(out1, out2, by = 'x')

out3 <- in3 %>% 
  group_by(x) %>% 
  summarize(f = mean(c))

out_all <- left_join(out12, out3, by = 'x')

3 个答案:

答案 0 :(得分:1)

我们将所有数据集对象放入list中,使用mapmean中按list进行分组,然后将reduce分组为一个dataste

library(tidyverse)
out <- mget(ls(pattern = "^in\\d+$")) %>%
          map(~ .x %>%
                 group_by(x)  %>% 
                 summarise_if(is.numeric, mean)) %>% 
          reduce(left_join)

此外,如果我们想用不同的方式命名列

mget(ls(pattern = "^in\\d+$")) %>% 
   map2(., c("d", "e", "f"), ~ 
        .x %>%
             group_by(x)  %>%
             summarise(!! .y := mean(!! rlang::sym(names(.)[2])))) %>% 
   reduce(left_join)

答案 1 :(得分:0)

对于大量输入表,先合并表然后汇总值而不是先汇总值然后再(递归)合并表,可能会更有效。这是一个带有26个输入表(已简化为单个小标题列表)的小型演示:

library(microbenchmark)
library(purrr)
library(dplyr)

## data
in_dfs <- map(LETTERS, function(var) {
          tibble(x = sample(1:3, 5, replace = TRUE), !!var := 1:5)
        }) %>%
    setNames(paste0("in", seq_along(LETTERS)))

## first combine then summarize
out_tidyverse1 <- function(input) 
      do.call(bind_rows, input) %>%
      group_by(x) %>%
      summarize_all(~mean(.x, na.rm = TRUE))

## first summarize then combine
out_tidyverse2 <- function(input)
  map(input, ~ .x %>%
              group_by(x)  %>% 
              summarise_if(is.numeric, mean)) %>% 
      reduce(left_join)         

microbenchmark(out_tidyverse1(in_dfs), out_tidyverse2(in_dfs))

#> Unit: microseconds
#>                    expr       min        lq     mean    median        uq
#>  out_tidyverse1(in_dfs)   891.425  1052.342  1356.81  1186.545  1345.594
#>  out_tidyverse2(in_dfs) 20482.967 23807.713 26453.08 26144.013 28163.417
#>        max neval cld
#>   6790.026   100  a 
#>  36884.574   100   b

NB:例如,使用data.table可能会更有效。 data.table的rbindlist

答案 2 :(得分:0)

回到您的第一条评论,如果您的原始输入是单个宽表,为什么不gather感兴趣的列并使用dplyrgroup_bysummarise他们几步?如果不需要这些临时表,则不需要创建各种临时表?

df <- data.frame(id=1:5,matrix(runif(n=26*5),ncol=26))

df %>% gather(k,v,-id) %>% group_by(id) %>% 
  summarise(m=mean(v))

# A tibble: 5 x 2
     id     m
  <int> <dbl>
1     1 0.522
2     2 0.596
3     3 0.535
4     4 0.548
5     5 0.605