如何改进这个算法?

时间:2011-04-13 06:33:48

标签: r

R版本2.11.1 Windows 7上的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的所有行动的总和)/(USER_B的推荐时间)

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

4 个答案:

答案 0 :(得分:6)

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

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

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

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 

答案 1 :(得分:5)

除了@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

答案 2 :(得分:4)

@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

答案 3 :(得分:0)

您可以尝试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..,但我最近读到最后一个解决方案更快(如果您没有NA等)。

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