Tidyr: عش () / unnest () في 1.0.0 أبطأ بشكل ملحوظ

تم إنشاؤها على ١٨ سبتمبر ٢٠١٩  ·  9تعليقات  ·  مصدر: tidyverse/tidyr

يبدو أن التنفيذ الجديد لـ nest() و unnest() قد أدى إلى تباطؤ كبير ، مقارنة بالإصدار السابق tidyR

ربما المشكلة تتعلق بحجم التخصيص المسبق؟

في هذه الحالة ، يجب أن يكون num_rows كبيرًا لملاحظة التباطؤ. انظر مقتطف الرمز أدناه.

  num_rows <- 100000

  x <- dplyr::tibble(first = 1:num_rows,
                     second = 5:(num_rows+5-1),
                     third = 7:(num_rows+7-1))

  before <- Sys.time()

  y <- dplyr::tibble(first = 1:num_rows,
                     second = 5:(num_rows+5-1),
                     third = 7:(num_rows+7-1)) %>%
      tidyr::nest(second_and_third = c(second, third)) %>%
      tidyr::unnest(second_and_third)

  after <- Sys.time()

  if(length(which(x != y)) != 0){
    stop("nest() and unnest() procedure results in corrupted data!")
  }

  cat(paste("Execution Time:",difftime(after,before,units="secs"),"seconds"))

على نظامي:

Execution Time: 61.2449209690094 seconds
feature performance rectangling vctrs ↗️

التعليق الأكثر فائدة

من الجدير بالذكر أن nest_legacy() و unnest_legacy() مازالا سريعين:

  num_rows <- 100000

  x <- dplyr::tibble(first = 1:num_rows,
                     second = 5:(num_rows+5-1),
                     third = 7:(num_rows+7-1))

  before <- Sys.time()

  y <- dplyr::tibble(first = 1:num_rows,
                     second = 5:(num_rows+5-1),
                     third = 7:(num_rows+7-1)) %>%
    tidyr::nest_legacy(second_and_third = c(second, third)) %>%
    tidyr::unnest_legacy()

  after <- Sys.time()

  if(length(which(x != y)) != 0){
    stop("nest() and unnest() procedure results in corrupted data!")
  }

  cat(paste("Execution Time:",difftime(after,before,units="secs"),"seconds"))

على جهازي

Execution Time: 3.19051384925842 seconds

لذلك ، بالنسبة لكود الأداء ، يمكن للمرء دائمًا استخدام وظائف legacy() . أود أن أقترح إصلاح هذا للإصدار الرئيسي التالي ، وتوجيه الأشخاص لاستخدام وظائف legacy() لتجنب التباطؤ غير المتوقع.

ال 9 كومينتر

لقد اختبرت هذا مع tidyR 0.8.3 :

num_rows <- 100000

x <- dplyr::tibble(first = 1:num_rows,
                   second = 5:(num_rows+5-1),
                   third = 7:(num_rows+7-1))

before <- Sys.time()

y <- dplyr::tibble(first = 1:num_rows,
                   second = 5:(num_rows+5-1),
                   third = 7:(num_rows+7-1)) %>%
  tidyr::nest(second_and_third = c(second, third)) %>%
  tidyr::unnest()

after <- Sys.time()

if(length(which(x != y)) != 0){
  stop("nest() and unnest() procedure results in corrupted data!")
}

cat(paste("Execution Time:",difftime(after,before,units="secs"),"seconds"))

وهناك رأيت

Execution Time: 5.78480935096741 seconds

إبطاء بمقدار 10.587x

من الجدير بالذكر أن nest_legacy() و unnest_legacy() مازالا سريعين:

  num_rows <- 100000

  x <- dplyr::tibble(first = 1:num_rows,
                     second = 5:(num_rows+5-1),
                     third = 7:(num_rows+7-1))

  before <- Sys.time()

  y <- dplyr::tibble(first = 1:num_rows,
                     second = 5:(num_rows+5-1),
                     third = 7:(num_rows+7-1)) %>%
    tidyr::nest_legacy(second_and_third = c(second, third)) %>%
    tidyr::unnest_legacy()

  after <- Sys.time()

  if(length(which(x != y)) != 0){
    stop("nest() and unnest() procedure results in corrupted data!")
  }

  cat(paste("Execution Time:",difftime(after,before,units="secs"),"seconds"))

على جهازي

Execution Time: 3.19051384925842 seconds

لذلك ، بالنسبة لكود الأداء ، يمكن للمرء دائمًا استخدام وظائف legacy() . أود أن أقترح إصلاح هذا للإصدار الرئيسي التالي ، وتوجيه الأشخاص لاستخدام وظائف legacy() لتجنب التباطؤ غير المتوقع.

أنا أيضا التعليقات ليست مفيدة جدا. يرجى فقط النقر فوق زر الإعجاب بدلاً من ذلك.

تم حل معظم المشاكل هنا في إصدار التطوير من vctrs. كانت المشكلة بشكل أساسي مع unnest() (حقًا ، unchop() ). راجع r-lib / vctrs # 530 لمزيد من المعلومات.

مع مثالك الدقيق:

# devtools::install_github("r-lib/vctrs")

library(tidyr)

num_rows <- 100000

x <- dplyr::tibble(
  first = 1:num_rows,
  second = 5:(num_rows+5-1),
  third = 7:(num_rows+7-1)
)

before <- Sys.time()

y <- dplyr::tibble(
  first = 1:num_rows,
  second = 5:(num_rows+5-1),
  third = 7:(num_rows+7-1)
) %>%
  tidyr::nest(second_and_third = c(second, third)) %>%
  tidyr::unnest(second_and_third)

after <- Sys.time()

if(length(which(x != y)) != 0){
  stop("nest() and unnest() procedure results in corrupted data!")
}

cat(paste("Execution Time:",difftime(after,before,units="secs"),"seconds"))
#> Execution Time: 9.04665207862854 seconds

تم إنشاؤه في 2019-09-24 بواسطة حزمة reprex (v0.2.1)

أعتقد أننا يمكن أن نحصل على 1-2 ثانية أسرع مع r-lib / vctrs # 592.

وربما أكثر قليلاً من تطبيق C أصلي vec_recycle_common() .

Reprex يقارن الوظائف الجديدة والقديمة مباشرةً:

library(tidyr)

num_rows <- 10000
df <- tibble(
  first = 1:num_rows,
  second = 5:(num_rows+5-1),
  third = 7:(num_rows+7-1)
)

bench::mark(
  new = df %>%
    nest(second_and_third = c(second, third)) %>%
    unnest(second_and_third),
  old = df %>% 
    nest_legacy(second, third) %>% 
    unnest_legacy(data)
)
#> Warning: Some expressions had a GC in every iteration; so filtering is disabled.
#> # A tibble: 2 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 new           906ms    906ms      1.10     3.6MB     27.6
#> 2 old           384ms    404ms      2.48    2.98MB     24.8

تم إنشاؤه في 2019-11-13 بواسطة حزمة reprex (v0.3.0)

لذلك تم الآن حل أسوأ فجوة في الأداء ، على الرغم من أنه من الواضح أنه سيكون من الأفضل القيام بعمل أفضل من الإصدار السابق (على الرغم من أن الإصدار الجديد أكثر عمومية ، لذا فليس من المستغرب أن يكون أبطأ قليلاً حاليًا). يُظهر التوصيف 15٪ من وقت التشغيل بدءًا من drop_null() ، و 70٪ من vec_rbind() لذا كما يقترح DavisVaughan ، فإن vctrs هي المكان الواضح لمتابعة تحسين الأداء .

من الناحية النظرية ، هناك فوائد من r-lib / vctrs # 592

library(tidyr)

num_rows <- 10000

tbl <- tibble(
  first = 1:num_rows,
  second = 5:(num_rows+5-1),
  third = 7:(num_rows+7-1)
)

tbl_nest <- tbl %>%
  nest(second_and_third = c(second, third))

df_nest <- as.data.frame(tbl_nest)
df_nest$second_and_third <- lapply(df_nest$second_and_third, as.data.frame)

bench::mark(
  tibble_new = unnest(tbl_nest, second_and_third),
  tibble_old = unnest_legacy(tbl_nest, second_and_third),
  dataframe_new = unnest(df_nest, second_and_third),
  dataframe_old = unnest_legacy(df_nest, second_and_third),
  iterations = 30
)
#> Warning: Some expressions had a GC in every iteration; so filtering is
#> disabled.
#> # A tibble: 4 x 6
#>   expression         min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>    <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 tibble_new     622.2ms    698ms      1.37    1.43MB     22.7
#> 2 tibble_old      92.4ms   98.9ms     10.1   808.02KB     23.2
#> 3 dataframe_new  345.6ms  431.9ms      2.25  680.05KB     19.4
#> 4 dataframe_old   62.3ms   67.2ms     14.7   575.67KB     28.3

تم إنشاؤه في 2019-11-13 بواسطة حزمة reprex (الإصدار 0.3.0.9000)

Reprex أبسط قليلاً:

library(tidyr)
n <- 10000

df <- tibble(
  g = 1:n,
  y = rep(list(tibble(x = 1:5)), n)
)

bench::mark(
  unnest(df, y),
  unnest_legacy(df, y)
)
#> Warning: Some expressions had a GC in every iteration; so filtering is disabled.
#> # A tibble: 2 x 6
#>   expression                min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>           <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 unnest(df, y)         488.4ms  512.8ms      1.95    2.82MB     22.4
#> 2 unnest_legacy(df, y)   88.6ms   91.9ms     10.7     1.44MB     24.9

تم إنشاؤه في 2019-11-28 بواسطة حزمة reprex (v0.3.0)

تحديث تزايدي. مع فرع VCTRS الرئيسي بعد تضمين الفوائد الكبيرة من r-lib / vctrs # 825 والمزايا الصغيرة من r-lib / vctrs # 824

library(tidyr)
n <- 10000

df <- tibble(
  g = 1:n,
  y = rep(list(tibble(x = 1:5)), n)
)

bench::mark(
  unnest(df, y),
  unnest_legacy(df, y),
  iterations = 50
)
#> Warning: Some expressions had a GC in every iteration; so filtering is disabled.
#> # A tibble: 2 x 6
#>   expression                min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>           <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 unnest(df, y)           204ms  297.5ms      3.39    3.38MB     14.0
#> 2 unnest_legacy(df, y)     48ms   73.6ms     13.4     1.25MB     11.6

تم إنشاؤه بتاريخ 2020-02-17 بواسطة حزمة reprex (v0.3.0)

لدي فكرة أخرى لتقليل هذا الأمر عن طريق نقل بعض تفاصيل التنفيذ باهظة الثمن tidyr::unchop() إلى C

مع dev dplyr ، أرى الآن:

library(tidyr)
n <- 10000

df <- tibble(
  g = 1:n,
  y = rep(list(tibble(x = 1:5)), n)
)

bench::mark(
  unnest(df, y),
  unnest_legacy(df, y)
)
#> Warning: Some expressions had a GC in every iteration; so filtering is disabled.
#> # A tibble: 2 x 6
#>   expression                min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>           <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 unnest(df, y)          87.2ms   90.8ms     11.0     3.62MB     20.1
#> 2 unnest_legacy(df, y)  123.7ms  129.6ms      7.73    4.11MB     23.2

تم إنشاؤه بتاريخ 2020-04-22 بواسطة حزمة reprex (v0.3.0)

لذا فقد تباطأ unnest_legacy() قليلاً ، لكن unnest() أصبح الآن أسرع ، وأبطأ قليلاً من ذي قبل.

أعتقد أن هذا مكان جيد لمغادرته. يمكننا بالتأكيد العودة لتحسين الأداء في المستقبل ، لكنني أعتقد أن الدافع الأساسي الملحة قد تم حله الآن.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات