2016-04-17 11 views
6

मैं विज़ुअलाइज़ेशन के लिए अधिकतर ggplot2 का उपयोग करता हूं। आम तौर पर, मैं साजिश इंटरैक्टिव रूप से (यानी कच्चे ggplot2 कोड जो एनएसई का उपयोग करता है) डिज़ाइन करता है लेकिन अंत में, मैं उस कोड को उस फ़ंक्शन में लपेटता हूं जो को साजिश के लिए डेटा और चर प्राप्त करता है। और यह हमेशा दुःस्वप्न का थोड़ा सा हिस्सा है।एक समारोह के अंदर ggplot2 के लिए आलसी मूल्यांकन

तो, सामान्य स्थितियां इस तरह दिखती हैं। मेरे पास कुछ डेटा है और मैं इसके लिए एक साजिश बना रहा हूं (इस मामले में, का उपयोग करके एक बहुत ही सरल उदाहरण, ggplot2 के साथ आता है mpg डेटासेट)।

library(ggplot2) 
data(mpg) 

ggplot(data = mpg, 
     mapping = aes(x = class, y = hwy)) + 
    geom_boxplot() + 
    geom_jitter(alpha = 0.1, color = "blue") 


और जब मैं साजिश को डिजाइन करने के बाद, मैं आम तौर पर विभिन्न चर या डेटा, आदि के लिए इसका उपयोग करना चाहते तो मैं एक समारोह है कि तर्क के रूप में भूखंड के लिए डेटा और चर प्राप्त करता है बनाने । लेकिन एनएसई के कारण, यह फ़ंक्शन हेडर लिखना जितना आसान नहीं है और फिर कॉपी/पेस्ट करें और फ़ंक्शन तर्कों के लिए चर बदलें। जैसा कि नीचे दिखाया गया है, यह काम नहीं करेगा।

mpg <- mpg 
plotfn <- function(data, xvar, yvar){ 
    ggplot(data = data, 
      mapping = aes(x = xvar, y = yvar)) + 
    geom_boxplot() + 
    geom_jitter(alpha = 0.1, color = "blue") 
} 
plotfn(mpg, class, hwy) # Can't find object 

## Don't know how to automatically pick scale for object of type function. Defaulting to continuous. 

## Warning: restarting interrupted promise evaluation 

## Error in eval(expr, envir, enclos): object 'hwy' not found 

plotfn(mpg, "class", "hwy") # 


तो मैं (वापस जाने के लिए और उदाहरण के लिए, कोड को ठीक करने, aes एनएसई का उपयोग करता है के aes_string intead का उपयोग कर इस उदाहरण यह नहीं बल्कि आसान है में है, लेकिन और अधिक जटिल भूखंडों के लिए , बहुत सारे परिवर्तन और परतों के साथ, यह एक दुःस्वप्न बन जाता है)।

plotfn <- function(data, xvar, yvar){ 
    ggplot(data = data, 
      mapping = aes_string(x = xvar, y = yvar)) + 
    geom_boxplot() + 
    geom_jitter(alpha = 0.1, color = "blue") 
} 
plotfn(mpg, "class", "hwy") # Now this works 


और बात यह है कि मैं बहुत सुविधाजनक एनएसई और भी lazyeval लगता है। तो मुझे ऐसा कुछ करना पसंद है।

mpg <- mpg 
plotfn <- function(data, xvar, yvar){ 
    data_gd <- data.frame(
     xvar = lazyeval::lazy_eval(substitute(xvar), data = data), 
     yvar = lazyeval::lazy_eval(substitute(yvar), data = data)) 

    ggplot(data = data_gd, 
      mapping = aes(x = xvar, y = yvar)) + 
    geom_boxplot() + 
    geom_jitter(alpha = 0.1, color = "blue") 
} 
plotfn(mpg, class, hwy) # Now this works 

plotfn(mpg, "class", "hwy") # This still works 

plotfn(NULL, rep(letters[1:4], 250), 1:100) # And even this crazyness works 


यह मेरा भूखंड समारोह बहुत लचीलापन देता है। उदाहरण के लिए, आप पास उद्धृत या अनगिनत चर नामों और यहां तक ​​कि डेटा को एक परिवर्तनीय नाम (आलसी मूल्यांकन के दुरुपयोग की तरह) के बजाय पास कर सकते हैं।

लेकिन इसमें एक बड़ी समस्या है। फ़ंक्शन का उपयोग प्रोग्रामेटिक रूप से नहीं किया जा सकता है।

dynamically_changing_xvar <- "class" 
plotfn(mpg, dynamically_changing_xvar, hwy) 

## Error in eval(expr, envir, enclos): object 'dynamically_changing_xvar' not found 

# This does not work, because it never finds the object 
# dynamically_changing_xvar in the data, and it does not get evaluated to 
# obtain the variable name (class) 

तो मैं छोरों का उपयोग नहीं कर सकते हैं (उदाहरण के लिए lapply) चर, या डेटा की विभिन्न संयोजनों के लिए एक ही भूखंड का उत्पादन करने के लिए।

तो मैं आलसी मानक और गैर मानक मूल्यांकन के लिए और भी अधिक का दुरुपयोग, और उन सब गठबंधन करने के लिए प्रयास करने के लिए सोचा था तो मैं दोनों है, लचीलापन ऊपर दिखाए गए और प्रोग्राम के रूप में समारोह का उपयोग करने की क्षमता। मूल रूप से, मैं क्या हर चर के लिए पहले lazy_eval अभिव्यक्ति को tryCatch उपयोग करने के लिए है और अगर यह विफल रहता है, पार्स अभिव्यक्ति का मूल्यांकन करने के।

plotfn <- function(data, xvar, yvar){ 
    data_gd <- NULL 
    data_gd$xvar <- tryCatch(
     expr = lazyeval::lazy_eval(substitute(xvar), data = data), 
     error = function(e) eval(envir = data, expr = parse(text=xvar)) 
    ) 
    data_gd$yvar <- tryCatch(
     expr = lazyeval::lazy_eval(substitute(yvar), data = data), 
     error = function(e) eval(envir = data, expr = parse(text=yvar)) 
    ) 


    ggplot(data = as.data.frame(data_gd), 
      mapping = aes(x = xvar, y = yvar)) + 
    geom_boxplot() + 
    geom_jitter(alpha = 0.1, color = "blue") 
} 

plotfn(mpg, class, hwy) # Now this works, again 

plotfn(mpg, "class", "hwy") # This still works, again 

plotfn(NULL, rep(letters[1:4], 250), 1:100) # And this crazyness still works 

# And now, I can also pass a local variable to the function, that contains 
# the name of the variable that I want to plot 
dynamically_changing_xvar <- "class" 
plotfn(mpg, dynamically_changing_xvar, hwy) 


तो, ऊपर उल्लिखित लचीलापन के अलावा, अब मैं एक लाइनर या तो उपयोग कर सकते हैं, विभिन्न चर (या डेटा) के साथ, एक ही भूखंड के कई निर्माण करने के लिए।

lapply(c("class", "fl", "drv"), FUN = plotfn, yvar = hwy, data = mpg) 

## [[1]] 

## 
## [[2]] 

## 
## [[3]] 


भले ही यह बहुत ही व्यावहारिक है, मुझे लगता है यह अच्छा अभ्यास नहीं है। लेकिन यह कितना बुरा अभ्यास है? यह मेरा मुख्य सवाल है। अन्य विकल्प क्या मैं दोनों दुनिया के सर्वश्रेष्ठ होने के लिए उपयोग कर सकता हूं?

बेशक

, मैं देख सकता इस पद्धति समस्या पैदा कर सकता है। उदाहरण के लिए।

# If I have a variable in the global environment that contains the variable 
# I want to plot, but whose name is in the data passed to the function, 
# then it will use the name of the variable and not its content 
drv <- "class" 
plotfn(mpg, drv, hwy) # Here xvar on the plot is drv and not class 


और कुछ (कई?) अन्य समस्याओं। लेकिन मुझे लगता है कि वाक्य रचना-लचीलेपन का संदर्भ में लाभ उन अन्य मुद्दों पल्ला झुकना। इस पर कोई विचार?

+1

सबसे अच्छा अभ्यास कार्यों की एक जोड़ी का उत्पादन करना है। एक एनएसई, दूसरा एसई है। यह 'विग्नेट ('एनएसई') में उल्लिखित है। इसका मतलब 'एईएस 'के बजाय' aes_' का उपयोग करना है। – Axeman

+0

धन्यवाद, ..., हाँ, मुझे डर था कि जवाब होने वाला था। हालांकि मैं dplyr और सह के लाभ देखता हूं। "लगातार नामकरण योजना: एसई अंत में _ के साथ एनएसई नाम है", यह हमेशा प्रोग्रामिंग के लिए एक अलग फ़ंक्शन का उपयोग करने और इंटरैक्टिव रूप से काम करने के लिए मुझे बग करता है। – elikesprogramming

उत्तर

2

स्पष्टता के लिए अपने प्रस्तावित समारोह निकाला जा रहा है:

library(ggplot2) 
data(mpg) 

plotfn <- function(data, xvar, yvar){ 
    data_gd <- NULL 
    data_gd$xvar <- tryCatch(
    expr = lazyeval::lazy_eval(substitute(xvar), data = data), 
    error = function(e) eval(envir = data, expr = parse(text=xvar)) 
) 
    data_gd$yvar <- tryCatch(
    expr = lazyeval::lazy_eval(substitute(yvar), data = data), 
    error = function(e) eval(envir = data, expr = parse(text=yvar)) 
) 

    ggplot(data = as.data.frame(data_gd), 
     mapping = aes(x = xvar, y = yvar)) + 
    geom_boxplot() + 
    geom_jitter(alpha = 0.1, color = "blue") 
} 

इस तरह के एक समारोह आम तौर पर काफी उपयोगी है के बाद से आप स्वतंत्र रूप से तार मिश्रण कर सकते हैं, और नंगे चर नाम। लेकिन जैसा कि आप कहते हैं, यह हमेशा सुरक्षित नहीं हो सकता है। निम्नलिखित प्रदूषित उदाहरण पर विचार करें:

class <- "drv" 
Class <- "drv" 
plotfn(mpg, class, hwy) 
plotfn(mpg, Class, hwy) 

आपका फ़ंक्शन क्या उत्पन्न करेगा? क्या ये वही होंगे (वे नहीं हैं)? यह वास्तव में मुझे स्पष्ट नहीं है कि परिणाम क्या होगा। इस तरह के एक समारोह के साथ प्रोग्रामिंग अप्रत्याशित परिणाम, निर्भर करता है जो चर data में मौजूद हैं और जो पर्यावरण में मौजूद दे सकता है। के बाद से बहुत से लोगों को x, xvar या count की तरह चर नाम का उपयोग करें (भले ही वे शायद नहीं करना चाहिए), चीजों को गंदा मिल सकती है।

इसके अलावा, अगर मैं या class के अन्य व्याख्या एक मजबूर करने के लिए चाहता था, मैं नहीं कर सकता।

मैं कहूंगा कि यह attach का उपयोग करने के समान है: सुविधाजनक, लेकिन किसी बिंदु पर यह आपको पीछे की ओर काट सकता है।

इसलिए, मैं एक एनएसई और एसई जोड़ी का उपयोग करेंगे:

plotfn <- function(data, xvar, yvar) { 
    plotfn_(data, 
      lazyeval::lazy_eval(xvar, data = data), 
      lazyeval::lazy_eval(yvar, data = data)) 
) 
} 

plotfn_ <- function(data, xvar, yvar){ 
    ggplot(data = data, 
     mapping = aes_(x = xvar, y = yvar)) + 
    geom_boxplot() + 
    geom_jitter(alpha = 0.1, color = "blue") 
} 

बनाना ये वास्तव में अपने कार्य की तुलना में आसान है, मुझे लगता है। आप lazy_dots के साथ भी सभी तर्कों को आलसी तरीके से कैप्चर करने का विकल्प चुन सकते हैं।

class <- "drv" 
Class <- "drv" 
plotfn_(mpg, class, 'hwy') 
plotfn_(mpg, Class, 'hwy') 

एनएसई संस्करण अभी भी प्रभावित होती है::

plotfn(mpg, class, hwy) 
plotfn(mpg, Class, hwy) 

(मैं इसे हल्का ggplot2::aes_ नहीं करता है कि कष्टप्रद

अब हम अधिक जब सुरक्षित एसई संस्करण का उपयोग कर परिणाम की भविष्यवाणी करने के लिए आसान प्राप्त तार भी नहीं लेते हैं।)

+1

हाँ, मैं 100% से सहमत हूं "इस तरह के एक समारोह के साथ प्रोग्रामिंग अप्रत्याशित परिणाम दे सकता है, इस पर निर्भर करता है कि डेटा में कौन से चर मौजूद हैं और पर्यावरण में मौजूद हैं।", ..., बस कभी-कभी मुझे लगता है कि इसकी सुविधा मेरे पीछे काटने का खतरा अधिक है। – elikesprogramming

+0

कोड चलाने की कोशिश की गई थी जब कोड की आखिरी दो पंक्तियां काम नहीं करतीं। – student

+0

tidyverse में एनएसई बदल गया है, तो यह बहुत संभव है। – Axeman

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