使用 data.table 加速 j 中顺序任务的 for 循环
Speed up for-loop with sequential tasks in j using data.table
我遇到了一个大型数据集的挑战,它有几百万行和几百列。我正在使用 data.table 格式。
我的(长)代码执行得很好,除了在数据集中的所有日期期间针对特定个人的处方的代码部分。
我想为每个日期的每个类别的药物创建一个单行“记忆”,以便稍后在代码中使用,并且已经使用 for 循环、按引用赋值和 toString 命令实现了这一点 - 但是这非常非常慢。
我已经看了好几个小时,并试图为这个问题做一个提炼的例子 - 欢迎任何建议。
我怀疑有一种更快的方法可以将几行按组合并为一行,即更快的 toString,可以解决问题,但我想不出更聪明的方法来做到这一点。欢迎提出任何建议。
这是代码(数据集故意很大,以便在几秒钟内重现速度问题),给我带来问题的循环是最后一段代码:
library(data.table)
##This is one long piece of code generating the dataset - apologies for the complexity, did what I could (within my abilities) to simplify:
set.seed(2532)
healthData <- data.table(id = sample(1:10000 , 10000))
healthData <- healthData[ , list(id = id ,
date = seq(as.Date("2000-01-01") ,
as.Date("2001-01-01") ,
by = "day")) ,
by = 1:nrow(healthData)]
healthData[ , nrow := NULL]
prescriptionRegistry <- data.table(id = sample(1:10000 , 1000 , replace = TRUE) ,
category = sample(c("paracetamol" , "oxycodon" , "seroquel") , 1000 , replace = TRUE) ,
dose = sample(c(0.5 , 1 , 2) , 1000 , replace = TRUE) ,
endDate = sample(as.Date(as.Date("2000-02-01"):as.Date("2000-12-31") ,
"1970-01-01") ,
1000 ,
replace = TRUE))
prescriptionRegistry <- prescriptionRegistry[ , list(id = id ,
category = category ,
dose = dose ,
endDate = endDate ,
date = seq(as.Date("2000-01-01") ,
endDate , by = "day")) ,
by = 1:nrow(prescriptionRegistry)]
prescriptionRegistry[ , nrow := NULL]
prescriptionRegistry[category == "seroquel" , c("seroquelDose" , "seroquelEndDate") :=
list(dose , endDate)]
prescriptionRegistry[category == "paracetamol" , c("paracetamolDose" , "paracetamolEndDate") :=
list(dose , endDate)]
prescriptionRegistry[category == "oxycodon" , c("oxycodonDose" , "oxycodonEndDate") :=
list(dose , endDate)]
healthData <- merge(healthData , prescriptionRegistry , by.x = c("id" , "date") , by.y = c("id" , "date") , all.x = TRUE , allow.cartesian = TRUE)
##The purpose of this is to reduce to the data that gives me problems - that is when an individual has several prescriptions a day for the same drug:
setorder(healthData , id , date)
healthData[ , index := 1:.N , by = c("id" , "date")]
index <- healthData[index == 2 , .(id)]
index <- unique(index)
setkey(healthData , id)
setkey(index , id)
healthData <- healthData[index]
rm(index)
##End of code generating dataset
##This is the loop that is very slow on large datasets - suggestions are most welcome.
categories <- c("paracetamol" , "oxycodon" , "seroquel")
for (i in categories) {
healthData[ ,
c(paste0(i , "DoseTotal") ,
paste0(i , "DoseText") ,
paste0(i , "EndDateText")) := list(
sum(get(paste0(i , "Dose")) , na.rm = TRUE) ,
toString(get(paste0(i , "Dose"))) ,
toString(get(paste0(i , "EndDate")))) ,
by = c("id" , "date")]
我真正的问题是在 data.table 1.12.2 和 Windows 服务器 2012 R2 上的 R 3.61 服务器上,但在我的 Lubuntu 20.04 笔记本电脑上似乎也很慢, R 4.1.2 和 data.table 4.14.2。为了量化,服务器上循环的每次迭代都需要 2-3 小时,使用 30 个处理器线程并访问 1 TB RAM。
感谢您的宝贵时间!
如果您正在寻找更快的 toString
,您可以改用列表列。在我的计算机上,您的示例从 2.3 秒变为 0.6 秒。
for (i in categories) {
healthData[ ,
c(paste0(i , "DoseTotal") ,
paste0(i , "DoseText") ,
paste0(i , "EndDateText")) := list(
sum(get(paste0(i , "Dose")) , na.rm = TRUE) ,
list(get(paste0(i , "Dose"))) ,
list(get(paste0(i , "EndDate")))) ,
by = c("id" , "date")]
}
我遇到了一个大型数据集的挑战,它有几百万行和几百列。我正在使用 data.table 格式。
我的(长)代码执行得很好,除了在数据集中的所有日期期间针对特定个人的处方的代码部分。
我想为每个日期的每个类别的药物创建一个单行“记忆”,以便稍后在代码中使用,并且已经使用 for 循环、按引用赋值和 toString 命令实现了这一点 - 但是这非常非常慢。
我已经看了好几个小时,并试图为这个问题做一个提炼的例子 - 欢迎任何建议。
我怀疑有一种更快的方法可以将几行按组合并为一行,即更快的 toString,可以解决问题,但我想不出更聪明的方法来做到这一点。欢迎提出任何建议。
这是代码(数据集故意很大,以便在几秒钟内重现速度问题),给我带来问题的循环是最后一段代码:
library(data.table)
##This is one long piece of code generating the dataset - apologies for the complexity, did what I could (within my abilities) to simplify:
set.seed(2532)
healthData <- data.table(id = sample(1:10000 , 10000))
healthData <- healthData[ , list(id = id ,
date = seq(as.Date("2000-01-01") ,
as.Date("2001-01-01") ,
by = "day")) ,
by = 1:nrow(healthData)]
healthData[ , nrow := NULL]
prescriptionRegistry <- data.table(id = sample(1:10000 , 1000 , replace = TRUE) ,
category = sample(c("paracetamol" , "oxycodon" , "seroquel") , 1000 , replace = TRUE) ,
dose = sample(c(0.5 , 1 , 2) , 1000 , replace = TRUE) ,
endDate = sample(as.Date(as.Date("2000-02-01"):as.Date("2000-12-31") ,
"1970-01-01") ,
1000 ,
replace = TRUE))
prescriptionRegistry <- prescriptionRegistry[ , list(id = id ,
category = category ,
dose = dose ,
endDate = endDate ,
date = seq(as.Date("2000-01-01") ,
endDate , by = "day")) ,
by = 1:nrow(prescriptionRegistry)]
prescriptionRegistry[ , nrow := NULL]
prescriptionRegistry[category == "seroquel" , c("seroquelDose" , "seroquelEndDate") :=
list(dose , endDate)]
prescriptionRegistry[category == "paracetamol" , c("paracetamolDose" , "paracetamolEndDate") :=
list(dose , endDate)]
prescriptionRegistry[category == "oxycodon" , c("oxycodonDose" , "oxycodonEndDate") :=
list(dose , endDate)]
healthData <- merge(healthData , prescriptionRegistry , by.x = c("id" , "date") , by.y = c("id" , "date") , all.x = TRUE , allow.cartesian = TRUE)
##The purpose of this is to reduce to the data that gives me problems - that is when an individual has several prescriptions a day for the same drug:
setorder(healthData , id , date)
healthData[ , index := 1:.N , by = c("id" , "date")]
index <- healthData[index == 2 , .(id)]
index <- unique(index)
setkey(healthData , id)
setkey(index , id)
healthData <- healthData[index]
rm(index)
##End of code generating dataset
##This is the loop that is very slow on large datasets - suggestions are most welcome.
categories <- c("paracetamol" , "oxycodon" , "seroquel")
for (i in categories) {
healthData[ ,
c(paste0(i , "DoseTotal") ,
paste0(i , "DoseText") ,
paste0(i , "EndDateText")) := list(
sum(get(paste0(i , "Dose")) , na.rm = TRUE) ,
toString(get(paste0(i , "Dose"))) ,
toString(get(paste0(i , "EndDate")))) ,
by = c("id" , "date")]
我真正的问题是在 data.table 1.12.2 和 Windows 服务器 2012 R2 上的 R 3.61 服务器上,但在我的 Lubuntu 20.04 笔记本电脑上似乎也很慢, R 4.1.2 和 data.table 4.14.2。为了量化,服务器上循环的每次迭代都需要 2-3 小时,使用 30 个处理器线程并访问 1 TB RAM。
感谢您的宝贵时间!
如果您正在寻找更快的 toString
,您可以改用列表列。在我的计算机上,您的示例从 2.3 秒变为 0.6 秒。
for (i in categories) {
healthData[ ,
c(paste0(i , "DoseTotal") ,
paste0(i , "DoseText") ,
paste0(i , "EndDateText")) := list(
sum(get(paste0(i , "Dose")) , na.rm = TRUE) ,
list(get(paste0(i , "Dose"))) ,
list(get(paste0(i , "EndDate")))) ,
by = c("id" , "date")]
}