ゴール
リストのリストが与えられた場合、私の目標はその構造を逆にすることです(R 言語)。
そのため、ネストされたリストの要素を第 1 層のリストの要素にしたいと考えています。
おそらく、例の方が私の目的をより明確に示しています。与えられた:
z <- list(z1 = list(a = 1, b = 2, c = 3), z2 = list(b = 4, a = 1, c = 0))
後続の R オブジェクトと同等の出力が必要です。
o <- list(a = list(z1 = 1, z2 = 1), b = list(z1 = 2, z2 = 4), c = list(z1 = 3, z2 = 0))
ソリューション
私の解決策
以下に添付する独自のソリューションを作成しましたが、もっと良いものがあれば教えてください。
revert_list_str_1 <- function(ls) {
res <- lapply(names(ls[[1]]), function(n, env) {
name <- paste(n, 'elements', sep = '_')
assign(name, vector('list', 0))
inner <- sapply(ls, function(x) {
assign(name, c(get(name), x[which(names(x) == n)]))
})
names(inner) <- names(ls)
inner
})
names(res) <- names(ls[[1]])
res
}
実行str(revert_list_str_1(z))
すると、必要なものに対応する後続の出力が得られます。
List of 3
$ a:List of 2
..$ z1: num 1
..$ z2: num 1
$ b:List of 2
..$ z1: num 2
..$ z2: num 4
$ c:List of 2
..$ z1: num 3
..$ z2: num 0
しかし、私が言ったように、よりエレガントで動的なソリューションの存在を調査 (および学習) したいと考えています。
実際、私のソリューションは、ネストされたすべてのリストが同じ名前(順序も異なる) である場合にのみ完全に機能します。これはnames(ls[[1]])
. また、報告されているように、2 レベルのリストでのみ機能することも指摘します。
では、より動的な他のソリューションを知っていますか? rapply
および/またはFilter
関数はこのタスクに役立ちますか?
編集終了 1.
提案されたソリューションの分析
提案されたソリューションを少し分析しました。ありがとうございます。. 分析は、すべての機能について次の点を確認することで構成されます。
- 受け入れられたクラス (ネストされたリスト要素)
- タイプの異なる要素が存在する場合にもタイプが保持されます (それらがアトミックである場合)
- 保存された要素に含まれるオブジェクト (例: マトリックス)
- 考慮される列(列の場合、ネストされたリストの名前を意味します)
- 一般的ではない列は無視されます (この場合、「not」という分類は肯定的に解釈されます)
- 共通ではない列が保持される
- 列が一致しない場合にも機能します(最初のネストされたリストの名前のみに基づいて)
このすべての場合において、「はい」という分類は、ポイント 2.1 を除いて肯定的に理解されます。
これは私が検討したすべての機能です(コメントは上記の分析項目に関連しています):
# yes 1.1
# yes 1.2
# yes 2.1, not 2.2, not 2.3
revert_list_str_1 <- function(ls) { # @leodido
# see above
}
# not 1.1
# not 1.2
# not 2.1, not 2.2, not 2.3
revert_list_str_2 <- function(ls) { # @mnel
# convert each component of list to a data.frame
# so rbind.data.frame so named elements are matched
x <- data.frame((do.call(rbind, lapply(ls, data.frame))))
# convert each column into an appropriately named list
o <- lapply(as.list(x), function(i, nam) as.list(`names<-`(i, nam)), nam = rownames(x))
o
}
# yes 1.1
# yes 1.2
# yes 2.1, not 2.2, yes 2.3
revert_list_str_3 <- function(ls) { # @mnel
# unique names
nn <- Reduce(unique, lapply(ls, names))
# convert from matrix to list `[` used to ensure correct ordering
as.list(data.frame(do.call(rbind,lapply(ls, `[`, nn))))
}
# yes 1.1
# yes 1.2
# yes 2.1, not 2.2, yes 2.3
revert_list_str_4 <- function(ls) { # @Josh O'Brien
# get sub-elements in same order
x <- lapply(ls, `[`, names(ls[[1]]))
# stack and reslice
apply(do.call(rbind, x), 2, as.list)
}
# not 1.1
# not 1.2
# not 2.1, not 2.2, not 2.3
revert_list_str_5 <- function(ls) { # @mnel
apply(data.frame((do.call(rbind, lapply(ls, data.frame)))), 2, as.list)
}
# not 1.1
# not 1.2
# not 2.1, yes 2.2, yes 2.3
revert_list_str_6 <- function(ls) { # @baptiste + @Josh O'Brien
b <- recast(z, L2 ~ L1)
apply(b, 1, as.list)
}
# yes 1.1
# yes 1.2
# not 2.1, yes 2.2, yes 2.3
revert_list_str_7 <- function(ll) { # @Josh O'Brien
nms <- unique(unlist(lapply(ll, function(X) names(X))))
ll <- lapply(ll, function(X) setNames(X[nms], nms))
ll <- apply(do.call(rbind, ll), 2, as.list)
lapply(ll, function(X) X[!sapply(X, is.null)])
}
考慮事項
この分析から、次のことがわかります。
- 関数
revert_list_str_7
でrevert_list_str_6
あり、ネストされたリストの名前に関して最も柔軟です - functions
revert_list_str_4
、それにrevert_list_str_3
続く私自身の関数は十分に完全であり、適切なトレードオフです。 - 絶対機能で最も完全
revert_list_str_7
なのは.
ベンチマーク
作業を完了するためにmicrobenchmark
、この 4 つの関数でいくつかの小さなベンチマーク (R パッケージを使用) を行いました (各ベンチマークで時間 = 1000 )。
ベンチマーク 1
入力:
list(z1 = list(a = 1, b = 2, c = 3), z2 = list(a = 0, b = 3, d = 22, f = 9))
.
結果:
Unit: microseconds
expr min lq median uq max
1 func_1 250.069 467.5645 503.6420 527.5615 2028.780
2 func_3 204.386 393.7340 414.5485 429.6010 3517.438
3 func_4 89.922 173.7030 189.0545 194.8590 1669.178
4 func_6 11295.463 20985.7525 21433.8680 21934.5105 72476.316
5 func_7 348.585 387.0265 656.7270 691.2060 2393.988
勝者: revert_list_str_4
.
ベンチマーク 2
入力:
list(z1 = list(a = 1, b = 2, c = 'ciao'), z2 = list(a = 0, b = 3, c = 5))
.
revert_list_str_6
異なるタイプのネストされた子要素をサポートしていないため、除外されました。
結果:
Unit: microseconds
expr min lq median uq max
1 func_1 249.558 483.2120 502.0915 550.7215 2096.978
2 func_3 210.899 387.6835 400.7055 447.3785 1980.912
3 func_4 92.420 170.9970 182.0335 192.8645 1857.582
4 func_7 257.772 469.9280 477.8795 487.3705 2035.101
勝者: revert_list_str_4
.
ベンチマーク 3
入力:
list(z1 = list(a = 1, b = m, c = 'ciao'), z2 = list(a = 0, b = 3, c = m))
.
m
は整数の行列 3x3 であり、revert_list_str_6
再び除外されました。
結果:
Unit: microseconds
expr min lq median uq max
1 func_1 261.173 484.6345 503.4085 551.6600 2300.750
2 func_3 209.322 393.7235 406.6895 449.7870 2118.252
3 func_4 91.556 174.2685 184.5595 196.2155 1602.983
4 func_7 252.883 474.1735 482.0985 491.9485 2058.306
勝者: revert_list_str_4
. また!
編集終了 2.
結論
まず第一に、素晴らしいソリューションに感謝します。
私の意見では、リストに同じ名前のネストされたリストがあることを事前に知っている場合reverse_str_4
は、パフォーマンスとさまざまなタイプのサポートの間の最良の妥協点として勝者です。
最も完全な解決策は、 (ネストされたリストの名前が異なる場合に役立ちます)revert_list_str_7
と比較して、完全な柔軟性が平均で約 2.5 倍のパフォーマンスの悪化を引き起こすことです。reverse_str_4