我的问题是:当我在工作中调试一些代码时,逐块运行,这时我意识到一个小块花费了不寻常的时间。我杀了它,做了一次小的(但逻辑上相当)的调整,它几乎立刻就跑了。我想知道为什么。下面的代码在R中,但是,我想答案可能不是特定于R,并且可能适用于大多数类似范式或“编译方法”的编程语言?
代码和信息:
使用R版本3.6.1
图书馆下载: dplyr,DataExplorer,胶水,动物园
old_df是5653380 obs的数据帧。91个变量中。
field1是一组具有类“字符”的策略数字。不是唯一的,每一次都会发生很多次。
date_col1和date_col2是类"date“的列。
方法1:
new_df <- old_df %>%
group_by(field1) %>%
mutate(checkfield = date_col1 - date_col2) %>%
filter(checkfield < 0) %>%
filter(row_number() == 1)
old_df$filter <- ifelse(old_df$field1 %in% new_df$field1,1,0)方法2:
new_df <- old_df %>%
group_by(field1) %>%
filter(date_col1 < date_col2) %>%
filter(row_number() == 1)
old_df$filter <- ifelse(old_df$field1 %in% new_df$field1,1, 0)正如您可能看到的,这两个方法的预期输出都是在date_col1 < date_col2的策略号的"filter“列中添加一个标志"1”。我没有写方法1,我写方法2的目的是尽可能少地改变它,同时也使它更快,所以请不要花太多的时间谈论方法1的问题,这些问题与为什么方法1比方法2慢得令人无法忍受无关。请尽管提及这些事情,但我希望关键是为什么方法1花费20、30分钟等。例如,我相信方法1的第一个过滤器调用可能在group_by调用之上。这可能会使速度增加一个不明显的数量。我不太担心这件事。
我的想法:显然方法2可能会更快一些,因为它避免了列"checkfield",但我认为这不是问题,因为我逐行运行方法1,而它似乎是错误的‘过滤器(checkfield< 0)’。在测试中,我定义了两个日期x,y,以及返回"difftime“的检查类(X)。因此,在这个过滤器调用中,我们将"difftime“与一个”数值“进行比较。这可能需要某种类型的类型杂耍来进行比较,在哪里,方法2将日期对象与日期对象进行比较?
让我知道你的想法!我对此很好奇。
发布于 2019-08-20 22:39:31
我相信大部分增加的时间都是因为创建了新的专栏。正如您所看到的,M1和M3有类似的时间。当然,M1和M3之间~2毫秒的差值将根据数据大小乘以
library(tidyverse)
library(microbenchmark)
set.seed(42)
n = 1e5
d = seq.Date(Sys.Date() - 10000, Sys.Date(), 1)
x = sample(d, n, TRUE)
y = sample(d, n, TRUE)
df1 = data.frame(x, y, id = sample(LETTERS, n, TRUE))
microbenchmark(M1 = {
df1 %>%
group_by(id) %>%
mutate(chk = x - y) %>%
filter(chk < 0) %>%
filter(row_number() == 1)
},
M2 = {
df1 %>%
group_by(id) %>%
filter(x < y) %>%
filter(row_number() == 1)
},
M3 = {
df1 %>%
group_by(id) %>%
mutate(chk = x - y) %>%
filter(x < y) %>%
filter(row_number() == 1)
})
#Unit: milliseconds
# expr min lq mean median uq max neval
# M1 13.130673 13.405151 15.088266 14.096772 15.56080 22.636533 100
# M2 5.931192 6.208457 6.564363 6.402879 6.71973 9.354252 100
# M3 11.360640 11.607993 12.449220 12.001383 12.57732 18.260131 100关于将difftime与numeric进行比较,似乎没有太大的区别
library(microbenchmark)
set.seed(42)
n = 1e7
x = sample(d, n, TRUE)
y = sample(d, n, TRUE)
df1 = data.frame(x, y)
df1$difference = df1$x - df1$y
class(df1$difference)
#[1] "difftime"
microbenchmark(date_vs_date = {
df1 %>% filter(x < y)
},
date_vs_numeric ={
df1 %>% filter(difference < 0)
})
#Unit: milliseconds
# expr min lq mean median uq max neval
# date_vs_date 177.1789 222.4112 243.6617 233.7221 244.2765 430.4273 100
# date_vs_numeric 181.6222 217.1121 251.6127 232.7213 249.8218 455.8285 100发布于 2019-08-20 22:40:00
到目前为止,我使用了一个简化的示例和稍微小一点的数据集(只有一百万行和极小的列子集)来进行单独的测试(test_cf用于过滤checkfield变量,test_lt用于筛选日期比较),这两个测试的时间与创建checkfield列的时间大致相同。同时做这两件事(comb,创建和比较)需要花费2.5倍的时间,不知道原因。
也许你可以用它作为一个起点,进行二分法/基准法来找出罪魁祸首。
test elapsed relative
2 comb 5.557 2.860
1 make_cf 1.943 1.000
4 test_cf 2.122 1.092
3 test_lt 2.109 1.085我使用rbenchmark::benchmark()是因为我更喜欢输出格式:microbenchmark::microbenchmark()可能更精确(但如果它能带来很大的不同,我会感到惊讶)。
代码
library(dplyr)
n <- 1e6 ## 5653380 in orig; reduce size for laziness
set.seed(101)
## sample random dates, following
## https://stackoverflow.com/questions/21502332/generating-random-dates
f <- function(n)
sample(seq(as.Date('1999/01/01'), as.Date('2000/01/01'), by="day"),
replace=TRUE,
size=n)
dd <- tibble(
date_col1=f(n),
date_col2=f(n)
## set up checkfield so we can use it without creating it
) %>% mutate(cf=date_col1-date_col2)基准:
library(rbenchmark)
benchmark(
make_cf=dd %>% mutate(checkfield=date_col1-date_col2),
comb=dd %>% mutate(checkfield=date_col1-date_col2) %>% filter(checkfield<0),
test_lt=dd %>% filter(date_col1<date_col2),
test_cf=dd %>% filter(cf<0),
columns=c("test","elapsed","relative")
)https://stackoverflow.com/questions/57582120
复制相似问题