首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将元素数可变的列表嵌套到数据帧中。

将元素数可变的列表嵌套到数据帧中。
EN

Stack Overflow用户
提问于 2018-01-31 13:03:26
回答 3查看 1.6K关注 0票数 0

我已经有了一个嵌套的列表,我想把这些列表压缩成一个包含id变量的dataframe,这样我就知道每个列表元素(和子列表元素)来自哪个列表元素。

代码语言:javascript
复制
> str(gc_all)
List of 3
$ 1: num [1:102, 1:2] -74 -73.5 -73 -72.5 -71.9 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : NULL
.. ..$ : chr [1:2] "lon" "lat"
$ 2: num [1:102, 1:2] -74 -73.3 -72.5 -71.8 -71 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : NULL
.. ..$ : chr [1:2] "lon" "lat"
$ 3:List of 2
..$ : num [1:37, 1:2] -74 -74.4 -74.8 -75.3 -75.8 ...
.. ..- attr(*, "dimnames")=List of 2
.. .. ..$ : NULL
.. .. ..$ : chr [1:2] "lon" "lat"
..$ : num [1:65, 1:2] 180 169 163 158 154 ...
.. ..- attr(*, "dimnames")=List of 2
.. .. ..$ : NULL
.. .. ..$ : chr [1:2] "lon" "lat"

我以前使用过plyr::ldply(mylist, rbind)来处理列表,但是由于列表长度的变化,我似乎遇到了麻烦:一些列表元素只包含一个数据文件,而另一些元素包含两个数据序列。

我找到了一个笨重的解决方案,使用了两个lapply和一个ifelse,如下所示:

代码语言:javascript
复制
# sample latitude-longitude data
df <- data.frame(source_lat = rep(40.7128, 3),
                 source_lon = rep(-74.0059, 3),
                 dest_lat = c(55.7982, 41.0082, -7.2575),
                 dest_lon = c(37.968, 28.9784, 112.7521),
                 id = 1:3)

# split into list
gc_list <- split(df, df$id)

# get great circles between lat-lon for each id; multiple list elements are outputted when the great circle crosses the dateline
gc_all <- lapply(gc_list, function(x) {
  geosphere::gcIntermediate(x[, c("source_lon", "source_lat")],
                 x[, c("dest_lon", "dest_lat")],
                 n = 100, addStartEnd=TRUE, breakAtDateLine=TRUE)
})

gc_fortified <- lapply(1:length(gc_all), function(i) {
  if(class(gc_all[[i]]) == "list") {
    lapply(1:length(gc_all[[i]]), function(j) {
      data.frame(gc_all[[i]][[j]], id = i, section = j)
    }) %>%
      plyr::rbind.fill()
  } else {
    data.frame(gc_all[[i]], id = i, section = 1)
  }
}) %>%
  plyr::rbind.fill()

但我觉得必须有一个更优雅的解决方案,作为一个单线,例如dputdata.table

下面是我期望输出的样子:

代码语言:javascript
复制
> gc_fortified %>% 
    group_by(id, section) %>%
    slice(1)

lon      lat    id section
<dbl>    <dbl> <int>   <dbl>
1 -74.0059 40.71280     1       1
2 -74.0059 40.71280     2       1
3 -74.0059 40.71280     3       1
4 180.0000 79.70115     3       2
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-01-31 15:41:01

首先,需要对列表的结构进行重新处理,使其成为一个常规的列表列表,然后使用map_dfr参数使用两次.id

代码语言:javascript
复制
library(purrr)
gc_all_df  <- map(map_if(gc_all,~class(.x)=="matrix",list),~map(.x,as.data.frame))
map_dfr(gc_all_df,~map_dfr(.x,identity,.id="id2"),identity,.id="id1")
票数 2
EN

Stack Overflow用户

发布于 2018-01-31 15:42:49

我想我更喜欢已经显示的递归解决方案,但是如果在最后一行中替换Ladd_n_s,这是表单L的一个语句。为了清楚起见,我把它们分开了。

我将结果作为一个矩阵,因为结果完全是数字的,我怀疑不是您更喜欢数据帧,而是rbind.fill在它们上工作,这就是您所使用的。如果您喜欢数据帧结果,请将add_n_s函数中的add_n_s替换为data.frame

不使用包,解决方案也不使用任何索引。

这里,gc_all被转换为L,这是相同的,只不过它是一个列表,而不是一个矩阵和列表的混合列表。add_n_s接受L的一个元素,并向其中添加ns列。最后,我们将add_n_s映射到L和平面上。

注意,如果输入首先是一个列表列表,那么L将等于gc_all,并且不需要第一行。

代码语言:javascript
复制
L <- lapply(gc_all, function(x) if (is.list(x)) x else list(x))

add_n_s <- function(x, n) Map(cbind, x, n = n, s = seq_along(x))
do.call("rbind", do.call("c", Map(add_n_s, L, seq_along(gc_all))))

更新修复。

票数 3
EN

Stack Overflow用户

发布于 2018-01-31 14:03:12

我不能提供一条单线,但你也可以考虑递归。

代码语言:javascript
复制
flat <- function(l, s = NULL) {
  lapply(1:length(l), function(i) {
    if (is.list(l[[i]])) {
      do.call(rbind, flat(l[[i]], i))
    } else {
      cbind(l[[i]], id = if (is.null(s)) i else s, section = if (is.null(s)) 1 else i)
    }
  })
}

a <- do.call(rbind, flat(gc_all))
all.equal(data.frame(a), gc_fortified)

[1] TRUE
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48542874

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档