使用 tidyr 扩展两列数据框
Spreading a two column data frame with tidyr
我有一个如下所示的数据框:
a b
1 x 8
2 x 6
3 y 3
4 y 4
5 z 5
6 z 6
我想把它变成这样:
x y z
1 8 3 5
2 6 4 6
但是调用
library(tidyr)
df <- data.frame(
a = c("x", "x", "y", "y", "z", "z"),
b = c(8, 6, 3, 4, 5, 6)
)
df %>% spread(a, b)
returns
x y z
1 8 NA NA
2 6 NA NA
3 NA 3 NA
4 NA 4 NA
5 NA NA 5
6 NA NA 6
我做错了什么?
有点像这样?
df <- data.frame(ind = rep(1:min(table(df$a)), length(unique(df$a))), df)
df %>% spread(a, b) %>% select(-ind)
ind x y z
1 1 8 3 5
2 2 6 4 6
虽然我知道您在 tidyr
之后,但 base
在这种情况下有解决方案:
unstack(df, b~a)
也快了一点点:
Unit: microseconds
expr min lq mean median uq max neval
df %>% spread(a, b) 657.699 679.508 717.7725 690.484 724.9795 1648.381 100
unstack(df, b ~ a) 309.891 335.264 349.4812 341.9635 351.6565 639.738 100
应大众需求,尺寸更大
我没有包含 data.table
解决方案,因为我不确定按引用传递是否会成为 microbenchmark
的问题。
library(microbenchmark)
library(tidyr)
library(magrittr)
nlevels <- 3
#Ensure that all levels have the same number of elements
nrow <- 1e6 - 1e6 %% nlevels
df <- data.frame(a=sample(rep(c("x", "y", "z"), length.out=nrow)),
b=sample.int(9, nrow, replace=TRUE))
microbenchmark(df %>% spread(a, b), unstack(df, b ~ a), data.frame(split(df$b,df$a)), do.call(cbind,split(df$b,df$a)))
即使是 100 万,拆叠也更快。值得注意的是,split
解决方案也非常快。
Unit: milliseconds
expr min lq mean median uq max neval
df %>% spread(a, b) 366.24426 414.46913 450.78504 453.75258 486.1113 542.03722 100
unstack(df, b ~ a) 47.07663 51.17663 61.24411 53.05315 56.1114 102.71562 100
data.frame(split(df$b, df$a)) 19.44173 19.74379 22.28060 20.18726 22.1372 67.53844 100
do.call(cbind, split(df$b, df$a)) 26.99798 27.41594 31.27944 27.93225 31.2565 79.93624 100
您也可以使用 data.table 包中的 dcast
和 rowid
执行此操作:
dat <- dcast(setDT(df), rowid(a) ~ a, value.var = "b")[,a:=NULL]
给出:
> dat
x y z
1: 8 3 5
2: 6 4 6
旧解:
# create a sequence number by group
setDT(df)[, r:=1:.N, by = a]
# reshape to wide format and remove the sequence variable
dat <- dcast(df, r ~ a, value.var = "b")[,r:=NULL]
给出:
> dat
x y z
1: 8 3 5
2: 6 4 6
另一个 base
答案(看起来也很快):
data.frame(split(df$b,df$a))
从 tidyr 1.0.0 开始,您可以使用 pivot_wider()
,并且因为 a
没有唯一值,您需要调用 unchop在上面:
library(tidyr)
df <- data.frame(
a = c("x", "x", "y", "y", "z", "z"),
b = c(8, 6, 3, 4, 5, 6)
)
pivot_wider(df, names_from = "a", values_from = "b", values_fn = list(b = list)) %>%
unchop(everything())
#> # A tibble: 2 x 3
#> x y z
#> <dbl> <dbl> <dbl>
#> 1 8 3 5
#> 2 6 4 6
由 reprex package (v0.3.0)
于 2019-09-14 创建
我有一个如下所示的数据框:
a b
1 x 8
2 x 6
3 y 3
4 y 4
5 z 5
6 z 6
我想把它变成这样:
x y z
1 8 3 5
2 6 4 6
但是调用
library(tidyr)
df <- data.frame(
a = c("x", "x", "y", "y", "z", "z"),
b = c(8, 6, 3, 4, 5, 6)
)
df %>% spread(a, b)
returns
x y z
1 8 NA NA
2 6 NA NA
3 NA 3 NA
4 NA 4 NA
5 NA NA 5
6 NA NA 6
我做错了什么?
有点像这样?
df <- data.frame(ind = rep(1:min(table(df$a)), length(unique(df$a))), df)
df %>% spread(a, b) %>% select(-ind)
ind x y z
1 1 8 3 5
2 2 6 4 6
虽然我知道您在 tidyr
之后,但 base
在这种情况下有解决方案:
unstack(df, b~a)
也快了一点点:
Unit: microseconds
expr min lq mean median uq max neval
df %>% spread(a, b) 657.699 679.508 717.7725 690.484 724.9795 1648.381 100
unstack(df, b ~ a) 309.891 335.264 349.4812 341.9635 351.6565 639.738 100
应大众需求,尺寸更大
我没有包含 data.table
解决方案,因为我不确定按引用传递是否会成为 microbenchmark
的问题。
library(microbenchmark)
library(tidyr)
library(magrittr)
nlevels <- 3
#Ensure that all levels have the same number of elements
nrow <- 1e6 - 1e6 %% nlevels
df <- data.frame(a=sample(rep(c("x", "y", "z"), length.out=nrow)),
b=sample.int(9, nrow, replace=TRUE))
microbenchmark(df %>% spread(a, b), unstack(df, b ~ a), data.frame(split(df$b,df$a)), do.call(cbind,split(df$b,df$a)))
即使是 100 万,拆叠也更快。值得注意的是,split
解决方案也非常快。
Unit: milliseconds
expr min lq mean median uq max neval
df %>% spread(a, b) 366.24426 414.46913 450.78504 453.75258 486.1113 542.03722 100
unstack(df, b ~ a) 47.07663 51.17663 61.24411 53.05315 56.1114 102.71562 100
data.frame(split(df$b, df$a)) 19.44173 19.74379 22.28060 20.18726 22.1372 67.53844 100
do.call(cbind, split(df$b, df$a)) 26.99798 27.41594 31.27944 27.93225 31.2565 79.93624 100
您也可以使用 data.table 包中的 dcast
和 rowid
执行此操作:
dat <- dcast(setDT(df), rowid(a) ~ a, value.var = "b")[,a:=NULL]
给出:
> dat x y z 1: 8 3 5 2: 6 4 6
旧解:
# create a sequence number by group
setDT(df)[, r:=1:.N, by = a]
# reshape to wide format and remove the sequence variable
dat <- dcast(df, r ~ a, value.var = "b")[,r:=NULL]
给出:
> dat x y z 1: 8 3 5 2: 6 4 6
另一个 base
答案(看起来也很快):
data.frame(split(df$b,df$a))
从 tidyr 1.0.0 开始,您可以使用 pivot_wider()
,并且因为 a
没有唯一值,您需要调用 unchop在上面:
library(tidyr)
df <- data.frame(
a = c("x", "x", "y", "y", "z", "z"),
b = c(8, 6, 3, 4, 5, 6)
)
pivot_wider(df, names_from = "a", values_from = "b", values_fn = list(b = list)) %>%
unchop(everything())
#> # A tibble: 2 x 3
#> x y z
#> <dbl> <dbl> <dbl>
#> 1 8 3 5
#> 2 6 4 6
由 reprex package (v0.3.0)
于 2019-09-14 创建