英文:
List of vectors to list of lists
问题
我有一个这样结构化的列表:
x <- list(id = c("a", "b"),
value = c(1,2),
othervalue = c(3,4)
)
我需要将这个列表转换为如下结构的列表:
y <- list(a = list(value = 1, othervalue = 3),
b = list(value = 2, othervalue = 4)
)
你会如何做这个?
_____
编辑:
我遇到了一个更复杂版本的问题:
这里输出中有一种嵌套列表。
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))
根据当前的回答,结果是:
$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
<details>
<summary>英文:</summary>
I have a list structured this way:
x <- list(id = c("a", "b"),
value = c(1,2),
othervalue = c(3,4)
)
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)
)
How would you do this ?
_____
Edit:
I stumbled upon a more advanced version of this problem:
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))
to
y <- list(a = list(foo = 1, bar = 2), b = list(foo = 3))
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
</details>
# 答案1
**得分**: 7
You can use `list` in `Map` with `do.call`.
```R
z <- setNames(do.call(Map, c(list, x[-1])), x[[1]])
identical(z, y)
#[1] TRUE
The same but using pipes:
z <- c(list, x[-1]) |>
do.call(what=Map) |>
setNames(x[[1]])
Benchmark
x <- list(id = c("a", "b"), value = c(1,2), othervalue = c(3,4) )
bench::mark(purr = purrr::transpose(x[-1], .names = x[[1]]), #@Maël
lapplySplit = lapply(split(as.data.frame(x)[-1], x$id), c), #@Allan Cameron
Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]) ) #@GKi
# expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
#1 purr 2.51ms 2.64ms 372. 3.69MB 34.2 152 14
#2 lapplySplit 461.77µs 490.48µs 2018. 102.21KB 52.0 892 23
#3 Map 14.13µs 15.9µ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:
x <- list(id = c(letters, LETTERS), value = 1:52, othervalue = (1:52 + 100))
bench::mark(check=FALSE,
purr = purrr::transpose(x[-1], .names = x[[1]]), #@Maël
lapplySplit = lapply(split(as.data.frame(x)[-1], x$id), c), #@Allan Cameron
Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]) ) #@GKi
# expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
#1 purr 2.5ms 2.59ms 385. 3.69MB 33.9 159 14
#2 lapplySplit 2.43ms 2.51ms 391. 137.21KB 68.3 149 26
#3 Map 50.98µs 61.06µs 15717. 3.51KB 72.5 6506 30
For the updated question use maybe:
lapply(split(setNames(x$value, x$key), x$id), as.list)
But is there a need for a list
? If not then use maybe:
split(setNames(x$value, x$key), x$id)
英文:
You can use list
in Map
with do.call
.
z <- setNames(do.call(Map, c(list, x[-1])), x[[1]])
identical(z, y)
#[1] TRUE
The same but using pipes:
z <- c(list, x[-1]) |>
do.call(what=Map) |>
setNames(x[[1]])
Benchmark
x <- list(id = c("a", "b"), value = c(1,2), othervalue = c(3,4) )
bench::mark(purr = purrr::transpose(x[-1], .names = x[[1]]), #@Maël
lapplySplit = lapply(split(as.data.frame(x)[-1], x$id), c), #@Allan Cameron
Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]) ) #@GKi
# expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
#1 purr 2.51ms 2.64ms 372. 3.69MB 34.2 152 14
#2 lapplySplit 461.77µs 490.48µs 2018. 102.21KB 52.0 892 23
#3 Map 14.13µs 15.9µ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:
x <- list(id = c(letters, LETTERS), value = 1:52, othervalue = (1:52 + 100))
bench::mark(check=FALSE,
purr = purrr::transpose(x[-1], .names = x[[1]]), #@Maël
lapplySplit = lapply(split(as.data.frame(x)[-1], x$id), c), #@Allan Cameron
Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]) ) #@GKi
# expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
#1 purr 2.5ms 2.59ms 385. 3.69MB 33.9 159 14
#2 lapplySplit 2.43ms 2.51ms 391. 137.21KB 68.3 149 26
#3 Map 50.98µs 61.06µs 15717. 3.51KB 72.5 6506 30
For the updated question use maybe:
lapply(split(setNames(x$value, x$key), x$id), as.list)
But is there a need for a list
? If not then use maybe:
split(setNames(x$value, x$key), x$id)
答案2
得分: 4
以下是您要翻译的内容:
你可以执行以下操作
lapply(split(as.data.frame(x)[-1], x$id), c)
#> $a
#> $a$value
#> [1] 1
#>
#> $a$othervalue
#> [1] 3
#>
#>
#> $b
#> $b$value
#> [1] 2
#>
#> $b$othervalue
#> [1] 4
这与 y
完全相同:
identical(lapply(split(as.data.frame(x)[-1], x$id), c), y)
#> [1] TRUE
英文:
You could do
lapply(split(as.data.frame(x)[-1], x$id), c)
#> $a
#> $a$value
#> [1] 1
#>
#> $a$othervalue
#> [1] 3
#>
#>
#> $b
#> $b$value
#> [1] 2
#>
#> $b$othervalue
#> [1] 4
Which is identical to y
:
identical(lapply(split(as.data.frame(x)[-1], x$id), c), y)
#> [1] TRUE
答案3
得分: 4
Since you're mentioning purrr
in your tags, you can use purrr::transpose
:
purrr::transpose(x[-1], .names = x[[1]])
# $a
# $a$value
# [1] 1
#
# $a$othervalue
# [1] 3
#
#
# $b
# $b$value
# [1] 2
#
# $b$othervalue
# [1] 4
Your second question looks like recursive splitting. To do so, a convenient option is collapse::rsplit
:
collapse::rsplit(data.frame(x), ~ id + key)
# $a
# $a$bar
# [1] 3
#
# $a$foo
# [1] 1
#
#
# $b
# $b$foo
# [1] 2
英文:
Since you're mentioning purrr
in your tags, you can use purrr::transpose
:
purrr::transpose(x[-1], .names = x[[1]])
# $a
# $a$value
# [1] 1
#
# $a$othervalue
# [1] 3
#
#
# $b
# $b$value
# [1] 2
#
# $b$othervalue
# [1] 4
Your second question looks like recursive splitting. To do so, a convenient option is collapse::rsplit
:
collapse::rsplit(data.frame(x), ~ id + key)
# $a
# $a$bar
# [1] 3
#
# $a$foo
# [1] 1
#
#
# $b
# $b$foo
# [1] 2
答案4
得分: 3
以下是您提供的代码的中文翻译:
保持简单:
foo <- function(x) {
n <- length(x$id)
y <- vector(mode = "list", length = n) | setNames(x$id)
for (i in seq_len(n)) y[[i]] <- list(value = x$value[i], othervalue = x$othervalue[i])
y
}
性能测试(使用稍大的数据):
x <- list(id = c(letters, LETTERS),
value = 1:52,
othervalue = (1:52 + 100)
)
bench::mark(purr = purrr::transpose(x[-1], .names = x[[1]]),
Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]),
loop = foo(x))
# 表达式 最小时间 中位时间 itr/秒 内存分配 gc/秒 n_itr n_gc 总时间
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
# 1 purr 5µs 5.8µs 137969. 1.2KB 27.6 9998 2 72.5ms
# 2 Map 32.3µs 34.7µs 27567. 464B 13.8 9995 5 362.6ms
# 3 loop 16.5µs 17.5µs 52962. 464B 15.9 9997 3 188.8ms
请注意,我已经忽略了代码部分,并只提供了翻译好的内容。如果您有其他需要,请随时告诉我。
英文:
Keeping it simple:
foo <- function(x) {
n <- length(x$id)
y <- vector(mode = "list", length = n) |> setNames(x$id)
for (i in seq_len(n)) y[[i]] <- list(value = x$value[i], othervalue = x$othervalue[i])
y
}
Benchmark (with slightly bigger data):
x <- list(id = c(letters, LETTERS),
value = 1:52,
othervalue = (1:52 + 100)
)
bench::mark(purr = purrr::transpose(x[-1], .names = x[[1]]),
Map = setNames(do.call(Map, c(list, x[-1])), x[[1]]),
loop = foo(x))
# expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
# 1 purr 5µs 5.8µs 137969. 1.2KB 27.6 9998 2 72.5ms
# 2 Map 32.3µs 34.7µs 27567. 464B 13.8 9995 5 362.6ms
# 3 loop 16.5µs 17.5µs 52962. 464B 15.9 9997 3 188.8ms
答案5
得分: 2
更新
关于问题中的更新,您可以尝试使用 split
+ lapply
:
lapply(split(list2DF(x[-1]), x[[1]]), \(v) with(v, split(value, key)))
或者我们可以使用 aggregate
+ Map
:
with(
aggregate(x[-1], x[1], as.list),
setNames(Map(setNames, value, key), id)
)
这将得到以下结果:
$a
$a$bar
[1] 3
$a$foo
[1] 1
$b
$b$foo
[1] 2
对于先前的问题
您可以尝试以下方法:
list2DF(x[-1]) %>%
split(1:nrow(.)) %>%
setNames(x[[1]]) %>%
lapply(c)
这将得到以下结果:
$a
$a$value
[1] 1
$a$othervalue
[1] 3
$b
$b$value
[1] 2
$b$othervalue
[1] 4
英文:
Update
Regarding the update in the question, you can try split
+ lapply
lapply(split(list2DF(x[-1]), x[[1]]), \(v) with(v, split(value, key)))
or we can use aggregate
+ Map
with(
aggregate(x[-1], x[1], as.list),
setNames(Map(setNames, value, key), id)
)
which gives
$a
$a$bar
[1] 3
$a$foo
[1] 1
$b
$b$foo
[1] 2
For Previous Question
You can try
list2DF(x[-1]) %>%
split(1:nrow(.)) %>%
setNames(x[[1]]) %>%
lapply(c)
which gives
$a
$a$value
[1] 1
$a$othervalue
[1] 3
$b
$b$value
[1] 2
$b$othervalue
[1] 4
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论