【问题标题】:How to make a nested for loop more efficient and use with apply如何使嵌套的 for 循环更有效并与 apply 一起使用
【发布时间】:2016-03-02 22:00:38
【问题描述】:

我正在尝试将功能嵌套的 for 循环转换为与 apply 一起使用。我希望这会使它更快。 (从我读到的应该是这样,尽管这并不总是正确的)主数据框中有大约 150K 行要循环...非常耗时

我在 R 中编写了一个 for 循环来检查 df1 中的 date.time 是否位于 df2 中的两个 date.time 之间,如果 df1 和 df2 中的代码匹配,则将 df2 中的位置粘贴到 df1 中

以下是子集样本数据

df1<-structure(list(date.time = structure(c(1455922438, 1455922445, 
1455922449, 1455922457, 1455922459, 1455922461), class = c("POSIXct", 
"POSIXt"), tzone = ""), code = c(32221, 32222, 32221, 32222, 
32222, 32221)), .Names = c("date.time", "code"), row.names = 50000:50005, class = "data.frame")

df2<-structure(list(Location = 11:12, Code = 32221:32222, t_in = structure(c(1455699600, 
1455699600), class = c("POSIXct", "POSIXt"), tzone = ""), t_out = structure(c(1456401600, 
1456401600), class = c("POSIXct", "POSIXt"), tzone = "")), .Names = c("Location", 
"Code", "t_in", "t_out"), class = "data.frame", row.names = 11:12)

For 循环可以正常工作,但需要很长时间:

for (i in 1:nrow(df1)[1]){
  for (j in 1:nrow(df2)){
    ifelse(df1$code[i] == df2$Code[j]
           & df1$date.time [i] < df2$t_out [j]
           & df1$date.time [i] > df2$t_in [j],
           df1$Location [i] <- df2$Location [j],
           NA)
  }
}

我已经做到了这一点:

ids <- as.numeric(df2$Location)
f <- function(x){
  a <- ids[ (df2$t_in < x) & (x < df2$t_out)  ]
  if (length(a) == 0 ) NA else a
}   

df1$Location <- lapply(df1$date.time, f)

这会返回两个数字,因为 df1 中的 date.time 位于 t_in 和 t_out 之间,因此为什么在粘贴位置时需要每个数据帧中的代码匹配

非常感谢任何指针

【问题讨论】:

  • 如果您可以提供一个稍微好一点的测试用例,其中一些 i,j 组合不满足 hte 时间条件,那将是理想的。无论如何,请报告 system.time 结果。
  • 您可以尝试使用 sqldf 包,看看是否将您的 df 转换为本地数据库,然后对其执行查询有助于您的速度。

标签: r for-loop lapply


【解决方案1】:

data.table 具有重叠范围连接,可以非常快速地完成此操作。您正在寻找的功能是foverlaps。这是一个在使用foverlaps之前稍微清理一下的例子:

require(data.table)

dt1 <- data.table(df1)
dt2 <- data.table(df2)

## need to create a range in dt 1 to find overlaps on
dt1[,start:=date.time]
dt1[,end:=date.time]

## clean up names to match each other
setnames(dt2,c("Location","Code","start","end"))
setnames(dt1,c("code"),c("Code"))

setkey(dt1,Code,start,end)
setkey(dt2,Code,start,end)

## use foverlaps with the additional matching variable Code
out <- foverlaps(dt1,dt2,type="any",
                 by.x=c("Code","start","end"),
                 by.y=c("Code","start","end"))

## more renaming and selection of the same subset of columns
setnames(out,"i.start","date.time")
out <- out[,.(date.time,Code,Location)]

它给出了输出:

> out
             date.time  Code Location
1: 2016-02-19 14:53:58 32221       11
2: 2016-02-19 14:54:09 32221       11
3: 2016-02-19 14:54:21 32221       11
4: 2016-02-19 14:54:05 32222       12
5: 2016-02-19 14:54:17 32222       12
6: 2016-02-19 14:54:19 32222       12

【讨论】:

  • 可能比我的三重 outer 方法快,但我希望 OP 报告两种方法的 system.time-results 与他的双 for-loop 方法。
【解决方案2】:

我尝试构建一个不依赖于forapply 的“无循环”版本。看看它是否更快:

trans <- which( outer(X=df1$code, Y=df2$Code,'==') & 
                outer(df1$date.time , df2$t_in, ">") & 
                outer(df1$date.time, df2$t_out , "<")  , arr.ind=TRUE)
df1$Location [ trans[,1] ] <- df2$Location [ trans[,2] ]
df1
#------
                date.time  code Location
50000 2016-02-19 14:53:58 32221       11
50001 2016-02-19 14:54:05 32222       12
50002 2016-02-19 14:54:09 32221       11
50003 2016-02-19 14:54:17 32222       12
50004 2016-02-19 14:54:19 32222       12
50005 2016-02-19 14:54:21 32221       11

对外部的三个调用将通过j 构建i 矩阵,这些矩阵是TRUE,当满足三个单独的条件时,它们是AND-ed 以给出联合满意的结果,然后是which( . , arr.ind=TRUE)返回一个矩阵,其中i 值在第一列,j 值在第二列,因此可以使用普通的[&lt;- 分配对应的向量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多