How to directly select the same column from all nested lists within a list?
Asked Answered
M

3

40

is it possible to directly select a column of all nested lists within a list?
My list is created using aggregate() with table():

AgN=aggregate(data,by=list(d$date),FUN=table,useNA="no")

AgN$x looks like:

$`0`

      1           2           3           9          11 
0.447204969 0.438509317 0.096894410 0.009937888 0.007453416 

$`1`

          1           2           4           8          11 
0.489974937 0.389724311 0.102756892 0.006265664 0.011278195 

…

$n

I want to get a vector of a specific column of each table, e.g. a vector containing the values of all columns named "1". I am still a R beginner, but even after searching and trying for a long time I found no nice solution. If I want to get the field of a list, I can simply index it with brackets, e.g. [i,j].
Online I found some examples for matrices, so I tried to do the same, at first only selecting one nested list’s column with AgN$x[1][1], but that is still selecting a whole list:

$0

     1           2           3           8          11 

0.447204969 0.438509317 0.096894410 0.009937888 0.007453416

My next try was AgN$x[[1]][1], and it was working:

  1 

0.447205

So I tried to to the same to select the value of each first column of all nested lists:

AgN$x[[1:length(AgN$x]][1]
Recursive indexing failed at level 2

Appearently the problem is that it is forbidden to select a range if you use a double brackets.

My last try was to use an for loop:

cduR=NULL 
for (i in 1:length(AgN$x)){
t=AgN$x[[i]]
cduR=c(cduR,as.vector(t["1"]))
}

Finally, so far that seems to working. But that way I had to build a loop each time when I want to select columns. Is there no direct way?

Thanks for your help.

Montalvo answered 22/10, 2012 at 17:12 Comment(0)
M
63

Assuming you have something like the following:

myList <- list(`0` = c(`1` = 10, `2` = 20, `3` = 30, `4` = 72),
               `1` = c(`1` = 15, `2` = 9, `3` = 7))
myList
# $`0`
#  1  2  3  4 
# 10 20 30 72 
# 
# $`1`
#  1  2  3 
# 15  9  7 

Use sapply() or lapply() to get into your list and extract whatever columns you want. Some examples.

# As a list of one-column data.frames
lapply(myList, `[`, 1)
# $`0`
#  1 
# 10 
# 
# $`1`
#  1 
# 15 

# As a list of vectors
lapply(myList, `[[`, 1)
# $`0`
# [1] 10
# 
# $`1`
# [1] 15

# As a named vector
sapply(myList, `[[`, 1)
#  0  1 
# 10 15 

# As an unnamed vector
unname(sapply(myList, `[[`, 1))
# [1] 10 15

Other variants of the syntax that also get you there include:

## Same output as above, different syntax
lapply(myList, function(x) x[1])
lapply(myList, function(x) x[[1]])
sapply(myList, function(x) x[[1]])
unname(sapply(myList, function(x) x[[1]]))

A Nested List Example

If you do have nested lists (lists within lists), try the following variants.

# An example nested list
myNestedList <- list(A = list(`0` = c(`1` = 10, `2` = 20, `3` = 30, `4` = 72),
                              `1` = c(`1` = 15, `2` = 9, `3` = 7)),
                     B = list(`0` = c(A = 11, B = 12, C = 13),
                              `1` = c(X = 14, Y = 15, Z = 16)))

# Run the following and see what you come up with....
lapply(unlist(myNestedList, recursive = FALSE), `[`, 1)
lapply(unlist(myNestedList, recursive = FALSE), `[[`, 1)
sapply(unlist(myNestedList, recursive = FALSE), `[[`, 1)
rapply(myNestedList, f=`[[`, ...=1, how="unlist")

Note that for lapply() and sapply() you need to use unlist(..., recursive = FALSE) while for rapply() (recursive apply), you refer to the list directly.

Marylinmarylinda answered 22/10, 2012 at 17:30 Comment(4)
Note that if you want all the columns named 1, rather than necessarily the first column, your selection criteria should be "1", not 1.Sightread
thank you, that seems to be what I was looking for. So [ is regarded as a function in R, therefore I can pass it to apply as FUN-parameter? And 1 is the x-parameter, passed by apply to the []-function?Montalvo
@Lukas, that's correct. [ is a function (but needs the backticks: `[`) so it works as it does in this example. I was not too comfortable with it when I first encountered it, so I usually resorted to anonymous functions, which were demonstrated in the "other variants" part of my answer.Marylinmarylinda
Any advice if I wanted to select multiple columns using this code?Zannini
E
2

One example I don't think is explicitly listed but also works is if you have a list of data.frames, matrix, xts, zoo, etc with row and column names, you can subsequently return an entire row, column or collection with the following syntax:

List with objects of format:

                           0%              1%             10%              50%              90%             99%            100%
Sec.1           -0.0005259283   -0.0002644018   -0.0001320010   -0.00005253342    0.00007852480    0.0002375756    0.0007870917
Sec.2           -0.0006620675   -0.0003931340   -0.0001588773   -0.00005251963    0.00007965378    0.0002121163    0.0004190017
Sec.4           -0.0006091183   -0.0003994136   -0.0001859032   -0.00005230263    0.00010592379    0.0003165986    0.0007870917
Sec.8           -0.0007679577   -0.0005321807   -0.0002636040   -0.00005232452    0.00014492480    0.0003930241    0.0007870917
Sec.16          -0.0009055318   -0.0007448356   -0.0003449334   -0.00005290166    0.00021238287    0.0004772207    0.0007870917
Sec.32          -0.0013007873   -0.0009552231   -0.0005243472   -0.00007836480    0.00028928104    0.0007382848    0.0013002350
Sec.64          -0.0016409500   -0.0012383696   -0.0006617173   -0.00005280668    0.00042354939    0.0011721508    0.0018579966
Sec.128         -0.0022575471   -0.0018858823   -0.0008466965   -0.00005298436    0.00068616576    0.0014665900    0.0027616991

Code (note the empty first row index, specifying all rows)

simplify2array(lapply(listOfIdenticalObjects,`[`,,"50%"))

Output

                     ListItem1        ListItem2        ListItem3        ListItem4         ListItem5
Sec.1           -0.00005253342   -0.00004673443    -0.0001112780   -0.00001870960    -0.00002051009
Sec.2           -0.00005251963   -0.00004663200    -0.0001112904   -0.00001878075     0.00000000000
Sec.4           -0.00005230263   -0.00004669297    -0.0001112780   -0.00001869911    -0.00002034403
Sec.8           -0.00005232452   -0.00004663635    -0.0001111296   -0.00001926096     0.00000000000
Sec.16          -0.00005290166   -0.00004668207    -0.0001109570    0.00000000000     0.00000000000
Sec.32          -0.00007836480    0.00000000000    -0.0001111667   -0.00001894496     0.00000000000
Sec.64          -0.00005280668    0.00000000000    -0.0001110926   -0.00001878305     0.00000000000
Sec.128         -0.00005298436    0.00004675191     0.0000000000   -0.00005582568     0.00001020502
Ethiopic answered 2/5, 2018 at 4:13 Comment(1)
Thanks! this was useful. However, I think we can condense the function call by just using sapply, i.e. sapply(listOfIdenticalObjects, `[`, , "50%") instead of simplify2array(lapply(listOfIdenticalObjects,`[`,,"50%"))Rozier
S
1

Here is a tidyverse solution using the purrr package. map and map_* are often used to apply a function to each element, but they can be used as an extractor (powered by pluck syntax). When used as an extractor you can also specify the .default argument to specify a default return value (NULL by default) if an element is NULL or does not exist (see nested example):

library(purrr)

# Flat list
myList <- list(`0` = c(`1` = 10, `2` = 20, `3` = 30, `4` = 72),
               `1` = c(`1` = 15, `2` = 9, `3` = 7))

map(myList, 3) # by index
map(myList, "3") # by name

# map_* variants to control output
map_int(myList, "3") # output integer vector

The map_* variant works here because each extraction returns a length-1 vector. If the nodes in this example were longer (instead of being a single value) this would error.

# Nested list
myNestedList <- list(A = list(`0` = c(`1` = 10, `2` = 20, `3` = 30, `4` = 72),
                              `1` = c(`1` = 15, `2` = 9, `3` = 7)),
                     B = list(`0` = c(A = 11, B = 12, C = 13),
                              `1` = c(X = 14, Y = 15, Z = 16)))

map_depth(myNestedList, -1, 4, .default = NA)
# $A
# $A$`0`
# [1] 72
# 
# $A$`1`
# [1] NA
# 
# 
# $B
# $B$`0`
# [1] NA
# 
# $B$`1`
# [1] NA

The second value specifies the depth at which extraction should occur. A negative value will count up from the lowest level of the list.

Data provided by @A5C1D2H2I1M1N2O1R2T1.

Standby answered 19/2 at 23:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.