【问题标题】:Increasing code execution time-efficiency using data.table and for-loop使用 data.table 和 for-loop 提高代码执行时间效率
【发布时间】:2020-06-03 19:17:40
【问题描述】:

问题:我怎样才能使下面代码中的 for 循环更高效地运行?对于这个玩具示例,它可以在合理的时间内工作。但是,unique_ids 将是一个包含大约 8000 个条目的向量,并且 for 循环会大大降低计算速度。有任何想法吗?非常感谢!

目的: 根据 for 循环中的计算逻辑,将每天的 IID 追溯聚类到 hop 和 top。

初始数据:

   IID      ENTRY     FINISH     TARGET max_finish_target_date
1:      1 2020-02-11 2020-02-19 2020-02-15             2020-02-19
2:      2 2020-02-13 2020-02-17 2020-02-19             2020-02-19

最终(目标)数据:

 IID      Dates    ind_frist
 1:      1 2020-02-10             
 2:      1 2020-02-11 hop
 3:      1 2020-02-12 hop
 4:      1 2020-02-13 hop
 5:      1 2020-02-14 hop
 6:      1 2020-02-15 hop
 7:      1 2020-02-16 top
 8:      1 2020-02-17 top
 9:      1 2020-02-18 top
10:      1 2020-02-19 top
11:      2 2020-02-10             
12:      2 2020-02-11             
13:      2 2020-02-12             
14:      2 2020-02-13 hop
15:      2 2020-02-14 hop
16:      2 2020-02-15 hop
17:      2 2020-02-16 hop
18:      2 2020-02-17 hop
19:      2 2020-02-18             
20:      2 2020-02-19             
21:      3 2020-02-10             
22:      3 2020-02-11             
23:      3 2020-02-12             
24:      3 2020-02-13             
25:      3 2020-02-14             
26:      3 2020-02-15 hop
27:      3 2020-02-16 hop
28:      3 2020-02-17 top
29:      3 2020-02-18 top
30:      3 2020-02-19 top

代码

rm(list = ls())

library(data.table)

# Some sample start data
initial_dt <- data.table(IID = c(1, 2, 3),
                         ENTRY = c("2020-02-11", "2020-02-13", "2020-02-15"),
                         FINISH = c("2020-02-19", "2020-02-17", ""),
                         TARGET = c("2020-02-15", "2020-02-19", "2020-02-16"))

initial_dt[, ":="(ENTRY = ymd(ENTRY),
                  FINISH = ymd(FINISH),
                  TARGET = ymd(TARGET))]

initial_dt[is.na(FINISH), FINISH := as.Date(ymd_hms(Sys.time()), format = "%Y-%m-%d")]


initial_dt[, max_finish_target_date := pmax(FINISH, TARGET)]


# Specify target data shape and output format
unique_ids <- c(1, 2, 3) 

dts <- seq(as.Date("2020-02-10", format = "%Y-%m-%d"), as.Date(ymd_hms(Sys.time()), format = "%Y-%m-%d"), by = "days")

ids <- rep(unique_ids, each = length(dts))
len <- length(unique_ids)

final_dt <- data.table(IID = ids,
                       Dates = rep(dts, times = len))

# Calculation logic
# QUESTION: How can I make this part below run more efficiently and less time costly?
for (d_id in unique_ids){
  final_dt[(IID == d_id) & (Dates %between% c(initial_dt[IID == d_id, ENTRY], initial_dt[IID == d_id, max_finish_target_date])), 
                ind_frist := ifelse((Dates > initial_dt[IID == d_id, TARGET]) & (Dates <= initial_dt[IID == d_id, max_finish_target_date]), 
                                    "hop", 
                                    "top")]
}

【问题讨论】:

  • 您需要为data.table 设置您的唯一密钥。您还需要使用内置的for i j 操作,该操作非常占用内存并且不会复制数据。看看套族for (i in from:to) set(DT, row, column, new value)来源:brooksandrew.github.io/simpleblog/articles/advanced-data-table/…
  • 您的问题中显示的目标数据与您的 for 循环的输出不同。

标签: r performance for-loop data.table


【解决方案1】:

使用-join 的可能替代方案:

final_dt[initial_dt
         , on = .(IID)
         , ind_frist := c("", "top","hop")[1L + (Dates > TARGET & Dates <= max_finish_target_date) +
                                             Dates %between% .(ENTRY, max_finish_target_date)]][]

给出:

    IID      Dates ind_frist
 1:   1 2020-02-10          
 2:   1 2020-02-11       top
 3:   1 2020-02-12       top
 4:   1 2020-02-13       top
 5:   1 2020-02-14       top
 6:   1 2020-02-15       top
 7:   1 2020-02-16       hop
 8:   1 2020-02-17       hop
 9:   1 2020-02-18       hop
10:   1 2020-02-19       hop
11:   2 2020-02-10          
12:   2 2020-02-11          
13:   2 2020-02-12          
14:   2 2020-02-13       top
15:   2 2020-02-14       top
16:   2 2020-02-15       top
17:   2 2020-02-16       top
18:   2 2020-02-17       top
19:   2 2020-02-18       top
20:   2 2020-02-19       top
21:   3 2020-02-10          
22:   3 2020-02-11          
23:   3 2020-02-12          
24:   3 2020-02-13          
25:   3 2020-02-14          
26:   3 2020-02-15       top
27:   3 2020-02-16       top
28:   3 2020-02-17       hop
29:   3 2020-02-18       hop
30:   3 2020-02-19       hop

这与for循环的输出相同。

一些解释:1L + (Dates &gt; TARGET &amp; Dates &lt;= max_finish_target_date) + Dates %between% .(ENTRY, max_finish_target_date)部分创建一个索引向量,长度与final_dt的行数相等;如果你把它放在c("", "top","hop")之后的方括号中,每一个你会得到一个空字符串,每两个你会得到"top",每三个你会得到"hop"

【讨论】:

  • 嗨,谢谢你的帮助。能否请您详细说明一下 ind_frist 语法(我以前没见过这种语法)
  • @rkraft 1L + (Dates &gt; TARGET &amp; Dates &lt;= max_finish_target_date) + Dates %between% .(ENTRY, max_finish_target_date) 部分创建一个与final_dt 的行数相等长度的一、二和三的索引向量;如果你把它放在c("", "top","hop")之后的方括号之间,每一个你会得到一个空字符串,每两个你会得到"top",每三个你会得到"hop"
【解决方案2】:

您的循环不会产生您显示的输出。以下非 equi 连接会产生该输出,但可以根据其他规则轻松调整(例如来自您的 for 循环的规则):

final_dt <- CJ(IID = initial_dt[["IID"]], Dates = dts)
final_dt[initial_dt, ind_frist := "hop", on = .(IID, Dates >= ENTRY, Dates <= FINISH)]
final_dt[initial_dt, ind_frist := "top", on = .(IID, Dates > TARGET, Dates <= FINISH)]

这些连接应该非常快。

结果:

#    IID      Dates ind_frist
# 1:   1 2020-02-10      <NA>
# 2:   1 2020-02-11       hop
# 3:   1 2020-02-12       hop
# 4:   1 2020-02-13       hop
# 5:   1 2020-02-14       hop
# 6:   1 2020-02-15       hop
# 7:   1 2020-02-16       top
# 8:   1 2020-02-17       top
# 9:   1 2020-02-18       top
#10:   1 2020-02-19       top
#11:   2 2020-02-10      <NA>
#12:   2 2020-02-11      <NA>
#13:   2 2020-02-12      <NA>
#14:   2 2020-02-13       hop
#15:   2 2020-02-14       hop
#16:   2 2020-02-15       hop
#17:   2 2020-02-16       hop
#18:   2 2020-02-17       hop
#19:   2 2020-02-18      <NA>
#20:   2 2020-02-19      <NA>
#21:   3 2020-02-10      <NA>
#22:   3 2020-02-11      <NA>
#23:   3 2020-02-12      <NA>
#24:   3 2020-02-13      <NA>
#25:   3 2020-02-14      <NA>
#26:   3 2020-02-15       hop
#27:   3 2020-02-16       hop
#28:   3 2020-02-17       top
#29:   3 2020-02-18       top
#30:   3 2020-02-19       top
#    IID      Dates ind_frist

【讨论】:

    猜你喜欢
    • 2014-06-02
    • 2019-05-01
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多