Проблемы с вложенным ifelse (), ссылающимся на предыдущую строку с помощью shift () - PullRequest
0 голосов
/ 29 апреля 2020

Я пытаюсь применить различные бизнес-логики c к моему исходному набору данных, используя вложенные ifelse () и shift (), но мой код дает мне результаты, которые я не понимаю. Проблема заключается в следующем: у меня есть набор данных клиентов, содержащий «CustomerID», «Month» и «Status».

CustomerID  Month   Status
43210       1/1/18  A
43210       2/1/18  B
43210       3/1/18  A
43210       4/1/18  B
43210       5/1/18  C
43210       6/1/18  D
43210       7/1/18  B
6543        1/1/19  C
6543        2/1/19  D
6543        3/1/19  A
6543        4/1/19  B
6543        5/1/19  C
6543        6/1/19  A

Мне нужно создать третий столбец с именем «Journey_Status», применив следующие логики c.

#Initialize the 1st record of each Customer.
Example1$Journey_Status=ifelse(!duplicated(Example1[,c("CustomerID")]), Example1$Status, "")

Example1[ , Journey_Status := ifelse(duplicated(CustomerID),
   ifelse(shift(Journey_Status, 1, type="lag") == "A" &
          Status %in% c("A", "B", "C", "D"),
          Status,
   ifelse(shift(Journey_Status, 1, type="lag") == "B" &
          Status %in% c("B", "C", "D"),
          Status,
   ifelse(shift(Journey_Status, 1, type="lag") == "C" &
          Status %in% c("C", "D"),
          Status,
   ifelse(shift(Journey_Status, 1, type="lag") == "D" &
          Status %in% c("D"),
          Status,
          shift(Journey_Status, 1, type="lag"))))),
Journey_Status)][]

Я ожидаю увидеть следующее:

CustomerID  Month   Status  Journey_Status
43210       1/1/18  A       A
43210       2/1/18  B       B
43210       3/1/18  A       B
43210       4/1/18  B       B
43210       5/1/18  C       C
43210       6/1/18  D       D
43210       7/1/18  B       D
6543        1/1/19  C       C
6543        2/1/19  D       D
6543        3/1/19  A       D
6543        4/1/19  B       D
6543        5/1/19  C       D
6543        6/1/19  A       D

Вместо этого я получаю следующее:

CustomerID  Month   Status  Journey_Status
43210       1/1/18  A       A
43210       2/1/18  B       B
43210       3/1/18  A       
43210       4/1/18  B       
43210       5/1/18  C       
43210       6/1/18  D       
43210       7/1/18  B       
6543        1/1/19  C       C
6543        2/1/19  D       D
6543        3/1/19  A       
6543        4/1/19  B       
6543        5/1/19  C       
6543        6/1/19  A       

Я не вижу, как выполняется shift() ... Ваша помощь очень ценится !!! Спасибо !!!

1 Ответ

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

Есть несколько проблем с вашей текущей реализацией:

  1. Выполните группировку за CustomerID с оператором by=, не пытайтесь сделать это вручную с duplicated. Это похоже на то, как электронные таблицы заставляют нас думать (так как они обычно не облегчают работу с подгруппами), где «если идентификатор этой строки отличается от идентификатора последней строки, сделайте что-то другое». Доверьтесь оператору data.table by= (или dplyr::group_by или нескольким другим).

  2. Вы не учитываете новый "сдвинутый" элемент в shift, либо fill= или другим механизмом. Это вводит NA в столбце, см. Код № 3.

  3. К сожалению, ваша реализация действительна только в определенный момент времени, даже если мы исправляем группировку и более используйте поля fifelse (я склонен полагать, что более 2-3 вложенных объектов используются с перерасходом), тогда мы по-прежнему видим проблему:

    dat[, lagstat := shift(Status, type = "lag"), by = .(CustomerID) ]
    dat[, Journey_Status :=
            fifelse(is.na(lagstat), Status,
                    fifelse(lagstat == "A" & Status %in% c("A", "B", "C", "D"), Status,
                            fifelse(lagstat == "B" & Status %in% c("B", "C", "D"), Status,
                                    fifelse(lagstat == "C" & Status %in% c("C", "D"), Status,
                                            fifelse(lagstat == "D" & Status %in% c("D"), Status,
                                                    lagstat))))),
        by = .(CustomerID)]
    dat
    #     CustomerID  Month Status lagstat Journey_Status
    #  1:      43210 1/1/18      A    <NA>              A
    #  2:      43210 2/1/18      B       A              B
    #  3:      43210 3/1/18      A       B              B
    #  4:      43210 4/1/18      B       A              B
    #  5:      43210 5/1/18      C       B              C
    #  6:      43210 6/1/18      D       C              D
    #  7:      43210 7/1/18      B       D              D
    #  8:       6543 1/1/19      C    <NA>              C
    #  9:       6543 2/1/19      D       C              D
    # 10:       6543 3/1/19      A       D              D
    # 11:       6543 4/1/19      B       A              B  # fail
    # 12:       6543 5/1/19      C       B              C  # fail
    # 13:       6543 6/1/19      A       C              C  # fail
    

    Хотя это выглядит лучше, давайте посмотрим, где это терпит неудачу: строка 11. Когда он проверяет значение lagstat, он просматривает его во время до запуска этой цепочки fifelse, а не сразу после обработки fifelse предыдущей строки. То есть он вычисляет все векторы в пределах fifelse s на основе одного и того же состояния данных. Даже если вы сделаете shift в каждом fifelse, оно не увидит значение предыдущего отстающего значения.

Я думаю, что вам нужна кумулятивная функция. Я собираюсь предположить, что в данных есть четкая ординальность, где max будет работать. Для записи, хотя я бы предпочел использовать cummax, max(c("A", "B")) работает, но cummax(c("A", "B")) - нет. Поэтому мы делаем это вручную, используя Reduce(..., accumulate=TRUE).

Начиная с данных fre sh dat.

dat[, Journey_Status := Reduce(max, Status, accumulate = TRUE),
    by = .(CustomerID) ]
dat
#     CustomerID  Month Status Journey_Status
#  1:      43210 1/1/18      A              A
#  2:      43210 2/1/18      B              B
#  3:      43210 3/1/18      A              B
#  4:      43210 4/1/18      B              B
#  5:      43210 5/1/18      C              C
#  6:      43210 6/1/18      D              D
#  7:      43210 7/1/18      B              D
#  8:       6543 1/1/19      C              C
#  9:       6543 2/1/19      D              D
# 10:       6543 3/1/19      A              D
# 11:       6543 4/1/19      B              D
# 12:       6543 5/1/19      C              D
# 13:       6543 6/1/19      A              D

Даже если max не работает, если у вас есть доморощенная функция, которая обрабатывает ординальность вручную, затем вы можете заменить ее на max, и это должно сработать.

Примечание: предложение DanY о том, что если Status было действительно целым числом или числом c, то это было бы еще проще:

datnum[, Journey_Status := cummax(Status), by = .(CustomerID) ]
datnum
#     CustomerID  Month Status Journey_Status
#  1:      43210 1/1/18      1              1
#  2:      43210 2/1/18      2              2
#  3:      43210 3/1/18      1              2
#  4:      43210 4/1/18      2              2
#  5:      43210 5/1/18      3              3
#  6:      43210 6/1/18      4              4
#  7:      43210 7/1/18      2              4
#  8:       6543 1/1/19      3              3
#  9:       6543 2/1/19      4              4
# 10:       6543 3/1/19      1              4
# 11:       6543 4/1/19      2              4
# 12:       6543 5/1/19      3              4
# 13:       6543 6/1/19      1              4

Данные:

dat <- fread(text="
CustomerID  Month   Status
43210       1/1/18  A
43210       2/1/18  B
43210       3/1/18  A
43210       4/1/18  B
43210       5/1/18  C
43210       6/1/18  D
43210       7/1/18  B
6543        1/1/19  C
6543        2/1/19  D
6543        3/1/19  A
6543        4/1/19  B
6543        5/1/19  C
6543        6/1/19  A")

datnum <- fread(text="
CustomerID  Month   Status
43210       1/1/18  1
43210       2/1/18  2
43210       3/1/18  1
43210       4/1/18  2
43210       5/1/18  3
43210       6/1/18  4
43210       7/1/18  2
6543        1/1/19  3
6543        2/1/19  4
6543        3/1/19  1
6543        4/1/19  2
6543        5/1/19  3
6543        6/1/19  1")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...