पाइप ऑपरेटर%>% का उपयोग करते समय सशर्त मूल्यांकन


93

पाइप ऑपरेटर का उपयोग करते समय %>%इस तरह के रूप पैकेज के साथ dplyr, ggvis, dycharts, आदि, कैसे करना है मैं सशर्त एक कदम है? उदाहरण के लिए;

step_1 %>%
step_2 %>%

if(condition)
step_3

ये दृष्टिकोण काम नहीं करते हैं:

step_1 %>%
step_2 
if(condition) %>% step_3

step_1 %>%
step_2 %>%
if(condition) step_3

एक लंबा रास्ता तय करना है:

if(condition)
{
step_1 %>%
step_2 
}else{
step_1 %>%
step_2 %>%
step_3
}

क्या सभी अतिरेक के बिना एक बेहतर तरीका है?


4
काम करने के लिए एक उदाहरण (बेन प्रदान के रूप में) बेहतर होगा, फी।
फ्रैंक

जवाबों:


104

यहाँ एक त्वरित उदाहरण है जो लाभ उठाता है .और ifelse:

X<-1
Y<-T

X %>% add(1) %>% { ifelse(Y ,add(.,1), . ) }

में ifelse, अगर Yहै TRUEअगर 1 जोड़ देगा, अन्यथा यह सिर्फ अंतिम मूल्य वापस करेगा X.एक स्टैंड में जो, समारोह जहां श्रृंखला के पिछले चरण से उत्पादन हो जाता है बताता है तो मैं दोनों शाखाओं पर इसका इस्तेमाल कर सकते हैं।

@BenBolker के रूप में संपादित करें , आप नहीं चाहते हैं ifelse, इसलिए यहां एक ifसंस्करण है।

X %>% 
add(1) %>% 
 {if(Y) add(.,1) else .}

उनका कहना है कि मैं का उपयोग करना चाहिए के लिए @Frank के लिए धन्यवाद {चारों ओर मेरी ब्रेसिज़ ifऔर ifelseश्रृंखला जारी रखने के लिए बयान।


4
मुझे पोस्ट-एडिट संस्करण पसंद है। ifelseनियंत्रण प्रवाह के लिए अप्राकृतिक लगता है।
फ्रैंक

7
एक बात ध्यान दें: यदि श्रृंखला में एक बाद का कदम है, तो उपयोग करें {}। उदाहरण के लिए, यदि आपके पास यहां नहीं है, तो खराब चीजें होती हैं (सिर्फ Yकिसी कारण से मुद्रण के लिए): X %>% "+"(1) %>% {if(Y) "+"(1) else .} %>% "*"(5)
फ्रैंक

मैग्रीट उर्फ ​​का उपयोग addउदाहरण को स्पष्ट करेगा।
ctbrown

कोड गोल्फिंग के संदर्भ में, यह विशिष्ट उदाहरण के रूप में लिखा जा सकता है, X %>% add(1*Y)लेकिन मूल प्रश्न का उत्तर नहीं देता है
ताल

1
बीच में सशर्त ब्लॉक के भीतर एक महत्वपूर्ण बात यह {}है कि आपको डॉट () के साथ dplyr पाइप (जिसे LHS) भी कहा जाता है) के पूर्ववर्ती तर्क का संदर्भ देना चाहिए - अन्यथा सशर्त ब्लॉक को प्राप्त नहीं होता है। बहस!
एजिल बीन

32

मुझे लगता है कि यह एक मामला है purrr::when। यदि उनकी राशि 25 से कम है, तो कुछ संख्याओं को योग करें, अन्यथा 0 वापस करें।


library("magrittr")
1:3 %>% 
  purrr::when(sum(.) < 25 ~ sum(.), 
              ~0
  )
#> [1] 6

whenपहली मान्य स्थिति की कार्रवाई से उत्पन्न मान लौटाता है। स्थिति को बाईं ओर रखें ~, और इसके दाईं ओर कार्रवाई करें। ऊपर, हमने केवल एक शर्त (और फिर एक और मामला) का उपयोग किया था, लेकिन आपके पास कई शर्तें हो सकती हैं।

आप आसानी से एक लंबे पाइप में एकीकृत कर सकते हैं।


2
अच्छा! यह 'स्विच' के लिए अधिक सहज विकल्प भी प्रदान करता है।
स्टीव जी। जोन्स

16

यहाँ @JohnPaul द्वारा उपलब्ध कराए गए उत्तर पर भिन्नता है। यह भिन्नता `if`यौगिक if ... else ...कथन के बजाय फ़ंक्शन का उपयोग करती है ।

library(magrittr)

X <- 1
Y <- TRUE

X %>% `if`(Y, . + 1, .) %>% multiply_by(2)
# [1] 4

ध्यान दें कि इस स्थिति में `if`फ़ंक्शन के चारों ओर घुंघराले ब्रेसिज़ की आवश्यकता नहीं है , न ही एक ifelseफ़ंक्शन के आसपास-केवल if ... else ...कथन के आसपास । हालाँकि, यदि डॉट प्लेसहोल्डर केवल नेस्टेड फ़ंक्शन कॉल में दिखाई देता है, तो मैग्रीट्राइफ़ डिफ़ॉल्ट रूप से बाएँ हाथ की ओर दाहिने हाथ की ओर के पहले तर्क में जाएगा। इस व्यवहार को घुंघराले ब्रेसिज़ में अभिव्यक्ति को संलग्न करके ओवरराइड किया जाता है। इन दो श्रृंखलाओं के बीच अंतर पर ध्यान दें:

X %>% `if`(Y, . + 1, . + 2)
# [1] TRUE
X %>% {`if`(Y, . + 1, . + 2)}
# [1] 4

डॉट प्लेसहोल्डर एक समारोह के भीतर नीडिंत है में दोनों बार ऐसा लगता है फोन `if`समारोह, के बाद से . + 1और . + 2के रूप में व्याख्या कर रहे हैं `+`(., 1)और `+`(., 2)क्रमश:। तो, पहली अभिव्यक्ति का परिणाम लौट रहा है `if`(1, TRUE, 1 + 1, 1 + 2)(विचित्र रूप से पर्याप्त है, `if`अतिरिक्त अप्रयुक्त तर्कों के बारे में शिकायत नहीं करता है), और दूसरी अभिव्यक्ति का परिणाम लौट रहा है `if`(TRUE, 1 + 1, 1 + 2), जो इस मामले में वांछित व्यवहार है।

कैसे के बारे में अधिक जानकारी के लिए magrittr पाइप ऑपरेटर व्यवहार करता डॉट प्लेसहोल्डर, देख मदद फ़ाइल के लिए %>%"माध्यमिक प्रयोजनों के लिए डॉट का उपयोग करना" पर अनुभाग विशेष रूप से,।


उपयोग करने `ìf`और के बीच अंतर क्या है ifelse? क्या वे व्यवहार में समान हैं?
चंचल बीन

@AgileBean का व्यवहार ifऔर ifelseकार्य समान नहीं है। ifelseसमारोह एक vectorized है if। यदि आप ifएक तार्किक वेक्टर के साथ फ़ंक्शन प्रदान करते हैं , तो यह एक चेतावनी प्रिंट करेगा और यह केवल उस लॉजिकल वेक्टर के पहले तत्व का उपयोग करेगा। से तुलना `if`(c(T, F), 1:2, 3:4)करें ifelse(c(T, F), 1:2, 3:4)
कैमरून बेगनकेक

महान, स्पष्टीकरण के लिए धन्यवाद! तो जैसा कि ऊपर की समस्या गैर-वेक्टरकृत है, आप अपना समाधान भी लिख सकते हैंX %>% { ifelse(Y, .+1, .+2) }
एजाइल बीन

12

यह मेरे लिए आसान होगा कि मैं पाइप से थोड़ा पीछे हट जाऊं (हालांकि मैं अन्य समाधानों को देखने में दिलचस्पी लूंगा), जैसे:

library("dplyr")
z <- data.frame(a=1:2)
z %>% mutate(b=a^2) -> z2
if (z2$b[1]>1) {
    z2 %>% mutate(b=b^2) -> z2
}
z2 %>% mutate(b=b^2) -> z3

यह @ जॉनपॉल के उत्तर का मामूली संशोधन है (आप वास्तव में नहीं चाहते हैं ifelse, जो इसके दोनों तर्कों का मूल्यांकन करता है और सदिश है)। यह संशोधित करने के लिए अच्छा होगा .यदि स्थिति झूठी है तो स्वचालित रूप से लौटने के लिए ... ( सावधानी : मुझे लगता है कि यह काम करता है लेकिन वास्तव में परीक्षण नहीं किया है / इसके बारे में बहुत सोचा है ...)

iff <- function(cond,x,y) {
    if(cond) return(x) else return(y)
}

z %>% mutate(b=a^2) %>%
    iff(cond=z2$b[1]>1,mutate(.,b=b^2),.) %>%
 mutate(b=b^2) -> z4

8

मुझे पसंद है purrr::whenऔर यहां दिए गए अन्य आधार समाधान सभी शानदार हैं, लेकिन मैं कुछ अधिक कॉम्पैक्ट और लचीला चाहता था इसलिए मैंने फ़ंक्शन pif(पाइप अगर) डिजाइन किया , तो कोड देखें और जवाब के अंत में डॉक्टर को देखें।

तर्क या तो फ़ंक्शन के भाव हो सकते हैं (सूत्र संकेतन समर्थित है), और इनपुट डिफ़ॉल्ट रूप से अपरिवर्तित है यदि स्थिति है FALSE

अन्य उत्तरों से उदाहरणों पर प्रयुक्त:

## from Ben Bolker
data.frame(a=1:2) %>% 
  mutate(b=a^2) %>%
  pif(~b[1]>1, ~mutate(.,b=b^2)) %>%
  mutate(b=b^2)
#   a  b
# 1 1  1
# 2 2 16

## from Lorenz Walthert
1:3 %>% pif(sum(.) < 25,sum,0)
# [1] 6

## from clbieganek 
1 %>% pif(TRUE,~. + 1) %>% `*`(2)
# [1] 4

# from theforestecologist
1 %>% `+`(1) %>% pif(TRUE ,~ .+1)
# [1] 3

अन्य उदाहरण:

## using functions
iris %>% pif(is.data.frame, dim, nrow)
# [1] 150   5

## using formulas
iris %>% pif(~is.numeric(Species), 
             ~"numeric :)",
             ~paste(class(Species)[1],":("))
# [1] "factor :("

## using expressions
iris %>% pif(nrow(.) > 2, head(.,2))
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1          5.1         3.5          1.4         0.2  setosa
# 2          4.9         3.0          1.4         0.2  setosa

## careful with expressions
iris %>% pif(TRUE, dim,  warning("this will be evaluated"))
# [1] 150   5
# Warning message:
# In inherits(false, "formula") : this will be evaluated
iris %>% pif(TRUE, dim, ~warning("this won't be evaluated"))
# [1] 150   5

समारोह

#' Pipe friendly conditional operation
#'
#' Apply a transformation on the data only if a condition is met, 
#' by default if condition is not met the input is returned unchanged.
#' 
#' The use of formula or functions is recommended over the use of expressions
#' for the following reasons :
#' 
#' \itemize{
#'   \item If \code{true} and/or \code{false} are provided as expressions they 
#'   will be evaluated wether the condition is \code{TRUE} or \code{FALSE}.
#'   Functions or formulas on the other hand will be applied on the data only if
#'   the relevant condition is met
#'   \item Formulas support calling directly a column of the data by its name 
#'   without \code{x$foo} notation.
#'   \item Dot notation will work in expressions only if `pif` is used in a pipe
#'   chain
#' }
#' 
#' @param x An object
#' @param p A predicate function, a formula describing such a predicate function, or an expression.
#' @param true,false Functions to apply to the data, formulas describing such functions, or expressions.
#'
#' @return The output of \code{true} or \code{false}, either as expressions or applied on data as functions
#' @export
#'
#' @examples
#'# using functions
#'pif(iris, is.data.frame, dim, nrow)
#'# using formulas
#'pif(iris, ~is.numeric(Species), ~"numeric :)",~paste(class(Species)[1],":("))
#'# using expressions
#'pif(iris, nrow(iris) > 2, head(iris,2))
#'# careful with expressions
#'pif(iris, TRUE, dim,  warning("this will be evaluated"))
#'pif(iris, TRUE, dim, ~warning("this won't be evaluated"))
pif <- function(x, p, true, false = identity){
  if(!requireNamespace("purrr")) 
    stop("Package 'purrr' needs to be installed to use function 'pif'")

  if(inherits(p,     "formula"))
    p     <- purrr::as_mapper(
      if(!is.list(x)) p else update(p,~with(...,.)))
  if(inherits(true,  "formula"))
    true  <- purrr::as_mapper(
      if(!is.list(x)) true else update(true,~with(...,.)))
  if(inherits(false, "formula"))
    false <- purrr::as_mapper(
      if(!is.list(x)) false else update(false,~with(...,.)))

  if ( (is.function(p) && p(x)) || (!is.function(p) && p)){
    if(is.function(true)) true(x) else true
  }  else {
    if(is.function(false)) false(x) else false
  }
}

"प्रासंगिक स्थिति पूरी होने पर ही डेटा पर कार्य या सूत्र डेटा पर लागू होंगे।" क्या आप बता सकते हैं कि आपने ऐसा करने का फैसला क्यों किया?
मिहागज़्वोडा

इसलिए मैं केवल वही गणना करता हूं जिसकी मुझे गणना करने की आवश्यकता है, लेकिन मुझे आश्चर्य है कि मैंने इसे अभिव्यक्ति के साथ क्यों नहीं किया। किसी कारण से ऐसा लगता है कि मैं गैर मानक मूल्यांकन का उपयोग नहीं करना चाहता था। मुझे लगता है कि मेरे पास मेरे कस्टम फ़ंक्शन में एक संशोधित संस्करण है, मैं मौका मिलने पर अपडेट करूंगा।
मूडी_मडस्किपर

जब आप इसे अपडेट करते हैं तो कृपया मुझे बताएं। धन्यवाद!
मिहागज़्वोडा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.