Использование mutate ifelse и rollappy для создания условного фактора на основе изменений в непрерывной переменной

Вкратце: мне нужно указать, была ли акция основана на падении (или нет) цены с течением времени или нет. Я открыт для альтернативных подходов.

У меня есть набор данных о ценах, разделенных по нескольким группирующим факторам с течением времени. Моя цель состоит в том, чтобы для каждого «ПУНКТА» в «КАЖДОМ» магазине проверить режим «ЦЕНА» за последние 7 дат (если они существуют). Если значение наблюдения составляет менее 10% от режима цены, то в столбце «Акция» следует поставить 1, если не 0.

ПРИМЕР ДАННЫХ

dat <- data.frame(Date = sample(seq(as.Date('1999/01/01'), as.Date('2000/01/01'), by="day"), 10),
              Item = rep(LETTERS[1:4], times = 10),
              Store =  as.factor(sample(rep(c("NY","SYD","LON","PAR"), each = 10))),
              Price = rnorm(n = 40, mean = 2.5, sd = 1))

До сих пор я использовал group_split dplyr, чтобы разбить элемент и сохранить группы в отдельные фреймы данных, чтобы зафиксировать все условия. Я считаю, что сейчас мне нужно сделать mutate новый столбец, используя оператор ifelse с rollapply. До сих пор я пытался использовать следующую строку кода...

data %>% mutate(Promotion = ifelse(rollapply(Price, 7, Mode <= Price*0.91,1,0)))

это возвращает сообщение об ошибке...

Error: Problem with `mutate()` input `PRMT_IND2`.
x comparison (5) is possible only for atomic and list types
i Input `PRMT_IND2` is `ifelse(...)`.

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

Примечание. Наблюдения (даты/строки) неравномерны по магазинам, а некоторые заполнены менее чем за 7 дней. Я могу удалить их, если скользящее применение не будет работать без него. Но при этом теряется довольно много данных.

Я использую эту функцию для режима...

  Mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

person Comte    schedule 29.07.2020    source источник
comment
Вы уверены, что хотите рассчитать режим? Функция, которую вы используете, возвращает наибольшее количество раз, когда значение встречается в векторе. Например, x <- c(2, 1, 3, 1, 2, 1) вернет Mode как 1, потому что это встречается 3 раза. Price в вашем наборе данных является числовым значением, и маловероятно, что какое-либо число будет встречаться несколько раз, поэтому я думаю, что Mode - неправильная функция.   -  person Ronak Shah    schedule 29.07.2020
comment
@RonakShah Я понимаю вашу точку зрения и соглашусь, но цена мало варьируется в каждом магазине, когда она не изменяется более чем на = 10%, когда действуют акции. Я опустил исходный индикатор продвижения из примера, но использовал его для фильтрации данных, которые использую. Исходный индикатор иногда бывает неверным, и именно его я хочу изменить, когда не сигнализируется продвижение (== 0). Есть ли лучший подход, который приходит на ум? Должен ли я добавить индикатор продвижения к вопросу?   -  person Comte    schedule 29.07.2020


Ответы (2)


Возможно, вы можете использовать скользящее среднее вместо режима.

library(dplyr)
library(zoo)

dat %>%
   group_by(Item, Store) %>%
   mutate(Promotion = as.integer(abs((Price - 
                         rollmeanr(Price, 7, fill = NA))/Price) > 0.1))

Это даст NA для первых 6 значений и даст 1, если Price отличается более чем на 10% от значения за предыдущие 7 дней, и 0 в противном случае. Также обратите внимание, что здесь мы берем абсолютное значение, поэтому оно даст 1, если цена увеличится на 10% или уменьшится.

person Ronak Shah    schedule 29.07.2020

Как отметила Рона Шак, эта функция не кажется самым подходящим выбором. Также обратите внимание, что использование tabulate преобразует значения в целые числа, что может быть проблематично для имеющихся у вас значений.

Что касается ошибки, как вы правильно догадались, проблема заключалась в том, что ваши разделенные данные не всегда имеют 7 дат, поэтому функция rollapply с width=7 вернула ошибку. Разрешение вашей функции использовать длину вектора даты ИЛИ 7, если доступно, решает проблему. Кроме того, вы можете просто применить свою функцию, используя group_by, разделение данных не требуется.

dat %>%
  group_by(Store,Item)%>%
  mutate(price_check = Price*0.91, 
         Promotion = ifelse(rollapply(Price, width = min(length(Date),7), Mode)>=price_check,1,0))
person Deter11    schedule 29.07.2020
comment
Благодарю за ваш ответ. Есть ли лучший способ реализовать режим, чтобы избежать целого числа? Я думаю, возможно, использовать разницу между средним значением окна и наблюдением с максимальным допуском -9%? Кстати, я уже отфильтровал продвигаемые значения, чтобы проверить ошибки, когда падение цен не было зарегистрировано, следовательно, отсутствие изменения значения. Кстати, я также изменил условие на ‹= Спасибо за вашу помощь. - person Comte; 29.07.2020
comment
rollapplyr может обрабатывать случай, когда данные короче, чем ширина. Он не возвращает ошибку. - person G. Grothendieck; 30.07.2020
comment
@ G.Grothendieck, спасибо, я не подумал использовать эту функцию. - person Comte; 31.07.2020