【问题标题】:How to improve this Algorithm?如何改进这个算法?
【发布时间】:2013-03-29 00:20:21
【问题描述】:

Windows 7 上的 R 版本 2.11.1 32 位

我得到如下数据 train.txt:

USER_A USER_B ACTION
1        7      0
1        8      1
2        6      2
2        7      1
3        8      2

我按照以下算法处理数据:

train_data=read.table("train.txt",header=T)
result=matrix(0,length(unique(train_data$USER_B)),2)
result[,1]=unique(train_data$USER_B)
for(i in 1:dim(result)[1])
{
    temp=train_data[train_data$USER_B%in%result[i,1],]
    result[i,2]=sum(temp[,3])/dim(temp)[1]
}

结果是train_data中每个USER_B的分数。分数定义为:

USER_B的得分=(USER_B的所有ACTION之和)/(USER_B的推荐次数)

但是train_data非常大,我可能需要三天才能完成这个程序,所以我来这里寻求帮助,这个算法可以改进吗?

【问题讨论】:

  • 我的猜测是,您应该能够通过矢量化代码来完全避免 for 循环。
  • 一些关于您的代码的指针。行尾不需要; - R 是 not C! ;-) 你不需要初始化i for() 调用会为你做这件事,同样也不需要在循环结束时增加i。编写for(i in seq_len(nrow(result))) 也比手动生成序列更好,尤其是在生产代码中。
  • 顺便问一下,你的数据集有多大 - 试试nrow(train_data)。如果它超过可用 RAM 的一半,请小心。如果发生这种情况,请查看 bigmemory 及其相关函数/包:bigsplitmwhichbigtabulate

标签: r


【解决方案1】:

运行您的示例,您想要的结果是计算每个唯一 USER_B 的平均 ACTION:

     [,1] [,2]
[1,]    7  0.5
[2,]    8  1.0
[3,]    6  2.0

您可以使用plyr 包中的ddply() 函数通过一行代码完成此操作

library(plyr)
ddply(train_data[, -1], .(USER_B), numcolwise(mean))

  USER_B ACTION
1      6    2.0
2      7    0.5
3      8    1.0

或者,基础 R 中的函数 tapply 也是如此:

tapply(train_data$ACTION, train_data$USER_B, mean)

根据表的大小,您可以将执行时间缩短 20 倍或更高。这是具有一百万个条目的 data.frame 的 system.time 测试。你的算法需要 116 秒,ddply() 需要 5.4 秒,tapply 需要 1.2 秒:

train_data <- data.frame(
        USER_A = 1:1e6,
        USER_B = sample(1:1e3, size=1e6, replace=TRUE),
        ACTION = sample (1:100, size=1e6, replace=TRUE))

yourfunction <- function(){
    result <- matrix(0,length(unique(train_data$USER_B)),2)
    result[,1] <- unique(train_data$USER_B);
    for(i in 1:dim(result)[1]){     
        temp=train_data[train_data$USER_B%in%result[i,1],]
        result[i,2]=sum(temp[,3])/dim(temp)[1]
    }
    result
}

system.time(XX <- yourfunction())
   user  system elapsed 
 116.29   14.04  134.33 

system.time(YY <- ddply(train_data[, -1], .(USER_B), numcolwise(mean)))
   user  system elapsed 
   5.43    1.60    7.19 

system.time(ZZ <- tapply(train_data$ACTION, train_data$USER_B, mean))
   user  system elapsed 
   1.17    0.06    1.25 

【讨论】:

  • system.time(VV &lt;- rowsum(train_data$ACTION, train_data$USER_B)/rowsum(rep(1L,nrow(train_data)), train_data$USER_B)) 怎么样?
【解决方案2】:

除了@Andrie 提供的方法之外,split() 然后lapply() 方法更快:

> system.time(ZZ <- tapply(train_data$ACTION, train_data$USER_B, mean))
   user  system elapsed 
  1.025   0.011   1.062 
> system.time(WW <- unlist(lapply(split(train_data$ACTION, 
+                                       f = train_data$USER_B), 
+                          mean)))
   user  system elapsed 
  0.465   0.007   0.483

sapply() 也同样快速解决这个问题:

> system.time(SS <- sapply(split(train_data$ACTION, f = train_data$USER_B), 
+                          mean))
   user  system elapsed 
  0.469   0.001   0.474

【讨论】:

  • 另外,也许值得注意的是,如果数据真的很大,可以通过将上面的 mean 替换为 function(x) sum(x) / length(x) 来实现一些加速。与答案中显示的解决方案相比,我得到了大约 0.04 秒的加速,但如果循环真的需要 3 天(!?),那么提高一点效率可能有用吗?
【解决方案3】:

@gavin 已经展示了结合使用splitlapply 时的高性能。

data.table 包的性能进一步显着提升了约 75%

library(data.table)
system.time({
      VV <- as.data.table(train_data)[, list(ACTION=mean(ACTION)), by=USER_B]
    })

user  system elapsed 
0.15    0.02    0.17 

system.time(WW <- unlist(lapply(split(train_data$ACTION, f = train_data$USER_B),mean)))

user  system elapsed 
0.61    0.02    0.63 

all(WW==VV$ACTION)
[1] TRUE

data.table 包可在CRAN 获得,并在r-forge 上有网站

【讨论】:

  • 请参阅data.table wiki 以进一步改进该时间;例如,使用.Internal(mean)。此外,0.17 也是 as.data.table() 的计时,如果它首先是 data.table,则不需要。
【解决方案4】:

你可以试试tapply:

train_data <- read.table("train.txt",header=T);
result <- tapply(train_data$ACTION,train_data$USER_B,function(x) sum(x)/length(x)); 

您可以使用mean 而不是function..,但我最近读到最后一个解决方案更快(如果您没有任何NAs 等)。

我没有测试过,但我相信这应该会更快。如果您想要更快的解决方案,请查看 Rcppinline 软件包...

【讨论】:

  • 即使你使用mean,你也可以提供额外的参数(比如na.rm = TRUE使用...
猜你喜欢
  • 2014-07-03
  • 1970-01-01
  • 2016-01-07
  • 1970-01-01
  • 2019-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多