在R

时间:2015-05-28 02:18:02

标签: r

我有以下数据框:

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

3 个答案:

答案 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 

对于许多行的几列情况并非如此。