Here's one way:
R> as.data.frame(as.matrix(h1))
V1
USA 10, 13, 17, 11
RUSSIA NULL
BRAZIL NULL
CHINA 11, 11, 10, 8, 12
TAIWAN 12, 9, 9, 11, 9, 12, 14
CHILE NULL
R> as.data.frame(as.matrix(h1))['TAIWAN', ]
$TAIWAN
[1] 12 9 9 11 9 12 14
Answer from GSee on Stack OverflowHere's one way:
R> as.data.frame(as.matrix(h1))
V1
USA 10, 13, 17, 11
RUSSIA NULL
BRAZIL NULL
CHINA 11, 11, 10, 8, 12
TAIWAN 12, 9, 9, 11, 9, 12, 14
CHILE NULL
R> as.data.frame(as.matrix(h1))['TAIWAN', ]
$TAIWAN
[1] 12 9 9 11 9 12 14
You can convert a list to a matrix, where every element of the matrix will be a list.
d1 <- data.frame(country_name=names(h1), list=matrix(h1))
That said, I recommend against doing this because it does not fit the R paradigm. Most R functions assume a matrix contains elements from one atomic type.
AS @dimitris_ps mentioned earlier, the answer could be:
do.call(rbind, listHolder)
Since do.call naturally "strips" 1 level of the "list of list", obtaining a list, not a list of lists.
After that, rbind can handle the elements on the list and create a matrix.
The value of nrow needs to be fixed. I fixed your code as follows:
dd <- as.data.frame(matrix(unlist(listHolder), nrow=length(unlist(listHolder[1]))))
One liner with plyr
plyr::ldply(word.list, rbind)
try this:
word.list <- list(letters[1:4], letters[1:5], letters[1:2], letters[1:6])
n.obs <- sapply(word.list, length)
seq.max <- seq_len(max(n.obs))
mat <- t(sapply(word.list, "[", i = seq.max))
the trick is, that,
c(1:2)[1:4]
returns the vector + two NAs
1. We can use sapply and paste:
df <- data.frame(Value = sapply(myList, paste, collapse = ','))
Output:
Value
a 1,2,3
b 4,5,6,7
c 9,10
2. We can unlist each list element before applying max lengths:
df <- data.frame(lapply(mynewList, function(x) {
x <- unlist(x)
length(x) <- max(lengths(mynewList))
return(x)
}))
Output:
a b c d
1 1 4 9 1
2 2 5 10 2
3 3 6 NA NA
4 f NA NA NA
This approach keeps your Value as a list:
myDF <- data.frame(t(rbind(myList)))
myDF
# myList
#a 1, 2, 3
#b 4, 5, 6, 7
#c 9, 10
str(myDF)
#'data.frame': 3 obs. of 1 variable:
# $ myList:List of 3
# ..$ a: num 1 2 3
# ..$ b: num 4 5 6 7
# ..$ c: num 9 10
Approach one:
First, we get the matrices to data.frames, then we add the rownames as a separate column called a, and gather them all. By unnesting we get one big data.frame. Adding in the NA values is easy with complete
library(tidyverse) # using dplyr, tidyr and purrr
df %>%
mutate(Value = map(Value, as.data.frame),
Value = map(Value, rownames_to_column, 'a'),
Value = map(Value, ~gather(., b, value, -a))) %>%
unnest(Value) %>%
complete(Step, a, b)
Approach two:
Manually define the data.frame, then do the same:
df %>%
mutate(Value = map(Value,
~data_frame(val = c(.),
a = rep(rownames(.), each = ncol(.)),
b = rep(colnames(.), nrow(.))))) %>%
unnest(Value) %>%
complete(Step, a, b))
Result:
Both give:
# A tibble: 30 × 4 Step a b value <int> <chr> <chr> <dbl> 1 1 4 0.01 NA 2 1 4 0.021 NA 3 1 4 0.044 NA 4 1 4 0.094 0.932 5 1 4 0.2 0.232 6 1 5 0.01 NA 7 1 5 0.021 NA 8 1 5 0.044 NA 9 1 5 0.094 0.875 10 1 5 0.2 0.261 # ... with 20 more rows
Not really a dplyr solution, but you could do:
## Get the maximum length in l$Value and the index where it is observed
m = max(lengths(l$Value))
[1] 10
j = which.max(lengths(l$Value))
[1] 2
Then construct a dataframe for each element of l$Value, rbind them together and add the Step column:
l2 = lapply(l$Value,function(x) data.frame(a=rep(row.names(x),length.out=m),
Value=x[1:m],b=rep(colnames(l$Value[[j]]),length.out=m)))
df = do.call(rbind,l2)
df$Step = rep(l$Step,each=m)
This returns:
a Value b Step
1 4 0.232 0.2 1
2 5 0.261 0.094 1
3 4 0.932 0.044 1
4 5 0.875 0.021 1
5 4 NA 0.01 1
6 5 NA 0.2 1
7 4 NA 0.094 1
8 5 NA 0.044 1
9 4 NA 0.021 1
10 5 NA 0.01 1
11 4 0.197 0.2 2
12 5 0.197 0.094 2
13 4 0.640 0.044 2
14 5 0.643 0.021 2
15 4 0.958 0.01 2
16 5 1.032 0.2 2
17 4 0.943 0.094 2
18 5 1.119 0.044 2
19 4 0.943 0.021 2
20 5 1.119 0.01 2
21 4 0.268 0.2 3
22 5 0.262 0.094 3
23 4 NA 0.044 3
24 5 NA 0.021 3
25 4 NA 0.01 3
26 5 NA 0.2 3
27 4 NA 0.094 3
28 5 NA 0.044 3
29 4 NA 0.021 3
30 5 NA 0.01 3
Here is a modified version with length<- assignment
setNames(do.call(cbind.data.frame, lapply(lapply(SampleData, unlist),
`length<-`, max(lengths(SampleData)))), paste0("V", 1:3))
# V1 V2 V3
#1 1 1 3
#2 2 2 4
#3 3 NA 6
#4 NA NA 7
My first instinct looking at your data is that, by using a data.frame, you are implicitly stating that items across a row are paired. That is, in your example, the "3" of $V1 and "6" of $V3 are meant to be associated with each other. (If you look at mtcars, each column of the first row is associated directly and solely with the "Mazda RX4".) If this is not true, then warping them into a data.frame like this is mis-representing your data and like to encourage incorrect analysis/assumptions.
Assuming that they are in fact "paired", my next instinct is to try something like do.call(cbind, SampleData), but that lends to recycled data, not what you want. So, the trick to deter recycling is to force them to be all the same length.
maxlen <- max(lengths(SampleData))
SampleData2 <- lapply(SampleData, function(lst) c(lst, rep(NA, maxlen - length(lst))))
We can rename first:
names(SampleData2) <- paste("V", seq_along(SampleData2), sep = "")
Since the data appears homogenous (and should be, if you intend to put each element as a column of a data.frame), it is useful to un-list it:
SampleData3 <- lapply(SampleData2, unlist)
Then it's as straight-forward as:
as.data.frame(SampleData3)
# V1 V2 V3
# 1 1 1 3
# 2 2 2 4
# 3 3 NA 6
# 4 NA NA 7
df = data.frame(col1 = unlist(lapply(ex.list, paste, collapse = ", ")))
> df
col1
1 Tom, Ron, Joe
2 Ron, Joe
3 Tom
To get the unique names you'd have to run something like this
> unique(unlist(strsplit(as.character(df[ , 1]), ", ")))
[1] "Tom" "Ron" "Joe"
You're not going to be able to look at the levels of the factor with the way you want your data.frame setup.
You can use list columns with tidyr::nest and tidyr::unnest:
library(tidyverse)
df <- data.frame(id=seq_along(ex.list), col1 = unlist(lapply(ex.list, paste, collapse = ","))) %>%
mutate(col1 = stringr::str_split(col1, ",")) %>%
unnest %>%
mutate(col1=as.factor(col1)) %>%
nest(col1)
str(df)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 3 obs. of 2 variables:
$ id : int 1 2 3
$ data:List of 3
..$ :Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 3 obs. of 1 variable:
.. ..$ col1: Factor w/ 3 levels "Joe","Ron","Tom": 3 2 1
..$ :Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 2 obs. of 1 variable:
.. ..$ col1: Factor w/ 3 levels "Joe","Ron","Tom": 2 1
..$ :Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 1 obs. of 1 variable:
.. ..$ col1: Factor w/ 3 levels "Joe","Ron","Tom": 3
We can use
data.frame(lapply(aa, "length<-", max(lengths(aa))))
Or using tidyverse
library(dplyr)
library(tibble)
library(tidyr)
enframe(aa) %>%
unnest(value)
Using tidyverse packages. Place the list in a nested data frame. Extract the name for each vector in the list. Unnest the data frame. Give a row index i for each element in each vector, spread the data in wide format
aa <- list(A = c(1, 3, 4), B = c(3, 5, 7, 7, 8))
library(tidyverse)
data_frame(data = aa) %>%
group_by(name = names(data)) %>%
unnest() %>%
mutate(i = row_number()) %>%
spread(name, data)
# A tibble: 5 x 3
i A B
* <int> <dbl> <dbl>
1 1 1 3
2 2 3 5
3 3 4 7
4 4 NA 7
5 5 NA 8
Using rbind.fill.matrix from "plyr" gets you very close to what you're looking for:
> library(plyr)
> t(rbind.fill.matrix(lapply(a, t)))
[,1] [,2] [,3]
1 1 1 1
2 2 2 2
3 NA 3 3
4 NA NA 4
This is a lot of code, so not as clean as Ananda's solution, but it's all base R:
maxl <- max(sapply(a,length))
out <- do.call(cbind, lapply(a,function(x) x[1:maxl]))
# out <- matrix(unlist(lapply(a,function(x) x[1:maxl])), nrow=maxl) #another way
out <- as.data.frame(out)
#names(out) <- names(a)
Result:
> out
V1 V2 V3
1 1 1 1
2 2 2 2
3 NA 3 3
4 NA NA 4
Note: names of the resulting df will depend on the names of your list (a), which doesn't currently have names.
Here's my initial thought. It doesn't speed up your approach, but it does simplify the code considerably:
# makeDF <- function(List, Names) {
# m <- t(sapply(List, function(X) unlist(X)[Names],
# as.data.frame(m)
# }
## vapply() is a bit faster than sapply()
makeDF <- function(List, Names) {
m <- t(vapply(List,
FUN = function(X) unlist(X)[Names],
FUN.VALUE = numeric(length(Names))))
as.data.frame(m)
}
## Test timing with a 50k-item list
ll <- createList(50000)
nms <- c("a", "b", "c")
system.time(makeDF(ll, nms))
# user system elapsed
# 0.47 0.00 0.47
Here is a short answer, I doubt it will be very fast though.
> library(plyr)
> rbind.fill(lapply(x, as.data.frame))
a b c
1 1 3 5
2 3 NA 2