簡體   English   中英

使用mutate創建新變量時,Dplyr代碼比預期慢

[英]Dplyr code is slower than expected when creating new variables with mutate

我正在使用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秒。

是的,您的代碼中存在一些效率低下的問題:

  1. 您將BIRTHDT列轉換為Date兩次。 (這是迄今為止最大的問題。)
  2. base::as.Date不是超級快
  3. 您可以使用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

在這里,我們看到基地做得很好。 但是請注意,這些時間是以微秒為單位,而日期的上述時間是以毫秒為單位 無論您使用base還是dplyr添加列,它大約是用於執行日期轉換的時間的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 

這里的時間仍然是毫秒,但ifelsedplyr::if_else之間的差異並不是那么極端。 dplyr::if_else要求返回向量是相同的類型,因此我們必須指定NA_real_才能使用數字輸出。 在弗蘭克的建議下,我也用NA_real_ base::replacebase::replace ,它快了大約10倍。 我認為,這里的教訓是“使用最簡單的功能”。


總之,在添加列時, dplyrbase更慢,但與正在進行的其他操作相比,兩者都非常快。 因此,使用哪種列添加方法並不重要。 您可以通過不重復計算和使用更快版本的更大操作來加速代碼。 使用我們學到的東西,更有效的代碼版本是:

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_)
    )

我們可以在大約180毫秒的時間內將1e5行的速度增益調整,如下所示。

  • 170毫秒(單個lubridate::mdy在30毫秒而不是兩個as.Date調用每個100毫秒)
  • 10毫秒( replace而不是ifelse

添加列基准測試表明,我們可以通過不使用管道節省大約0.1毫秒。 由於我們要添加多個列,因此使用dplyr可能比使用$<-單獨添加它們更有效,但對於單個列,我們可以通過不使用dplyr節省大約0.5 ms。 由於我們已經加速了180毫秒,因此不使用mutate所獲得的潛在分數毫秒是舍入誤差,而不是效率提升。

在這種情況下,您正在做的最復雜的事情是Date轉換,但如果您正在進行更多處理,即使這可能不是您的瓶頸 要優化代碼,您應該看到哪些部分很慢,並且處理慢速位。 這稱為分析 在這個答案中,我使用microbenchmark來比較競爭的短方法,但其他工具(如lineprof包)更適合識別代碼塊中最慢的部分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM