R 将具有值的多列按包含键的一列展开

R 将具有值的多列按包含键的一列展开,r,tidyr,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

输入数据:

> 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")` 

不起作用

我一直在写我的答案,直到我看到了很好的评论,我用他的话修改了我的答案

我明白了:

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)
df%
选择(c(以“花瓣”开头)、种类))%>%
聚集(键、值、物种)%>%
联合(tmp、物种、键、sep=“.”)%>%
分组依据(tmp)%>%
变异(indx=行数())%>%
价差(tmp,价值)
我不知道
unite()
函数,这是主要区别



Obs:因为我不能发表评论,所以我需要发布一个答案,如果你觉得我在窃取你的答案,我可以删除这个。遇到这个问题,我对“大数据集”的说法很好奇。作为tidyverse的狂热粉丝,我经常在大型电视台工作时进行绩效基准测试。对于这个例子,这就是我想到的,它非常支持使用基本包函数,而不是使用tidyverse调用的管道

出于这些目的,使用拆分和基函数比使用管道快得惊人。有人对此有任何反馈吗??? 复制iris数据集以伪造更大的数据集

iris_1000 <- plyr::ldply(1:1000, function(i){
  iris
})
因此,这将表示一个大小为:

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%>%gather(key,value,-Species)%%>%unite(tmp,Species,key)%%>%groupby(tmp)%%>%mutate(indx=row_number())%%>%spread(tmp,value)
perhaps@David但是您的代码生成长度为
numberOfColumns*numberOfRows
的向量,因此,对于大型数据集,会有问题,我不确定您到底关心什么。速度还是内存使用?如果速度很快,就不要使用tidyverse,试试像
library(data.table)这样的方法;dcast(melt(setDT(df),“物种”),rowid(物种,变量)~物种+变量)
。如果是内存使用情况,那么您可能需要使用一些非分割的方法(例如
lappy
),以便在间隙中运行操作。我不确定OP是否关心性能或内存使用情况。我对后者印象深刻。如果您使用的是大数据集,那么tidyverse的速度很慢也就不足为奇了。这是早已为人所知的事实。tidyverse agenda在性能方面是“简单语法”。如果速度是个问题,我只需要使用
library(data.table);dcast(熔体(setDT(df),“物种”),rowid(物种,变量)~物种+变量)
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
}
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)
}
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