使用tidyr

时间:2015-11-07 16:07:25

标签: r dplyr 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)

返回

   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

我做错了什么?

5 个答案:

答案 0 :(得分:13)

虽然我知道你在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

答案 1 :(得分:8)

不知怎的这样?

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

答案 2 :(得分:6)

您也可以使用包中的dcastrowid执行此操作:

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

答案 3 :(得分:5)

另一个base答案(看起来也很快):

data.frame(split(df$b,df$a))

答案 4 :(得分:0)

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创建