如何加速这种条件连接(for-loop很慢)?

时间:2016-07-17 18:00:47

标签: r performance for-loop join

以下是该问题的示例数据,代码和说明:

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. 然后通过仅考虑 distinct (或 unique )三列组合来创建另一个data.frame
  3. 然后重命名df2的列名。
  4. 创建一个空列表res,对于df2中的每一行,执行与df1的连接,然后执行条件过滤,聚合并将结果存储在{的相应索引中{1}}。
  5. res最后的结果是获得最终rbind
  6. 问题是,对于data.frame,需要花费很多时间。我怎样才能加快速度?

1 个答案:

答案 0 :(得分:1)

这是 data.table 解决方案,使用新的非equi 加入功能,目前在development version of data.table, v1.9.7中可用:

请参阅链接以获取安装说明。从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_7 df1$col3 < df2$col3

[4] + [5]对于每个 df2.EACHI)的匹配行,计算所需的表达式(平均值和计数以及其他cols)。详细了解by=.EACHI here

[6]如果df2在指定的条件df1中没有任何匹配的行,则不要返回任何内容。

[7] allow.cartesian是用于保护意外无效连接的参数。阅读它here

[8]手动重命名间隔列(现在,应该很快就会自动处理)。