将参差不齐的数据框收集到键值列中
Gather ragged data frame into key-value columns
我最近发现了如何使用 I
函数创建 ragged data frames,但是很难将它们与 tidyr
、ggplot2
和其他哈德利宇宙。更具体地说,如何将包含命名向量的列收集到键值列中?
假设我创建了一个这样的数据框
make.vector <- function(length.out){
x <- sample(9, length.out)
names(x) <- switch(length.out,
"Alice",
c("Bob", "Charlie"),
c("Dave", "Erin", "Frank"),
c("Gwen", "Harold", "Inez", "James"))
x
}
mydf <- data.frame(Game = gl(3, 3, labels=LETTERS[1:3]),
Set = rep(1:3, 3),
Score = I(lapply(rep(2:4, each=3), make.vector)))
生产
> print(mydf)
Game Set Score
1 A 1 8, 3
2 A 2 2, 8
3 A 3 3, 8
4 B 1 1, 5, 4
5 B 2 2, 3, 5
6 B 3 2, 8, 5
7 C 1 7, 2, 3, 4
8 C 2 1, 6, 3, 7
9 C 3 6, 9, 3, 7
只要结果符合预期长度,就可以使用 dplyr
和 tidyr
直接操作数据框。
mydf %>%
mutate(nPlayers = sapply(Score, length))
mydf %>%
group_by(Game) %>%
summarize(TotalScore = list(Reduce("+", Score)))
但是,我不知道如何为每个原始行创建多行结果。假设我想通过操作 mydf
:
创建以下数据框
Game Set Player Score
1 A 1 Bob 8
2 A 1 Charlie 3
3 A 2 Bob 2
4 A 2 Charlie 8
5 A 3 Bob 3
6 A 3 Charlie 8
7 B 1 Dave 1
8 B 1 Erin 5
9 B 1 Frank 4
10 B 2 Dave 2
...
我知道这样做的唯一工具是 tidyr
包的 gather
函数,但它似乎不能很好地处理非原子数据。
mydf %>%
mutate(Player = lapply(Score, names)) %>%
gather(P = Player, S = Score)
我想我可以拼凑出一个解决方案(就像之前类似的问题 [1][2] 所做的那样),
cbind(
mydf[rep(1:nrow(mydf), sapply(mydf$Score, length)),
c("Game", "Set")],
data.frame(
Player = unlist(lapply(mydf$Score, names)),
Score = unlist(mydf$Score)
)
)
但感觉下周回看代码会很难消化。有没有 "official" 或至少更聪明的方法来做到这一点?否则我会为它做一个通用函数并添加到我的个人库中。
更新
根据下面的 我发现用 dplyr
也可以达到相同的结果。
mydf %>%
group_by(Game, Set) %>%
do(with(., data.frame(Player = names(unlist(Score)),
Score = unlist(Score))))
# Game Set Player Score
# 1 A 1 Bob 8
# 2 A 1 Charlie 6
# 3 A 2 Bob 7
# 4 A 2 Charlie 6
# 5 A 3 Bob 5
# 6 A 3 Charlie 8
# 7 B 1 Dave 1
# 8 B 1 Erin 9
# 9 B 1 Frank 3
# 10 B 2 Dave 8
# .. ... ... ... ...
# Warning message:
# In rbind_all(out[[1]]) : Unequal factor levels: coercing to character
我会尝试使用 data.table
按组取消列表。在第 j
个表达式
中,您可以 运行 每个组仅使用一次,同时使用大括号将其存储在临时变量中(就像您在函数中所做的那样)
library(data.table)
setDT(mydf)[, {
temp <- unlist(Score)
.(Player = names(temp), Score = temp)
}, by = .(Game, Set)]
# Game Set Player Score
# 1: A 1 Bob 2
# 2: A 1 Charlie 9
# 3: A 2 Bob 6
# 4: A 2 Charlie 3
# 5: A 3 Bob 2
# 6: A 3 Charlie 8
# 7: B 1 Dave 1
# 8: B 1 Erin 6
# 9: B 1 Frank 5
# 10: B 2 Dave 3
#...
我最近发现了如何使用 I
函数创建 ragged data frames,但是很难将它们与 tidyr
、ggplot2
和其他哈德利宇宙。更具体地说,如何将包含命名向量的列收集到键值列中?
假设我创建了一个这样的数据框
make.vector <- function(length.out){
x <- sample(9, length.out)
names(x) <- switch(length.out,
"Alice",
c("Bob", "Charlie"),
c("Dave", "Erin", "Frank"),
c("Gwen", "Harold", "Inez", "James"))
x
}
mydf <- data.frame(Game = gl(3, 3, labels=LETTERS[1:3]),
Set = rep(1:3, 3),
Score = I(lapply(rep(2:4, each=3), make.vector)))
生产
> print(mydf)
Game Set Score
1 A 1 8, 3
2 A 2 2, 8
3 A 3 3, 8
4 B 1 1, 5, 4
5 B 2 2, 3, 5
6 B 3 2, 8, 5
7 C 1 7, 2, 3, 4
8 C 2 1, 6, 3, 7
9 C 3 6, 9, 3, 7
只要结果符合预期长度,就可以使用 dplyr
和 tidyr
直接操作数据框。
mydf %>%
mutate(nPlayers = sapply(Score, length))
mydf %>%
group_by(Game) %>%
summarize(TotalScore = list(Reduce("+", Score)))
但是,我不知道如何为每个原始行创建多行结果。假设我想通过操作 mydf
:
Game Set Player Score
1 A 1 Bob 8
2 A 1 Charlie 3
3 A 2 Bob 2
4 A 2 Charlie 8
5 A 3 Bob 3
6 A 3 Charlie 8
7 B 1 Dave 1
8 B 1 Erin 5
9 B 1 Frank 4
10 B 2 Dave 2
...
我知道这样做的唯一工具是 tidyr
包的 gather
函数,但它似乎不能很好地处理非原子数据。
mydf %>%
mutate(Player = lapply(Score, names)) %>%
gather(P = Player, S = Score)
我想我可以拼凑出一个解决方案(就像之前类似的问题 [1][2] 所做的那样),
cbind(
mydf[rep(1:nrow(mydf), sapply(mydf$Score, length)),
c("Game", "Set")],
data.frame(
Player = unlist(lapply(mydf$Score, names)),
Score = unlist(mydf$Score)
)
)
但感觉下周回看代码会很难消化。有没有 "official" 或至少更聪明的方法来做到这一点?否则我会为它做一个通用函数并添加到我的个人库中。
更新
根据下面的 dplyr
也可以达到相同的结果。
mydf %>%
group_by(Game, Set) %>%
do(with(., data.frame(Player = names(unlist(Score)),
Score = unlist(Score))))
# Game Set Player Score
# 1 A 1 Bob 8
# 2 A 1 Charlie 6
# 3 A 2 Bob 7
# 4 A 2 Charlie 6
# 5 A 3 Bob 5
# 6 A 3 Charlie 8
# 7 B 1 Dave 1
# 8 B 1 Erin 9
# 9 B 1 Frank 3
# 10 B 2 Dave 8
# .. ... ... ... ...
# Warning message:
# In rbind_all(out[[1]]) : Unequal factor levels: coercing to character
我会尝试使用 data.table
按组取消列表。在第 j
个表达式
library(data.table)
setDT(mydf)[, {
temp <- unlist(Score)
.(Player = names(temp), Score = temp)
}, by = .(Game, Set)]
# Game Set Player Score
# 1: A 1 Bob 2
# 2: A 1 Charlie 9
# 3: A 2 Bob 6
# 4: A 2 Charlie 3
# 5: A 3 Bob 2
# 6: A 3 Charlie 8
# 7: B 1 Dave 1
# 8: B 1 Erin 6
# 9: B 1 Frank 5
# 10: B 2 Dave 3
#...