R中的循环特定太慢

时间:2013-01-11 17:32:51

标签: r

我必须使用2个数据帧200万条记录和另外200万条记录。我使用for循环来获取彼此的数据,但它太慢了。我已经创建了一个示例来演示我需要做什么。

ratings = data.frame(id = c(1,2,2,3,3),
                     rating = c(1,2,3,4,5),
                     timestamp = c("2006-11-07 15:33:57","2007-04-22 09:09:16","2010-07-16 19:47:45","2010-07-16 19:47:45","2006-10-29 04:49:05"))
stats = data.frame(primeid = c(1,1,1,2),
                   period = c(1,2,3,4),
                   user = c(1,1,2,3), 
                   id = c(1,2,3,2), 
                   timestamp = c("2011-07-01 00:00:00","2011-07-01 00:00:00","2011-07-01 00:00:00","2011-07-01 00:00:00"))

ratings$timestamp = strptime(ratings$timestamp, "%Y-%m-%d %H:%M:%S")
stats$timestamp = strptime(stats$timestamp, "%Y-%m-%d %H:%M:%S")

for (i in(1:nrow(stats)))
{
   cat("Processing ",i," ...\r\n")
   temp = ratings[ratings$id == stats$id[i],]
   stats$idrating[i] = max(temp$rating[temp$timestamp < stats$timestamp[i]])
}

有人可以为我提供替代方案吗?我知道apply可能有用,但我不知道如何翻译for函数。

更新:谢谢你的帮助。我正在提供更多信息。

表统计信息具有primeid,period,user,id的唯一组合。 表评级具有多个具有不同评级和时间戳的id记录。

我想做的是以下内容。对于在统计数据中找到的每个ID,查找评级表(id列)中的所有记录,然后根据从统计数据获得的特定时间戳获得最大评级。

4 个答案:

答案 0 :(得分:6)

我喜欢plyr,以及Hadley Wickham创造的大多数工具,但我发现它可能会非常缓慢,特别是如果我试图分裂ID字段。发生这种情况时,我转向sqldf。我的速度通常是20倍。

首先我需要使用lubridate,因为sqldf类型上的POSIXlt窒息:

library(lubridate)
ratings$timestamp = ymd_hms(ratings$timestamp)
stats$timestamp = ymd_hms(stats$timestamp)

像Vincent一样合并数据帧,并删除违反日期约束的数据框:

tmp <- merge(stats, ratings, by="id")
tmp <- subset(tmp, timestamp.y < timestamp.x )

最后,获取每个ID的最高评级:

library(sqldf)
sqldf("SELECT *, MAX(rating) AS rating FROM tmp GROUP BY id")

答案 1 :(得分:4)

根据id s与数据点的比率,这可能会更好:

r = split(ratings, ratings$id)
stats$idrating = sapply(seq.int(nrow(stats)), function(i) {
  rd = r[[stats$id[i]]]
  if (length(rd))
    max(rd$rating[rd$timestamp < stats$timestamp[i]])
  else NA
})

如果您的ID 不是连续的整数(您可以使用all(names(r) == seq_along(r))进行检查),则在引用as.character()或使用{时,您必须添加r[[ {1}}一旦创建映射,它将花费你一些速度。

显然,你可以在没有分割的情况下做同样的事情,但这通常会更慢,但会占用更少的内存:

match

如果您知道不存在不匹配,也可以删除stats$idrating = sapply(seq.int(nrow(stats)), function(i) { rd = ratings[ratings$id == stats$id[i],] if (nrow(rd)) max(rd$rating[rd$timestamp < stats$timestamp[i]]) else NA })

答案 2 :(得分:3)

虽然我使用了另一种方法来获得相同的结果,但我投了答案

在合并数据集中,我首先删除了早于条件日期的日期,然后运行:

aggregate (rating ~ id+primeid+period+user, data=new_stats, FUN = max)

答案 3 :(得分:1)

从数据结构的角度来看,您似乎想要合并两个表,然后执行split-group-apply方法。

您可以简单地合并两个表(非常类似于SQL中的JOIN语句),而不是循环检查哪一行属于哪一行,然后执行&#39; aaply&#39;方法类型。我建议你下载&#39; plyr&#39;图书馆。

new_stats = merge(stats, ratings, by='id')

library(plyr) 
ddply(new_stats, 
      c('primeid', 'period', 'user'),  
      function(new_stats) 
      c( max(new_stats[as.Date(new_stats$timestamp.x) > as.Date(new_stats$timestamp.y)]$rating )))

如果使用plyr让您感到困惑,请访问本教程:http://www.creatapreneur.com/2013/01/split-group-apply/

相关问题