如何获取列表中元素的位置?

时间:2016-02-17 12:35:58

标签: r list

给定一个列表变量,我想拥有每个元素位置的数据框。对于一个简单的非嵌套列表,它看起来非常简单。

例如,这是一个字符向量列表。

l <- replicate(
  10,
  sample(letters, rpois(1, 2), replace = TRUE),
  simplify = FALSE
)

l看起来像这样:

[[1]]
[1] "m"

[[2]]
[1] "o" "r"

[[3]]
[1] "g" "m"
# etc.

要获取职位数据框,我可以使用:

d <- data.frame(
  value = unlist(l),
  i = rep(seq_len(length(l)), lengths(l)),
  j = rapply(l, seq_along, how = "unlist"),
  stringsAsFactors = FALSE
)
head(d)
##   value i j
## 1     m 1 1
## 2     o 2 1
## 3     r 2 2
## 4     g 3 1
## 5     m 3 2
## 6     w 4 1

给出一个比较棘手的嵌套列表,例如:

l2 <- list(
  "a",
  list("b", list("c", c("d", "a", "e"))),
  character(),
  c("e", "b"),
  list("e"),
  list(list(list("f")))
)

这不容易概括。

我希望这个例子的输出是:

data.frame(
  value = c("a", "b", "c", "d", "a", "e", "e", "b", "e", "f"), 
  i1 = c(1, 2, 2, 2, 2, 2, 4, 4, 5, 6), 
  i2 = c(1, 1, 2, 2, 2, 2, 1, 2, 1, 1), 
  i3 = c(NA, 1, 1, 2, 2, 2, NA, NA, 1, 1), 
  i4 = c(NA, NA, 1, 1, 2, 3, NA, NA, NA, 1), 
  i5 = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, 1)
)

如何获取嵌套列表的位置数据框?

4 个答案:

答案 0 :(得分:14)

这是一种产生与您所展示的输出略有不同的输出方法,但它在未来的发展中会更有用。

gplots

函数f <- function(l) { names(l) <- seq_along(l) lapply(l, function(x) { x <- setNames(x, seq_along(x)) if(is.list(x)) f(x) else x }) } 简单地迭代(递归地)遍历给定列表的所有级别并命名它的元素f,其中1,2,...,n是(子)列表的长度。然后,我们可以利用这样一个事实:n默认情况下unlist参数为use.names,并且在命名列表中使用时有效(这就是为什么我们有使用TRUE首先命名列表。

对于嵌套列表f,它返回:

l2

现在,为了按照问题的要求返回unlist(f(l2)) # 1.1 2.1.1 2.2.1.1 2.2.2.1 2.2.2.2 2.2.2.3 4.1 4.2 5.1.1 6.1.1.1.1 # "a" "b" "c" "d" "a" "e" "e" "b" "e" "f" ,我执行此操作:

data.frame

并像这样应用:

g <- function(l) {
  vec <- unlist(f(l))
  n <- max(lengths(strsplit(names(vec), ".", fixed=TRUE)))
  require(tidyr)
  data.frame(
    value = unname(vec),
    i = names(vec)
  ) %>% 
    separate(i, paste0("i", 1:n), sep = "\\.", fill = "right", convert = TRUE)
}

由@AnandaMahto(谢谢!)提供的g(l2) # value i1 i2 i3 i4 i5 #1 a 1 1 NA NA NA #2 b 2 1 1 NA NA #3 c 2 2 1 1 NA #4 d 2 2 2 1 NA #5 a 2 2 2 2 NA #6 e 2 2 2 3 NA #7 e 4 1 NA NA NA #8 b 4 2 NA NA NA #9 e 5 1 1 NA NA #10 f 6 1 1 1 1 的改进版本将使用g

data.table

修改(积分转到@TylerRinkler - 谢谢!)

这有利于轻松转换为 data.tree 对象,然后可以将其转换为许多其他数据类型。使用g <- function(inlist) { require(data.table) temp <- unlist(f(inlist)) setDT(tstrsplit(names(temp), ".", fixed = TRUE))[, value := unname(temp)][] } 的轻微模式:

g

并制作一个不错的情节:

g <- function(l) {
  vec <- unlist(f(l))
  n <- max(lengths(strsplit(names(vec), ".", fixed=TRUE)))
  require(tidyr)
  data.frame(
    i = names(vec),
    value = unname(vec)
  ) %>% 
    separate(i, paste0("i", 1:n), sep = "\\.", fill = "right", convert = TRUE)
}

library(data.tree)

x <- data.frame(top=".", g(l2))
x$pathString <- apply(x, 1, function(x) paste(trimws(na.omit(x)), collapse="/"))
mytree <- data.tree::as.Node(x)

mytree
#                   levelName
#1  .                        
#2   ¦--1                    
#3   ¦   °--1                
#4   ¦       °--a            
#5   ¦--2                    
#6   ¦   ¦--1                
#7   ¦   ¦   °--1            
#8   ¦   ¦       °--b        
#9   ¦   °--2                
#10  ¦       ¦--1            
#11  ¦       ¦   °--1        
#12  ¦       ¦       °--c    
#13  ¦       °--2            
#14  ¦           ¦--1        
#15  ¦           ¦   °--d    
#16  ¦           ¦--2        
#17  ¦           ¦   °--a    
#18  ¦           °--3        
#19  ¦               °--e    
#20  ¦--4                    
#21  ¦   ¦--1                
#22  ¦   ¦   °--e            
#23  ¦   °--2                
#24  ¦       °--b            
#25  ¦--5                    
#26  ¦   °--1                
#27  ¦       °--1            
#28  ¦           °--e        
#29  °--6                    
#30      °--1                
#31          °--1            
#32              °--1        
#33                  °--1    
#34                      °--f 

pic

其他形式的数据呈现:

plot(mytree)

有关转换 data.tree 类型的详情:

https://cran.r-project.org/web/packages/data.tree/vignettes/data.tree.html#tree-conversion http://www.r-bloggers.com/how-to-convert-an-r-data-tree-to-json/

答案 1 :(得分:6)

这是另一种选择。它不会像@docendodiscimus那样快,但它仍然非常简单。

基本思路是使用“reshape2”/“data.table”中的meltmelt method list melt(l2) # value L3 L2 L4 L1 # 1 a NA NA NA 1 # 2 b NA 1 NA 2 # 3 c 1 2 NA 2 # 4 d 2 2 NA 2 # 5 a 2 2 NA 2 # 6 e 2 2 NA 2 # 7 e NA NA NA 4 # 8 b NA NA NA 4 # 9 e NA 1 NA 5 # 10 f 1 1 1 6 可创建如下输出:

rapply(l2, seq_along)

除了您感兴趣的列排序和最后一个值之外,它似乎包含您所需的所有信息。要获得您感兴趣的最后一个值,可以使用myFun <- function(inlist) { require(reshape2) ## Load required package x1 <- melt(inlist) ## Melt the data x1[[paste0("L", ncol(x1))]] <- NA_integer_ ## Add a column to hold the position info x1 <- x1[c(1, order(names(x1)[-1]) + 1)] ## Reorder the columns vals <- rapply(inlist, seq_along) ## These are the positional values positions <- max.col(is.na(x1), "first") ## This is where the positions should go x1[cbind(1:nrow(x1), positions)] <- vals ## Matrix indexing for replacement x1 ## Return the output } myFun(l2) # value L1 L2 L3 L4 L5 # 1 a 1 1 NA NA NA # 2 b 2 1 1 NA NA # 3 c 2 2 1 1 NA # 4 d 2 2 2 1 NA # 5 a 2 2 2 2 NA # 6 e 2 2 2 3 NA # 7 e 4 1 NA NA NA # 8 b 4 2 NA NA NA # 9 e 5 1 1 NA NA # 10 f 6 1 1 1 1

将这两个要求放在一起,你就会有这样的事情:

g

来自@docendodiscimus的答案中的g <- function(inlist) { require(data.table) temp <- unlist(f(inlist)) setDT(tstrsplit(names(temp), ".", fixed = TRUE))[, value := unname(temp)][] } 的“data.table”版本更为直接:

ia32-libs

答案 2 :(得分:2)

与docendo类似,但尝试在递归内尽可能多地操作,而不是之后修复结果:

ff = function(x)
{
    if(!is.list(x)) if(length(x)) return(seq_along(x)) else return(NA)
    lapply(seq_along(x), 
           function(i) cbind(i, do.call(rBind, as.list(ff(x[[i]])))))
}

ans = do.call(rBind, ff(l2))
data.frame(value = unlist(l2), 
           ans[rowSums(is.na(ans[, -1L])) != (ncol(ans) - 1L), ])
#   value X1 X2 X3 X4 X5
#1      a  1  1 NA NA NA
#2      b  2  1  1 NA NA
#3      c  2  2  1  1 NA
#4      d  2  2  2  1 NA
#5      a  2  2  2  2 NA
#6      e  2  2  2  3 NA
#7      e  4  1 NA NA NA
#8      b  4  2 NA NA NA
#9      e  5  1  1 NA NA
#10     f  6  1  1  1  1

rBindrbind的包装,以避免出现“不匹配列”错误:

rBind = function(...) 
{
    args = lapply(list(...), function(x) if(is.matrix(x)) x else matrix(x))
    nc = max(sapply(args, ncol))
    do.call(rbind, 
            lapply(args, function(x) 
                           do.call(cbind, c(list(x), rep_len(list(NA), nc - ncol(x))))))
}

答案 3 :(得分:0)

这也可以通过使用rrapply返回rrapply包中的rapply包(基础how = "melt"的扩展版本)来返回融化的数据。 {1}}:

reshape2::melt

注意:以后可以将级别列转换为数字列。

library(rrapply)

## use rapply or rrapply to convert terminal nodes to lists
l2_list <- rapply(l2, f = as.list, how = "replace")

## use rrapply with how = "melt" to return melted data.frame
l2_melt <- rrapply(l2_list, how = "melt")
#>     L1  L2   L3   L4   L5 value
#> 1  ..1 ..1 <NA> <NA> <NA>     a
#> 2  ..2 ..1  ..1 <NA> <NA>     b
#> 3  ..2 ..2  ..1  ..1 <NA>     c
#> 4  ..2 ..2  ..2  ..1 <NA>     d
#> 5  ..2 ..2  ..2  ..2 <NA>     a
#> 6  ..2 ..2  ..2  ..3 <NA>     e
#> 7  ..4 ..1 <NA> <NA> <NA>     e
#> 8  ..4 ..2 <NA> <NA> <NA>     b
#> 9  ..5 ..1  ..1 <NA> <NA>     e
#> 10 ..6 ..1  ..1  ..1  ..1     f

计算时间

使用rrapply(l2_melt, condition = function(x, .xname) grepl("^L", .xname), f = function(x) as.numeric(sub("\\.+", "", x))) #> L1 L2 L3 L4 L5 value #> 1 1 1 NA NA NA a #> 2 2 1 1 NA NA b #> 3 2 2 1 1 NA c #> 4 2 2 2 1 NA d #> 5 2 2 2 2 NA a #> 6 2 2 2 3 NA e #> 7 4 1 NA NA NA e #> 8 4 2 NA NA NA b #> 9 5 1 1 NA NA e #> 10 6 1 1 1 1 f 而不是rrapply可以显着提高(非常)大型嵌套列表的速度,如下面的基准时间所示:

reshape2::melt