Say I have a nested list like this
lst <- list(a=list(b=list("a", "b")), c=list("d"))
str(lst)
#List of 2
# $ a:List of 1
# ..$ b:List of 2
# .. ..$ : chr "a"
# .. ..$ : chr "b"
# $ c:List of 1
# ..$ : chr "d"
and I want to remove all the elements that don't match a vector of names (characters here), but I also want to remove the entire nested component if there are no matches. So, for example, using rapply I have this
## Just keep the branches that have an "a" value
keeps <- "a"
## Pass this function to rapply
f <- function(x) if(any(unlist(x) %in% keeps)) x else NULL
res <- rapply(lst, f, how="replace")
str(res)
# List of 2
# $ a:List of 1
# ..$ b:List of 2
# .. ..$ : chr "a"
# .. ..$ : NULL
# $ c:List of 1
# ..$ : NULL
So, I would have liked the entire c list to be cleaved. I don't think I can do this with a single rapply operation? If not, what would be a good way to do this.
Related
I have a list which contain a list of list. The structure looks like this:
Is it possible to create a with none empty list of list in it?
I tried datalist2 <- datalist[!is.na(datalist[[]])] which return 0 list, and datalist2 <- datalist[!is.na(datalist[[]])] whih return 5 lists(no changes). How can I only get 3 lists?
Any suggestion?
You can use sapply and length and then select those with non-zero length:
# create an example
dat <- list(list(1:3), list(), list(letters[1:4]), list(LETTERS[1:4]),
list(), list())
str(dat)
#R> List of 6
#R> $ :List of 1
#R> ..$ : int [1:3] 1 2 3
#R> $ : list()
#R> $ :List of 1
#R> ..$ : chr [1:4] "a" "b" "c" "d"
#R> $ :List of 1
#R> ..$ : chr [1:4] "A" "B" "C" "D"
#R> $ : list()
#R> $ : list()
# get the non-empty lists
res <- dat[sapply(dat, length) > 0]
# show the results
str(res)
#R> List of 3
#R> $ :List of 1
#R> ..$ : int [1:3] 1 2 3
#R> $ :List of 1
#R> ..$ : chr [1:4] "a" "b" "c" "d"
#R> $ :List of 1
#R> ..$ : chr [1:4] "A" "B" "C" "D"
You might wanna use purrr:
datalist2 <- datalist[!purrr:is_empty(datalist[[]])]
Don't know if it works though, could you please provide a sample?
I have the following two data frames that in a list called df.list
df1 <- data.frame(name=c("a","b","c"),total=c("1","2","3"),other=c("100","200","300"))
df2 <- data.frame(name=c("d","e","f"),total=c("4","5","6"),other=c("100","200","300"))
df.list <- list(df1,df2)
[[1]]
name total other
1 a 1 100
2 b 2 200
3 c 3 300
[[2]]
name total other
1 d 4 100
2 e 5 200
3 f 6 300
I want to be able to go through each data frame in the list and covert the total and other columns to be numeric, and assign it back to df.list
I tried the following but it does not seem to work
lapply(df.list, function(x) as.numeric(x[2:3]))
We may use type.convert directly on the list
df.list2 <- type.convert(df.list, as.is = TRUE)
-checking the structure
str(df.list2)
List of 2
$ :'data.frame': 3 obs. of 3 variables:
..$ name : chr [1:3] "a" "b" "c"
..$ total: int [1:3] 1 2 3
..$ other: int [1:3] 100 200 300
$ :'data.frame': 3 obs. of 3 variables:
..$ name : chr [1:3] "d" "e" "f"
..$ total: int [1:3] 4 5 6
..$ other: int [1:3] 100 200 300
If we want to loop, then as.integer/as.numeric works on vectors. So, we need to loop again
df.list2 <- lapply(df.list, function(x) {
x[2:3] <- lapply(x[2:3], as.integer)
x})
Or maybe this one:
library(purrr)
df.list %>%
map(., ~mutate(.x, across(c(other, total), ~as.numeric(.x)))) %>%
str()
List of 2
$ :'data.frame': 3 obs. of 3 variables:
..$ name : chr [1:3] "a" "b" "c"
..$ total: num [1:3] 1 2 3
..$ other: num [1:3] 100 200 300
$ :'data.frame': 3 obs. of 3 variables:
..$ name : chr [1:3] "d" "e" "f"
..$ total: num [1:3] 4 5 6
..$ other: num [1:3] 100 200 300
you can create a function that works for each data frame such as the following functional_as_numeric() and then apply to each element in the list with map() from {purrr}. Personally I find {purrr}'s interface more consistent and easier to follow than the traditional _apply() functions.
library(purrr)
functional_as_numeric <- function(df) {
df %>% mutate(
total = as.numeric(total),
other = as.numeric(other)
)
}
df.list.result <- df.list %>%
purrr::map(functional_as_numeric)
str(df.list.result)
List of 2
$ :'data.frame': 3 obs. of 3 variables:
..$ name : chr [1:3] "a" "b" "c"
..$ total: num [1:3] 1 2 3
..$ other: num [1:3] 100 200 300
$ :'data.frame': 3 obs. of 3 variables:
..$ name : chr [1:3] "d" "e" "f"
..$ total: num [1:3] 4 5 6
..$ other: num [1:3] 100 200 300
I have a list like this:
x = list(a = 1:4, b = 3:10, c = NULL)
x
#$a
#[1] 1 2 3 4
#
#$b
#[1] 3 4 5 6 7 8 9 10
#
#$c
#NULL
and I want to extract all elements that are not null. How can this be done? Thanks.
Here's another option:
Filter(Negate(is.null), x)
What about:
x[!unlist(lapply(x, is.null))]
Here is a brief description of what is going on.
lapply tells us which elements are NULL
R> lapply(x, is.null)
$a
[1] FALSE
$b
[1] FALSE
$c
[1] TRUE
Next we convect the list into a vector:
R> unlist(lapply(x, is.null))
a b c
FALSE FALSE TRUE
Then we switch TRUE to FALSE:
R> !unlist(lapply(x, is.null))
a b c
TRUE TRUE FALSE
Finally, we select the elements using the usual notation:
x[!unlist(lapply(x, is.null))]
x[!sapply(x,is.null)]
This generalizes to any logical statement about the list, just sub in the logic for "is.null".
Simpler and likely quicker than the above, the following works for lists of any non-recursive (in the sense of is.recursive) values:
example_1_LST <- list(NULL, a=1.0, b=Matrix::Matrix(), c=NULL, d=4L)
example_2_LST <- as.list(unlist(example_1_LST, recursive=FALSE))
str(example_2_LST) prints:
List of 3
$ a: num 1
$ b:Formal class 'lsyMatrix' [package "Matrix"] with 5 slots
.. ..# x : logi NA
.. ..# Dim : int [1:2] 1 1
.. ..# Dimnames:List of 2
.. .. ..$ : NULL
.. .. ..$ : NULL
.. ..# uplo : chr "U"
.. ..# factors : list()
$ d: int 4
This question already has answers here:
Create a data.frame where a column is a list
(4 answers)
Closed 9 years ago.
I can't create a data frame with a column made of a collection of characters.
Is it not possible / should I stick with lists ?
>subsets <- c(list("a","d","e"),list("a","b","c","e"))
customerids <- c(1,1)
transactions <- data.frame(customerid = customerids,subset =subsets)
> str(transactions)
'data.frame': 2 obs. of 8 variables:
$ customerid : num 1 1
$ subset..a. : Factor w/ 1 level "a": 1 1
$ subset..d. : Factor w/ 1 level "d": 1 1
$ subset..e. : Factor w/ 1 level "e": 1 1
$ subset..a..1: Factor w/ 1 level "a": 1 1
$ subset..b. : Factor w/ 1 level "b": 1 1
$ subset..c. : Factor w/ 1 level "c": 1 1
$ subset..e..1: Factor w/ 1 level "e": 1 1
I think you've written subsets wrongly. If it is in fact this:
subsets <- list(c("a", "d", "e"), c("a", "b", "c", "e"))
# [[1]]
# [1] "a" "d" "e"
# [[2]]
# [1] "a" "b" "c" "e"
And customerids is c(1,1), then you can have subsets as a list in a column of data.frame as the total number of rows will still be the same. You can do it as follows:
DF <- data.frame(id = customerids, value = I(subsets))
# id value
# 1 1 a, d, e
# 2 1 a, b, c, e
sapply(DF, class)
# id value
# "numeric" "AsIs"
Now you can access DF$value and perform operations as you would on a list.
Use data.table instead:
library(data.table)
# note the extra list here
subsets <- list(list("a","d","e"),list("a","b","c","e"))
customerids <- c(1,1)
transactions <- data.table(customerid = customerids, subset = subsets)
str(transactions)
#Classes ‘data.table’ and 'data.frame': 2 obs. of 2 variables:
# $ customerid: num 1 1
# $ subset :List of 2
# ..$ :List of 3
# .. ..$ : chr "a"
# .. ..$ : chr "d"
# .. ..$ : chr "e"
# ..$ :List of 4
# .. ..$ : chr "a"
# .. ..$ : chr "b"
# .. ..$ : chr "c"
# .. ..$ : chr "e"
# - attr(*, ".internal.selfref")=<externalptr>
transactions
# customerid subset
#1: 1 <list>
#2: 1 <list>
I have a list like this:
x = list(a = 1:4, b = 3:10, c = NULL)
x
#$a
#[1] 1 2 3 4
#
#$b
#[1] 3 4 5 6 7 8 9 10
#
#$c
#NULL
and I want to extract all elements that are not null. How can this be done? Thanks.
Here's another option:
Filter(Negate(is.null), x)
What about:
x[!unlist(lapply(x, is.null))]
Here is a brief description of what is going on.
lapply tells us which elements are NULL
R> lapply(x, is.null)
$a
[1] FALSE
$b
[1] FALSE
$c
[1] TRUE
Next we convect the list into a vector:
R> unlist(lapply(x, is.null))
a b c
FALSE FALSE TRUE
Then we switch TRUE to FALSE:
R> !unlist(lapply(x, is.null))
a b c
TRUE TRUE FALSE
Finally, we select the elements using the usual notation:
x[!unlist(lapply(x, is.null))]
x[!sapply(x,is.null)]
This generalizes to any logical statement about the list, just sub in the logic for "is.null".
Simpler and likely quicker than the above, the following works for lists of any non-recursive (in the sense of is.recursive) values:
example_1_LST <- list(NULL, a=1.0, b=Matrix::Matrix(), c=NULL, d=4L)
example_2_LST <- as.list(unlist(example_1_LST, recursive=FALSE))
str(example_2_LST) prints:
List of 3
$ a: num 1
$ b:Formal class 'lsyMatrix' [package "Matrix"] with 5 slots
.. ..# x : logi NA
.. ..# Dim : int [1:2] 1 1
.. ..# Dimnames:List of 2
.. .. ..$ : NULL
.. .. ..$ : NULL
.. ..# uplo : chr "U"
.. ..# factors : list()
$ d: int 4