datacubeR

Cómo vectorizar el cálculo de la Recencia

Calcular la Recencia usando dplyr

Si quieres hacer un RFM, la recencia es una de las variables importantes y no siempre es sencillo calcularlo, aquí muestro una forma sencilla.

picture of me picture of me Tenemos un practicante en mi pega que está trabajando en su Tesis. El tema es que dentro de un modelo que está haciendo necesita calcular la Recencia de un cliente, es decir, el número de meses que han tranascurrido desde su última compra. Me pareció un desafío bastante interesante, porque calcularlo de manera individual para un cliente no es dificil, pero ser capaz de vectorizarlo me pareció desafiante, en especial porque está trabajando en R.

El problema

Este es el problema, con la solución esperada (Recency):

(data <- tibble::tribble(
  ~Client_ID, ~Date_ID, ~Purchase_Amount, ~Recency,
           1,        1,             2344,        0,
           1,        2,                0,        1,
           1,        3,                0,        2,
           1,        4,             5676,        0,
           1,        5,             4587,        0,
           1,        6,                0,        1,
           1,        7,                0,        2,
           1,        8,                0,        3,
           2,        1,             2500,        0,
           2,        2,             2634,        0,
           2,        3,                0,        1,
           2,        4,                0,        2,
           2,        5,                0,        3,
           2,        6,             4578,        0,
           2,        7,             4562,        0,
           2,        8,                0,        1
  )
)

Como se puede ver, cada vez que tengo una compra el contador de Recencia necesita reiniciarse en cero y comenzar a contar hasta la última compra de cada cliente. Adicionalmente el contador tiene que reiniciarse también cuando hay un cambio de Cliente, según Client_ID

La solución

En este caso particular voy a detallar los distintos pasos de la solución porque puede ser medio complicado de seguir.

Primero, creo una variable auxiliar llamada has_purchased y date_group. Estas variables se tienen que crear al nivel de cliente. cumsum() irá creando grupos que permitirán reiniciar el cálculo de la recencia.

data %>%
  filter(Client_ID == 1) %>% 
  mutate(has_purchased = as.numeric(Purchase_Amount > 0),
         date_group = cumsum(has_purchased))

Luego no es nada más que calcular el número de fila y restarle uno, el tema es que esta operación se debe realizar de manera agrupada date_group.

data %>%
  filter(Client_ID == 1) %>%
  mutate(
    has_purchased = as.numeric(Purchase_Amount > 0),
    date_group = cumsum(has_purchased)
  ) %>%
  group_by(date_group) %>%
  mutate(calculated_recency = row_number() - 1)

Ahora para hacer esta operación más eficiente computacionalmente y poder generalizar la solución para todos los clientes, hay que generar esto para cada Client_ID. Para eso se utilizará group_modify(). Esta función es muy similar a los map_ de purrr, pero para datos agrupados.

data %>%
  #grouped by client
  group_by(Client_ID) %>%
  group_modify(
    #This is the same pipeline showed before but applied to element .x that represents each group
    ~ .x %>%
      mutate(
        has_purchased = as.numeric(Purchase_Amount > 0),
        date_group = cumsum(has_purchased)
      ) %>%
      group_by(date_group) %>%
      mutate(calculated_recency = row_number() - 1)
    
  ) %>%
   select(-has_purchased, -date_group)

Se puede ver que calculated_recency es igual al resultado esperado al principio del artículo, y puede ser fácilmente logrado por medio de group_by. Creo que la gracia de esta solución es que permite generar una solución vectorizada evitando una solución que normalmente requeriría de 2 for loops.

Alfonso

Go to top