gpt4 book ai didi

r - 使用 mutate 创建新变量时,Dplyr 代码比预期慢

转载 作者:行者123 更新时间:2023-12-01 23:29:11 25 4
gpt4 key购买 nike

我正在使用 dplyr在我的数据框上创建三个新变量。数据框为 84,253 obs。 164 个变量。下面是我的代码。

# ptm <- proc.time()
D04_Base2 <- D04_Base %>%
mutate(
birthyr = year(as.Date(BIRTHDT,"%m/%d/%Y")),
age = (snapshotDt - as.Date(BIRTHDT,"%m/%d/%Y")) / 365.25,
age = ifelse(age > 100, NA, age)
)
# proc.time() - ptm
user system elapsed
12.34 0.03 12.42

但是,我想知道我的代码是否存在明显问题,因为它运行的时间比我预期的要长得多,或者这是别的什么。如上所示,代码完成大约需要 12 秒。

最佳答案

是的,您的代码中存在一些效率低下的问题:

  • 您转换 BIRTHDT列至 Date两次。 (这是迄今为止最大的问题。)
  • base::as.Date不是 super 快
  • 您可以使用 dplyr::if_else而不是 base::ifelse为了一点点性能增益。

  • 让我们做一些测试:
    library(microbenchmark)
    library(dplyr)
    library(lubridate)

    mbm = microbenchmark::microbenchmark

    # generate big-ish sample data
    n = 1e5
    dates = seq.Date(from = Sys.Date(), length.out = n, by = "day")
    raw_dates = format(dates, "%m/%d/%Y")
    df = data.frame(x = 1:n)

    日期转换
    mbm(
    mdy = mdy(raw_dates),
    base = as.Date(raw_dates, format = "%m/%d/%Y")
    )
    # Unit: milliseconds
    # expr min lq mean median uq max neval cld
    # mdy 21.39190 27.97036 37.35768 29.50610 31.44242 197.2258 100 a
    # base 86.75255 92.30122 99.34004 96.78687 99.90462 262.6260 100 b

    看起来像 lubridate::mdyas.Date 快 2-3 倍在这个特定的日期转换。

    提取年份
    mbm(
    year = year(dates),
    format = format(dates, "%Y")
    )
    # Unit: milliseconds
    # expr min lq mean median uq max neval cld
    # year 29.10152 31.71873 44.84572 33.48525 40.17116 478.8377 100 a
    # format 77.16788 81.14211 96.42225 83.54550 88.11994 242.7808 100 b

    同样, lubridate::year (您似乎已经在使用)比 base::format 快大约 2 倍用于提取年份。

    添加列:
    mbm(
    base_dollar = {dd = df; dd$y = 1},
    base_bracket = {dd = df; dd[["y"]] = 1},
    mutate = {dd = mutate(df, y = 1)},
    mutate_pipe = {dd = df %>% mutate(y = 1)},
    times = 100L
    )
    # Unit: microseconds
    # expr min lq mean median uq max neval cld
    # base_dollar 114.834 129.1715 372.8024 146.2275 408.4255 3315.964 100 a
    # base_bracket 118.585 139.6550 332.1661 156.3530 255.2860 3126.967 100 a
    # mutate 420.515 466.8320 673.9109 554.4960 745.7175 2821.070 100 b
    # mutate_pipe 522.402 600.6325 852.2037 715.1110 906.4700 3319.950 100 c

    在这里,我们看到基地做得很好。但也请注意,这些时间在 微秒 而上述日期内容的时间是 毫秒 .无论您使用 basedplyr要添加一列,大约需要 1% 的时间用于进行日期转换。

    如果别的
    x = rnorm(1e5)
    mbm(
    base_na = ifelse(x > 0, NA, x),
    base_na_real = ifelse(x > 0, NA_real_, x),
    base_replace = replace(x, x > 0, NA_real_),
    dplyr = if_else(x > 0, NA_real_, x),
    units = "ms"
    )
    # Unit: milliseconds
    # expr min lq mean median uq max neval cld
    # base_na 9.399593 13.399255 18.502441 14.734466 15.998573 138.33834 100 bc
    # base_na_real 8.785988 12.638971 22.885304 14.075802 16.980263 132.18165 100 c
    # base_replace 0.748265 1.136756 2.292686 1.384161 1.802833 9.05869 100 a
    # dplyr 5.141753 6.875031 14.157227 10.095069 11.561044 124.99218 100 b

    这里的时间仍然以毫秒为单位,但是 ifelse之间的差异和 dplyr::if_else不是那么极端。 dplyr::if_else要求返回向量是相同的类型,所以我们必须指定 NA_real_让它与数字输出一起工作。在弗兰克的建议下,我加入了 base::replaceNA_real_也是如此,而且速度快了大约 10 倍。我认为这里的教训是“使用最简单的有效功能”。

    总之, dplyrbase 慢在添加一列时,但与正在发生的其他所有事情相比,两者都 super 快。因此,您使用哪种列添加方法并不重要。您可以通过不重复计算和使用更快版本的更大操作来加速您的代码。使用我们学到的知识,您的代码的更高效版本将是:
    library(dplyr)
    library(lubridate)
    D04_Base2 <- D04_Base %>%
    mutate(
    birthdate = mdy(BIRTHDT),
    birthyr = year(birthdate),
    age = (snapshotDt - birthdate) / 365.25,
    age = replace(age > 100, NA_real_)
    )

    我们可以将 1e5 行的速度增益估计为大约 180 毫秒,如下所示。
  • 170 毫秒(单个 lubridate::mdy 以 30 毫秒而不是两次 as.Date 调用,每次 100 毫秒)
  • 10 毫秒(replace 而不是 ifelse)

  • 添加列基准表明我们可以通过不使用管道节省大约 0.1 毫秒。由于我们要添加多个列,因此使用 dplyr 可能更有效。而不是用 $<- 单独添加它们,但是对于单个列,我们可以通过不使用 dplyr 节省大约 0.5 毫秒.由于我们已经加快了 180 毫秒,因此不使用 mutate 可能会增加一毫秒的时间。是舍入误差,而不是效率提升。

    在这种情况下,您正在做的最复杂的事情是 Date转换,但如果您进行更多处理,即使这也可能不是您的瓶颈。要优化您的代码,您应该查看哪些部分较慢,并处理较慢的部分。 This is called profiling.在这个答案中,我使用了 microbenchmark直接比较竞争的短方法,但其他工具(如 lineprof 包)更适合识别代码块中最慢的部分。

    关于r - 使用 mutate 创建新变量时,Dplyr 代码比预期慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42519653/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com