3

我必须使用 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 函数。

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

表 stats 具有 primeid、period、user、id 的独特组合。表 rating 有多个不同等级和时间戳的 id 记录。

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

4

4 回答 4

6

我喜欢plyr, 和 Hadley Wickham 创建的大多数工具,但我发现它可能会非常缓慢,尤其是当我试图在 ID 字段上进行拆分时。发生这种情况时,我转向sqldf. 我通常会得到 20 倍的加速。

首先我需要使用lubridate因为类型sqldf阻塞POSIXlt

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

像文森特一样合并数据框,并删除违反日期约束的数据框:

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")
于 2013-01-11T22:09:14.137 回答
4

Depending on the ratio of ids to data points this may work better:

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
})

If your IDs are not contiguous integers (you can check that with all(names(r) == seq_along(r))) you'll have to add as.character() when referencing r[[ or use match once to create the mapping and it will cost you some speed.

Obviously, you can do the same without the split, but that's typically slower yet will use less memory:

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
})

You can also drop the if if you know there will be no mismatches.

于 2013-01-11T19:37:02.083 回答
3

尽管我使用了另一种方法来获得相同的结果,但我对提供的答案进行了投票

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

aggregate (rating ~ id+primeid+period+user, data=new_stats, FUN = max)
于 2013-01-11T20:20:14.513 回答
1

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

您可以简单地合并两个表(很像 SQL 中的 JOIN 语句),然后执行“aaply”类型的方法,而不是使用 for 循环来检查哪一行属于哪一行。我建议您下载“plyr”库。

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/ 。

于 2013-01-11T18:06:52.830 回答