Лучшее решение, чем для циклов, при сравнении значений сгруппированных переменных в R

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

Я работаю с данными о продажах. Мой конкретный фрейм данных включает в себя категории продуктов ("CAT_NO"), децили клиентов ("CUST_DECILE") (клиенты попадают в группу децилей от 1 до 10, где 1 - "лучшие" клиенты) и минимальная валовая маржа ("floorGM")) для этой категории продуктов, комбинация децилий клиентов. Может быть, стоит отметить, что не в каждой категории продуктов будут представлены все децили клиента (например, в категории образцов "А" могут быть только децили клиентов 4, 7 и 9. Для упрощения приведенный ниже воспроизводимый пример гарантирует, что каждая категория продукта имеет все 10 клиентских децилей). Мой набор данных может быть представлен как:

    df <- data.frame(CAT_NO = c(rep(c("A"), times = 10), rep(c("B"), times = 10),
                        rep(c("C"), times = 10), rep(c("D"), times = 10))
             , CUST_DECILE = rep(c(1:10), times = 4), floorGM = runif(40, 0.2, 0.8))

    df

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

Логика, которую я использовал, подразделяет данные по каждому CAT_NO, а затем применяет цикл для сравнения floorGM каждого CUST_DECILE в этом CAT_NO. Мой код:

    Product_Categories <- as.character(unique(df$CAT_NO))

    for(k in seq_along(Product_Categories)) {
      subdata <- subset(df, CAT_NO == Product_Categories[k])
      deciles <- sort(unique(subdata$CUST_DECILE))

      for(k in 2:length(deciles)) {
        if(subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k], "floorGM"< subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k-1], "floorGM"]) {
          subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k], "floorGM"] <- subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k-1], "floorGM"]
        }
       }
      if (!exists("temp")) {
         temp <- subdata
      } else {
         temp <- rbind(temp, subdata) 
      }
    }

Хотя это работает, я уверен, что есть более быстрый способ выполнить эту операцию, особенно при увеличении моего набора данных с rbind() во время цикла будет снижаться производительность, поскольку я масштабирую это решение на миллионы транзакций.

Спасибо за любой вклад и / или дополнительные ссылки!

1 ответ

Решение

Не могу ручаться за то, как быстро это будет для миллионов строк (на моей медленной системе это определенно заняло 40 000 строк), но вот решение (использование dplyr):

df<-group_by(df,CAT_NO)
df<-mutate(df, lag=lag(floorGM))
while (any(df$floorGM<df$lag,na.rm=T)) {
  df<-mutate(df, floorGM=ifelse(!is.na(lag),ifelse(floorGM<lag,lag,floorGM),floorGM))
  df<-mutate(df, lag=lag(floorGM))
}

while петля в основном пузыри floorGM номера по всей категории.

(на самом деле, если подумать, в любом случае это не должно требовать большого количества циклов - поскольку каждая категория может иметь только 10 децилей - поэтому я думаю, что все должно быть в порядке).

Другие вопросы по тегам