1

I have the following nested list object, containing unequal length numeric vectors at the last level:

preallocated_vector_quad <- c(1:2)
preallocated_vector_lin  <- c(3:5)
specification_list       <- list(quadratic = preallocated_vector_quad, linear = preallocated_vector_lin)
effects_list             <- list(fixed     = specification_list,        mixed = specification_list)
object_list              <- list(fit       = effects_list,              draws = effects_list)
str(object_list)

List of 2
 $ fit  :List of 2
  ..$ fixed:List of 2
  .. ..$ quadratic: int [1:2] 1 2
  .. ..$ linear   : int [1:3] 3 4 5
  ..$ mixed:List of 2
  .. ..$ quadratic: int [1:2] 1 2
  .. ..$ linear   : int [1:3] 3 4 5
 $ draws:List of 2
  ..$ fixed:List of 2
  .. ..$ quadratic: int [1:2] 1 2
  .. ..$ linear   : int [1:3] 3 4 5
  ..$ mixed:List of 2
  .. ..$ quadratic: int [1:2] 1 2
  .. ..$ linear   : int [1:3] 3 4 5

I would like to obtain an output that has every value of the last level, together with the specific location within the list from which it was obtained (e.g. 3 and fixed.linear). Preferably no tidyverse functions. The ideal output could be either in wide or long format:

    value     fixed.quadratic fixed.linear mixed.quadratic mixed.linear
[1]  1               1               0               0               0
[2]  2               1               0               0               0
[3]  3               0               1               0               0
[4]  4               0               1               0               0
[5]  5               0               1               0               0
[6]  1               0               0               1               0
[7]  2               0               0               1               0
[8]  3               0               0               0               1
[9]  4               0               0               0               1
[10] 5               0               0               0               1
       value         type
    [1]  1        'fixed.quadratic' 
    [2]  2        'fixed.quadratic'  
    [3]  3        'fixed.linear'  
    [4]  4        'fixed.linear'  
    [5]  5        'fixed.linear'  
    [6]  1        'mixed.quadratic'  
    [7]  2        'mixed.quadratic'  
    [8]  3        'mixed.linear'  
    [9]  4        'mixed.linear'  
    [10] 5        'mixed.linear'  

I have tried solutions to similar questions in this site but they do not give me the expected result, as either they have equal size length vectors or they do not need to maintain the sublist name. My closest solution so far is:

>unlist(object_list$draws, recursive = FALSE)

$fixed.quadratic
[1] 1 2

$fixed.linear
[1] 3 4 5

$mixed.quadratic
[1] 1 2

$mixed.linear
[1] 3 4 5

However, posted solutions break with the unequal vector length, e.g.:

> do.call(dplyr::bind_rows,unlist(object_list$draws, recursive = FALSE) )
Error:
! Tibble columns must have compatible sizes.
* Size 2: Columns `fixed.quadratic` and `mixed.quadratic`.
* Size 3: Columns `fixed.linear` and `mixed.linear`.
i Only values of size one are recycled.

Other attempts get a named numeric vector which appends a digit to the list name for every element in it, which can be worked to get my desired output by manipulating attributes and strings, but seems highly unefficient.

> unlist((unlist(object_list$draws, recursive = FALSE)))
fixed.quadratic1 fixed.quadratic2    fixed.linear1    fixed.linear2 
               1                2                3                4 
   fixed.linear3 mixed.quadratic1 mixed.quadratic2    mixed.linear1 
               5                1                2                3 
   mixed.linear2    mixed.linear3 
               4                5 
Kuku
  • 168
  • 1
  • 9

1 Answers1

1

You hinted at this solution at the end of your post; you can unlist (with the default recursive = TRUE), and remove the trailing digits from the names:

unlisted <- unlist(object_list$draws)

data.frame(
  value = unname(unlisted),
  type = gsub("[0-9]*$", "", names(unlisted))
)
   value            type
1      1 fixed.quadratic
2      2 fixed.quadratic
3      3    fixed.linear
4      4    fixed.linear
5      5    fixed.linear
6      1 mixed.quadratic
7      2 mixed.quadratic
8      3    mixed.linear
9      4    mixed.linear
10     5    mixed.linear

This alternative feels clunkier to me, but avoids any string manipulation:

unlisted <- unlist(object_list$draws, recursive = FALSE)

dfs <- lapply(names(unlisted), \(x) data.frame(value = unlisted[[x]], type = x))

do.call(rbind, dfs)
zephryl
  • 14,633
  • 3
  • 11
  • 30
  • Thanks, this is indeed shorter than the makeshift code I was using! But still I was wondering whether there was a built-in `base` tool that would retrieve the sublist name without the number appending, i.e. without needing to manipulate strings to get the result. I will most surely accept your answer and assume that this is just the cleanest `R`-way to go about it. – Kuku Mar 06 '23 at 09:59
  • 1
    I added an alternative that avoids string manipulation (though feels like a clunkier solution to me). If you’re looking for a single function that does what you want out of the box, I’m not aware of one. – zephryl Mar 06 '23 at 15:22