R Оператор Case, использующий оба или один оператор - между датами, некоторые с пропущенными значениями в нескольких столбцах - PullRequest
0 голосов
/ 06 июня 2019

Я пытаюсь узнать, был ли участник программы активным в начале каждого месяца.

У меня есть даты начала и окончания программы, но некоторые из этих участников все еще зарегистрированы в программе, поэтому дата окончания равна нулю.

В моей таблице есть первые два столбца, но я бы хотел, чтобы оставшиеся столбцы были заполнены следующим регистром:

case when Date > Admission and (Date < Discharge or Discharge is null) then 'Active' else 'Inactive'

с датой, являющейся первым днем ​​столбца месяца (У меня уже есть столбцы, предварительно заполненные этой датой, поэтому уравнение можно сделать построчно)

Admission Discharge Jan-19   Feb-19   Mar-19
12/3/18   4/3/19    Active   Active   Active
01/7/19   NA        Inactive Active   Active
02/25/19  03/02/19  Inactive Inactive Active

Я пробовал операторы ifelse и if, но не могу понять, как выполнить оба эти условия. Если ничего не помогает, я буду использовать пакет sqldf, но я надеюсь, что есть способ сделать это в R с помощью цикла, так как с течением времени будут добавляться более последние месяцы.

Я попытался запустить этот фрагмент кода, но он не работает с отсутствующими датами выгрузки

Dates$`Sep-2018` <- ifelse(Dates$`Sep-2018` > Dates$Admission_Date & Dates$`Sep-2018` < Dates$Discharge_Date, "Active",
       ifelse(Dates$`Sep-2018` > Dates$Admission_Date & is.na(Dates$Discharge_Date), "Active", "Inactive"))

Есть ли способ применить оператор case к нескольким столбцам?

Ответы [ 3 ]

1 голос
/ 07 июня 2019

В качестве альтернативы, это может быть решено с помощью комбинированного перекрестного соединения и неэквивалентного объединения и последующего изменения формы из длинного в широкий формат.

library(data.table)
months <- seq(as.Date("2019-01-01"), Sys.Date(), by = "month")
cbind(
  dates,
  setDT(dates)[, lapply(.SD, as.Date, format = "%m/%d/%y")][
    is.na(Discharge), Discharge := Sys.Date()][
      , rn := .I][
        .(months), on = .(Admission <= V1, Discharge >= V1)
        , allow.cartesian = TRUE, .(rn, V1, active = "Active")][
          , V1 := factor(V1, labels = format(months, "%b-%y"))][
          , dcast(.SD, rn ~ V1, value.var = "active", fill = "Inactive")][
            , rn := NULL]
)
   Admission Discharge   Jan-19   Feb-19 Mrz-19   Apr-19   Mai-19   Jun-19
1:   12/3/18    4/3/19   Active   Active Active   Active Inactive Inactive
2:   01/7/19      <NA> Inactive   Active Active   Active   Active   Active
3:  02/25/19  03/02/19 Inactive Inactive Active Inactive Inactive Inactive

Пояснение

  1. months содержит вектор контрольных дат.Здесь используется первый день каждого месяца.
  2. Даты Admission и Discharge приводятся от символа к классу Date, чтобы можно было рассчитать дату.
  3. Любые пропущенные Discharge даты заполняются до текущей даты.
  4. Добавляется номер строки, чтобы сохранить исходный порядок строк в последующем преобразовании.
  5. Затем он соединяется справа с months.Это неэквивалентное перекрестное объединение , которое возвращает только случаи, когда первый день месяца V1 находится между датами приема и выписки.V1 - это имя столбца по умолчанию, создаваемое при превращении вектора months в список на .(months).allow.cartesian = TRUE указывает на перекрестное соединение.При объединении создается новый столбец active со значением по умолчанию "Active".
  6. V1 преобразуется в коэффициент с соответствующим названием месяцев, например, "Jan-19", "Feb-19",и т. д. Это гарантирует, что даты будут отображаться в правильном порядке (вместо сортировки лексикографически) при последующем преобразовании.
  7. dcast() изменяет данные с длинного на широкий формат, в то время как отсутствующие записи заполняются "Inactive".
  8. Номера строк удалены.
  9. Наконец, результат объединяется с исходным набором данных dates с использованием cbind().

Укороченная версия

Выше код пытается воспроизвести ожидаемый результат OP как можно ближе.Та же самая информация (но в другом виде) может быть получена с использованием более краткого кода:

setDT(dates)[, lapply(.SD, as.Date, format = "%m/%d/%y")][
  is.na(Discharge), Discharge := Sys.Date()][
    , rn := .I][
      .(months), on = .(Admission <= V1, Discharge >= V1), allow.cartesian = TRUE
      , .(rn, Admission = x.Admission, Disscharge = x.Discharge, V1)][
        , dcast(.SD, rn + ... ~ V1, length)]

, который возвращает

   rn  Admission Disscharge 2019-01-01 2019-02-01 2019-03-01 2019-04-01 2019-05-01 2019-06-01
1:  1 2018-12-03 2019-04-03          1          1          1          1          0          0
2:  2 2019-01-07 2019-06-07          0          1          1          1          1          1
3:  3 2019-02-25 2019-03-02          0          0          1          0          0          0

Данные

library(data.table)
dates <- fread("Admission Discharge Jan-19   Feb-19   Mar-19
12/3/18   4/3/19    Active   Active   Active
01/7/19   NA        Inactive Active   Active
02/25/19  03/02/19  Inactive Inactive Active"
            , select = 1:2)

dates
   Admission Discharge
1:   12/3/18    4/3/19
2:   01/7/19      <NA>
3:  02/25/19  03/02/19
1 голос
/ 07 июня 2019

Использование создания динамической переменной dplyr:

library(dplyr) # version 0.6 and above
library(lubridate)

df <- df %>% 
  select(Admission, Discharge) %>% 
  mutate_all(mdy) # convert the columns to date format

# Start dates of the months and respective month names
my_months <- ymd("2019-01-01", "2019-02-01", "2019-03-01", "2019-04-01")
month_names <- month(my_months, label=T) %>% as.character()


# Looping through the months and dynamic creation of month columns
for (i in seq(length(my_months))){
  df <- df %>%  
    mutate(!!month_names[i] := ifelse(my_months[i] > Admission &
                                        (my_months[i] < Discharge | is.na(Discharge)), 
                                        "Active", "NotActive"))
}
1 голос
/ 06 июня 2019

Это немного неуклюже, но если вы хотите сделать цикл for, вы можете сделать что-то вроде этого:

for(i in 1:nrow(df)){
  startDate<-df[i,1] #Puts the start date in assuming the admission column is column 1
  endDate<-df[i,2] #Puts the end date in assuming the discharge column is column 2
  for(z in 3:ncol(df)){ #Starts at the date column
    colValue<-colnames(df[z]) #Gives the value of the column name
    if(startDate>colValue){ #If the participant has not been enrolled yet
      df[i,z]<-"Inactive"
    }
    if(startDate<colValue){ #If the startdate is before the value of the column
      if(colValue<endDate | is.null(endDate)){
        df[i,z]<-"Active"
      }else{
        df[i,z]<="Inactive"
      }
    }
    }
  }

Надеюсь, я правильно понял ваш вопрос, и это работает. Если этого не произойдет, то даты, вероятно, нужно будет преобразовать в даты-времени POSIXct в R.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...