Get the column of a list of lists in R - r

Using R, my data set L is a list of lists. My print(L) produces the following output:
[[1]]
[[1]][[1]]
[1] 0.8198689
[[1]][[2]]
[1] 0.8166747
[[2]]
[[2]][[1]]
[1] 0.5798426
[[2]][[2]]
[1] 0.5753511
[[3]]
[[3]][[1]]
[1] 0.4713508
[[3]][[2]]
[1] 0.4698621
And I want to get a vector of the second column. However unlist(L[[2]]) gives me the second row (not the second column) and L[,2] gives me the error Error in L[, 2] : incorrect number of dimensions. I tried also L$'2' and didn't work.
How can I get the vector of the second column of this data set in R?

1) Assuming the input shown reproducibly in the Note at the end use sapply (or use lapply if you want it as a list). No packages are used.
sapply(L, `[[`, 2)
## [1] 0.8166747 0.5753511 0.4698621
2) Using purrr we have:
library(purrr)
transpose(L)[[2]]
## [[1]]
## [1] 0.8166747
##
## [[2]]
## [1] 0.5753511
##
## [[3]]
## [1] 0.4698621
3) If we know that L is regularly shaped we could convert it to a matrix and then take the second column.
matrix(unlist(L), length(L), byrow = TRUE)[, 2]
## [1] 0.8166747 0.5753511 0.4698621
or as a list
do.call("rbind", L)[, 2]
4) The second column here is the last item in each "row" and if that is what you want as it would work even if the list is ragged then try this
mapply('[[', L, lengths(L))
## [1] 0.8166747 0.5753511 0.4698621
or as a list
Map('[[', L, lengths(L))
Note
# input in reproducible form
L <- list(
list(0.8198689, 0.8166747),
list(0.5798426, 0.5753511),
list(0.4713508, 0.4698621))

The simple way to do this using purrr is just to use map(), which returns a list.
library(purrr)
map(L, 2)
If you want a (numeric) vector, use map_dbl().
map_dbl(L, 2)
# [1] 0.8166747 0.5753511 0.4698621

Related

Sort matrix colnames to match element order in a list

I have a particular list such as:
my_list<-list("Cluster18904", "Cluster6294", "Cluster17424", "Cluster26257",
"Cluster27053", "Cluster2905", "Cluster16096", "Cluster14552")
which looks like:
my_list
[[1]]
[1] "Cluster18904"
[[2]]
[1] "Cluster6294"
[[3]]
[1] "Cluster17424"
[[4]]
[1] "Cluster26257"
[[5]]
[1] "Cluster27053"
[[6]]
[1] "Cluster2905"
[[7]]
[1] "Cluster16096"
[[8]]
[1] "Cluster14552"
and I have a matrix with the same colnames but I'm looking for a solution in order to order the column of the matrix to match the same order as in my_list
I tried:
as.data.frame(matrix)[,my_list]
But I get :
Error in .subset(x, j) : 'list' incorrect index type
We need to unlist
as.data.frame(matrix)[, unlist(my_list)]
if there are column names not matching, then use intersect
dat1 <- as.data.frame(matrix)
nm1 <- intersect(names(dat1), unlist(my_list))
dat1[nm1]
Another base R option
as.data.frame(matrix)[simplify2array(my_list)]
This will sort the column names according to the list names, by first converting the list names to a vector of strings.
matrix[,match(colnames(matrix),as.character(my_list))]

How to find NULL in nested lists [duplicate]

How do I remove the null elements from a list of lists, like below, in R:
lll <- list(list(NULL),list(1),list("a"))
The object I want would look like:
lll <- list(list(1),list("a"))
I saw a similar answer here: How can I remove an element from a list? but was not able to extend it from simple lists to a list of lists.
EDIT
Bad example above on my part. Both answers work on simpler case (above). What if list is like:
lll <- list(list(NULL),list(1,2,3),list("a","b","c"))
How to get:
lll <- list(list(1,2,3),list("a","b","c"))
This recursive solution has the virtue of working on even more deeply nested lists.
It's closely modeled on Gabor Grothendieck's answer to this quite similar question. My modification of that code is needed if the function is to also remove objects like list(NULL) (not the same as NULL), as you are wanting.
## A helper function that tests whether an object is either NULL _or_
## a list of NULLs
is.NullOb <- function(x) is.null(x) | all(sapply(x, is.null))
## Recursively step down into list, removing all such objects
rmNullObs <- function(x) {
x <- Filter(Negate(is.NullOb), x)
lapply(x, function(x) if (is.list(x)) rmNullObs(x) else x)
}
rmNullObs(lll)
# [[1]]
# [[1]][[1]]
# [1] 1
#
#
# [[2]]
# [[2]][[1]]
# [1] "a"
Here is an example of its application to a more deeply nested list, on which the other currently proposed solutions variously fail.
LLLL <- list(lll)
rmNullObs(LLLL)
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
# [[1]][[1]][[1]][[1]]
# [1] 1
#
#
# [[1]][[1]][[2]]
# [[1]][[1]][[2]][[1]]
# [1] "a"
Here's an option using Filter and Negate combination
Filter(Negate(function(x) is.null(unlist(x))), lll)
# [[1]]
# [[1]][[1]]
# [1] 1
#
#
# [[2]]
# [[2]][[1]]
# [1] "a"
Using purrr
purrr::map(lll, ~ purrr::compact(.)) %>% purrr::keep(~length(.) != 0)
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 2
[[1]][[3]]
[1] 3
[[2]]
[[2]][[1]]
[1] "a"
[[2]][[2]]
[1] "b"
[[2]][[3]]
[1] "c"
For this particular example you can also use unlist with its recursive argument.
lll[!sapply(unlist(lll, recursive=FALSE), is.null)]
# [[1]]
# [[1]][[1]]
# [1] 1
#
#
# [[2]]
# [[2]][[1]]
# [1] "a"
Since you have lists in lists, you probably need to run l/sapply twice, like:
lll[!sapply(lll,sapply,is.null)]
#[[1]]
#[[1]][[1]]
#[1] 1
#
#
#[[2]]
#[[2]][[1]]
#[1] "a"
There is a new package rlist on CRAN, thanks to Kun Ren for making our life easier.
list.clean(.data, fun = is.null, recursive = FALSE)
or for recursive removal of NULL:
list.clean(.data, fun = is.null, recursive = TRUE)
Quick fix on Josh O'Brien's solution. There's a bit of an issue with lists of functions
is.NullOb <- function(x) if(!(is.function(x))) is.null(x) | all(sapply(x, is.null)) else FALSE
## Recursively step down into list, removing all such objects
rmNullObs <- function(x) {
if(!(is.function(x))) {
x = x[!(sapply(x, is.NullOb))]
lapply(x, function(x) if (is.list(x)) rmNullObs(x) else x)
}
}

Versions of lapply() and mclapply() that avoid redundant processing

I am looking for versions of lapply() and mclapply() that only process unique elements of the argument list X. Does something like this already exist?
EDIT: In other words, I want lapply() to not bother processing duplicates, but I want length(lapply(X, ...)) to equal length(X), not length(unique(X)) (and the appropriate values to match). Also, I am assuming each element of X is rather small, so taking unique values should not be too much trouble.
Current behavior:
long_computation <- function(task){
cat(task, "\n")
# Sys.sleep(1000) #
return(task)
}
tasks <- rep(LETTERS[1:2], 2)
lapply(tasks, long_computation)
## A
## B
## A
## B
## [[1]]
## [1] "A"
##
## [[2]]
## [1] "B"
##
## [[3]]
## [1] "A"
##
## [[4]]
## [1] "B"
Desired behavior:
lapply(tasks, long_computation)
## A
## B
## [[1]]
## [1] "A"
##
## [[2]]
## [1] "B"
##
## [[3]]
## [1] "A"
##
## [[4]]
## [1] "B"
You can find the intended use case here.
You can try something like this: I have created a map object which stores the result after long_computation for each unique 'key'. Once an existing 'key' is encountered, it returns from map else it calls the long_computation function and stores the result in map for future use. Not sure if it's the ideal way but it works.
tasks <- rep(letters[1:2], 2)
map=list()
lapply(tasks,function(t){if(t %in% names(.GlobalEnv$map)){
return(.GlobalEnv$map[[t]])
}else{
result=toupper(t)
if(!t %in% names(.GlobalEnv$map)){
.GlobalEnv$map[[t]]=result
}
}
})
This actually seems to work:
lightly_parallelize_atomic <- function(X, FUN, jobs = 1, ...){
keys <- unique(X)
index <- match(X, keys)
values <- mclapply(X = keys, FUN = FUN, mc.cores = jobs, ...)
values[index]
}
And in my case, it's okay that X is atomic.
But it would be neat to find something already built into either a package or R natively.

convert list of lists to list of vectors in R

I have a list of list of numbers that I want to change to a list of vectors of numbers. How could I do that?
Here's what I have,
>coords
[[1]]
[1] -106.24328 39.00774
[[2]]
[1] -106.18677 38.83261
[[3]]
[1] -106.1493 38.8303
> class(coords[1])
[1] "list"
> class(coords[1[1]])
[1] "list"
>
What I want is
>coords
[1] -106.24328 39.00774
[2] -106.18677 38.83261
[3] -106.1493 38.8303
The coords object seems to be a list. To convert it to a matrix with 2 columns, we can rbind the list elements.
m1 <- do.call(rbind, coords)

Remove NULL elements from list of lists

How do I remove the null elements from a list of lists, like below, in R:
lll <- list(list(NULL),list(1),list("a"))
The object I want would look like:
lll <- list(list(1),list("a"))
I saw a similar answer here: How can I remove an element from a list? but was not able to extend it from simple lists to a list of lists.
EDIT
Bad example above on my part. Both answers work on simpler case (above). What if list is like:
lll <- list(list(NULL),list(1,2,3),list("a","b","c"))
How to get:
lll <- list(list(1,2,3),list("a","b","c"))
This recursive solution has the virtue of working on even more deeply nested lists.
It's closely modeled on Gabor Grothendieck's answer to this quite similar question. My modification of that code is needed if the function is to also remove objects like list(NULL) (not the same as NULL), as you are wanting.
## A helper function that tests whether an object is either NULL _or_
## a list of NULLs
is.NullOb <- function(x) is.null(x) | all(sapply(x, is.null))
## Recursively step down into list, removing all such objects
rmNullObs <- function(x) {
x <- Filter(Negate(is.NullOb), x)
lapply(x, function(x) if (is.list(x)) rmNullObs(x) else x)
}
rmNullObs(lll)
# [[1]]
# [[1]][[1]]
# [1] 1
#
#
# [[2]]
# [[2]][[1]]
# [1] "a"
Here is an example of its application to a more deeply nested list, on which the other currently proposed solutions variously fail.
LLLL <- list(lll)
rmNullObs(LLLL)
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
# [[1]][[1]][[1]][[1]]
# [1] 1
#
#
# [[1]][[1]][[2]]
# [[1]][[1]][[2]][[1]]
# [1] "a"
Here's an option using Filter and Negate combination
Filter(Negate(function(x) is.null(unlist(x))), lll)
# [[1]]
# [[1]][[1]]
# [1] 1
#
#
# [[2]]
# [[2]][[1]]
# [1] "a"
Using purrr
purrr::map(lll, ~ purrr::compact(.)) %>% purrr::keep(~length(.) != 0)
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 2
[[1]][[3]]
[1] 3
[[2]]
[[2]][[1]]
[1] "a"
[[2]][[2]]
[1] "b"
[[2]][[3]]
[1] "c"
For this particular example you can also use unlist with its recursive argument.
lll[!sapply(unlist(lll, recursive=FALSE), is.null)]
# [[1]]
# [[1]][[1]]
# [1] 1
#
#
# [[2]]
# [[2]][[1]]
# [1] "a"
Since you have lists in lists, you probably need to run l/sapply twice, like:
lll[!sapply(lll,sapply,is.null)]
#[[1]]
#[[1]][[1]]
#[1] 1
#
#
#[[2]]
#[[2]][[1]]
#[1] "a"
There is a new package rlist on CRAN, thanks to Kun Ren for making our life easier.
list.clean(.data, fun = is.null, recursive = FALSE)
or for recursive removal of NULL:
list.clean(.data, fun = is.null, recursive = TRUE)
Quick fix on Josh O'Brien's solution. There's a bit of an issue with lists of functions
is.NullOb <- function(x) if(!(is.function(x))) is.null(x) | all(sapply(x, is.null)) else FALSE
## Recursively step down into list, removing all such objects
rmNullObs <- function(x) {
if(!(is.function(x))) {
x = x[!(sapply(x, is.NullOb))]
lapply(x, function(x) if (is.list(x)) rmNullObs(x) else x)
}
}

Resources