Why does unlist() kill dates in R?
do.call
is a handy function to "do something" with a list. In our case, concatenate it using c
. It's not uncommon to cbind
or rbind
data.frames from a list into a single big data.frame.
What we're doing here is actually concatenating elements of the dd
list. This would be analogous to c(dd[[1]], dd[[2]])
. Note that c
can be supplied as a function or as a character.
> dd <- list(dd, dd)
> (d <- do.call("c", dd))
[1] "2013-01-01" "2013-02-01" "2013-03-01" "2013-01-01" "2013-02-01" "2013-03-01"
> class(d) # proof that class is still Date
[1] "Date"
Why might one use the unlist() function? (example inside)
dplyr functions, such as filter()
and select()
, return tibbles (a variant on data.frames). Data frames and tibbles are a special type of list, where each element is a vector of the same length, but not necessarily the same type.
In the example given, each statement is selecting a single column, returned as a 1-column tibble. A 1-column tibble is a list with one element, in this case the vector of Bodyweights. However, many functions do not expect a 1-column tibble (or data.frame), but want a vector. By using unlist()
, we are squashing the structure down to a single vector. This would be true whether you selected a single column or multiple columns.
The idiomatic way in dplyr would be to pipe pull(Bodyweight)
, as opposed to using unlist()
.
Consider this simple example for the difference
tib <- tibble(a = 1:5, b = letters[1:5])
select(tib, a)
class(select(tib, a))
# Notice the different printing and class when we unlist
unlist(select(tib, a))
class(unlist(select(tib, a))
unlist removes class in list elements (POSIXlt)
Usually it's far better to use POSIXct
. However, if your list is not nested you could use c
:
do.call(c, dates)
How to unlist result of lapply involving function(x) as.Date(as.POSIXct(x, origin = 1970-01-01 ))
Use do.call()
:
list <- lapply(data$displayDate, function(x) as.Date(as.POSIXct(x, origin = "1970-01-01")))
vec <- do.call("c", list)
By the way, a quick search through the SO database would have turned up this gem.
Unlist lists of lists in R
Here's a recursive approach. It applies a similar logic to what you would use as a human.
flat <- list()
finder <- function(l) {
for (element in l) {
if (inherits(element, "data.frame")) {
flat <<- c(flat, list(element))
} else {
finder(element)
}
}
return(flat)
}
Once you have run the above, you can call it with Reduce(rbind, finder(your_list))
I'm not sure how to approach it without having to use <<-
so would love feedback from those more knowledgeable than myself.
Unlisting nested lists and without loosing object classes
You can use Reduce()
or do.call()
to be able to combine all of the to one dataframe. The code below should work
Reduce(rbind,lapply(myList,data.frame,stringsAsFactors=F))
var1 var2 var3
1 A 1 1999-01-01
2 B 2 2000-01-01
3 C 3 2001-01-01
4 D 4 2002-01-01
5 Q 11 1999-01-02
6 W 22 2000-01-03
7 E 33 2001-01-04
8 R 44 2002-01-05
Also the class is maintained:
mapply(class,Reduce(rbind,lapply(myList,data.frame,stringsAsFactors=F)))
var1 var2 var3
"character" "numeric" "Date"
How to keep zero-length element when using c() on a list?
For use in a data frame you'd prefer NA
's over Date(0)
. E.g.
ex_list[sapply(ex_list, rlang::is_empty)] <- NA
ex_vector <- do.call(c, ex_list)
df <- data.frame(id = c("A", "B", "C"))
df$dates <- ex_vector
Or a tidyverse
-solution:
library(tidyverse)
data.frame(id = c("A", "B", "C")) |>
add_column(as_tibble_col(ex_list, column_name = "dates")) |>
unnest(dates, keep_empty = TRUE)
unlist() gives unexpected results when used against a dataframe
You can split the string on comma (,
), convert the data to numeric and use order
to get the order and collapse the data in one comma-separated string.
x = c( " 11, 13, 2", " 10 ,100, 11")
df2 = data.frame(x)
df2$y <- sapply(strsplit(trimws(df2$x), "\\s*,\\s*"), function(x)
toString(order(as.numeric(x))))
df2
# x y
#1 11, 13, 2 3, 1, 2
#2 10 ,100, 11 1, 3, 2
Using sort(..., index.return = TRUE)
.
df2$y <- sapply(strsplit(trimws(df2$x), "\\s*,\\s*"), function(x)
toString(sort(as.numeric(x), index.return=TRUE)$ix))
Using lapply
will give y
as list :
df2$y <- lapply(strsplit(trimws(df2$x), "\\s*,\\s*"), function(x)
order(as.numeric(x)))
Related Topics
"Correct" Way to Specifiy Optional Arguments in R Functions
Load Multiple Packages at Once
Convert Data.Frame Column to a Vector
Add New Row to Dataframe, at Specific Row-Index, Not Appended
How to Search for "R" Materials
Use Trycatch Skip to Next Value of Loop Upon Error
Using R to List All Files with a Specified Extension
How to Assign the Result of the Previous Expression to a Variable
Performing Dplyr Mutate on Subset of Columns
Re-Ordering Factor Levels in Data Frame
Ggplot2: Change Order of Display of a Factor Variable on an Axis
R Command for Setting Working Directory to Source File Location in Rstudio
Using Gsub to Extract Character String Before White Space in R