将具有不同长度的嵌套列表有效转换为data.frame(使用基数R)

时间:2018-04-11 12:03:29

标签: r list-comprehension

我希望能够帮助您将嵌套列表转换为数据框。我的解决方案多次迭代,我想知道是否有更有效的解决方案。

嵌套列表的玩具示例:

nested_list <- list(list('date' = '2018-01-10', 'value1' = 1, 'value2' = 2), 
                    list('date' = '2018-01-09', 'value1' = 3, 'value2' = 4), 
                    list('date' = '2018-01 08', 'value1' = NULL, 'value2' = NULL), 
                    list('date' = '2018-01-07', 'value1' = NULL, 'value2' = NULL), 
                    list('date' = '2018-01-06', 'value1' = 5, 'value2' = 6))

如您所见,某些值为NULL。我的目的是完全省略这些数据。

目前,我首先屏蔽所有长度大于1的嵌套列表。

mask <- sapply((lapply(nested_list, unlist)), length) > 1 

然后我通过do.call应用rbind并转换为data.frame。在过程结束时,我必须强制数值,因为它们都被转换为字符串。

data.frame(do.call('rbind', lapply(nested_list[mask], unlist)), stringsAsFactors = FALSE)

正如您所看到的,这看起来很混乱,我想知道是否有更有效的方法来完成到数据框的转换。

谢谢

3 个答案:

答案 0 :(得分:2)

我知道你的标题是&#34;基础R&#34;,但你也要求更高效的方式。因此,替代方案是tidyverse解决方案:

nested_list %>%
    map(unlist) %>%
    rbind_all() %>%
    filter(complete.cases(.)) %>%
    mutate_at(vars(contains("value")), as.numeric);
## A tibble: 3 x 3
#  date       value1 value2
#  <chr>       <dbl>  <dbl>
#1 2018-01-10     1.     2.
#2 2018-01-09     3.     4.
#3 2018-01-09     5.     6.

或者(感谢@arun):

nested_list %>% 
    transpose %>% 
    map_df(~ .x %>% replace(., lengths(.)==0, NA) %>% unlist) %>% 
    filter(!is.na(value1))  

答案 1 :(得分:2)

这是一个base R选项,我们循环遍历'nested_list'和if的元素,any元素的length为0,然后将其分配给NULL或者返回data.frame

res <- do.call(rbind, lapply(nested_list, function(x) 
      if(any(lengths(x) == 0)) NULL else data.frame(x, stringsAsFactors = FALSE)))
res
#         date value1 value2
#1 2018-01-10      1      2
#2 2018-01-09      3      4
#3 2018-01-06      5      6

在输入数据集中,“日期”值为character,最好存储为Date

res$date <- as.Date(res$date)
str(res)
# 'data.frame':   3 obs. of  3 variables:
# $ date  : Date, format: "2018-01-10" "2018-01-09" "2018-01-06"
# $ value1: num  1 3 5
# $ value2: num  2 4 6

答案 2 :(得分:2)

另一种解决方案:

> data.table::rbindlist(nested_list[sapply(nested_list, function(x) min(lengths(x))) > 0])
         date value1 value2
1: 2018-01-10      1      2
2: 2018-01-09      3      4
3: 2018-01-06      5      6