CLV Inicio Rápido#
El Valor del Tiempo de Vida del Cliente (CLV) es la medida de la contribución de un cliente a un negocio a lo largo del tiempo. Esta métrica se utiliza para informar los niveles de gasto en la adquisición de nuevos clientes, la retención y otros esfuerzos de marketing y ventas, por lo que una estimación fiable es esencial.
PyMC-Marketing proporciona herramientas para segmentar a los clientes según su comportamiento pasado (ver Segmentación RFM) así como los siguientes modelos probabilísticos Buy Till You Die (BTYD) para predecir el comportamiento futuro:
Modelo BG/NBD para modelado en tiempo continuo y no contractual
Modelo Pareto/NBD para modelado en tiempo continuo, no contractual, con covariables
Shifted BG model for discrete time, contractual modeling with cohorts and covariates
BG/BB model for discrete time, contractual modeling
Modelo Gamma-Gamma para el valor monetario esperado
Modelo BG/NBD modificado, similar al modelo BG/NBD, pero asume que los clientes no recurrentes aún están activos.
Esta tabla contiene un desglose de los cuatro dominios de modelado BTYD y ejemplos para cada uno:
No contractual |
Contractual |
|
|---|---|---|
Continuo |
compras en línea |
tiempo de conversión de anuncios |
Discreto |
conciertos y eventos deportivos |
suscripciones recurrentes |
En este cuaderno, demostraremos cómo estimar la actividad de compra futura y el CLV con el conjunto de datos de CDNOW, un conjunto de datos de referencia popular en la investigación de CLV y BTYD. Los datos están disponibles {aquí}, con detalles adicionales {aquí}.
import arviz as az
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from arviz.labels import MapLabeller
from pymc_extras.prior import Prior
from pymc_marketing import clv
az.style.use("arviz-darkgrid")
%config InlineBackend.figure_format = "retina" # nice looking plots
1.1 Requisitos de Datos#
Para todos los modelos, se utiliza la siguiente nomenclatura:
customer_idrepresenta un identificador único para cada cliente.frequencyrepresenta el número de compras repetidas que un cliente ha realizado, es decir, uno menos que el total de compras.Trepresenta la «edad» de un cliente, es decir, la duración entre la primera compra de un cliente y el final del período de estudio. En este cuaderno de ejemplo, las unidades de tiempo están en semanas.recencyrepresenta el período de tiempo en el que un cliente realizó su compra más reciente. Esto es igual a la duración entre la primera y la última compra de un cliente. Si un cliente ha realizado solo 1 compra, su recency es 0.monetary_valuerepresenta el valor promedio de las compras repetidas de un cliente dado. Los clientes que solo han realizado una única compra tienen valores monetarios de cero.
La función rfm_summary se puede utilizar para preprocesar datos de transacciones en bruto para la modelización:
raw_trans = pd.read_csv(
"https://raw.githubusercontent.com/pymc-labs/pymc-marketing/main/data/cdnow_transactions.csv"
)
raw_trans.head(5)
| _id | id | date | cds_bought | spent | |
|---|---|---|---|---|---|
| 0 | 4 | 1 | 19970101 | 2 | 29.33 |
| 1 | 4 | 1 | 19970118 | 2 | 29.73 |
| 2 | 4 | 1 | 19970802 | 1 | 14.96 |
| 3 | 4 | 1 | 19971212 | 2 | 26.48 |
| 4 | 21 | 2 | 19970101 | 3 | 63.34 |
rfm_data = clv.utils.rfm_summary(
raw_trans,
customer_id_col="id",
datetime_col="date",
monetary_value_col="spent",
datetime_format="%Y%m%d",
time_unit="W",
)
rfm_data
| customer_id | frequency | recency | T | monetary_value | |
|---|---|---|---|---|---|
| 0 | 1 | 3.0 | 49.0 | 78.0 | 23.723333 |
| 1 | 2 | 1.0 | 2.0 | 78.0 | 11.770000 |
| 2 | 3 | 0.0 | 0.0 | 78.0 | 0.000000 |
| 3 | 4 | 0.0 | 0.0 | 78.0 | 0.000000 |
| 4 | 5 | 0.0 | 0.0 | 78.0 | 0.000000 |
| ... | ... | ... | ... | ... | ... |
| 2352 | 2353 | 2.0 | 53.0 | 66.0 | 19.775000 |
| 2353 | 2354 | 5.0 | 24.0 | 66.0 | 44.928000 |
| 2354 | 2355 | 1.0 | 44.0 | 66.0 | 24.600000 |
| 2355 | 2356 | 6.0 | 62.0 | 66.0 | 31.871667 |
| 2356 | 2357 | 0.0 | 0.0 | 66.0 | 0.000000 |
2357 rows × 5 columns
Es importante señalar que estas definiciones difieren de las utilizadas en la segmentación RFM, donde se incluye la primera compra, no se utiliza T, y recencia es el número de períodos de tiempo desde la compra más reciente de un cliente.
Para visualizar datos en formato RFM, podemos trazar la recencia y T de los clientes con la función plot_customer_exposure. Observamos que un gran porcentaje (>60%) de los clientes no ha realizado otra compra en un tiempo.
Predicción del Comportamiento de Compra Futuro con el Modelo BG/NBD#
This dataset is an example of continuous time, non-contractual transactions because it comprises purchases from an online music store. PyMC-Marketing provides several models for this use case:
Usaremos el modelo BG/NBD en este cuaderno porque funciona bien para casos de uso básicos. Para un modelado más completo, los modelos Pareto/NBD y MBG/NBD tienen una funcionalidad ampliada y menos limitaciones.
Veamos la estructura subyacente del modelo BG/NBD:
bgm = clv.BetaGeoModel()
bgm.build_model(data=rfm_data)
bgm.graphviz()
Los priors predeterminados para los parámetros de tasa de compra r y alpha siguen una distribución HalfFlat, que es una distribución positiva no informativa. Los parámetros de abandono a y b se agrupan con los priors jerárquicos kappa_dropout y phi_dropout para mejorar el rendimiento.
Se pueden especificar priors más informativos en una configuración de modelo personalizada pasando un diccionario con claves para los nombres de los priors y valores como distribuciones definidas con la clase Prior en PyMC-Marketing.
model_config = {
"a": Prior("HalfNormal", sigma=10),
"b": Prior("HalfNormal", sigma=10),
"alpha": Prior("HalfNormal", sigma=10),
"r": Prior("HalfNormal", sigma=10),
}
bgm = clv.BetaGeoModel(model_config=model_config)
bgm.build_model(data=rfm_data)
bgm
BG/NBD
alpha ~ HalfNormal(0, 10)
a ~ HalfNormal(0, 10)
b ~ HalfNormal(0, 10)
r ~ HalfNormal(0, 10)
recency_frequency ~ BetaGeoNBD(a, b, r, alpha, <constant>)
bgm.graphviz()
Habiendo especificado el modelo, ahora podemos ajustarlo.
Ajuste de Modelo con MAP#
Por defecto, fit genera posteriors bayesianos completos a través del muestreo de MCMC. Para conjuntos de datos extremadamente grandes donde no se necesitan estimaciones de incertidumbre y/o MCMC es demasiado lento, se puede utilizar máximo a posteriori para estimar estimaciones puntuales de los parámetros del modelo.
Utilice rfm_train_test_split para dividir en entrenamiento/prueba. Aquí estamos entrenando con 52 semanas de datos y reteniendo las 26 semanas restantes para las pruebas:
rfm_train_test_data = clv.utils.rfm_train_test_split(
raw_trans,
customer_id_col="id",
datetime_col="date",
monetary_value_col="spent",
train_period_end="19980101",
datetime_format="%Y%m%d",
time_unit="W",
)
rfm_train_test_data
| customer_id | frequency | recency | T | monetary_value | test_frequency | test_monetary_value | test_T | |
|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 3.0 | 49.0 | 52.0 | 23.723333 | 0.0 | 0.000 | 26.0 |
| 1 | 2 | 1.0 | 2.0 | 52.0 | 11.770000 | 0.0 | 0.000 | 26.0 |
| 2 | 3 | 0.0 | 0.0 | 52.0 | 0.000000 | 0.0 | 0.000 | 26.0 |
| 3 | 4 | 0.0 | 0.0 | 52.0 | 0.000000 | 0.0 | 0.000 | 26.0 |
| 4 | 5 | 0.0 | 0.0 | 52.0 | 0.000000 | 0.0 | 0.000 | 26.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2352 | 2353 | 0.0 | 0.0 | 40.0 | 0.000000 | 2.0 | 19.775 | 26.0 |
| 2353 | 2354 | 5.0 | 24.0 | 40.0 | 44.928000 | 0.0 | 0.000 | 26.0 |
| 2354 | 2355 | 0.0 | 0.0 | 40.0 | 0.000000 | 1.0 | 24.600 | 26.0 |
| 2355 | 2356 | 4.0 | 26.0 | 40.0 | 33.317500 | 2.0 | 28.980 | 26.0 |
| 2356 | 2357 | 0.0 | 0.0 | 40.0 | 0.000000 | 0.0 | 0.000 | 26.0 |
2357 rows × 8 columns
Tenga en cuenta que cualquier cliente que haya realizado su primera compra durante el período de prueba será excluido. Utilice rfm_summary para retener a todos los clientes para el modelo final.
bgm_map = clv.BetaGeoModel()
bgm_map.fit(
data=rfm_train_test_data,
method="map",
)
bgm_map.fit_summary()
alpha 6.754
phi_dropout 0.219
kappa_dropout 2.171
r 0.283
a 0.475
b 1.696
Name: value, dtype: float64
Evaluemos el rendimiento del modelo al rastrear las predicciones en comparación con las compras históricas.
clv.plot_expected_purchases_over_time(
model=bgm_map,
purchase_history=raw_trans,
datetime_col="date",
customer_id_col="id",
datetime_format="%Y%m%d",
time_unit="W",
t=78,
set_index_date=True,
t_start_eval=52,
);
Parece que MAP está sobreestimando el número de compras durante el período de prueba, y estas predicciones tampoco tienen estimaciones de incertidumbre.
Ajuste de Modelo con MCMC#
Se recomienda el muestreo MCMC para ilustrar la incertidumbre en las estimaciones de parámetros y crear intervalos de credibilidad en torno a las predicciones.
bgm.fit()
bgm.fit_summary()
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [alpha, a, b, r]
Sampling 4 chains for 1_000 tune and 1_000 draw iterations (4_000 + 4_000 draws total) took 7 seconds.
| mean | sd | hdi_3% | hdi_97% | mcse_mean | mcse_sd | ess_bulk | ess_tail | r_hat | |
|---|---|---|---|---|---|---|---|---|---|
| alpha | 7.092 | 0.509 | 6.106 | 8.020 | 0.012 | 0.009 | 1727.0 | 2042.0 | 1.0 |
| a | 0.687 | 0.156 | 0.431 | 0.972 | 0.004 | 0.004 | 1667.0 | 1616.0 | 1.0 |
| b | 3.259 | 0.977 | 1.763 | 5.103 | 0.025 | 0.030 | 1624.0 | 1739.0 | 1.0 |
| r | 0.276 | 0.012 | 0.254 | 0.299 | 0.000 | 0.000 | 1558.0 | 2098.0 | 1.0 |
Podemos utilizar ArviZ, una biblioteca de Python diseñada para producir visualizaciones de modelos bayesianos, para trazar la distribución posterior de cada parámetro.
1.2.1. Visualizando Predicciones a lo Largo del Tiempo#
Al observar nuevamente los gráficos de tiempo, podemos ver que los resultados se ajustan mucho más de cerca cuando se utilizan MCMC:
clv.plot_expected_purchases_over_time(
model=bgm,
purchase_history=raw_trans,
datetime_col="date",
customer_id_col="id",
datetime_format="%Y%m%d",
time_unit="W",
t=78,
);
Purchases can be plotted cumulatively as well as incrementally:
clv.plot_expected_purchases_over_time(
model=bgm,
purchase_history=raw_trans,
datetime_col="date",
customer_id_col="id",
datetime_format="%Y%m%d",
time_unit="W",
t=78,
set_index_date=True,
plot_cumulative=False,
);
Observe cómo el modelo es capaz de responder a los cambios de tendencia a lo largo del tiempo.
Visualizando Matrices de Predicción#
Podemos ver que nuestros mejores clientes han estado activos durante más de 60 semanas y han realizado más de 20 compras (esquina inferior derecha). Observe la «cola» que se eleva hacia la esquina superior izquierda; estos clientes son poco frecuentes y/o pueden no haber comprado recientemente. ¿Cuál es la probabilidad de que sigan activos?
clv.plot_probability_alive_matrix(bgm)
Tenga en cuenta que todos los clientes no recurrentes tienen una probabilidad de vida de 1, lo cual es una de las peculiaridades de BetaGeoModel. En muchos casos de uso, esta sigue siendo una suposición válida, pero si los clientes no recurrentes son un enfoque clave en su caso de uso, puede que desee probar ParetoNBDModel en su lugar.
Al observar la matriz de probabilidad de permanencia, podemos inferir que los clientes que han realizado menos compras tienen menos probabilidades de regresar, y puede ser conveniente enfocarse en ellos para la retención.
Clasificando a los clientes de mejor a peor#
Having fit the model, we can ask what is the expected number of purchases for our customers over the next 10 time periods. Let’s look at the four most promising customers.
num_purchases = bgm.expected_purchases(future_t=10)
sdata = rfm_data.copy()
sdata["expected_purchases"] = num_purchases.mean(("chain", "draw")).values
sdata.sort_values(by="expected_purchases").tail(4)
| customer_id | frequency | recency | T | monetary_value | expected_purchases | |
|---|---|---|---|---|---|---|
| 812 | 813 | 30.0 | 72.0 | 74.0 | 35.654000 | 3.442797 |
| 1202 | 1203 | 32.0 | 71.0 | 72.0 | 47.172187 | 3.813601 |
| 156 | 157 | 36.0 | 74.0 | 77.0 | 30.603611 | 3.900803 |
| 1980 | 1981 | 35.0 | 66.0 | 68.0 | 46.748857 | 4.307768 |
También podemos trazar intervalos de credibilidad para las compras esperadas:
Predicción del comportamiento de compra de un nuevo cliente#
Podemos utilizar el modelo ajustado para predecir el número de compras de un nuevo cliente.
Historiales de Probabilidad de Clientes#
Dada la historia de transacciones de un cliente, podemos calcular su probabilidad histórica de estar vivo, según nuestro modelo entrenado.
Let’s look at active customer 1516 and assess the change in probability that the user will ever return if they do no other purchases in the next 9 time periods.
customer_1516 = rfm_data.loc[1515]
customer_1516
customer_id 1516.000000
frequency 27.000000
recency 67.000000
T 70.000000
monetary_value 51.944074
Name: 1515, dtype: float64
customer_1516_history = pd.DataFrame(
dict(
customer_id=np.arange(10),
frequency=np.full(10, customer_1516["frequency"], dtype="int"),
recency=np.full(10, customer_1516["recency"]),
T=(np.arange(0, 10) + customer_1516["recency"]).astype("int"),
)
)
customer_1516_history
| customer_id | frequency | recency | T | |
|---|---|---|---|---|
| 0 | 0 | 27 | 67.0 | 67 |
| 1 | 1 | 27 | 67.0 | 68 |
| 2 | 2 | 27 | 67.0 | 69 |
| 3 | 3 | 27 | 67.0 | 70 |
| 4 | 4 | 27 | 67.0 | 71 |
| 5 | 5 | 27 | 67.0 | 72 |
| 6 | 6 | 27 | 67.0 | 73 |
| 7 | 7 | 27 | 67.0 | 74 |
| 8 | 8 | 27 | 67.0 | 75 |
| 9 | 9 | 27 | 67.0 | 76 |
p_alive = bgm.expected_probability_alive(data=customer_1516_history)
az.plot_hdi(customer_1516_history["T"], p_alive, color="C0")
plt.plot(customer_1516_history["T"], p_alive.mean(("draw", "chain")), marker="o")
plt.axvline(
customer_1516_history["recency"].iloc[0], c="black", ls="--", label="Purchase"
)
plt.title("Probability Customer 1516 will purchase again")
plt.xlabel("T")
plt.ylabel("p")
plt.legend();
We can see that, if no purchases are being made in the next 9 weeks, the model has low confidence that the customer will ever return. What if they had done one purchase in between?
customer_1516_history.loc[7:, "frequency"] += 1
customer_1516_history.loc[7:, "recency"] = customer_1516_history.loc[7, "T"] - 0.5
customer_1516_history
| customer_id | frequency | recency | T | |
|---|---|---|---|---|
| 0 | 0 | 27 | 67.0 | 67 |
| 1 | 1 | 27 | 67.0 | 68 |
| 2 | 2 | 27 | 67.0 | 69 |
| 3 | 3 | 27 | 67.0 | 70 |
| 4 | 4 | 27 | 67.0 | 71 |
| 5 | 5 | 27 | 67.0 | 72 |
| 6 | 6 | 27 | 67.0 | 73 |
| 7 | 7 | 28 | 73.5 | 74 |
| 8 | 8 | 28 | 73.5 | 75 |
| 9 | 9 | 28 | 73.5 | 76 |
p_alive = bgm.expected_probability_alive(data=customer_1516_history)
az.plot_hdi(customer_1516_history["T"], p_alive, color="C0")
plt.plot(customer_1516_history["T"], p_alive.mean(("draw", "chain")), marker="o")
plt.axvline(
customer_1516_history["recency"].iloc[0], c="black", ls="--", label="Purchase"
)
plt.axvline(customer_1516_history["recency"].iloc[-1], c="black", ls="--")
plt.title("Probability Customer 1516 will purchase again")
plt.xlabel("T")
plt.ylabel("p")
plt.legend();
Del gráfico anterior, se puede decir que el cliente 1516 realiza una compra en la semana 73.5, poco más de 6 semanas después de haber realizado su última compra registrada. ¡Podemos ver que la probabilidad de que el cliente regrese rápidamente vuelve a aumentar!
Estimando el Valor de Vida del Cliente Utilizando el Modelo Gamma-Gamma#
Hasta ahora nos hemos centrado principalmente en las frecuencias y probabilidades de transacción, pero para estimar el valor económico podemos utilizar el modelo Gamma-Gamma.
El modelo Gamma-Gamma asume que se ha observado al menos 1 transacción repetida por cliente. Por lo tanto, filtramos aquellos con cero compras repetidas.
nonzero_data = rfm_data.query("frequency>0")
nonzero_data
| customer_id | frequency | recency | T | monetary_value | |
|---|---|---|---|---|---|
| 0 | 1 | 3.0 | 49.0 | 78.0 | 23.723333 |
| 1 | 2 | 1.0 | 2.0 | 78.0 | 11.770000 |
| 5 | 6 | 14.0 | 76.0 | 78.0 | 76.503571 |
| 6 | 7 | 1.0 | 5.0 | 78.0 | 11.770000 |
| 7 | 8 | 1.0 | 61.0 | 78.0 | 26.760000 |
| ... | ... | ... | ... | ... | ... |
| 2351 | 2352 | 1.0 | 47.0 | 66.0 | 14.490000 |
| 2352 | 2353 | 2.0 | 53.0 | 66.0 | 19.775000 |
| 2353 | 2354 | 5.0 | 24.0 | 66.0 | 44.928000 |
| 2354 | 2355 | 1.0 | 44.0 | 66.0 | 24.600000 |
| 2355 | 2356 | 6.0 | 62.0 | 66.0 | 31.871667 |
1126 rows × 5 columns
Si está calculando el valor monetario a partir de sus propios datos, tenga en cuenta que es la media del valor de un cliente dado, no la suma. monetary_value se puede utilizar para representar ganancias, ingresos o cualquier valor siempre que se calcule de manera consistente para cada cliente.
The Gamma-Gamma model relies on the important assumption that there is no relationship between the monetary value and the purchase frequency. In practice we need to check whether the Pearson correlation is less than 0.3:
nonzero_data[["monetary_value", "frequency"]].corr()
| monetary_value | frequency | |
|---|---|---|
| monetary_value | 1.000000 | 0.052819 |
| frequency | 0.052819 | 1.000000 |
Las frecuencias de transacción y los valores monetarios no están correlacionados; ahora podemos ajustar nuestro modelo Gamma-Gamma para predecir el gasto promedio y los valores de vida esperados de nuestros clientes.
El modelo Gamma-Gamma recibe un parámetro “data”, un DataFrame de pandas con 3 columnas que representan el ID del cliente, el gasto promedio de compras repetidas y el número de compras repetidas para ese cliente. Al igual que con el modelo BG/NBD, estos parámetros se les asignan priors HalfFlat, que pueden ser demasiado difusos para conjuntos de datos pequeños. Para este ejemplo, utilizaremos los priors predeterminados, pero se pueden especificar otros priors de la misma manera que en el ejemplo de BG/NBD mencionado anteriormente.
gg = clv.GammaGammaModel()
gg.build_model(data=nonzero_data)
gg
Gamma-Gamma Model (Mean Transactions)
p ~ HalfFlat()
q ~ HalfFlat()
v ~ HalfFlat()
likelihood ~ Potential(f(q, p, v))
gg.fit();
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [p, q, v]
Sampling 4 chains for 1_000 tune and 1_000 draw iterations (4_000 + 4_000 draws total) took 4 seconds.
gg.fit_summary()
| mean | sd | hdi_3% | hdi_97% | mcse_mean | mcse_sd | ess_bulk | ess_tail | r_hat | |
|---|---|---|---|---|---|---|---|---|---|
| p | 4.799 | 0.750 | 3.456 | 6.195 | 0.025 | 0.022 | 929.0 | 1099.0 | 1.0 |
| q | 3.933 | 0.280 | 3.374 | 4.409 | 0.010 | 0.008 | 893.0 | 928.0 | 1.0 |
| v | 23.707 | 5.264 | 14.324 | 33.157 | 0.189 | 0.186 | 818.0 | 970.0 | 1.0 |
Predicción del valor de gasto de los clientes#
Habiendo ajustado nuestro modelo, ahora podemos utilizarlo para predecir el valor promedio de vida condicional esperado de nuestros clientes, incluidos aquellos con cero compras repetidas.
expected_spend = gg.expected_customer_spend(data=rfm_data)
az.summary(expected_spend.isel(customer_id=range(10)), kind="stats")
| mean | sd | hdi_3% | hdi_97% | |
|---|---|---|---|---|
| x[1] | 26.115 | 0.438 | 25.339 | 26.975 |
| x[2] | 21.643 | 1.325 | 19.206 | 24.174 |
| x[3] | 37.612 | 0.891 | 35.961 | 39.305 |
| x[4] | 37.612 | 0.891 | 35.961 | 39.305 |
| x[5] | 37.612 | 0.891 | 35.961 | 39.305 |
| x[6] | 74.829 | 0.368 | 74.169 | 75.483 |
| x[7] | 21.643 | 1.325 | 19.206 | 24.174 |
| x[8] | 30.901 | 0.605 | 29.739 | 32.028 |
| x[9] | 36.430 | 0.153 | 36.148 | 36.727 |
| x[10] | 37.612 | 0.891 | 35.961 | 39.305 |
labeller = MapLabeller(var_name_map={"x": "customer"})
az.plot_forest(
expected_spend.isel(customer_id=(range(10))), combined=True, labeller=labeller
)
plt.xlabel("Expected mean spend");
También podemos analizar el gasto medio esperado promedio entre todos los clientes.
az.summary(expected_spend.mean("customer_id"), kind="stats")
| mean | sd | hdi_3% | hdi_97% | |
|---|---|---|---|---|
| x | 37.998 | 0.562 | 36.966 | 39.073 |
Predicción del valor de gasto de un nuevo cliente#
Estimando el CLV#
Finalmente, podemos combinar el GG con el modelo BG/NBD para obtener una estimación del valor de vida del cliente. Esto se basa en el modelo de flujo de caja descontado, ajustando por el costo de capital.
Si se encuentran problemas computacionales, utilice el método thin_fit_result antes de estimar el CLV.
bgm.thin_fit_result(keep_every=2)
BG/NBD
alpha ~ HalfNormal(0, 10)
a ~ HalfNormal(0, 10)
b ~ HalfNormal(0, 10)
r ~ HalfNormal(0, 10)
recency_frequency ~ BetaGeoNBD(a, b, r, alpha, <constant>)
clv_estimate = gg.expected_customer_lifetime_value(
transaction_model=bgm,
data=rfm_data,
future_t=12, # months
discount_rate=0.01, # monthly discount rate ~ 12.7% annually
time_unit="W", # original data is in weeks
)
az.summary(clv_estimate.isel(customer_id=range(10)), kind="stats")
| mean | sd | hdi_3% | hdi_97% | |
|---|---|---|---|---|
| x[1] | 29.244 | 1.049 | 27.306 | 31.182 |
| x[2] | 3.102 | 0.311 | 2.540 | 3.708 |
| x[3] | 5.615 | 0.224 | 5.205 | 6.047 |
| x[4] | 5.615 | 0.224 | 5.205 | 6.047 |
| x[5] | 5.615 | 0.224 | 5.205 | 6.047 |
| x[6] | 501.233 | 16.738 | 470.280 | 531.458 |
| x[7] | 4.080 | 0.352 | 3.414 | 4.735 |
| x[8] | 16.210 | 0.437 | 15.358 | 16.984 |
| x[9] | 46.878 | 1.312 | 44.499 | 49.302 |
| x[10] | 5.615 | 0.224 | 5.205 | 6.047 |
az.plot_forest(
clv_estimate.isel(customer_id=range(10)), combined=True, labeller=labeller
)
plt.xlabel("Expected CLV");
Según nuestros modelos, el cliente[6] tiene un CLV esperado mucho más alto. También hay una gran variabilidad en esta estimación que surge únicamente de la incertidumbre en los parámetros de los modelos BG/NBD y GG.
En general, estos modelos tienden a inducir una fuerte correlación entre el CLV esperado y la incertidumbre. Este modelado de la incertidumbre puede ser muy útil al tomar decisiones de marketing.
%load_ext watermark
%watermark -n -u -v -iv -w -p pymc,pytensor
Last updated: Thu Dec 04 2025
Python implementation: CPython
Python version : 3.12.11
IPython version : 9.3.0
pymc : 5.25.1
pytensor: 2.31.7
matplotlib : 3.10.3
arviz : 0.21.0
pymc_extras : 0.4.0
pymc_marketing: 0.15.1
numpy : 2.2.0
pandas : 2.3.0
Watermark: 2.5.0