İfelse () yönteminin Date nesnelerini sayısal nesnelere dönüştürmesi nasıl önlenir


162

ifelse()Bir tarih vektörü işlemek için işlevi kullanıyorum . Sonucun sınıf olmasını bekliyordum ve bunun yerine Datebir numericvektör elde etmek beni şaşırttı . İşte bir örnek:

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)

Bu özellikle şaşırtıcıdır, çünkü tüm vektör boyunca işlemi gerçekleştirmek bir Datenesneyi döndürür .

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

DateVektörler üzerinde çalışmak için başka bir işlev mi kullanmalıyım ? Varsa, hangi işlev? Değilse ifelse, girişle aynı türden bir vektörü döndürmeye nasıl zorlarım?

İçin yardım sayfası ifelse, bunun bir hata değil, bir özellik olduğunu gösterir, ancak hala şaşırtıcı bir davranış bulduğum şey için bir açıklama bulmakta zorlanıyorum.


4
Şimdi if_else()dplyr paketinde ifelsedoğru Date nesnesi sınıflarını korurken yerine kullanılabilecek bir işlev var - aşağıda yeni bir yanıt olarak yayınlandı . Burada dikkat çekiyorum, çünkü bu sorunu çözdüğü gibi bir CRAN paketinde birim test edilmiş ve belgelenmiş bir fonksiyon sağlayarak (bu yorumdan itibaren) önde sıralanan diğer cevapların aksine bu sorunu çözüyor.
Sam Firke

Yanıtlar:


132

data.table::fifelse( data.table >= 1.12.3) Veya kullanabilirsiniz dplyr::if_else.


data.table::fifelse

Bunun aksine ifelse, fifelsegirişlerin türünü ve sınıfını korur.

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

Gönderen dplyr 0.5.0sürüm notları :

[ if_else] daha katı semantiğe sahiptir ifelse(): trueve falseargümanları aynı tür olmalıdır. Bu daha az şaşırtıcı bir dönüş tipi verir ve tarih gibi S3 vektörlerini korur ".

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
Bana bir onay işareti gevşek bile kesinlikle yararlı. Yardım sayfasının geçerli sürümü, faktör bağımsız değişkenlerinden ne bekleyeceğini söylemez. Benim oyum, true've false' seviyelerinin birliği olan düzeylere sahip bir faktör dönüş nesnesi için olurdu .
IRTFM

3
if_elseNA olmanın argümanlarından birine sahip olmanın bir yolu var mı? Mantıksal NA_seçenekleri denedim ve hiçbir şey NA_double_
yapışmıyor ve

11
@Zak Bir olasılık sarılmasıdır NAiçinde as.Date.
Henrik

Var NA_real_, @roarkz. ve @Henrik, buradaki yorumunuz sorunumu çözdü.
BLT

63

Dokümante edilmiş değeri ile ilgilidir ifelse:

Veya ile aynı uzunluk ve niteliklere (boyutlar ve " class" dahil ) testve değerlerinden veri değerlerine sahip bir vektör . Yanıt modu önce alınan tüm değerleri ve sonra alınan tüm değerleri içerecek şekilde mantıksal olarak zorlanacaktır .yesnoyesno

Sonuçları kaynatılır, ifelsefaktörler seviyelerini kaybeder ve Tarihler sınıflarını kaybeder ve sadece modları ("sayısal") geri yüklenir. Bunun yerine şunu deneyin:

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"

Şunları oluşturabilirsiniz 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"

Daha sonraki bir not: Hadley'nin if_elsemagrittr / dplyr / tidyr veri şekillendirme paketlerinin kompleksini oluşturduğunu görüyorum.


37
Biraz daha zarif versiyonu:safe.ifelse <- function(cond, yes, no) structure(ifelse(cond, yes, no), class = class(yes))
hadley

5
Güzel. Bunun varsayılan davranış olmamasının bir nedeni var mı?
IRTFM

sadece "evet" ne koymak dikkatli olun çünkü NA vardı ve işe yaramadı. Sınıfı parametre olarak iletmek, "evet" koşulunun sınıfı olduğunu varsaymaktan daha iyidir.
Denis

1
Son yorumun bu anlama geldiğinden emin değilim. Bir şeyin NA değeri olması, onun bir sınıfı olamaz anlamına gelmez.
IRTFM

Bu sorunun ortaya çıkmasından bu yana 8 yıl geçti ve hala "güvenli"ifelse() değil .
E--

16

DWin'in açıklaması yerinde. İfelse ifadesinden sonra sınıfı zorlayabileceğimi fark etmeden önce bir süre uğraştım ve bununla savaştım:

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)

İlk başta bu bana biraz "hackish" hissetti. Ama şimdi bunu ifelse () 'den aldığım performans iadelerini ödemek için küçük bir fiyat olarak düşünüyorum. Ayrıca hala bir döngüden çok daha özlü.


Bu (güzel, eğer evet, hackish) tekniği gerçeği ile de yardım görünüyor ki R'ın forifadesi atar değeri öğelerin VECTORiçin NAMEdeğil, onların sınıf .
Greg Minshall

6

Önerilen yöntem faktör sütunlarıyla çalışmaz. Bu gelişmeyi önermek istiyorum:

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)
}

Bu arada: ifelse berbat ... büyük bir güçle büyük sorumluluk geliyor, yani 1x1 matris ve / veya nümeriklerin [örneğin eklenmeleri gerektiğinde] tip dönüşümleri bana uygun ama ifelse'deki bu tip dönüşüm açıkça istenmeyen. Şu anda birden fazla kez ifelse 'böcek' içine çarptı ve sadece zamanımı çalmaya devam ediyor :-(

FW


Bu, faktörler için benim için çalışan tek çözüm.
bshor

Ben seviyeleri düzeylerinin birliğini olacağını iade edilecek düşünürdü yesve noilk ikisi faktörler olduğunu görmek için kontrol edeceğini ve. Muhtemelen karaktere dönüştürmeniz ve daha sonra "sendikalaşmış" seviyelerle tekrar oynamanız gerekir.
IRTFM

6

Bunun işe yaramamasının nedeni, ifelse () işlevinin değerleri faktörlere dönüştürmesidir. Değerlendirilmeden önce karakterlere dönüştürmek hoş bir çözüm olabilir.

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)))

Bu, R tabanı dışında herhangi bir kütüphane gerektirmez.


5

@ Fabian-werner tarafından verilen cevap harika, ama nesneler birden fazla sınıfa sahip olabilir ve "faktör" illa döndürülmeyebilir class(yes), bu yüzden tüm sınıf özelliklerini kontrol etmek için bu küçük değişikliği öneriyorum:

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)
    }

Ben de R :: Geliştirme ekibi ile temel :: ifelse () korumak için hangi özellikleri korumak için kullanıcı seçimi dayalı nitelikleri korumak için belgelenmiş bir seçenek eklemek için bir istek gönderdim. İstek burada: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16609 - Her zaman olduğu gibi olduğu için zaten "WONTFIX" olarak işaretlenmiş, ancak basit bir eklemenin neden birçok R kullanıcısının baş ağrısını kurtarabileceğine dair bir takip argümanı sundum. Belki de bu hata dizisindeki "+1", R Core ekibini ikinci bir bakış atmaya teşvik edecektir.

DÜZENLEME: Burada, kullanıcının "cond" (varsayılan ifelse () davranışı), "yes", yukarıdaki koda göre davranışı veya "no" ifadesini, "hayır" değerinin özellikleri daha iyidir:

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")daha "doğru" olabilir"factor" %in% class.y
IRTFM

Aslında. inheritsen iyisi olabilir.
Mekki MacAulay
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.