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