2017-01-31 19 views
8

में कस्टम dplyr तरीकों मैं कस्टम summary(), वस्तुओं एक विशेष वर्ग के लिए है कि print() तरीकों के साथ एक पैकेज है। इस पैकेज भी डेटा हेरफेर के लिए अद्भुत dplyr पैकेज का उपयोग करता है - और मैं अपने उन दोनों स्क्रिप्ट अपने पैकेज और dplyr का उपयोग करें कि लिखने के लिए उम्मीद है।को परिभाषित आर पैकेज

एक अवरोध है, जो दूसरों here और here द्वारा उल्लेख किया गया है कि dplyr क्रियाओं कस्टम वर्गों की रक्षा करता है नहीं है - जिसका अर्थ है कि एक ungroup आदेश अपने कस्टम कक्षाओं की मेरी data.frames पट्टी कर सकते हैं, और इस तरह के लिए विधि प्रेषण तक पेंच summary, आदि

हैडली कहते हैं, "ऐसा करने से सही ढंग से आप पर निर्भर है - आपको हर dplyr विधि है कि सही ढंग से सभी वर्गों को पुनर्स्थापित करता है और विशेषताओं के लिए अपने वर्ग के लिए एक विधि को परिभाषित करने की जरूरत है" और मैं advice लेने के लिए कोशिश कर रहा हूँ - लेकिन मैं समझ नहीं सकता कि कैसे dplyr क्रियाओं को सही तरीके से लपेटें।

यहाँ एक सरल खिलौना उदाहरण है। मान लीजिए कि मैं एक cars वर्ग परिभाषित किया है, और मैं इसे के लिए एक कस्टम summary करते हैं।

इस काम करता है

library(tidyverse) 

class(mtcars) <- c('cars', class(mtcars)) 

summary.cars <- function(x, ...) { 
    #gather some summary stats 
    df_dim <- dim(x) 
    quantile_sum <- map(mtcars, quantile) 

    cat("A cars object with:\n") 
    cat(df_dim[[1]], 'rows and ', df_dim[[2]], 'columns.\n') 

    print(quantile_sum) 

} 

summary(mtcars) 

यहाँ समस्या है

small_cars <- mtcars %>% filter(cyl < 6) 
summary(small_cars) 
class(small_cars) 

कि small_cars के लिए summary कॉल बस मुझे सामान्य सारांश, नहीं मेरे कस्टम विधि, क्योंकि small_cars अब dplyr के बाद cars वर्ग को बरकरार रखे हुए देता है छानने।

मैं क्या करने की कोशिश की

सबसे पहले मैं filter (filter.cars) के चारों ओर एक कस्टम विधि लेखन की कोशिश की। यही कारण है कि काम नहीं किया, क्योंकि filter वास्तव में एक आवरण के आसपास filter_ कि गैर मानक मूल्यांकन के लिए अनुमति देता है।

तो मैं cars वस्तुओं के लिए एक कस्टम filter_ विधि लिखा था, @jwdink लागू करने के लिए प्रयास कर रहा है advice

filter_.cars <- function(df, ...) { 

    old_classes <- class(df) 
    out <- dplyr::filter_(df, ...) 
    new_classes <- class(out) 

    class(out) <- c(new_classes, old_classes) %>% unique() 

    out 
} 

कि काम नहीं करता है - मैं एक अनंत प्रत्यावर्तन त्रुटि मिलती है:

Error: evaluation nested too deeply: infinite recursion/options(expressions=)? 
Error during wrapup: evaluation nested too deeply: infinite recursion/options(expressions=)? 

मैं बस इतना करना चाहता हूं कि आने वाले डीएफ पर कक्षाओं को पकड़ो, डीप्लर को हाथ से हटा दें, फिर ऑब्जेक्ट को उसी क्लासनाम के साथ वापस करें जैसा कि डेलर कॉल से पहले था। कैसे मुझे लगता है कि पूरा करने के लिए मेरी filter_ आवरण बदल सकता हूँ? धन्यवाद!

उत्तर

7

आगे सुझाव the thread में की पेशकश की गई तो मैंने सोचा कि मैं क्या सबसे अच्छा अभ्यास, NextMethod() उपयोग करने के लिए है जो हो रहा है के साथ अद्यतन चाहते हैं।

filter_.cars <- function(.data, ...) { 
    result <- NextMethod() 
    reclass(.data, result) 
} 

कहाँ reclass एक सामान्य कि कम से कम पर वर्ग वापस जोड़ता है:

reclass <- function(x, result) { 
    UseMethod('reclass') 
} 

reclass.default <- function(x, result) { 
    class(result) <- unique(c(class(x)[[1]], class(result))) 
    result 
} 

लेकिन आपको लगता है कि यह भी प्रतियां वापस जिम्मेदार बताते हैं अपने वर्ग के लिए एक कस्टम विधि निर्धारित कर सकते हैं:

reclass.cars <- function(x, result) { 
    class(result) <- unique(c(class(x)[[1]], class(result))) 
    attr(result,'cars') <- attr(x,'cars') 
    result 
} 

मुझे लगता है कि एक बेहतर डिफ़ॉल्ट विधि मान लीजिए कि वहां एक विशेषता है जिसका नाम कक्षा के समान है:

reclass.default <- function(x, result) { 
    class(result) <- unique(c(class(x)[[1]], class(result))) 
    attr(result, class(x)[[1]]) <- attr(x, class(x)[[1]]) 
    result 
} 

ध्यान दें कि dplyr 0.7 के लिए, क्रियाओं के अंडरस्कोर संस्करण का विरोध कर रहे हैं। यदि आपकी 'कार' श्रेणी tbl_df से प्राप्त होती है, तो आपको गैर-अंडरस्कोर क्रियाओं के लिए एक विधि लिखनी होगी। लेकिन फिर आप पीछे की संगतता के लिए अंडरस्कोर संस्करण रखना चाह सकते हैं।

इस सभी प्रतिकृति को देखते हुए, मुझे यहां एक क्रिया का विचार पसंद है।

preservatively <- function(fun) { 
    function(x, ...) { 
    result <- NextMethod() 
    reclass(x, result) 
    } 
} 

फिर बातें अच्छी और अपने पैकेज में संक्षिप्त कर रहे हैं:

filter_.cars <- preservatively(filter_) 
filter.cars <- preservatively(filter) 
mutate_.cars <- preservatively(mutate_) 
mutate.cars <- preservatively(mutate) 

आदि


संपादित करें:

preservatively प्रयोग न करें। यदि कोई व्यक्ति नामित पहले तर्क के साथ dplyr क्रिया को कॉल करता है तो यह टूट जाएगा, क्योंकि नाम आम तौर पर .data है, x नहीं।

filter.cars <- preservatively(filter) 
filter(my_data, condition) # good 
filter(.data = my_data, condition) # oh no 

यदि यह पता चलता है कि कोई क्रियाविधि सभी के बाद काम कर सकती है तो मैं यह जवाब अपडेट करूंगा। अन्यथा, मुझे लगता है कि यह वास्तव में कोई और वर्बोज़ नहीं है:

filter.cars <- function(.data, ...) reclass(.data, NextMethod()) 
+1

मुझे आपका आखिरी उदाहरण पसंद है, हालांकि मुझे लगता है कि 'reclass (डेटा, NextMethod()) को' reclass (.data, NextMethod()) ' – Eric

+0

' में बदलना होगा, जबकि यह एक अच्छा कामकाज है, यह बहुत उचित प्रतीत नहीं होता है शुरू करने के लिए कक्षाओं को हटाने के लिए ** dplyr ** के लिए। इस कामकाज का मतलब है कि कस्टम डेटा फ्रेम वर्गों का उपयोग करने वाले प्रत्येक पैकेज को अब प्रत्येक ** dplyr ** क्रिया के लिए विधियों को जोड़ना होगा ... – Deleet

+0

एरिक, मैंने टाइपो को '.data' बनाम 'डेटा' के साथ तय किया है (मैंने नहीं किया जब तक मैंने कोड को लागू करने की कोशिश नहीं की तब तक इसे नोटिस न करें।) – Deleet

8

आपकी नई filter_ विधि परिभाषा के भीतर नई कक्षा में आवेदन करने की कोशिश करती है, इसलिए रिकर्सन।

the advice in the issue you linked के बाद, filter_ से पहले अपनी नई विधि को निकालने का प्रयास करें।

class(out) <- class(out)[-1] 
+0

यह वास्तव में दिलचस्प था। जब मैंने यह लिखा तो मैंने सोचा कि dplyr :: filter_ * गारंटीकृत * कि आंतरिक कॉल को dplyr-flavored प्रेषण मिला, लेकिन यह पर्याप्त नहीं है! रिकर्सन अब समझ में आता है। – Andrew

संबंधित मुद्दे