在大型数据集上使用多个sum()优化dplyr summary()
我有一个2200万行的表格,每行包含一组生命体征、一个患者ID和一个时间。我试图获得一个汇总表,其中包含每个生命体征(列)的ID和非空值的数量 我下面的代码执行并给出合理的答案,但需要花费很长时间。我想知道是否有更有效的方法来解决这个问题。有什么优化策略吗 下面的代码将ID的名称从“pcrid”转换为“pcrid”,以使生成的表与我的旧代码兼容。我还对表格进行了一些过滤。这在整个数据集上运行很快,所以这似乎不是慢的部分 下面是它如何在不同大小的数据集(使用head())下执行的:在大型数据集上使用多个sum()优化dplyr summary(),r,optimization,dplyr,R,Optimization,Dplyr,我有一个2200万行的表格,每行包含一组生命体征、一个患者ID和一个时间。我试图获得一个汇总表,其中包含每个生命体征(列)的ID和非空值的数量 我下面的代码执行并给出合理的答案,但需要花费很长时间。我想知道是否有更有效的方法来解决这个问题。有什么优化策略吗 下面的代码将ID的名称从“pcrid”转换为“pcrid”,以使生成的表与我的旧代码兼容。我还对表格进行了一些过滤。这在整个数据集上运行很快,所以这似乎不是慢的部分 下面是它如何在不同大小的数据集(使用head())下执行的: 1000行=
- 1000行=0.2秒
- 10000行=1.7秒
- 100000行=15秒
- 1000000行=2.9分钟
- 22000000行=42分钟
Start%
过滤器((pta==“no”| pta==“unk”)和!is.na(pta))%>%
分组依据(PCRID)%>%
总结(
n_AVPU=总和(!is.na(AVPU)),
n_SBP=总和(!is.na(SBP)),
n_DBP=sum(!is.na(DBP)),
n_HR=sum(!is.na(pulserate)),
n_RR=sum(!is.na(RR)),
n_SpO2=总和(!is.na(SpO2)),
n_EtCO2=总和(!is.na(EtCO2)),
n_CO=sum(!is.na(CO)),
n_BGL=总和(!is.na(葡萄糖)),
n_Temp=sum(!is.na(tempf)),
n_Pain=sum(!is.na(疼痛量表)),
n_GCS=总和(!is.na(GCS)))
Sys.time()-Start
我是数据表的初学者,但我知道当有大量组需要计算时,它可以比dplyr
有显著的性能改进
我还没有弄清楚data.table的语法,它既可以按PCRID分组,也可以跨许多列计算非NAs的计数。为了解决这个问题,我尝试使用dtplyr
,一种基于dplyr
的data.table
前端,并获得了一些实质性的性能改进
使用一些与您的日志大小相似的伪数据(见下图),从您的日志中进行计数需要197秒,但当我加载数据.table
和dtplyr
并重新运行时,需要77秒,减少了61%的时间,输出相同。您的结果可能会有所不同,但如果有进一步的数据,我不会感到惊讶。表效率可能会进一步减少这一时间
library(data.table); library(dtplyr)
vitals_fake_DT <- data.table(vitals_fake)
vitals_fake_DT %>%
arrange(PCRID) %>% # to make output order the same way between methods
group_by(PCRID) %>%
summarise(
n_AVPU = sum(!is.na(avpu)),
n_SBP = sum(!is.na(sbp)),
# etc.
库(data.table);图书馆(dtplyr)
生命体征\u假\u DT%
排列(PCRID)%>%#使方法之间的输出顺序相同
分组依据(PCRID)%>%
总结(
n_AVPU=总和(!is.na(AVPU)),
n_SBP=总和(!is.na(SBP)),
#等等。
包含2000万行和1000万组的假数据:
rows = 20000000
grps = 10000000 # max, somewhat less in practice
set.seed(42)
vitals_fake <- data.frame(
PCRID = sample(1:grps, size = rows, replace = T),
avpu = sample(c(NA, 1:10), size = rows, replace = T),
sbp = sample(c(NA, 1:10), size = rows, replace = T),
dbp = sample(c(NA, 1:10), size = rows, replace = T),
pulserate = sample(c(NA, 1:10), size = rows, replace = T),
rr = sample(c(NA, 1:10), size = rows, replace = T),
spo2 = sample(c(NA, 1:10), size = rows, replace = T),
etco2 = sample(c(NA, 1:10), size = rows, replace = T),
co = sample(c(NA, 1:10), size = rows, replace = T),
glucose = sample(c(NA, 1:10), size = rows, replace = T),
tempf = sample(c(NA, 1:10), size = rows, replace = T),
painscale = sample(c(NA, 1:10), size = rows, replace = T),
gcs = sample(c(NA, 1:10), size = rows, replace = T)
)
行数=20000000
grps=10000000(最大值),实际值略低
种子(42)
vitals_fake答案在很大程度上取决于数据的外观,尤其是每组有多少行
例如,对于100000个组和42行(即总共4200000行),对于data.table
,我得到2秒,而对于dplyr
,我得到84秒。对于只有100个组的相同总行,dt得到0.28秒,dplyr得到0.37秒
我还做了@Jon Springs的例子,每组2行,每组10000000组。我的数据表解决方案是339秒,我在2464秒停止了dplyr版本。也许解决方案的一部分是得到一个更好的处理器,如@Jon:)
编辑:我认为如果有很多组,首先融合/收集数据会更快。@Jon的10000000组示例大约需要60秒。注意:为了让它恢复到广域格式,它又增加了100秒,结束时的速度是严格意义上的数据的两倍
melt(dt, id.vars = 'ID')[!is.na(value), .N, by = .(ID, variable)]
#or to end wide
dcast(melt(dt, id.vars = 'ID')[!is.na(value), .N, by = .(ID, variable)], ID ~ variable)
下面是我使用的函数调用。注意:我使用了summated_all()
,因为我没有能力写出所有这些列
#Assume using all columns except the ID column
#data.table
dt[, lapply(.SD, function(x) sum(!is.na(x))), by = ID]
#dplyr
tib%>%
group_by(ID)%>%
summarize_all(~sum(!is.na(.)))
数据:
n_groups <- 10
n_rows <- 42
n_cols <- 12
NA_prob <- 0.3
library(data.table)
library(dplyr)
set.seed(0)
dt <- data.table(ID = rep(seq_len(n_groups), each = n_rows)
, matrix(sample(x = c(NA_integer_, 0L)
, size = n_rows * n_cols * n_groups
, replace = T
, prob = c(NA_prob, 1 - NA_prob))
, ncol = 12)
)
tib <- as_tibble(dt)
n_组我对此进行了尝试。我认为你可以使用Hadley Wickhams multidplyr,它利用了多核的优势。您使用分区
而不是分组
,并在汇总
后收集
结果
我还通过使用rename_at
更改列名和mutate_at
在汇总数据之前创建1和0的值,使代码更加动态dummy
如果不是NA,则创建1,否则创建0。这段代码似乎运行得很快:
# devtools::install_github("hadley/multidplyr")
library(dplyr)
library(multidplyr)
library(hablar)
vitals_all <- vitals_all.df %>%
rename_at(vars(-PCRID), ~paste0("n_", toupper(.))) %>%
mutate_at(vars(-PCRID), ~dummy_(!is.na(.))) %>%
partition(PCRID) %>%
summarise_all(~sum(.)) %>%
collect()
#devtools::install_github(“hadley/multidplyr”)
图书馆(dplyr)
库(多DPLYR)
图书馆(hablar)
所有生命体征%
重命名_at(vars(-PCRID),~paste0(“n_u”),toupper(.))%>%
在(vars(-PCRID),~dummy_uu(!is.na())%%>处突变
分区(PCRID)%>%
总结所有内容(~sum(%)%%>%
收集
从Jon Spring那里借来的假数据(谢谢!):
行数=20000000
grps=10000000(最大值),实际值略低
种子(42)
vitals_all.df对于许多组的问题,数据。表
可能比dplyr
快得多。例如,类似问题请参见此处:
# devtools::install_github("hadley/multidplyr")
library(dplyr)
library(multidplyr)
library(hablar)
vitals_all <- vitals_all.df %>%
rename_at(vars(-PCRID), ~paste0("n_", toupper(.))) %>%
mutate_at(vars(-PCRID), ~dummy_(!is.na(.))) %>%
partition(PCRID) %>%
summarise_all(~sum(.)) %>%
collect()
rows = 20000000
grps = 10000000 # max, somewhat less in practice
set.seed(42)
vitals_all.df <- data.frame(
PCRID = sample(1:grps, size = rows, replace = T),
avpu = sample(c(NA, 1:10), size = rows, replace = T),
sbp = sample(c(NA, 1:10), size = rows, replace = T),
dbp = sample(c(NA, 1:10), size = rows, replace = T),
pulserate = sample(c(NA, 1:10), size = rows, replace = T),
rr = sample(c(NA, 1:10), size = rows, replace = T),
spo2 = sample(c(NA, 1:10), size = rows, replace = T),
etco2 = sample(c(NA, 1:10), size = rows, replace = T),
co = sample(c(NA, 1:10), size = rows, replace = T),
glucose = sample(c(NA, 1:10), size = rows, replace = T),
tempf = sample(c(NA, 1:10), size = rows, replace = T),
painscale = sample(c(NA, 1:10), size = rows, replace = T),
gcs = sample(c(NA, 1:10), size = rows, replace = T)
)