2

I have a list of lists of dataframes, where each dataframe has a name according to its position in its list. I want to rbind my dataframes together according to their name.

A1 <- data.frame(cars*rnorm(50,2))
B1 <- data.frame(cars*rnorm(60,2))
C1 <- data.frame(cars*rnorm(70,2))

A2 <- data.frame(cars*rnorm(50,2))
B2 <- data.frame(cars*rnorm(60,2))
C2 <- data.frame(cars*rnorm(70,2))

A3 <- data.frame(cars*rnorm(50,2))
B3 <- data.frame(cars*rnorm(60,2))
C3 <- data.frame(cars*rnorm(70,2))

Gen1 <- list(A1,B1,C1) ; names(Gen1) <- c('A','B','C')
Gen2 <- list(A2,B2,C2) ; names(Gen2) <- c('A','B','C')
Gen3 <- list(A3,B3,C3) ; names(Gen3) <- c('A','B','C')

Data1 <- list(Gen1, Gen2, Gen3) ; names(Data1) <- c('Gen1', 'Gen2', 'Gen3')

# Example of desired output
A_values <- rbind(Gen1$A, Gen2$A, Gen3$A)
B_values <- rbind(Gen1$B, Gen2$B, Gen3$B)
C_values <- rbind(Gen1$C, Gen2$C, Gen3$C)
output <- list(A_values, B_values, C_values)

output illustrates what I'm trying to get, but I want to use an apply family function on Data1 where I don't have to specify each item in each list by name.

This post covers a similar issue issue, but the solution using lapply leads to a list grouped by the the "upper" list level (Gen) rather than the "lower" list level (A, B, or C).

TEST <- lapply(split(Data1, names(Data1)), function(i){
    xsub <- do.call(c, unname(i))
    lapply(split(xsub, names(xsub)), function(j) do.call(rbind, unname(j)))
})
str(TEST)
List of 3
 $ Gen1:List of 3
  ..$ A:'data.frame':   50 obs. of  2 variables:
  .. ..$ A.speed: num [1:50] 10.54 8.98 6.12 16.78 7.25 ...
  .. ..$ A.dist : num [1:50] 5.27 22.44 3.5 52.74 14.5 ...
  ..$ B:'data.frame':   50 obs. of  2 variables:
  .. ..$ B.speed: num [1:50] 3.699 3.199 13.915 -0.945 9.957 ...
  .. ..$ B.dist : num [1:50] 5.54 36.25 17.13 -5.3 37.93 ...
  ..$ C:'data.frame':   50 obs. of  2 variables:
  .. ..$ C.speed: num [1:50] 14.72 12.95 8.58 16.55 13.02 ...
  .. ..$ C.dist : num [1:50] 2.11 11.98 3.82 32.4 8.97 ...
 $ Gen2:List of 3
 $ Gen3:List of 3

I've also tried tapply with no success. Any help is much appreciated. Thanks!

2 Answers 2

4
library(tidyverse)
my_output <- Data1 |> 
  list_transpose() |> 
  map(bind_rows)

str(my_output)
    List of 3
 $ A:'data.frame':    150 obs. of  2 variables:
  ..$ speed: num [1:150] 4.35 2.92 5.11 21.88 11.12 ...
  ..$ dist : num [1:150] 2.18 7.29 2.92 68.76 22.23 ...
 $ B:'data.frame':    150 obs. of  2 variables:
  ..$ speed: num [1:150] 10.3 8.75 9.99 15.3 18.86 ...
  ..$ dist : num [1:150] 5.14 40.54 8.35 43.6 31.41 ...
 $ C:'data.frame':    150 obs. of  2 variables:
  ..$ speed: num [1:150] 2.74 4.56 13.52 7.26 24.34 ...
  ..$ dist : num [1:150] 5.15 6.68 1.98 29.18 50.56 ...

Note that list_transpose transposes based on names if present, which is what you want.

In base it's a bit more messy:

names(Data1[[1]]) |>
  lapply(\(i) lapply(Data1, `[[`, i)) |>
  lapply(\(x) do.call(rbind, x))
Sign up to request clarification or add additional context in comments.

2 Comments

I think the base version doesn't have to be as complex - do.call(Map, c(rbind, Data1))
@thelatemail Very nice, I think I could look at this problem for an hour and not come up with that.
1

I ultimately achieved this using the base R approach: do.call(Map, c(rbind, Data1))

The tidyverse approach using list_transpose also works.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.