डेटा टेबल को फ़िल्टर करते समय एंडिंग पर जंजीर का प्रदर्शन लाभ


12

मैं एक ही पंक्ति में समान कार्यों को एक साथ करने की आदत में हूं। उदाहरण के लिए, अगर मैं पर फिल्टर करने के लिए की जरूरत है a, bऔर cएक डेटा तालिका में, मैं उन्हें एक साथ एक में डाल देता हूँ []ands के साथ। कल, मैंने देखा कि मेरे विशेष मामले में इसके बजाय अविश्वसनीय रूप से धीमा और परीक्षण किया गया था। मैंने नीचे एक उदाहरण शामिल किया है।

सबसे पहले, मैं यादृच्छिक संख्या जनरेटर को , लोड करता , और एक डमी डेटा सेट

# Set RNG seed
set.seed(-1)

# Load libraries
library(data.table)

# Create data table
dt <- data.table(a = sample(1:1000, 1e7, replace = TRUE),
                 b = sample(1:1000, 1e7, replace = TRUE),
                 c = sample(1:1000, 1e7, replace = TRUE),
                 d = runif(1e7))

अगला, मैं अपने तरीकों को परिभाषित करता हूं। पहला अप्रोच चेन एक साथ फिल्टर करता है। दूसरा ANDs फ़िल्टर को एक साथ करता है।

# Chaining method
chain_filter <- function(){
  dt[a %between% c(1, 10)
     ][b %between% c(100, 110)
       ][c %between% c(750, 760)]
}

# Anding method
and_filter <- function(){
  dt[a %between% c(1, 10) & b %between% c(100, 110) & c %between% c(750, 760)]
}

यहाँ, मैं जाँचता हूँ कि वे समान परिणाम देते हैं।

# Check both give same result
identical(chain_filter(), and_filter())
#> [1] TRUE

अंत में, मैं उन्हें बेंचमार्क करता हूं।

# Benchmark
microbenchmark::microbenchmark(chain_filter(), and_filter())
#> Unit: milliseconds
#>            expr      min        lq      mean    median        uq       max
#>  chain_filter() 25.17734  31.24489  39.44092  37.53919  43.51588  78.12492
#>    and_filter() 92.66411 112.06136 130.92834 127.64009 149.17320 206.61777
#>  neval cld
#>    100  a 
#>    100   b

2019-10-25 को रेप्रेक्स पैकेज (v0.3.0) द्वारा बनाया गया

इस मामले में, जंजीरों को चलाने का समय लगभग 70% कम हो जाता है। यह एक केस क्यों है? मेरा मतलब है, डेटा तालिका में हुड के तहत क्या हो रहा है? मैंने उपयोग करने के खिलाफ कोई चेतावनी नहीं देखी है &, इसलिए मुझे आश्चर्य हुआ कि अंतर इतना बड़ा है। दोनों ही मामलों में वे समान स्थितियों का मूल्यांकन करते हैं, ताकि अंतर न हो। AND मामले में, &एक त्वरित ऑपरेटर है और फिर इसे केवल एक बार डेटा तालिका को फ़िल्टर करना होता है (यानी, ANDs से उत्पन्न तार्किक वेक्टर का उपयोग करते हुए), जैसा कि चेनिंग मामले में तीन बार फ़िल्टर करने का विरोध किया गया है।

बोनस का सवाल

क्या यह सिद्धांत सामान्य रूप से डेटा टेबल संचालन के लिए है? क्या मॉड्यूलर काम करना हमेशा बेहतर रणनीति होती है?


1
मैंने इस अवलोकन को ध्यान में रखते हुए सोचा है। मेरे अनुभव में, सामान्य परिचालन में चिनिंग स्पीड पिक-अप देखा गया है।
JDG

9
जबकि data.tavle इस तरह के मामलों के लिए कुछ अनुकूलन करता है (यह अकेले एक करतब और एक बड़ा सुधार बनाम आधार है!), सामान्य तौर पर A & B & C & D परिणाम और संयोजन को फ़िल्टर करने से पहले सभी N तार्किक परिस्थितियों का मूल्यांकन करेंगे। । 2nd 3rd और 4th लॉजिकल कॉल्स का पीछा करने के दौरान केवल n समय का मूल्यांकन किया जाता है (जहाँ n <= N प्रत्येक स्थिति के बाद बची हुई पंक्तियों की संख्या है)
माइकलचिरिको

@ मिचेलचिरिको वाह। यह आश्चर्य की बात है! मुझे पता नहीं क्यों, लेकिन मैंने अभी यह मान लिया है कि यह C ++ शॉर्ट-सर्किटिंग की तरह काम करेगा
duckmayr

@ माइकलचिरिको की टिप्पणी के बाद, आप baseनिम्न कार्य करके वैक्टर के साथ एक समान अवलोकन कर सकते हैं : chain_vec <- function() { x <- which(a < .001); x[which(b[x] > .999)] }और and_vec <- function() { which(a < .001 & b > .999) }। (जहां aऔर bउसी लंबाई के वैक्टर हैं runif- मैंने n = 1e7इन कटऑफ के लिए उपयोग किया है )।
क्लिक्सस्टैट्स

@MichaelChirico आह, मैं देख रहा हूँ। तो, बड़ा अंतर यह है कि श्रृंखला के प्रत्येक चरण में, डेटा तालिका काफी छोटी है और इसलिए स्थिति का मूल्यांकन करने और फ़िल्टर करने के लिए तेज है? यह समझ आता है। आपकी अंतर्दृष्टि के लिए धन्यवाद!
लिंगबाग्र

जवाबों:


8

अधिकतर, इसका जवाब टिप्पणियों में दिया गया था: data.tableअलौकिक "पीछा करने की विधि" इस मामले में "एंडिंग विधि" की तुलना में तेज है क्योंकि पीछा एक के बाद एक शर्तों को चलाता है। जैसा कि प्रत्येक चरण कम data.tableहोता है, अगले एक के लिए मूल्यांकन करने के लिए कम होता है। "एंडिंग" हर बार पूर्ण आकार के डेटा के लिए स्थितियों का मूल्यांकन करता है।

हम इसे एक उदाहरण के साथ प्रदर्शित कर सकते हैं: जब अलग-अलग चरणों का आकार कम नहीं होता है data.table(यानी जाँच करने की शर्तें दोनों के लिए समान हैं):

chain_filter <- function(){
  dt[a %between% c(1, 1000) # runs evaluation but does not filter out cases
     ][b %between% c(1, 1000)
       ][c %between% c(750, 760)]
}

# Anding method
and_filter <- function(){
  dt[a %between% c(1, 1000) & b %between% c(1, 1000) & c %between% c(750, 760)]
}

उसी डेटा का उपयोग करना लेकिन benchपैकेज, जो परिणाम समान होने पर स्वचालित रूप से जाँच करता है:

res <- bench::mark(
  chain = chain_filter(),
  and = and_filter()
)
summary(res)
#> # 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 chain         299ms    307ms      3.26     691MB     9.78
#> 2 and           123ms    142ms      7.18     231MB     5.39
summary(res, relative = TRUE)
#> # A tibble: 2 x 6
#>   expression   min median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
#> 1 chain       2.43   2.16      1         2.99     1.82
#> 2 and         1      1         2.20      1        1

जैसा कि आप देख सकते हैं कि इस मामले में anding दृष्टिकोण 2.43 गुना तेज है । इसका मतलब है कि वास्तव में चेनिंग कुछ ओवरहेड जोड़ता है , यह सुझाव देता है कि आमतौर पर एंडिंग जल्दी होना चाहिए। EXCEPT अगर स्थितियांdata.table कदम से कदम के आकार को कम कर रही हैं । सैद्धांतिक रूप से, जंजीर दृष्टिकोण भी धीमा हो सकता है (यहां तक ​​कि ओवरहेड को एक तरफ छोड़कर), अर्थात् यदि कोई शर्त डेटा के आकार में वृद्धि करेगी। लेकिन व्यावहारिक रूप से मुझे लगता है कि तार्किक वैक्टर के पुनर्चक्रण के बाद से यह संभव नहीं है data.table। मुझे लगता है कि यह आपके बोनस प्रश्न का उत्तर देता है।

तुलना के लिए, मेरी मशीन पर मूल कार्य bench:

res <- bench::mark(
  chain = chain_filter_original(),
  and = and_filter_original()
)
summary(res)
#> # 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 chain        29.6ms   30.2ms     28.5     79.5MB     7.60
#> 2 and         125.5ms  136.7ms      7.32   228.9MB     7.32
summary(res, relative = TRUE)
#> # A tibble: 2 x 6
#>   expression   min median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
#> 1 chain       1      1         3.89      1        1.04
#> 2 and         4.25   4.52      1         2.88     1
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.