दिनांक ऑब्जेक्ट्स को संख्यात्मक ऑब्जेक्ट में बदलने से ifelse () को कैसे रोकें


161

मैं फ़ंक्शन ifelse()का उपयोग दिनांक वेक्टर में हेरफेर करने के लिए कर रहा हूं । मुझे उम्मीद थी कि परिणाम कक्षा का होगा Date, और numericइसके बजाय एक वेक्टर प्राप्त करने के लिए आश्चर्यचकित था । यहाँ एक उदाहरण है:

dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05'))
dates <- ifelse(dates == '2011-01-01', dates - 1, dates)
str(dates)

यह विशेष रूप से आश्चर्य की बात है क्योंकि पूरे वेक्टर में ऑपरेशन करने से एक Dateवस्तु वापस आ जाती है ।

dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04','2011-01-05'))
dates <- dates - 1
str(dates)

क्या मुझे Dateवैक्टर पर काम करने के लिए किसी अन्य फ़ंक्शन का उपयोग करना चाहिए ? यदि हां, तो क्या कार्य है? यदि नहीं, तो मैं ifelseइनपुट के समान प्रकार के वेक्टर को वापस करने के लिए कैसे मजबूर करूं ?

इसके लिए सहायता पृष्ठ ifelseइंगित करता है कि यह एक विशेषता है, बग नहीं है, लेकिन मैं अभी भी जो मुझे आश्चर्यचकित करने वाला व्यवहार के लिए एक स्पष्टीकरण खोजने के लिए संघर्ष कर रहा हूं।


4
अब if_else()dplyr पैकेज में एक फंक्शन है जो ifelseडेट ऑब्जेक्ट्स की सही कक्षाओं को बनाए रखते हुए स्थानापन्न कर सकता है - यह एक हालिया उत्तर के रूप में नीचे पोस्ट किया गया है । मैं यहाँ इस पर ध्यान आकर्षित कर रहा हूँ क्योंकि यह इस समस्या को हल करता है जो कि एक यूनिट परीक्षण है और एक CRAN पैकेज में प्रलेखित है, कई अन्य उत्तरों के विपरीत जो (इस टिप्पणी के अनुसार) इसके आगे रैंक किए गए थे।
सैम फर्के

जवाबों:


132

आप data.table::fifelse( data.table >= 1.12.3) या का उपयोग कर सकते हैं dplyr::if_else


data.table::fifelse

इसके विपरीत ifelse, fifelseइनपुट के प्रकार और वर्ग को संरक्षित करता है।

library(data.table)
dates <- fifelse(dates == '2011-01-01', dates - 1, dates)
str(dates)
# Date[1:5], format: "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"

dplyr::if_else

से dplyr 0.5.0रिलीज नोट्स :

[ if_else] सख्त शब्दार्थ है कि ifelse(): trueऔर falseतर्क एक ही प्रकार के होने चाहिए। यह कम आश्चर्यजनक रिटर्न प्रकार देता है, और तारीखों की तरह S3 वैक्टर को संरक्षित करता है ”।

library(dplyr)
dates <- if_else(dates == '2011-01-01', dates - 1, dates)
str(dates)
# Date[1:5], format: "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05" 

2
निश्चित रूप से उपयोगी भले ही उसने मुझे एक चेकमार्क ढीला कर दिया हो। मदद पृष्ठ का वर्तमान संस्करण यह नहीं कहता है कि कारक तर्कों से क्या उम्मीद की जाए। मेरा वोट एक ऐसे कारक रिटर्न ऑब्जेक्ट के लिए होगा जिसमें ऐसे स्तर थे जो true's false' और 's' के स्तर के मेल थे।
IRTFM

3
क्या कोई तरीका है कि if_elseNA का एक तर्क हो? मैंने तार्किक NA_विकल्पों का प्रयास किया है और कुछ भी नहीं चिपका है और मुझे विश्वास नहीं है किNA_double_
गर्ज

11
@Zak एक संभावना में लपेटने के लिए NAहै as.Date
हेनरिक

वहाँ है NA_real_, @roarkz। और @ हेनरिक, आपकी टिप्पणी से मेरी समस्या हल हो गई।
बीएलटी

63

यह संबंधित मान से संबंधित है ifelse:

समान लंबाई और विशेषताओं का एक वेक्टर (आयामों और " class") के रूप में testऔर डेटा मानों से yesया के मानों से no। उत्तर के मोड को पहले से लिए गए किसी भी मान yesऔर फिर से लिए गए किसी भी मान को समायोजित करने के लिए तार्किक से ज़ब्त किया जाएगा no

इसके निहितार्थों से उबला हुआ, ifelseकारक अपने स्तर को खो देता है और तिथियां अपनी कक्षा खो देती हैं और केवल उनका मोड ("संख्यात्मक") बहाल हो जाता है। इसके बजाय यह प्रयास करें:

dates[dates == '2011-01-01'] <- dates[dates == '2011-01-01'] - 1
str(dates)
# Date[1:5], format: "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"

आप एक बना सकते हैं safe.ifelse:

safe.ifelse <- function(cond, yes, no){ class.y <- class(yes)
                                  X <- ifelse(cond, yes, no)
                                  class(X) <- class.y; return(X)}

safe.ifelse(dates == '2011-01-01', dates - 1, dates)
# [1] "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"

एक बाद का नोट: मैं देखता हूं कि हेडली ने if_elseडेटा-शेपिंग पैकेजों के मैग्रिट्र / डप्पर / टिडियर कॉम्प्लेक्स में बनाया है।


37
थोड़ा और अधिक सुंदर संस्करण:safe.ifelse <- function(cond, yes, no) structure(ifelse(cond, yes, no), class = class(yes))
हैडली

5
अच्छा लगा। क्या आप कोई कारण देखते हैं कि डिफ़ॉल्ट व्यवहार क्यों नहीं है?
IRTFM

बस सावधान रहें कि आपने "हां" में क्या डाला क्योंकि मेरे पास एनए था और यह काम नहीं करता था। संभवतः कक्षा को एक मानदंड मानने से बेहतर है कि यह "हाँ" स्थिति का वर्ग है।
डेनिस

1
मुझे यकीन नहीं है कि अंतिम टिप्पणी का मतलब यह है। सिर्फ इसलिए कि कुछ एनए मूल्य है इसका मतलब यह नहीं है कि यह एक वर्ग नहीं हो सकता है।
IRTFM

8 साल के बाद से इस मुद्दे आते हैं और अभी भी है ifelse()नहीं है "सुरक्षित"
M--

16

DWin की व्याख्या मौके पर है। मैं कुछ समय के लिए इससे लड़ता और लड़ता, इससे पहले कि मुझे एहसास होता कि मैं बस ifelse स्टेटमेंट के बाद क्लास को मजबूर कर सकता हूं:

dates <- as.Date(c('2011-01-01','2011-01-02','2011-01-03','2011-01-04','2011-01-05'))
dates <- ifelse(dates=='2011-01-01',dates-1,dates)
str(dates)
class(dates)<- "Date"
str(dates)

पहले तो यह मुझे थोड़ा "हैकिश" लगा। लेकिन अब मैं इसे केवल उस छोटे रिटर्न के रूप में सोचता हूं, जो प्रदर्शन रिटर्न के लिए भुगतान करने के लिए मिलता है, जो मुझे इफेलसे () से मिलता है। प्लस यह अभी भी एक लूप की तुलना में बहुत अधिक संक्षिप्त है।


यह (अच्छा, अगर, हाँ, हैकिश) तकनीक इस तथ्य के साथ भी मदद करती है कि आर के forबयान में आइटम के मूल्य को निर्दिष्ट किया गया VECTORहै NAME, लेकिन उनकी कक्षा को नहीं ।
ग्रेग मिनशाल

6

सुझाई गई विधि कारक कॉलम के साथ काम नहीं करती है। इस सुधार का सुझाव देना चाहता हूं:

safe.ifelse <- function(cond, yes, no) {
  class.y <- class(yes)
  if (class.y == "factor") {
    levels.y = levels(yes)
  }
  X <- ifelse(cond,yes,no)
  if (class.y == "factor") {
    X = as.factor(X)
    levels(X) = levels.y
  } else {
    class(X) <- class.y
  }
  return(X)
}

वैसे: ifelse बेकार है ... महान शक्ति के साथ बड़ी जिम्मेदारी आती है, अर्थात 1x1 मैट्रिसेस और / या संख्यात्मक के रूपांतरण [जब उन्हें उदाहरण के लिए जोड़ा जाना चाहिए] मेरे लिए ठीक है लेकिन ifelse में इस प्रकार का रूपांतरण स्पष्ट रूप से अवांछित है। मैं कई बार ifelse के बहुत ही 'बग' में टकरा गया और यह मेरा समय चुराता रहता है :-(

परिवार कल्याण


यह एकमात्र समाधान है जो कारकों के लिए मेरे लिए काम करता है।
bshor

मैं सोचा होगा कि स्तरों के स्तर का मिलन होगा वापस करने yesऔर noऔर है कि आप पहले देखने के लिए कि वे दोनों कारक थे की जाँच करेगा। आपको संभवतः चरित्र को रूपांतरित करने की आवश्यकता होगी और फिर "संघबद्ध" -वेल्स के साथ विद्रोह करना होगा।
IRTFM

6

कारण यह काम नहीं करेगा क्योंकि, ifelse () फ़ंक्शन मानों को कारकों में परिवर्तित करता है। इसका मूल्यांकन करने से पहले एक अच्छा समाधान इसे पात्रों में बदलना होगा।

dates <- as.Date(c('2011-01-01','2011-01-02','2011-01-03','2011-01-04','2011-01-05'))
dates_new <- dates - 1
dates <- as.Date(ifelse(dates =='2011-01-01',as.character(dates_new),as.character(dates)))

इसके लिए आधार आर के अलावा किसी भी पुस्तकालय की आवश्यकता नहीं होगी।


5

@ फैबियन-वर्नर द्वारा प्रदान किया गया उत्तर बहुत अच्छा है, लेकिन वस्तुओं में कई कक्षाएं हो सकती हैं, और "कारक" जरूरी नहीं कि पहले एक द्वारा लौटा जा सकता है class(yes), इसलिए मैं सभी वर्ग विशेषताओं की जांच करने के लिए इस छोटे संशोधन का सुझाव देता हूं:

safe.ifelse <- function(cond, yes, no) {
      class.y <- class(yes)
      if ("factor" %in% class.y) {  # Note the small condition change here
        levels.y = levels(yes)
      }
      X <- ifelse(cond,yes,no)
      if ("factor" %in% class.y) {  # Note the small condition change here
        X = as.factor(X)
        levels(X) = levels.y
      } else {
        class(X) <- class.y
      }
      return(X)
    }

मैंने आर डेवलपमेंट टीम के साथ आधार :: ifelse () संरक्षित करने के लिए उपयोगकर्ता के चयन के आधार पर एक डॉक्यूमेंटेड विकल्प जोड़ने के लिए एक अनुरोध प्रस्तुत किया है, जिसमें विशेषताओं को संरक्षित करना है। निवेदन यहाँ है: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=1669 - यह पहले से ही इस आधार पर "WONTFIX" के रूप में फ़्लैग किया गया है कि यह हमेशा से अब है, लेकिन मैंने एक अनुवर्ती तर्क प्रदान किया है कि क्यों एक साधारण जोड़ बहुत सारे आर उपयोगकर्ताओं को सिरदर्द से बचा सकता है। शायद उस बग थ्रेड में आपका "+1" आर कोर टीम को दूसरा रूप लेने के लिए प्रोत्साहित करेगा।

EDIT: यहां एक बेहतर संस्करण है जो उपयोगकर्ता को यह निर्दिष्ट करने की अनुमति देता है कि कौन से विशेषताओं को संरक्षित करना है, या तो "cond" (डिफ़ॉल्ट ifelse () व्यवहार), "हां", उपरोक्त कोड के अनुसार व्यवहार, या "no", उन मामलों के लिए जहां "नहीं" मान के गुण बेहतर हैं:

safe_ifelse <- function(cond, yes, no, preserved_attributes = "yes") {
    # Capture the user's choice for which attributes to preserve in return value
    preserved           <- switch(EXPR = preserved_attributes, "cond" = cond,
                                                               "yes"  = yes,
                                                               "no"   = no);
    # Preserve the desired values and check if object is a factor
    preserved_class     <- class(preserved);
    preserved_levels    <- levels(preserved);
    preserved_is_factor <- "factor" %in% preserved_class;

    # We have to use base::ifelse() for its vectorized properties
    # If we do our own if() {} else {}, then it will only work on first variable in a list
    return_obj <- ifelse(cond, yes, no);

    # If the object whose attributes we want to retain is a factor
    # Typecast the return object as.factor()
    # Set its levels()
    # Then check to see if it's also one or more classes in addition to "factor"
    # If so, set the classes, which will preserve "factor" too
    if (preserved_is_factor) {
        return_obj          <- as.factor(return_obj);
        levels(return_obj)  <- preserved_levels;
        if (length(preserved_class) > 1) {
          class(return_obj) <- preserved_class;
        }
    }
    # In all cases we want to preserve the class of the chosen object, so set it here
    else {
        class(return_obj)   <- preserved_class;
    }
    return(return_obj);

} # End safe_ifelse function

1
inherits(y, "factor")हो सकता है "से अधिक सही""factor" %in% class.y
IRTFM

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