R 将嵌套列表安全地转换为数据帧,其中有些值是单数,有些是向量

R 将嵌套列表安全地转换为数据帧,其中有些值是单数,有些是向量,r,purrr,R,Purrr,我有一个列表,其中每个元素本身都是一个命名的属性列表,这是一种典型的JSON输出: l <- list( list( "attr1" = 1, "attr2" = "x", "attr3" = 3:4 ), list( "attr1" = 5, "attr2" = "y", "attr3" = 7:9 ), list( "attr1" = 10, "attr2" = "z", "attr3" = 1

我有一个列表,其中每个元素本身都是一个命名的属性列表,这是一种典型的JSON输出:

l <- list(
  list(
    "attr1" = 1,
    "attr2" = "x",
    "attr3" = 3:4
  ),
  list(
    "attr1" = 5,
    "attr2" = "y",
    "attr3" = 7:9
  ),
  list(
    "attr1" = 10,
    "attr2" = "z",
    "attr3" = 12
  )
)
错误:参数3的长度必须为1,而不是2

这种方法是有效的,但我必须事先知道哪些属性应该是单数的,哪些列表是单数的。我使用的是一个命名的向量attrs_dict,有点像Python字典,Pulk_包装函数会引用它来返回一个单数值或一个列表:

attrs_dict <- c("attr1" = FALSE, "attr2" = FALSE, "attr3" = TRUE)
pluck_wrapper <- function(element, attr, attrs_dict) {
  res <- pluck(element, attr)
  if (attrs_dict[attr]) {
    return(list(res))
  }
  return(res)
}
get_element_details <- function(element, attrs_dict) {
  attrs <- names(attrs_dict)
  element_list <- map(attrs, function(attr) pluck_wrapper(element, attr, attrs_dict))
  names(element_list) <- attrs
  element_list
}
df <- l %>% map_dfr(get_element_details, attrs_dict)
df

我不知道如何简单地简化,但这也是一个很好的方向。

您可以检查列表中每个元素的长度,如果所有元素都是1,我们可以取消列出它们

如果您想拥有不需要的完整数据,您可以这样做

transpose(l) %>% map_dfc(list) %>% tidyr::unnest(cols = V1:V3)

# A tibble: 6 x 3
#     V1 V2       V3
#  <dbl> <chr> <dbl>
#1     1 x         3
#2     1 x         4
#3     5 y         7
#4     5 y         8
#5     5 y         9
#6    10 z        12

您可以检查列表中每个元素的长度,如果所有元素都是1,我们可以取消它们的列表

如果您想拥有不需要的完整数据,您可以这样做

transpose(l) %>% map_dfc(list) %>% tidyr::unnest(cols = V1:V3)

# A tibble: 6 x 3
#     V1 V2       V3
#  <dbl> <chr> <dbl>
#1     1 x         3
#2     1 x         4
#3     5 y         7
#4     5 y         8
#5     5 y         9
#6    10 z        12
在base R中,您可以使用cbind

在base R中,您可以使用cbind


如果将一个观察值设为1行,表示前两行分别为1 x 3和1 x 4,这不是很有帮助吗?或者您只希望它们作为列表?@RonakShah您的意思是生成的数据帧将不被列出?在这个例子中,总共2+3+1=6行是的,这就是我的意思。我看到的问题是,列表类型的许多属性,其长度远远超过1、2或3,可能对一些非常丑陋的东西毫无意义。。。但是你下面的答案太棒了,谢谢!如果将一个观察值设为1行,表示前两行分别为1 x 3和1 x 4,这不是很有帮助吗?或者您只希望它们作为列表?@RonakShah您的意思是生成的数据帧将不被列出?在这个例子中,总共2+3+1=6行是的,这就是我的意思。我看到的问题是,列表类型的许多属性,其长度远远超过1、2或3,可能对一些非常丑陋的东西毫无意义。。。但是你下面的答案太棒了,谢谢!这在最新的purrr中不再有效。但这是:transposel%>%map\u dfc~ifalllength.x==1 unlist.x else tibble.x,除了不保留列的名称,我不知道该怎么做。此外,它只适用于单个列表列。这在最新的purrr中不再适用。但这是:transposel%>%map\u dfc~ifalllength.x==1 unlist.x else tibble.x,除了不保留列的名称,我不知道该怎么做。而且,它只适用于单个列表列。
attrs_dict <- c("attr1" = FALSE, "attr2" = FALSE, "attr3" = TRUE)
pluck_wrapper <- function(element, attr, attrs_dict) {
  res <- pluck(element, attr)
  if (attrs_dict[attr]) {
    return(list(res))
  }
  return(res)
}
get_element_details <- function(element, attrs_dict) {
  attrs <- names(attrs_dict)
  element_list <- map(attrs, function(attr) pluck_wrapper(element, attr, attrs_dict))
  names(element_list) <- attrs
  element_list
}
df <- l %>% map_dfr(get_element_details, attrs_dict)
df
# A tibble: 3 x 3
  attr1 attr2 attr3    
  <dbl> <chr> <list>   
1     1 x     <int [2]>
2     5 y     <int [3]>
3    10 z     <dbl [1]>
get_element_details <- function(element, attrs) {
  element_list <- map(attrs, function(attr) list(pluck(element, attr)))
  names(element_list) <- attrs
  element_list
}

df <- l %>% map_dfr(get_element_details, attrs)
df
# A tibble: 3 x 3
  attr1     attr2     attr3    
  <list>    <list>    <list>   
1 <dbl [1]> <chr [1]> <int [2]>
2 <dbl [1]> <chr [1]> <int [3]>
3 <dbl [1]> <chr [1]> <dbl [1]>
library(purrr)
transpose(l) %>% map_dfc(~if(all(lengths(.x) ==1)) unlist(.x) else .x)

# A tibble: 3 x 3
#  attr1 attr2 attr3    
#  <dbl> <chr> <list>   
#1     1 x     <int [2]>
#2     5 y     <int [3]>
#3    10 z     <dbl [1]>
transpose(l) %>% map_dfc(list) %>% tidyr::unnest(cols = V1:V3)

# A tibble: 6 x 3
#     V1 V2       V3
#  <dbl> <chr> <dbl>
#1     1 x         3
#2     1 x         4
#3     5 y         7
#4     5 y         8
#5     5 y         9
#6    10 z        12
res <- do.call(rbind, lapply(l, function(x) data.frame(t(cbind(x)))))

res
#    attr1 attr2   attr3
# x      1     x    3, 4
# x1     5     y 7, 8, 9
# x2    10     z      12

str(res)
'data.frame':   3 obs. of  3 variables:
 $ attr1:List of 3
  ..$ : num 1
  ..$ : num 5
  ..$ : num 10
 $ attr2:List of 3
  ..$ : chr "x"
  ..$ : chr "y"
  ..$ : chr "z"
 $ attr3:List of 3
  ..$ : int  3 4
  ..$ : int  7 8 9
  ..$ : num 12