2013-01-11 154 views
3

我必須使用2個數據幀200萬條記錄和另外200萬條記錄。我使用了for循環來從另一個獲取數據,但它太慢了。我創建了一個示例來演示我需要做什麼。R中的循環速度太慢

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

有人能爲我提供一個替代方案嗎?我知道申請可能有效,但我不知道如何翻譯for函數。

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

表格統計信息具有primeid,句點,用戶,id的獨特組合。 表格評分有多個具有不同評分和時間戳記的ID記錄。

我想要做的是以下幾點。對於在統計中找到的每個ID,要在評級表(id列)中查找所有記錄,然後根據從統計獲得的特定時間戳獲取最大評級。

+1

我是很確定這不需要循環。你應該能夠基本上將你定義的「temp」嵌入你想要使用的「rating」的子集中,然後用一個公式完成整個事情。順便說一下,是否可以在'rating $ id'中找到'stats $ id'的每個元素?但一些'ddply'專家會想出一個更好的方法。編輯:在矢量公式中使用'pmax' –

+1

請添加你想要做什麼的描述。從代碼中很難理解。 – Roland

+1

看看'xts'和'zoo'對於這類時間序列數據來說可能也是一個好主意。 –

回答

1

從數據結構的角度來看,您似乎想要合併兩個表,然後執行拆分組應用方法。

而不是循環檢查哪些行屬於哪一行可以簡單地合併兩個表(很像SQL中的JOIN語句),然後執行'aaply'類型的方法。我建議你下載'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/

+0

'for'背後的代碼是什麼?你認爲它是用R寫的嗎?爲什麼循環在R中很慢? – agstudy

+0

這篇文章中出現錯誤: 屬性(out)< - 屬性(col)中的錯誤: 「名稱」屬性[9]必須與向量[1] – Michael

+0

的長度相同。現在讓我試一試大數據集 – Michael

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[[或使用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 
}) 

您也可以刪除if,如果你知道會有錯配。

3

我投給出了答案,雖然我用另一種方法來獲得同樣的結果

在合併數據集我第一次刪除的是比空調日期的日期,然後運行這個:

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

我不知道這個功能是否存在。這種方法更加優雅,感謝分享! – cantdutchthis

6

我愛plyr,以及Hadley Wickham創建的大部分工具,但我發現它可能會非常緩慢,特別是如果我試圖在ID字段上分割。發生這種情況時,我轉向sqldf。我通常得到20倍的加速。

首先我需要使用lubridate因爲sqldf扼流圈POSIXlt類型:

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

合併的dataframes,文森特一樣,和刪除那些違反日期約束:

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