将不完整列表解析为具有两个不同问题的数据框
Parsing incomplete lists into data frames with two different problems
如果您通过 R 请求 Web 数据,您通常会使用 json 或 xml,其中如果字段没有值则不会命名。有时,甚至没有任何数据,它作为某个索引的空列表出现。所以,我认为这是两个不同的问题。我也提出了我用来解决这个问题的解决方案,但我知道有一些更好的解决方案。对于初学者,我有一个我创建的非常混乱和虚假的列表,它缺少字段名称(故意来自 xml、json 规范)并且缺少整个索引(也是故意的)。
(messy_list <- list(list(x = 2, y = 3),
list(),
list(y = 4),
list(x = 5)))
现在,我将其分解为 "solved"。
library(plyr)
messy_list_no_empties <- lapply(messy_list, function(x) if(length(x) == 0) {list(NA, NA)} else x)
ldply(messy_list_no_empties, data.frame)[,1:2]
最终结果是我想要的,但我想找到一种更优雅的方法来处理这个问题。
和purrr::map_df
,
library(purrr)
messy_list <- list(list(x = 2, y = 3),
list(),
list(y = 4),
list(x = 5))
messy_list %>% map_df(~list(x = .x$x %||% NA,
y = .x$y %||% NA))
#> # A tibble: 4 × 2
#> x y
#> <dbl> <dbl>
#> 1 2 3
#> 2 NA NA
#> 3 NA 4
#> 4 5 NA
map_df
像 lapply
一样遍历列表并将结果强制为 data.frame。该函数(以 purrr 的公式形式)组合一个包含 x
和 y
元素的列表,查找现有值(如果存在)。如果不是,子集将 return NULL
,%||%
将替换为它后面的值 NA
.
在基本等价的基础 R 中,
as.data.frame(do.call(rbind,
lapply(messy_list, function(.x){
list(x = ifelse(is.null(.x$x), NA, .x$x),
y = ifelse(is.null(.x$y), NA, .x$y))
})))
#> x y
#> 1 2 3
#> 2 NA NA
#> 3 NA 4
#> 4 5 NA
请注意,基本方法无法很好地处理不同的类型。为此,将所有内容强制转换为字符(rbind
可能无论如何,所以只需将 stringsAsFactors = FALSE
添加到 as.data.frame
)和 lapply
type.convert
.
您的方法已经非常紧凑,但如果您正在寻找其他方法,一种方法可能是使用 rbindlist
from data.table
:
library(data.table)
new_list <- lapply(messy_list, function(x) if(identical(x,list())){list(x = NA)} else {x})
rbindlist(new_list, fill = T, use.names = T)
# x y
#1: 2 3
#2: NA NA
#3: NA 4
#4: 5 NA
请注意,我们需要 lapply
,这样它就不会删除空行
如果您通过 R 请求 Web 数据,您通常会使用 json 或 xml,其中如果字段没有值则不会命名。有时,甚至没有任何数据,它作为某个索引的空列表出现。所以,我认为这是两个不同的问题。我也提出了我用来解决这个问题的解决方案,但我知道有一些更好的解决方案。对于初学者,我有一个我创建的非常混乱和虚假的列表,它缺少字段名称(故意来自 xml、json 规范)并且缺少整个索引(也是故意的)。
(messy_list <- list(list(x = 2, y = 3),
list(),
list(y = 4),
list(x = 5)))
现在,我将其分解为 "solved"。
library(plyr)
messy_list_no_empties <- lapply(messy_list, function(x) if(length(x) == 0) {list(NA, NA)} else x)
ldply(messy_list_no_empties, data.frame)[,1:2]
最终结果是我想要的,但我想找到一种更优雅的方法来处理这个问题。
和purrr::map_df
,
library(purrr)
messy_list <- list(list(x = 2, y = 3),
list(),
list(y = 4),
list(x = 5))
messy_list %>% map_df(~list(x = .x$x %||% NA,
y = .x$y %||% NA))
#> # A tibble: 4 × 2
#> x y
#> <dbl> <dbl>
#> 1 2 3
#> 2 NA NA
#> 3 NA 4
#> 4 5 NA
map_df
像 lapply
一样遍历列表并将结果强制为 data.frame。该函数(以 purrr 的公式形式)组合一个包含 x
和 y
元素的列表,查找现有值(如果存在)。如果不是,子集将 return NULL
,%||%
将替换为它后面的值 NA
.
在基本等价的基础 R 中,
as.data.frame(do.call(rbind,
lapply(messy_list, function(.x){
list(x = ifelse(is.null(.x$x), NA, .x$x),
y = ifelse(is.null(.x$y), NA, .x$y))
})))
#> x y
#> 1 2 3
#> 2 NA NA
#> 3 NA 4
#> 4 5 NA
请注意,基本方法无法很好地处理不同的类型。为此,将所有内容强制转换为字符(rbind
可能无论如何,所以只需将 stringsAsFactors = FALSE
添加到 as.data.frame
)和 lapply
type.convert
.
您的方法已经非常紧凑,但如果您正在寻找其他方法,一种方法可能是使用 rbindlist
from data.table
:
library(data.table)
new_list <- lapply(messy_list, function(x) if(identical(x,list())){list(x = NA)} else {x})
rbindlist(new_list, fill = T, use.names = T)
# x y
#1: 2 3
#2: NA NA
#3: NA 4
#4: 5 NA
请注意,我们需要 lapply
,这样它就不会删除空行