1

I have a named list with tables (1 or 2 columns).

mlst=list(test1 = table(c(rep("x", 12), rep("t", 10))), 
          test2 = table(c(rep("y", 20), rep("z", 5))), 
          test3 = table(c(rep("w", 30), rep("m", 50))))
 

$test1

 t  x 
10 12 

$test2

 y  z 
20  5 

$test3

 m  w 
50 30 


do.call(rbind, mlst)
       t  x
test1 10 12
test2 20  5
test3 50 30

As you can see, the columns names are t and x, but each title is different.

Here you see it's easy to get the names of the list.

names(mlst)
[1] "test1" "test2" "test3"

But how can I get the names of the columns of the tables in a new column?

      v1 v2 v1t v2t
test1 10 12   t   x
test2 20  5   y   z
test3 50 30   m   w 

Same as above but a data frame has 1 column, the other 2.

  mlst2=list(test1 = table(c(rep("x", 20), rep("t", 10))),
             test2 = table(c(rep("z", 30))),
             test3 = table(c(rep("w", 40), rep("m", 60))))
  do.call(rbind, mlst2)
       t  x
test1 10 20
test2 30 30
test3 60 40

But you see that this last command prints non-sense for test2 (recycling the z var, in this case it should be test2 30 NA z NA)

I'm trying to avoid for loops for efficiency.

M. Beausoleil
  • 3,141
  • 6
  • 29
  • 61

2 Answers2

2

You could do:

cbind(do.call(rbind, mlst), as.data.frame(do.call(rbind, lapply(mlst, names))))
       t  x V1 V2
test1 10 12  t  x
test2 20  5  y  z
test3 50 30  m  w
Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • Excellent! So `do.call(rbind, mlst)` finds the 'numerical values' (frequencies) and `as.data.frame(do.call(rbind, lapply(mlst, names)))` finds the names of each of those values. What about uneven data like `mlst2`? `t x V1 V2 test1 10 20 t x test2 30 30 z z test3 60 40 m w` – M. Beausoleil Jul 31 '23 at 17:04
  • @M.Beausoleil you would need to add extra columns to those members of `mlst2` that are less than `max(lengths(mlst2))`. You could do this inside an `lapply` call. – Allan Cameron Jul 31 '23 at 17:07
  • Something like this?: `list.table.to.df = function(data) {cbind(do.call(rbind, data), as.data.frame(do.call(rbind, lapply( lapply(data, `length<-`, max(lengths(data))),names)))) }` This still recycle the z vector, but the second entry for z is empty... halfway through what I need! – M. Beausoleil Jul 31 '23 at 17:13
  • I used this trick https://stackoverflow.com/questions/34570860/add-nas-to-make-all-list-elements-equal-length – M. Beausoleil Jul 31 '23 at 17:13
  • I could clean it with `df.tmp = cbind(do.call(rbind[...]; names(df.tmp) <- c("fq1","fq2", "v1", "v2"); df.tmp[df.tmp$v2 %in% "",c("fq2","v2")] = NA` – M. Beausoleil Jul 31 '23 at 17:17
1

An approach without a loop construct, exploiting as_tibble to get the right data structure and row names

library(tibble)

setNames(data.frame(t(as_tibble(mlst)), 
         t(data.frame(mlst)[,seq_along(mlst) + (seq_along(mlst) - 1)])), 
  c("v1", "v2", "v1t", "v2t"))
      v1 v2 v1t v2t
test1 10 12   t   x
test2 20  5   y   z
test3 50 30   m   w

In case of mlst2 with non matching list lengths using sapply (needs to be 2 separate calls to get numbers as numeric)

setNames(data.frame(
  t(sapply(mlst2, \(x) 
    c(x, rep(NA, max(lengths(mlst2)) - length(x))))), 
  t(sapply(mlst2, \(x) 
    c(names(x), rep(NA, max(lengths(mlst2)) - length(x)))))), 
  c("v1", "v2", "v1t", "v2t"))
      v1 v2 v1t  v2t
test1 10 20   t    x
test2 30 NA   z <NA>
test3 60 40   m    w
Andre Wildberg
  • 12,344
  • 3
  • 12
  • 29