Converting a list (with a repeated pattern) into data.frame in R

91 views Asked by At

I am running some simulations in R using the repeat{} function, for some contextual reasons I am not able to convert the block of code to something that uses apply functions (or something more effective/efficient). I have output I am saving from the repeat{} function iteratively into a list(), using the following code:

results <- list()
sim <- list()
reps <- 5
n1 <- 5
n2 <- 5
low_eqbound_d <- -.3
high_eqbound_d <- .3
count <- 0
repeat {
  x <- rnorm(n1, 0, 2)
  y <- rnorm(n2, 2, 2)
  print(mid <- mean(x)-mean(y))
  sdpooled <- sqrt((((n1 - 1)*(sd(x)^2)) + (n2 - 1)*(sd(y)^2))/((n1 + n2) - 2))
  low_eqbound <- low_eqbound_d*sdpooled
  high_eqbound <- high_eqbound_d*sdpooled
  if (mid < low_eqbound & mid > high_eqbound) {
    next
  }
  if (mid >= low_eqbound & mid <= high_eqbound) {
    sim <- TOST(m1=mean(x), m2=mean(y), sd1=sd(x), sd2=sd(y), n1=n, n2=n, 
                low_eqbound_d=-0.3, high_eqbound_d=0.3)
    results <- append(results, sim)
    count <- count+1
  }
  if (count == 5) {
    break
  }
}

results1 <- as.data.frame(results)

The list looks like this at the end:

enter image description here

I want to convert this to a data.frame for further analysis/visualisation. Note that there are 5 pieces of data I need extracted from the list, and this repeats along the entire object. I want to create a data.frame with 5 columns, and take each entry from the list and put it into each column of the data.frame, repeating this for all repetitions in my code. I have attempted to use as.data.frame()

results1 <- as.data.frame(results)

But get this:

enter image description here

Any help would be fantastic!

3

There are 3 answers

2
Phil On
# Toy data:

mylist <- list(
  diff = 1,
  TOST_r1 = 2,
  TOST_r2 = 3,
  diff = 4, 
  TOST_r1 = 5,
  TOST_r2 = 6)

You can first rename the column names for consistency throughout, and then use the pattern to pivot the data to long format:

library(tidyr)
library(dplyr)

mylist |> 
  as.data.frame() |> 
  rename_with(function(x) paste0(x, ".0"), .cols = -contains(".")) |> 
  pivot_longer(everything(),
               names_to = c(".value", "id"),
               names_sep = "\\.")

# A tibble: 2 × 4
  id     diff TOST_r1 TOST_r2
  <chr> <dbl>   <dbl>   <dbl>
1 0         1       2       3
2 1         4       5       6

The id column can then be removed as you wish.

0
GKi On

You can use unlist and split this vector by names. With list2DF this list will be converted into a data.frame.

L <- list(diff = 1, TOST_r1 = 2, TOST_r2 = 3,
          diff = 4, TOST_r1 = 5, TOST_r2 = 6)

list2DF(split(unlist(L), names(L)))
#list2DF(split(unlist(L, FALSE, FALSE), names(L)))  # Variant
#  diff TOST_r1 TOST_r2
#1    1       2       3
#2    4       5       6

Another way based on the comment of @Friede:

N <- length(unique(names(L)))
setNames(data.frame(matrix(unlist(L), ncol = N, byrow = TRUE)), names(L)[1:N])
#  diff TOST_r1 TOST_r2
#1    1       2       3
#2    4       5       6
0
ThomasIsCoding On

You can try stack + reshape

reshape(
    transform(
        stack(L),
        id = ave(seq_along(ind), ind, FUN = seq_along)
    ),
    direction = "wide",
    idvar = "id",
    timevar = "ind"
)

which gives

  id values.diff values.TOST_r1 values.TOST_r2
1  1           1              2              3
4  2           4              5              6