Kuku
Kuku

Reputation: 200

Unlist elements from unequal vectors at the last level of a nested list while keeping the sublist name in R

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 

Upvotes: 1

Views: 44

Answers (1)

zephryl
zephryl

Reputation: 17144

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)

Upvotes: 1

Related Questions