使用包含键的一个列传播多个值

时间:2017-03-08 13:19:08

标签: r tidyr

输入数据:

> head(iris[c(48:50, 98:100), 3:5])
    Petal.Length Petal.Width    Species
48           1.4         0.2     setosa
49           1.5         0.2     setosa
50           1.4         0.2     setosa
98           4.3         1.3 versicolor
99           3.0         1.1 versicolor
100          4.1         1.3 versicolor

输出数据:

setosa.Petal.Length versicolor.Petal.Length setosa.Petal.Width versicolor.Petal.Width 
1.4                 4.3                     0.2                1.3
1.5                 3.0                     0.2                1.1
1.4                 4.1                     0.2                1.3

使用例如:

spread(
  iris%>%mutate(n=row_number()), 
  key=Species, 
  value=Petal.Length:Petal.Width) 
  #or c("Petal.Length", "Petal.Width")` 

不起作用

2 个答案:

答案 0 :(得分:2)

碰到了这一点,并对“大型数据集”的提及感到好奇。作为Tidyverse所有东西的狂热粉丝,我在不断地使用大型套装时,我是实际的基准性能。对于这个例子,这就是我想出的,这有点令人担忧的是使用基本包函数而不是使用tidyverse调用的管道。

出于这些目的,使用拆分和基础功能比管道快得多。任何人对此有任何反馈???

复制虹膜数据集以伪造更大的数据集。

iris_1000 <- plyr::ldply(1:1000, function(i){
  iris
})

尺寸为150,000行乘5列:

dim(iris_1000)
[1] 150000      5

因此,这将代表一个大小为的数据集:

sprintf("%s MB", object.size(iris_1000) * 0.001^2)
[1] "5.401688 MB"

在函数调用中包装提供的anser以进行测试但是更改以便它适用于所有列,而不仅仅是那些包含'Petal'并且还删除索引列并作为数据框返回的列

tidy_fn <- function(){
  iris_1000 %>% 
    gather(key, value, -Species) %>%
    unite(tmp, Species, key, sep=".") %>%
    group_by(tmp) %>%
    mutate(indx = row_number()) %>%
    spread(tmp, value) %>% 
    select(-indx) %>% data.frame
}

功能主要使用基础包提供的功能并进行输入 数据框作为'df'变量和数据的列名 汇总,或者在我们的案例中分开

baseish_fn <- function(df, col_split){

  df_split <- split(df, df[[col_split]])

  df_loop <- lapply(names(df_split), function(i){
    iter_df <- df_split[[i]][-which(colnames(df_split[[i]]) == col_split)]
    new_names <- sprintf("%s.%s", i, colnames(iter_df))
    colnames(iter_df) <- new_names
    iter_df
  })
  names(df_loop) <- NULL
  as.data.frame(df_loop)
}

现在速度比较令人惊讶......  需要注意的是,baseish函数中的列名将被安排为  匹配tudy函数的返回col排列以供稍后比较  值如此运行一次才能得到

tidy_val_test <- tidy_fn()

microbenchmark::microbenchmark(
  tidyverse = tidy_fn(),
  `base-ish` = baseish_fn(iris_1000, "Species")[colnames(tidy_val_test)],times = 100L
)

Unit: milliseconds                                                       
      expr       min        lq     mean   median        uq      max neval
 tidyverse 378.46753 419.93116 447.8020 438.5375 466.78367 718.9666   100
  base-ish  76.57918  83.21915  92.8109  87.8329  94.58085 342.2290   100

确保他们产生相同的产出:

all(mapply(function(i){
    identical(tidyverse[[i]], `base-ish`[[i]])
},colnames(tidyverse)))

[1] TRUE


head(`base-ish`[1:4,1:5])
  setosa.Sepal.Length setosa.Sepal.Width setosa.Petal.Length setosa.Petal.Width
1                 5.1                3.5                 1.4                0.2
2                 4.9                3.0                 1.4                0.2
3                 4.7                3.2                 1.3                0.2
4                 4.6                3.1                 1.5                0.2
  versicolor.Sepal.Length
1                     7.0
2                     6.4
3                     6.9
4                     5.5

答案 1 :(得分:0)

我正在写我的答案,直到我看到David Arenburg出色的评论,然后我用他的答案调整了答案。

我明白了:

df <- iris %>%
  select(c(starts_with("Petal"), Species)) %>%
  gather(key, value, -Species) %>%
  unite(tmp, Species, key, sep=".") %>%
  group_by(tmp) %>%
  mutate(indx = row_number()) %>%
  spread(tmp, value)

我不知道unite()功能,这是主要区别。

Obs:由于我不能发表评论,我需要发帖作为答案,如果你觉得我在偷你的答案,我可以删除它。