Использование условного оператора elseif в R, но не работает - PullRequest
1 голос
/ 06 апреля 2020

Я впервые использую elseif. Я sh создаю новый столбец mobile$tenuredate (в месяцах) и пытаюсь выяснить проблему с моим кодом, который выдает значения NA.

Результаты

mobile$status == 'active'

строки дают значения NA для mobile$tenuredate (они не должны быть NA).

mobile$status == 'stopped'

строки дают действительные значения для mobile$tenuredate.

Ниже приведен код

mobile$tenuredate = if (mobile$status=="stopped") {
  round(difftime(mobile$EFFECTIVEDATE, mobile$STARTDATE, units="weeks") / 4.348125)
} else if ((mobile$status == "active") && (mobile$difftemp >= 0)) {
  round(difftime(mobile$CONTRACTENDDATE, mobile$STARTDATE, units="weeks") / 4.348125)
} else {
  round(difftime(mobile$CUTOFFDATE, mobile$STARTDATE, units="weeks") / 4.348125)
}

Файл данных в формате CSV доступен здесь

Вот примерный кадр данных.

structure(list(STARTDATE = structure(c(11413, 11639, 11953, 12212, 
11335, 12050, 12142, 11225, 12176, 11386), class = "Date"), STOPDATE = structure(c(11436, 
12079, NA, 12225, 11345, 12124, 12226, 11999, 12176, 11758), class = "Date"), 
    EFFECTIVEDATE = structure(c(11436, 12079, NA, 12225, 11345, 
    12124, 12226, 11999, 12176, 11758), class = "Date"), CONTRACTENDDATE = structure(c(11778, 
    12004, 12318, 12578, 11700, 12415, 12508, 11977, 12542, 11751
    ), class = "Date"), CUTOFFDATE = structure(c(12273, 12273, 
    12273, 12273, 12273, 12273, 12273, 12273, 12273, 12273), class = "Date"), 
    status = c("stopped", "stopped", "active", "stopped", "stopped", 
    "stopped", "stopped", "stopped", "stopped", "stopped"), tenuredate = structure(c(1, 
    14, NA, 0, 0, 2, 3, 25, 0, 12), class = "difftime", units = "weeks")), row.names = c(NA, 
-10L), class = c("tbl_df", "tbl", "data.frame"))

Заранее спасибо.

1 Ответ

1 голос
/ 06 апреля 2020

if требует, чтобы его условное выражение имело длину 1, тогда как вы предоставляете вектор. Логической заменой было бы использование ifelse, но это хорошо известная (среди ветеранов R) проблема с ifelse в том, что он отбросит класс, поэтому ваши Date или difftime столбцы станут numeric, и ты должен их переделать. (Это не конец света, но давайте просто оставим класс таким, какой он есть сейчас.)

mobile$tenuredate <- NULL # just to clean up your previous attempt, otherwise not needed
mobile$usedate <- Sys.Date()[NA] # all NAs are not created equal ...
ind <- mobile$status == "stopped"
mobile$usedate[ind] <- mobile$EFFECTIVEDATE[ind]
ind <- (mobile$status == "active") && (mobile$difftemp >= 0)
mobile$usedate[ind] <- mobile$CONTRACTENDDATE[ind]
ind <- is.na(mobile$usedate)
mobile$usedate[ind] <- mobile$CUTOFFDATE[ind]
mobile
# # A tibble: 10 x 7
#    STARTDATE  STOPDATE   EFFECTIVEDATE CONTRACTENDDATE CUTOFFDATE status  usedate   
#    <date>     <date>     <date>        <date>          <date>     <chr>   <date>    
#  1 2001-04-01 2001-04-24 2001-04-24    2002-04-01      2003-08-09 stopped 2001-04-24
#  2 2001-11-13 2003-01-27 2003-01-27    2002-11-13      2003-08-09 stopped 2003-01-27
#  3 2002-09-23 NA         NA            2003-09-23      2003-08-09 active  2003-08-09
#  4 2003-06-09 2003-06-22 2003-06-22    2004-06-09      2003-08-09 stopped 2003-06-22
#  5 2001-01-13 2001-01-23 2001-01-23    2002-01-13      2003-08-09 stopped 2001-01-23
#  6 2002-12-29 2003-03-13 2003-03-13    2003-12-29      2003-08-09 stopped 2003-03-13
#  7 2003-03-31 2003-06-23 2003-06-23    2004-03-31      2003-08-09 stopped 2003-06-23
#  8 2000-09-25 2002-11-08 2002-11-08    2002-10-17      2003-08-09 stopped 2002-11-08
#  9 2003-05-04 2003-05-04 2003-05-04    2004-05-04      2003-08-09 stopped 2003-05-04
# 10 2001-03-05 2002-03-12 2002-03-12    2002-03-05      2003-08-09 stopped 2002-03-12

Может быть полезно сделать здесь паузу и проверить, что все значения usedate взяты из соответствующих столбцов.

Я использую usedate в качестве промежуточного значения по двум причинам: (1) для проверки; и (2) потому что вы делаете одну и ту же математику для всего остального ... так зачем хранить одну и ту же математику в трех местах, просто сделайте это один раз. Конечно, есть и другие способы сделать это.

mobile$tenuredate <- round(difftime(mobile$usedate, mobile$STARTDATE, units = "weeks") / 4.348125)
mobile
# # A tibble: 10 x 8
#    STARTDATE  STOPDATE   EFFECTIVEDATE CONTRACTENDDATE CUTOFFDATE status  usedate    tenuredate
#    <date>     <date>     <date>        <date>          <date>     <chr>   <date>     <drtn>    
#  1 2001-04-01 2001-04-24 2001-04-24    2002-04-01      2003-08-09 stopped 2001-04-24  1 weeks  
#  2 2001-11-13 2003-01-27 2003-01-27    2002-11-13      2003-08-09 stopped 2003-01-27 14 weeks  
#  3 2002-09-23 NA         NA            2003-09-23      2003-08-09 active  2003-08-09 11 weeks  
#  4 2003-06-09 2003-06-22 2003-06-22    2004-06-09      2003-08-09 stopped 2003-06-22  0 weeks  
#  5 2001-01-13 2001-01-23 2001-01-23    2002-01-13      2003-08-09 stopped 2001-01-23  0 weeks  
#  6 2002-12-29 2003-03-13 2003-03-13    2003-12-29      2003-08-09 stopped 2003-03-13  2 weeks  
#  7 2003-03-31 2003-06-23 2003-06-23    2004-03-31      2003-08-09 stopped 2003-06-23  3 weeks  
#  8 2000-09-25 2002-11-08 2002-11-08    2002-10-17      2003-08-09 stopped 2002-11-08 25 weeks  
#  9 2003-05-04 2003-05-04 2003-05-04    2004-05-04      2003-08-09 stopped 2003-05-04  0 weeks  
# 10 2001-03-05 2002-03-12 2002-03-12    2002-03-05      2003-08-09 stopped 2002-03-12 12 weeks  

(Если вы знаете, что вам это не нужно, mobile$usedate <- NULL.)


Если вы используете любой из пакетов tidyverse, это можно сделать, используя case_when гораздо более кратко:

library(dplyr)
as_tibble(mobile) %>%
  mutate(
    usedate = case_when(
      status == "stopped"                     ~ EFFECTIVEDATE,
      (status == "active") && (difftemp >= 0) ~ CONTRACTENDDATE,
      TRUE                                    ~ CUTOFFDATE
    ),
    tenuredate = round(difftime(usedate, STARTDATE, units = "weeks") / 4.348125)
  )

или решение data.table:

library(data.table)
as.data.table(mobile)[
  , usedate := Sys.Date()[NA] ][
    status == "stopped", usedate := EFFECTIVEDATE ][
      (status == "active") && (difftemp >= 0), usedate := CONTRACTENDDATE ][
        is.na(usedate), usedate := CUTOFFDATE ][
          , tenuredate := round(difftime(usedate, STARTDATE, units = "weeks") / 4.348125) ]

Если вы объедините data.table с трубкой magrittr, тогда вы можете найти это более читабельным:

library(data.table)
library(magrittr)
as.data.table(mobile) %>%
  .[ , usedate := Sys.Date()[NA] ] %>%
  .[ status == "stopped", usedate := EFFECTIVEDATE ] %>%
  .[ (status == "active") && (difftemp >= 0), usedate := CONTRACTENDDATE ] %>%
  .[ is.na(usedate), usedate := CUTOFFDATE ] %>%
  .[ , tenuredate := round(difftime(usedate, STARTDATE, units = "weeks") / 4.348125) ]

Подтверждает мое утверждение, что ifelse отбрасывает класс:

...