我有以下数据框:
df <- data.frame(type = c("planes", "trains", "automobiles"), t1 = c(4, 5, 6), t2 = c(20, 60, 24), t3 = c(100, 120, 72), t4 = c(800, 360, 144))
df
type t1 t2 t3 t4
1 planes 4 20 100 800
2 trains 5 60 120 360
3 automobiles 6 24 72 144
我现在想编写一个函数,将第3:5列转换为每行运行聚合(例如,t2 = t1 + t2和t3 = t1 + t2 + t3),这样我的新数据框架看起来像这样:
new_df
type t1 t2 t3 t4
1 planes 4 24 124 924
2 trains 5 65 185 545
3 automobiles 6 30 102 246
答案 0 :(得分:2)
您可以使用apply
执行此操作,在每行调用cumsum
后将数据框的相关部分替换为结果。
df[-1] <- t(apply(df[-1], 1, cumsum))
df
# type t1 t2 t3 t4
# 1 planes 4 24 124 924
# 2 trains 5 65 185 545
# 3 automobiles 6 30 102 246
答案 1 :(得分:2)
这里使用Reduce()
:
df[-1] <- Reduce(function(a,b) cbind(a,b+a[,ncol(a)]),c(list(as.matrix(df[2])),df[-1:-2]));
df;
## type t1 t2 t3 t4
## 1 planes 4 24 124 924
## 2 trains 5 65 185 545
## 3 automobiles 6 30 102 246
对于少数列多行数据集来说,这是一个显着的性能优势(这比许多列少数行更常见,至少在我的经验中),因为这个解决方案将有效地迭代列而不是行。这是一个包含10,000行,4列的演示:
set.seed(1); R <- 1e4; df <- data.frame(type=sample(c('planes','trains','automobiles'),R,replace=T), t1=sample(10,R,replace=T), t2=sample(10,R,replace=T), t3=sample(10,R,replace=T), t4=sample(10,R,replace=T) );
bgoldst <- function(df) { df[-1] <- Reduce(function(a,b) cbind(a,b+a[,ncol(a)]),c(list(as.matrix(df[2])),df[-1:-2])); df; };
josilber <- function(df) { df[-1] <- t(apply(df[-1], 1, cumsum)); df; };
M <- 250; system.time({ replicate(M,josilber(df)); });
## user system elapsed
## 11.781 0.016 11.835
M <- 250; system.time({ replicate(M,bgoldst(df)); });
## user system elapsed
## 1.187 0.000 1.191
identical(bgoldst(df),josilber(df));
## [1] TRUE
虽然公平,但这是一个性能测试,其中包含多列少数行数据集,其中@ josilber的apply()
解决方案优于我自己的解决方案,尽管利润率较低。这使用4行和10,000列来镜像我的第一次性能测试(但必须使用较小的M
,实际上是M=3
,因为无论解决方案如何,列似乎都比行要求更高CPU ):
set.seed(1); R <- 4; C <- 1e4; df <- cbind(data.frame(type=sample(c('planes','trains','automobiles'),R,replace=T)),matrix(sample(10,R*C,replace=T),R));
M <- 3; system.time({ replicate(M,josilber(df)); });
## user system elapsed
## 10.188 0.000 10.181
M <- 3; system.time({ replicate(M,bgoldst(df)); });
## user system elapsed
## 13.484 0.000 13.492
identical(bgoldst(df),josilber(df));
## [1] TRUE
我刚注意到的一个小问题,我的解决方案正确处理了只有1个加数列的退化情况,而apply()
解决方案错误地处理了Error in `[<-.data.frame`(`*tmp*`, -1, value = c(4, 5, 6)) : replacement has 1 row, data has 3"
;我相信会发生的事情是apply()
将单列输出展平为向量,然后t()
调用将其转换为1x3矩阵,从而不与赋值的目标对齐,这是一个3x1 data.frame:
df <- data.frame(type = c("planes", "trains", "automobiles"), t1 = c(4, 5, 6));
josilber(df);
## Error in `[<-.data.frame`(`*tmp*`, -1, value = c(4, 5, 6)) :
## replacement has 1 row, data has 3
bgoldst(df);
## type t1
## 1 planes 4
## 2 trains 5
## 3 automobiles 6
答案 2 :(得分:2)
只需添加到@bgoldst更快,因为很多列的少量行情况将是for循环:
brandon <- function(df) {
part <- as.matrix(df[2:ncol(df)])
for(x in 1:nrow(df)) {
df[x,2:ncol(df)] <- cumsum((part[x,]))
}
return(df)
}
set.seed(1); R <- 4; C <- 1e4; df <- cbind(data.frame(type=sample(c('planes','trains','automobiles'),R,replace=T)),matrix(sample(10,R*C,replace=T),R));
M <- 3; system.time({ replicate(M,josilber(df)); });
# user system elapsed
# 10.96 0.00 10.95
M <- 3; system.time({ replicate(M,bgoldst(df)); });
# user system elapsed
# 14.056 0.000 14.040
M <- 3; system.time({ replicate(M,brandon(df)); });
# user system elapsed
# 0.400 0.000 0.401
identical(bgoldst(df),brandon(df))
# [1] TRUE
对于许多行的几列情况并非如此。