将向量列表转换为列表列表。

huangapple go评论110阅读模式
英文:

List of vectors to list of lists

问题

  1. 我有一个这样结构化的列表:

x <- list(id = c("a", "b"),
value = c(1,2),
othervalue = c(3,4)
)

  1. 我需要将这个列表转换为如下结构的列表:

y <- list(a = list(value = 1, othervalue = 3),
b = list(value = 2, othervalue = 4)
)

  1. 你会如何做这个?
  2. _____
  3. 编辑:
  4. 我遇到了一个更复杂版本的问题:
  5. 这里输出中有一种嵌套列表。

x <- list(id = c("a", "b", "a"), key = c("foo", "foo", "bar"), value = c(1, 2, 3))

y <- list(a = list(foo = 1, bar = 2), b = list(foo = 3))

  1. 根据当前的回答,结果是:

$a
$a$key
[1] "foo"

$a$value
[1] 1

$b
$b$key
[1] "foo"

$b$value
[1] 2

$a
$a$key
[1] "bar"

$a$value
[1] 3

  1. <details>
  2. <summary>英文:</summary>
  3. I have a list structured this way:

x <- list(id = c("a", "b"),
value = c(1,2),
othervalue = c(3,4)
)

  1. I need to transform the list to this structure like this:

y <- list(a = list(value = 1, othervalue = 3),
b = list(value = 2, othervalue = 4)
)

  1. How would you do this ?
  2. _____
  3. Edit:
  4. I stumbled upon a more advanced version of this problem:
  5. Here there is some kind of nested list in the output.

x <- list(id = c("a", "b", "a"), key = c("foo", "foo", "bar"), value = c(1, 2, 3))

  1. to

y <- list(a = list(foo = 1, bar = 2), b = list(foo = 3))

  1. With the current answers, the result is:

$a
$a$key
[1] "foo"

$a$value
[1] 1

$b
$b$key
[1] "foo"

$b$value
[1] 2

$a
$a$key
[1] "bar"

$a$value
[1] 3

  1. </details>
  2. # 答案1
  3. **得分**: 7
  4. You can use `list` in `Map` with `do.call`.
  5. ```R
  6. z <- setNames(do.call(Map, c(list, x[-1])), x[[1]])
  7. identical(z, y)
  8. #[1] TRUE

The same but using pipes:

  1. z <- c(list, x[-1]) |>
  2. do.call(what=Map) |>
  3. setNames(x[[1]])

Benchmark

  1. x <- list(id = c("a", "b"), value = c(1,2), othervalue = c(3,4) )
  2. bench::mark(purr = purrr::transpose(x[-1], .names = x[[1]]), #@Ma&#235;l
  3. lapplySplit = lapply(split(as.data.frame(x)[-1], x$id), c), #@Allan Cameron
  4. Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]) ) #@GKi
  5. # expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
  6. # &lt;bch:expr&gt; &lt;bch:tm&gt; &lt;bch:tm&gt; &lt;dbl&gt; &lt;bch:byt&gt; &lt;dbl&gt; &lt;int&gt; &lt;dbl&gt;
  7. #1 purr 2.51ms 2.64ms 372. 3.69MB 34.2 152 14
  8. #2 lapplySplit 461.77&#181;s 490.48&#181;s 2018. 102.21KB 52.0 892 23
  9. #3 Map 14.13&#181;s 15.9&#181;s 61557. 3.06KB 80.1 9987 13

Map is in this case about 30 times faster and allocates much less memory compared to lapplySplit the second.

And with the dataset from @s_baldur:

  1. x <- list(id = c(letters, LETTERS), value = 1:52, othervalue = (1:52 + 100))
  2. bench::mark(check=FALSE,
  3. purr = purrr::transpose(x[-1], .names = x[[1]]), #@Ma&#235;l
  4. lapplySplit = lapply(split(as.data.frame(x)[-1], x$id), c), #@Allan Cameron
  5. Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]) ) #@GKi
  6. # expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
  7. # &lt;bch:expr&gt; &lt;bch:tm&gt; &lt;bch:tm&gt; &lt;dbl&gt; &lt;bch:byt&gt; &lt;dbl&gt; &lt;int&gt; &lt;dbl&gt;
  8. #1 purr 2.5ms 2.59ms 385. 3.69MB 33.9 159 14
  9. #2 lapplySplit 2.43ms 2.51ms 391. 137.21KB 68.3 149 26
  10. #3 Map 50.98&#181;s 61.06&#181;s 15717. 3.51KB 72.5 6506 30

For the updated question use maybe:

  1. lapply(split(setNames(x$value, x$key), x$id), as.list)

But is there a need for a list? If not then use maybe:

  1. split(setNames(x$value, x$key), x$id)
英文:

You can use list in Map with do.call.

  1. z &lt;- setNames(do.call(Map, c(list, x[-1])), x[[1]])
  2. identical(z, y)
  3. #[1] TRUE

The same but using pipes:

  1. z &lt;- c(list, x[-1]) |&gt;
  2. do.call(what=Map) |&gt;
  3. setNames(x[[1]])

Benchmark

  1. x &lt;- list(id = c(&quot;a&quot;, &quot;b&quot;), value = c(1,2), othervalue = c(3,4) )
  2. bench::mark(purr = purrr::transpose(x[-1], .names = x[[1]]), #@Ma&#235;l
  3. lapplySplit = lapply(split(as.data.frame(x)[-1], x$id), c), #@Allan Cameron
  4. Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]) ) #@GKi
  5. # expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
  6. # &lt;bch:expr&gt; &lt;bch:tm&gt; &lt;bch:tm&gt; &lt;dbl&gt; &lt;bch:byt&gt; &lt;dbl&gt; &lt;int&gt; &lt;dbl&gt;
  7. #1 purr 2.51ms 2.64ms 372. 3.69MB 34.2 152 14
  8. #2 lapplySplit 461.77&#181;s 490.48&#181;s 2018. 102.21KB 52.0 892 23
  9. #3 Map 14.13&#181;s 15.9&#181;s 61557. 3.06KB 80.1 9987 13

Map is in this case about 30 times faster and allocates mutch less memory compared to lapplySplit the second.
And with the dataset from @s_baldur:

  1. x &lt;- list(id = c(letters, LETTERS), value = 1:52, othervalue = (1:52 + 100))
  2. bench::mark(check=FALSE,
  3. purr = purrr::transpose(x[-1], .names = x[[1]]), #@Ma&#235;l
  4. lapplySplit = lapply(split(as.data.frame(x)[-1], x$id), c), #@Allan Cameron
  5. Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]) ) #@GKi
  6. # expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
  7. # &lt;bch:expr&gt; &lt;bch:tm&gt; &lt;bch:tm&gt; &lt;dbl&gt; &lt;bch:byt&gt; &lt;dbl&gt; &lt;int&gt; &lt;dbl&gt;
  8. #1 purr 2.5ms 2.59ms 385. 3.69MB 33.9 159 14
  9. #2 lapplySplit 2.43ms 2.51ms 391. 137.21KB 68.3 149 26
  10. #3 Map 50.98&#181;s 61.06&#181;s 15717. 3.51KB 72.5 6506 30

For the updated question use maybe:

  1. lapply(split(setNames(x$value, x$key), x$id), as.list)

But is there a need for a list? If not then use maybe:

  1. split(setNames(x$value, x$key), x$id)

答案2

得分: 4

以下是您要翻译的内容:

你可以执行以下操作

  1. lapply(split(as.data.frame(x)[-1], x$id), c)
  2. #&gt; $a
  3. #&gt; $a$value
  4. #&gt; [1] 1
  5. #&gt;
  6. #&gt; $a$othervalue
  7. #&gt; [1] 3
  8. #&gt;
  9. #&gt;
  10. #&gt; $b
  11. #&gt; $b$value
  12. #&gt; [1] 2
  13. #&gt;
  14. #&gt; $b$othervalue
  15. #&gt; [1] 4

这与 y 完全相同:

  1. identical(lapply(split(as.data.frame(x)[-1], x$id), c), y)
  2. #&gt; [1] TRUE
英文:

You could do

  1. lapply(split(as.data.frame(x)[-1], x$id), c)
  2. #&gt; $a
  3. #&gt; $a$value
  4. #&gt; [1] 1
  5. #&gt;
  6. #&gt; $a$othervalue
  7. #&gt; [1] 3
  8. #&gt;
  9. #&gt;
  10. #&gt; $b
  11. #&gt; $b$value
  12. #&gt; [1] 2
  13. #&gt;
  14. #&gt; $b$othervalue
  15. #&gt; [1] 4

Which is identical to y:

  1. identical(lapply(split(as.data.frame(x)[-1], x$id), c), y)
  2. #&gt; [1] TRUE

答案3

得分: 4

Since you're mentioning purrr in your tags, you can use purrr::transpose:

  1. purrr::transpose(x[-1], .names = x[[1]])
  2. # $a
  3. # $a$value
  4. # [1] 1
  5. #
  6. # $a$othervalue
  7. # [1] 3
  8. #
  9. #
  10. # $b
  11. # $b$value
  12. # [1] 2
  13. #
  14. # $b$othervalue
  15. # [1] 4

Your second question looks like recursive splitting. To do so, a convenient option is collapse::rsplit:

  1. collapse::rsplit(data.frame(x), ~ id + key)
  2. # $a
  3. # $a$bar
  4. # [1] 3
  5. #
  6. # $a$foo
  7. # [1] 1
  8. #
  9. #
  10. # $b
  11. # $b$foo
  12. # [1] 2
英文:

Since you're mentioning purrr in your tags, you can use purrr::transpose:

  1. purrr::transpose(x[-1], .names = x[[1]])
  2. # $a
  3. # $a$value
  4. # [1] 1
  5. #
  6. # $a$othervalue
  7. # [1] 3
  8. #
  9. #
  10. # $b
  11. # $b$value
  12. # [1] 2
  13. #
  14. # $b$othervalue
  15. # [1] 4

Your second question looks like recursive splitting. To do so, a convenient option is collapse::rsplit:

  1. collapse::rsplit(data.frame(x), ~ id + key)
  2. # $a
  3. # $a$bar
  4. # [1] 3
  5. #
  6. # $a$foo
  7. # [1] 1
  8. #
  9. #
  10. # $b
  11. # $b$foo
  12. # [1] 2

答案4

得分: 3

以下是您提供的代码的中文翻译:

保持简单:

  1. foo <- function(x) {
  2. n <- length(x$id)
  3. y <- vector(mode = "list", length = n) | setNames(x$id)
  4. for (i in seq_len(n)) y[[i]] <- list(value = x$value[i], othervalue = x$othervalue[i])
  5. y
  6. }

性能测试(使用稍大的数据):

  1. x <- list(id = c(letters, LETTERS),
  2. value = 1:52,
  3. othervalue = (1:52 + 100)
  4. )
  5. bench::mark(purr = purrr::transpose(x[-1], .names = x[[1]]),
  6. Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]),
  7. loop = foo(x))
  8. # 表达式 最小时间 中位时间 itr/秒 内存分配 gc/秒 n_itr n_gc 总时间
  9. # <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
  10. # 1 purr 5µs 5.8µs 137969. 1.2KB 27.6 9998 2 72.5ms
  11. # 2 Map 32.3µs 34.7µs 27567. 464B 13.8 9995 5 362.6ms
  12. # 3 loop 16.5µs 17.5µs 52962. 464B 15.9 9997 3 188.8ms

请注意,我已经忽略了代码部分,并只提供了翻译好的内容。如果您有其他需要,请随时告诉我。

英文:

Keeping it simple:

  1. foo &lt;- function(x) {
  2. n &lt;- length(x$id)
  3. y &lt;- vector(mode = &quot;list&quot;, length = n) |&gt; setNames(x$id)
  4. for (i in seq_len(n)) y[[i]] &lt;- list(value = x$value[i], othervalue = x$othervalue[i])
  5. y
  6. }

Benchmark (with slightly bigger data):

  1. x &lt;- list(id = c(letters, LETTERS),
  2. value = 1:52,
  3. othervalue = (1:52 + 100)
  4. )
  5. bench::mark(purr = purrr::transpose(x[-1], .names = x[[1]]),
  6. Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]),
  7. loop = foo(x))
  8. # expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
  9. # &lt;bch:expr&gt; &lt;bch:tm&gt; &lt;bch:tm&gt; &lt;dbl&gt; &lt;bch:byt&gt; &lt;dbl&gt; &lt;int&gt; &lt;dbl&gt; &lt;bch:tm&gt;
  10. # 1 purr 5&#181;s 5.8&#181;s 137969. 1.2KB 27.6 9998 2 72.5ms
  11. # 2 Map 32.3&#181;s 34.7&#181;s 27567. 464B 13.8 9995 5 362.6ms
  12. # 3 loop 16.5&#181;s 17.5&#181;s 52962. 464B 15.9 9997 3 188.8ms

答案5

得分: 2

更新

关于问题中的更新,您可以尝试使用 split + lapply

  1. lapply(split(list2DF(x[-1]), x[[1]]), \(v) with(v, split(value, key)))

或者我们可以使用 aggregate + Map

  1. with(
  2. aggregate(x[-1], x[1], as.list),
  3. setNames(Map(setNames, value, key), id)
  4. )

这将得到以下结果:

  1. $a
  2. $a$bar
  3. [1] 3
  4. $a$foo
  5. [1] 1
  6. $b
  7. $b$foo
  8. [1] 2

对于先前的问题

您可以尝试以下方法:

  1. list2DF(x[-1]) %&gt;%
  2. split(1:nrow(.)) %&gt;%
  3. setNames(x[[1]]) %&gt;%
  4. lapply(c)

这将得到以下结果:

  1. $a
  2. $a$value
  3. [1] 1
  4. $a$othervalue
  5. [1] 3
  6. $b
  7. $b$value
  8. [1] 2
  9. $b$othervalue
  10. [1] 4
英文:

Update

Regarding the update in the question, you can try split + lapply

  1. lapply(split(list2DF(x[-1]), x[[1]]), \(v) with(v, split(value, key)))

or we can use aggregate + Map

  1. with(
  2. aggregate(x[-1], x[1], as.list),
  3. setNames(Map(setNames, value, key), id)
  4. )

which gives

  1. $a
  2. $a$bar
  3. [1] 3
  4. $a$foo
  5. [1] 1
  6. $b
  7. $b$foo
  8. [1] 2

For Previous Question

You can try

  1. list2DF(x[-1]) %&gt;%
  2. split(1:nrow(.)) %&gt;%
  3. setNames(x[[1]]) %&gt;%
  4. lapply(c)

which gives

  1. $a
  2. $a$value
  3. [1] 1
  4. $a$othervalue
  5. [1] 3
  6. $b
  7. $b$value
  8. [1] 2
  9. $b$othervalue
  10. [1] 4

huangapple
  • 本文由 发表于 2023年6月12日 18:35:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76455797.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定