2016-07-17 18 views
-2

這裏的問題的例子數據,代碼和解釋:如何加快這個條件連接(for-loop很慢)?

require(data.table) 
require(dplyr) 
df1 <- read.table(text= " 
     col1  col2 col3 col4 col5 
     123   121 16519 1  4 
     123   121 16519 2  5 
     123   121 16518 3  5 
     123   121 16517 4  6 
     123   121 16512 5  7 
     123   121 16554 6  8 
     124   333 16554 7  9 
     124   333 16552 8  5 
     124   333 16549 1  1 
     124   333 16495 2  2 
     124   555 16573 4  4 
     125   555 16573 5  3 
     125   555 16569 6  5 
     125   555 16567 7  6 
     125   555 16568 8  7 
", header=TRUE, na.strings=NA, stringsAsFactors=FALSE) 

df2 <- distinct(df1[c("col1","col2","col3")]) 
setnames(df2, old=c("col1","col2","col3"), new=c("col11","col22","col33")) 

res <- vector("list", nrow(df2)) 
for(i in 1:nrow(df2)) { 
    one_row <- df2[i,] 
    df <- merge(select(one_row, col11, col22, col33), 
      select(df1,col1,col2,col3,col4,col5),by=NULL)%>% 
      filter((col3 >= (col33-(7))) & (col3 < col33)) 

    res[[i]] = df%>% 
       group_by(col11, col22,col33)%>% 
       summarise(Averagecol4=mean(col4,na.rm=TRUE), Count=n()) 
} 

as.data.frame(do.call("rbind", res)) 
# col11 col22 col33 Averagecol4 Count 
# 1 123 121 16519   4.0  3 
# 2 123 121 16518   4.5  2 
# 3 123 121 16517   5.0  1 
# 4 123 121 16554   4.5  2 
# 5 124 333 16554   4.5  2 
# 6 124 333 16552   1.0  1 
# 7 124 555 16573   7.0  3 
# 8 125 555 16573   7.0  3 
# 9 125 555 16569   7.5  2 
# 10 125 555 16568   7.0  1 
  1. 的代碼創建一個data.frame
  2. 然後通過考慮三列
  3. 的僅不同(或獨特)組合然後重命名DF2的列名創建另一個data.frame從它。
  4. 創建一個空列表res,並且對於df2中的每一行,執行與df1的加入,然後執行條件過濾,將結果聚合並存儲在對應索引res中。
  5. rbind結果最終得到data.frame

問題是,對於nrow(df1) = ~225,000,這需要很多時間。我如何加快速度?

+0

您的代碼錯誤現在出來。 'setnames'來自哪裏?它是不同版本的'setNames'嗎? – alistaire

+0

@alistaire感謝您指出。它的固定! – KGarg

+0

現在仍在出錯,因爲'res'沒有結束爲data.frame。更好的問題:你想要的輸出是什麼? – alistaire

回答

1

下面是使用新的非等距加入的功能,目前在development version of data.table, v1.9.7可用data.table解決方案:

查看鏈接以安裝說明。從df1開始(這是一個data.frame),這裏就是我會繼續:

require(data.table) # v1.9.7+ 
df2 = setDT(df1)[, .N, by = col1:col3][, col3_minus_7 := col3 - 7] ## (1) 
ans = df1[df2,              ## (2) 
     on = .(col3 >= col3_minus_7, col3 < col3),     ## (3) 
     .(col1 = i.col1, col2 = i.col2, 
      mean = mean(col4, na.rm=TRUE), count = .N),   ## (4) 
     by = .EACHI,            ## (5) 
     nomatch = 0L,            ## (6) 
     allow.cartesian = TRUE]         ## (7) 
setnames(ans, 1:2, c("col3_minus_7", "col3"))      ## (8) 
#  col3_minus_7 col3 col1 col2 mean count 
# 1:  16512 16519 123 121 4.0  3 
# 2:  16511 16518 123 121 4.5  2 
# 3:  16510 16517 123 121 5.0  1 
# 4:  16547 16554 123 121 4.5  2 
# 5:  16547 16554 124 333 4.5  2 
# 6:  16545 16552 124 333 1.0  1 
# 7:  16566 16573 124 555 7.0  3 
# 8:  16566 16573 125 555 7.0  3 
# 9:  16562 16569 125 555 7.5  2 
# 10:  16561 16568 125 555 7.0  1 

[1]獲取唯一行(通過生成計數,而間接通過col1, col2, col3分組 - 只是另一種方式),並添加新列,col3_minus_7,我們稍後需要加入條件。

[2] df1[df2, - 對於df2的每一行,查找df1中匹配的行索引。

[3]基於所述條件:on = .(col3 >= col3_minus_7, col3 < col3),即df1$col3 >= df2$col3_minus_7df1$col3 < df2$col3

[4] + [5]對於匹配的行爲每個df2.EACHI),計算所需的表達(平均值和連同其他COLS計數)。詳細閱讀by=.EACHIhere

[6]當df2df1中沒有任何匹配的行時,不返回任何內容。

[7] allow.cartesian是用來保護意外無效連接的參數。閱讀關於它here

[8]手動重命名間隔列(現在應該儘快自動處理)。

+0

感謝您的解決方案。但我在'[.data.table'(setDT(df1),.N,by = col1:col3)中收到以下錯誤錯誤: 'by'或'keyby'列表中的項目是長度(16397 )。每個必須與x中的行或i(15)返回的行數相同。 此外:警告信息: 1:在COL1:COL3: 數值表達式具有15個元素:僅用於第一 2:在COL1:COL3: 數值表達式具有15個元素:僅用於第一 – KGarg

+0

我曾提到這需要*開發版本*,並提供了一個鏈接,顯示如何安裝它。 – Arun

+0

我正在關注這個獲得v1.9.7,但仍然獲得v1.9.6#安裝開發版data.table install.packages(「data.table」,type =「source」, repos =「http:// Rdatatable.github.io/data.table「) #恢復到CRAN版本 remove.packages(」data.table「)#首先刪除當前版本 install.packages(」data.table「)#然後重新安裝CRAN版本 – KGarg