首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么方法1比方法2快?

为什么方法1比方法2快?
EN

Stack Overflow用户
提问于 2019-08-20 22:10:30
回答 2查看 100关注 0票数 1

我的问题是:当我在工作中调试一些代码时,逐块运行,这时我意识到一个小块花费了不寻常的时间。我杀了它,做了一次小的(但逻辑上相当)的调整,它几乎立刻就跑了。我想知道为什么。下面的代码在R中,但是,我想答案可能不是特定于R,并且可能适用于大多数类似范式或“编译方法”的编程语言?

代码和信息:

使用R版本3.6.1

图书馆下载: dplyr,DataExplorer,胶水,动物园

old_df是5653380 obs的数据帧。91个变量中。

field1是一组具有类“字符”的策略数字。不是唯一的,每一次都会发生很多次。

date_col1和date_col2是类"date“的列。

方法1:

代码语言:javascript
复制
    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:

代码语言:javascript
复制
    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将日期对象与日期对象进行比较?

让我知道你的想法!我对此很好奇。

EN

回答 2

Stack Overflow用户

发布于 2019-08-20 22:39:31

我相信大部分增加的时间都是因为创建了新的专栏。正如您所看到的,M1M3有类似的时间。当然,M1M3之间~2毫秒的差值将根据数据大小乘以

代码语言:javascript
复制
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

关于将difftimenumeric进行比较,似乎没有太大的区别

代码语言:javascript
复制
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
票数 1
EN

Stack Overflow用户

发布于 2019-08-20 22:40:00

到目前为止,我使用了一个简化的示例和稍微小一点的数据集(只有一百万行和极小的列子集)来进行单独的测试(test_cf用于过滤checkfield变量,test_lt用于筛选日期比较),这两个测试的时间与创建checkfield列的时间大致相同。同时做这两件事(comb,创建和比较)需要花费2.5倍的时间,不知道原因。

也许你可以用它作为一个起点,进行二分法/基准法来找出罪魁祸首。

代码语言:javascript
复制
     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()可能更精确(但如果它能带来很大的不同,我会感到惊讶)。

代码

代码语言:javascript
复制
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)

基准:

代码语言:javascript
复制
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")
)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57582120

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档