2016-02-17 28 views
13

リスト変数を指定すると、各要素の位置のデータフレームが必要です。シンプルなネストされていないリストの場合、それはかなり簡単です。リスト内の要素の位置を取得する方法は?

たとえば、次の文字ベクタのリストがあります。

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) 
) 

どのように私は、ネストされたリストのための位置のデータフレームを得るのですか?

+2

'l2'の結果として6列(値+ 5レベルのネスト)のdata.frameが必要ですか? –

+1

これは基本的に 'melt(l2)' + 'rapply(l2、seq_along)'の組み合わせのようです。質問は簡単にそれらの2つを組み合わせる方法です:-) – A5C1D2H2I1M1N2O1R2T1

+0

@AnandaMahto、それは信じられないほど単純です - あなたが回答 –

答えて

14

ここでは、あなたが示したものとは少し異なる出力が得られるアプローチがありますが、それはさらに道を掘り下げるのに役立ちます。すべての与えられたリストのレベルと名前を介し

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,...,nnは(サブ)リストの長さです。そして、unlistにはデフォルトでTRUEというuse.namesという引数があり、名前付きリストで使用すると効果があります(そのため、最初にリストに名前を付けるにはfを使用する必要があります)。

は、ネストされたリストl2について、それが返されます。

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) 
} 

をし、それを適用します。

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(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 

の改良版(!ありがとう)@AnandaMahtoで貢献、data.table使用します。

g <- function(inlist) { 
    require(data.table) 
    temp <- unlist(f(inlist)) 
    setDT(tstrsplit(names(temp), ".", fixed = TRUE))[, value := unname(temp)][] 
} 

編集(!クレジット@TylerRinklerに行く - 感謝)

は、これは簡単に変換されるのbeneftを持っていますdata.treeオブジェクトに変換し、このオブジェクトを他の多くのデータ型に変換することができます。

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 

そして素敵なプロットを生成する:変換に

as.list(mytree) 
ToDataFrameTypeCol(mytree) 

詳細:データを提示する

plot(mytree) 

pic

他の形態をgにわずかなMODに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

すばらしいもの。私は予想される出力を指定するのはうんざりですが、 'i = names(vec)'を設定してから 'tidyr :: separate'を呼び出すと、私は何を望みますか? –

+1

@docendo discimus私はあなたが意図したものに合わない場合、私は追加しました。 +1 –

+0

@TylerRinker、あなたの編集のおかげで、それは実際にはかなりクールです!私もそのグラフを追加するかもしれない –

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 

rBindを避けるためにrbindのラッパーです「一致しない列」エラー:

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)))))) 
} 
6

これは別の方法です。 @docendodiscimusのアプローチほど速くはないでしょうが、依然としてかなり簡単です。

基本的な考え方は、 "reshape2"/"data.table"のmeltを使用することです。列の順序とあなたが後にしているすべての情報を持っているように見えるあなたが興味を持っている最後の値を除き、

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 

meltは、次のような出力が作成されますlistのためmethodを持っています。興味のある最後の価値を得るには、rapply(l2, seq_along)を使用してください。一緒に、これらの二つの要求を置く

、あなたはこのようなものだろう:@docendodiscimusによって

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の「data.table」バージョンをもう少し直接的である:

+0

良い解決策。私の答えがより速くなると言っていることに、私はちょっと驚きました。しかし、いずれにしても、それは非常に良いアプローチです。 –

+0

@docendodiscimus、それは本質的に 'unlist'と楽しい再帰を伴う文字列分割ですよね?:-) – A5C1D2H2I1M1N2O1R2T1

+0

ええ、それは本当です –

関連する問題