함수 - r 조건문 예제




ifelse()가 Date 객체를 숫자 객체로 바꾸는 것을 방지하는 방법 (4)

@ fabian-werner가 제공 한 대답은 훌륭하지만 객체는 여러 클래스를 가질 수 있으며 "factor"가 반드시 class(yes) 의해 반환 된 첫 번째 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)
    }

또한 R development 팀과 함께 base :: ifelse ()가 보관할 속성을 사용자가 선택하여 속성을 보존하도록하는 문서화 된 옵션을 추가하는 요청을 제출했습니다. 요청은 여기에 있습니다 : https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16609 - 그것은 항상 지금과 같은 이유로 "WONTFIX"로 이미 플래그되었습니다. 그러나 나는 왜 단순한 추가가 많은 R 사용자 두통을 구할 수 있는지에 대한 후속 논의를 제공했다. 그 버그 스레드의 "+1"로 인해 R 핵심 팀이 두 번째 모습을 보일 것입니다.

수정 : 여기에 보존 할 속성을 지정할 수있는 더 나은 버전이 있습니다. "cond"(기본 ifelse () 동작), "yes", 위의 코드에 따른 동작 또는 "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

함수 ifelse() 를 사용하여 날짜 벡터를 조작하고 있습니다. 나는 결과가 클래스 Date 일 것을 기대 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 대한 도움말 페이지는 이것이 버그가 아니라 기능이라는 것을 나타내지 만 나는 놀라운 행동으로 밝혀진 것에 대한 설명을 찾기 위해 여전히 고심하고 있습니다.


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)

처음에 이것은 나에게 조금 "hackish"하다고 느꼈다. 하지만 이제는 ifelse ()에서 얻은 성과 수익을 지불하기위한 작은 가격으로 생각합니다. 게다가 루프보다 훨씬 간결합니다.


이것이 작동하지 않는 이유는 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)))

이것은베이스 R을 제외한 라이브러리를 필요로하지 않습니다.


제안 된 방법은 요인 열과 함께 작동하지 않습니다. 이 개선을 제안하고자하는 ID :

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와 동일한 '버그'에 여러 번 부딪 혔고 지금도 내 시간을 훔치고있다 .-(

FW





if-statement