Saltar al contenido principal

Customer Lifetime Value

En este tutorial, aprende a calcular el customer lifetime value en Python.
· 11 min leer

Explora con IA

El economista italiano Vilfredo Pareto afirmaba que el 80% del efecto procede del 20% de las causas: es la regla 80/20 o principio de Pareto. Del mismo modo, el 80% del negocio de muchas empresas proviene del 20% de sus clientes. Las compañías deben identificar a esos clientes clave y cultivar su relación para asegurar ingresos constantes. Para mantener una relación a largo plazo con los clientes, conviene planificar programas de fidelización como descuentos, ofertas, cupones, puntos y regalos.

Captar un cliente nuevo suele ser más caro que retener a uno existente: no necesitas invertir tantos recursos, tiempo ni esfuerzo en adquisición; basta con mantener satisfechos a los que ya tienes. Los analistas de negocio calculan con precisión el coste de adquisición de clientes usando el CLTV (Customer Lifetime Value). El CLTV indica los ingresos totales que aporta un cliente durante toda su relación con la empresa. Ayuda a centrarse en aquellos clientes con mayor potencial de ingresos futuros.

Customer Lifetime Value (CLTV)

«Customer Lifetime Value es un valor monetario que representa la cantidad de ingresos o beneficio que un cliente aportará a la empresa durante el tiempo que dure la relación». El CLTV muestra las implicaciones de captar clientes a largo plazo frente a clientes a corto plazo. El customer lifetime value (CLV) te ayuda a responder las preguntas de ventas más importantes para cualquier empresa:

  • ¿Cómo identificar a los clientes más rentables?
  • ¿Cómo puede una empresa ofrecer el mejor producto y maximizar los ingresos?
  • ¿Cómo segmentar a los clientes rentables?
  • ¿Qué presupuesto hay que invertir para captar clientes?

Calcular el customer lifetime value

Existen muchos enfoques para calcular el CLTV. Cada cual tiene su punto de vista. Para calcularlo necesitamos datos históricos de clientes, por lo que no se puede estimar directamente para clientes nuevos. Para resolverlo, los analistas de negocio desarrollan modelos de machine learning que predicen el CLTV de nuevos clientes. Veamos algunos enfoques para calcular el CLTV:

1) Puedes calcularlo sumando el beneficio/ingresos del cliente en un período dado. Por ejemplo, si el cliente lleva contigo 3 años, suma todo el beneficio de esos 3 años. Puedes promediar por año, semestre o mes, pero con este enfoque no podrás construir un modelo predictivo para clientes nuevos.

2) Construye un modelo de regresión para clientes existentes. Usa los últimos seis meses como variables independientes y los ingresos totales de tres años como variable dependiente, y entrena un modelo de regresión con estos datos.

3) El CLTV también puede implementarse usando valores RFM (recencia, frecuencia y valor monetario). Para más detalles, puedes consultar mi tutorial.

4) Usando la siguiente ecuación: CLTV = ((valor medio de pedido × frecuencia de compra)/tasa de abandono) × margen de beneficio.

      Customer Value = Average Order Value * Purchase Frequency
  • Average Order Value (AOV): el valor medio de pedido es el cociente entre los ingresos totales y el número total de pedidos. Representa la cantidad media que gasta un cliente en un pedido.

                         Average Order Value = Total Revenue / Total Number of Orders
    
  • Purchase Frequency (PF): la frecuencia de compra es el cociente entre el número total de pedidos y el número total de clientes. Indica el promedio de pedidos por cliente.

                        Purchase Frequency =  Total Number of Orders / Total Number of Customers
    
  • Churn rate: la tasa de abandono es el porcentaje de clientes que no vuelven a realizar un pedido.

  • Customer lifetime: es el período durante el cual el cliente ha estado realizando pedidos de forma continuada.

                                  Customer Lifetime=1/Churn Rate
    
  • Repeat rate: se define como el cociente entre el número de clientes con más de un pedido y el número de clientes únicos. Ejemplo: si tienes 10 clientes en un mes y 4 repiten, tu repeat rate es del 40%.

                                   Churn Rate= 1-Repeat Rate
    

Implementación de CLTV en Python (usando la fórmula)

Importar las librerías necesarias

#import modules
import pandas as pd # for dataframes
import matplotlib.pyplot as plt # for plotting graphs
import seaborn as sns # for plotting graphs
import datetime as dt
import numpy as np

Cargar el dataset

Primero cargamos el dataset Online Retail usando la función de pandas para leer CSV/Excel. Puedes descargar los datos desde aquí.

data = pd.read_excel("Online_Retail.xlsx")
data.head()
 InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 08:26:002.5517850.0United Kingdom
153636571053WHITE METAL LANTERN62010-12-01 08:26:003.3917850.0United Kingdom
253636584406BCREAM CUPID HEARTS COAT HANGER82010-12-01 08:26:002.7517850.0United Kingdom
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 08:26:003.3917850.0United Kingdom
453636584029ERED WOOLLY HOTTIE WHITE HEART.62010-12-01 08:26:003.3917850.0United Kingdom

Eliminar duplicados

A veces el dataset llega con ruido. Puede haber duplicados que sesguen tu análisis. En Python, pandas ofrece la función drop_duplicates(), que elimina los registros repetidos.

filtered_data=data[['Country','CustomerID']].drop_duplicates()

Vamos con los insights

#Top ten country's customer
filtered_data.Country.value_counts()[:10].plot(kind='bar')
<matplotlib.axes._subplots.AxesSubplot at 0x7fe677a887f0>
bar chart

En el dataset, la mayoría de clientes son del «United Kingdom». Así que puedes filtrar los datos para clientes del Reino Unido.

uk_data=data[data.Country=='United Kingdom']
uk_data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 495478 entries, 0 to 541893
Data columns (total 8 columns):
InvoiceNo      495478 non-null object
StockCode      495478 non-null object
Description    494024 non-null object
Quantity       495478 non-null int64
InvoiceDate    495478 non-null datetime64[ns]
UnitPrice      495478 non-null float64
CustomerID     361878 non-null float64
Country        495478 non-null object
dtypes: datetime64[ns](1), float64(2), int64(1), object(4)
memory usage: 34.0+ MB

La función describe() de pandas es muy útil para obtener estadísticas de resumen: devuelve el recuento, la media, la desviación estándar, los valores mínimo y máximo y los cuartiles.

uk_data.describe()
 QuantityUnitPriceCustomerID
count495478.000000495478.000000361878.000000
mean8.6054864.53242215547.871368
std227.58875699.3154381594.402590
min-80995.000000-11062.06000012346.000000
25%1.0000001.25000014194.000000
50%3.0000002.10000015514.000000
75%10.0000004.13000016931.000000
max80995.00000038970.00000018287.000000

Aquí se observa que algunos clientes han pedido cantidades negativas, lo cual no es posible. Por tanto, filtra Quantity mayor que cero.

uk_data = uk_data[(uk_data['Quantity']>0)]
uk_data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 486286 entries, 0 to 541893
Data columns (total 8 columns):
InvoiceNo      486286 non-null object
StockCode      486286 non-null object
Description    485694 non-null object
Quantity       486286 non-null int64
InvoiceDate    486286 non-null datetime64[ns]
UnitPrice      486286 non-null float64
CustomerID     354345 non-null float64
Country        486286 non-null object
dtypes: datetime64[ns](1), float64(2), int64(1), object(4)
memory usage: 33.4+ MB

Filtrar las columnas necesarias

Aquí filtramos las columnas imprescindibles para calcular el CLTV. Solo necesitas cinco: CustomerID, InvoiceDate, InvoiceNo, Quantity y UnitPrice.

  • CustomerID identifica de forma única a tus clientes.
  • InvoiceDate te ayuda a calcular los días que el cliente ha permanecido activo.
  • InvoiceNo te permite contar cuántas transacciones ha realizado (frecuencia).
  • Quantity son las unidades compradas en cada transacción.
  • UnitPrice es el precio por unidad y te servirá para calcular el importe total comprado.
uk_data=uk_data[['CustomerID','InvoiceDate','InvoiceNo','Quantity','UnitPrice']]
#Calulate total purchase
uk_data['TotalPurchase'] = uk_data['Quantity'] * uk_data['UnitPrice']

Ahora vamos a realizar estas operaciones:

  • Calcular los días entre la fecha actual y la última compra de cada cliente.
  • Calcular el número de pedidos de cada cliente.
  • Calcular la suma del importe comprado por cada cliente.
uk_data_group=uk_data.groupby('CustomerID').agg({'InvoiceDate': lambda date: (date.max() - date.min()).days,
                                        'InvoiceNo': lambda num: len(num),
                                        'Quantity': lambda quant: quant.sum(),
                                        'TotalPurchase': lambda price: price.sum()})
uk_data_group.head()
 InvoiceDateInvoiceNoQuantityTotalPurchase
CustomerID    
12346.0017421577183.60
12747.036610312754196.01
12748.037245962574833719.73
12749.020919914714090.88
12820.032359722942.34

Renombrar columnas

# Change the name of columns
uk_data_group.columns=['num_days','num_transactions','num_units','spent_money']
uk_data_group.head()
 num_daysnum_transactionsnum_unitsspent_money
CustomerID    
12346.0017421577183.60
12747.036610312754196.01
12748.037245962574833719.73
12749.020919914714090.88
12820.032359722942.34

Calcular el CLTV con la siguiente fórmula:

 CLTV = ((Average Order Value x Purchase Frequency)/Churn Rate) x Profit margin.

 Customer Value = Average Order Value * Purchase Frequency

1. Calcular el valor medio de pedido

# Average Order Value
uk_data_group['avg_order_value']=uk_data_group['spent_money']/uk_data_group['num_transactions']
uk_data_group.head()
 num_daysnum_transactionsnum_unitsspent_moneyavg_order_value
CustomerID     
12346.0017421577183.6077183.600000
12747.036610312754196.0140.737961
12748.037245962574833719.737.336756
12749.020919914714090.8820.557186
12820.032359722942.3415.971864

2. Calcular la frecuencia de compra

purchase_frequency=sum(uk_data_group['num_transactions'])/uk_data_group.shape[0]

3. Calcular el repeat rate y la tasa de abandono

# Repeat Rate
repeat_rate=uk_data_group[uk_data_group.num_transactions > 1].shape[0]/uk_data_group.shape[0]
#Churn Rate
churn_rate=1-repeat_rate
purchase_frequency,repeat_rate,churn_rate
(90.37107880642694, 0.9818923743942872, 0.018107625605712774)

4. Calcular el margen de beneficio

El margen de beneficio es un ratio de rentabilidad muy usado. Representa qué porcentaje de las ventas totales se convierte en ganancia. Supongamos que nuestro negocio tiene aproximadamente un 5% de margen sobre la venta total.

# Profit Margin
uk_data_group['profit_margin']=uk_data_group['spent_money']*0.05
uk_data_group.head()
 num_daysnum_transactionsnum_unitsspent_moneyavg_order_valueprofit_margin
CustomerID      
12346.0017421577183.6077183.6000003859.1800
12747.036610312754196.0140.737961209.8005
12748.037245962574833719.737.3367561685.9865
12749.020919914714090.8820.557186204.5440
12820.032359722942.3415.97186447.1170

5. Calcular el customer lifetime value

# Customer Value
uk_data_group['CLV']=(uk_data_group['avg_order_value']*purchase_frequency)/churn_rate
#Customer Lifetime Value
uk_data_group['cust_lifetime_value']=uk_data_group['CLV']*uk_data_group['profit_margin']
uk_data_group.head()
 num_daysnum_transactionsnum_unitsspent_moneyavg_order_valueprofit_marginCLVcust_lifetime_value
CustomerID        
12346.0017421577183.6077183.6000003859.18003.852060e+081.486579e+12
12747.036610312754196.0140.737961209.80052.033140e+054.265538e+07
12748.037245962574833719.737.3367561685.98653.661610e+046.173424e+07
12749.020919914714090.8820.557186204.54401.025963e+052.098545e+07
12820.032359722942.3415.97186447.11707.971198e+043.755789e+06

Modelo predictivo de CLTV

Vamos a construir un modelo de predicción de CLTV.

Aquí vas a predecir el CLTV con un modelo de regresión lineal.

Primero, usa los datos cargados y filtrados arriba.

uk_data.head()
 CustomerIDInvoiceDateInvoiceNoQuantityUnitPriceTotalPurchasemonth_yr
017850.02010-12-01 08:26:0053636562.5515.30Dec-2010
117850.02010-12-01 08:26:0053636563.3920.34Dec-2010
217850.02010-12-01 08:26:0053636582.7522.00Dec-2010
317850.02010-12-01 08:26:0053636563.3920.34Dec-2010
417850.02010-12-01 08:26:0053636563.3920.34Dec-2010

Extrae mes y año de InvoiceDate.

uk_data['month_yr'] = uk_data['InvoiceDate'].apply(lambda x: x.strftime('%b-%Y'))
uk_data.head()
 CustomerIDInvoiceDateInvoiceNoQuantityUnitPriceTotalPurchasemonth_yr
017850.02010-12-01 08:26:0053636562.5515.30Dec-2010
117850.02010-12-01 08:26:0053636563.3920.34Dec-2010
217850.02010-12-01 08:26:0053636582.7522.00Dec-2010
317850.02010-12-01 08:26:0053636563.3920.34Dec-2010
417850.02010-12-01 08:26:0053636563.3920.34Dec-2010

La tabla dinámica (pivot table) agrupa columnas en una tabla bidimensional para ofrecer un resumen multidimensional de los datos.

sale=uk_data.pivot_table(index=['CustomerID'],columns=['month_yr'],values='TotalPurchase',aggfunc='sum',fill_value=0).reset_index()
sale.head()
month_yrCustomerIDApr-2011Aug-2011Dec-2010Dec-2011Feb-2011Jan-2011Jul-2011Jun-2011Mar-2011May-2011Nov-2011Oct-2011Sep-2011
012346.00.000.000.000.000.0077183.600.000.000.000.000.000.000.00
112747.00.00301.70706.27438.500.00303.040.00376.30310.78771.31312.73675.380.00
212748.01100.37898.244228.131070.27389.64418.771113.272006.261179.372234.5010639.232292.846148.84
312749.00.001896.130.00763.060.000.000.000.000.00859.10572.590.000.00
412820.00.000.000.00210.350.00170.460.000.000.000.000.00343.76217.77

Sumemos las ventas de todos los meses.

sale['CLV']=sale.iloc[:,2:].sum(axis=1)
sale.head()
month_yrCustomerIDApr-2011Aug-2011Dec-2010Dec-2011Feb-2011Jan-2011Jul-2011Jun-2011Mar-2011May-2011Nov-2011Oct-2011Sep-2011CLV
012346.00.000.000.000.000.0077183.600.000.000.000.000.000.000.0077183.60
112747.00.00301.70706.27438.500.00303.040.00376.30310.78771.31312.73675.380.004196.01
212748.01100.37898.244228.131070.27389.64418.771113.272006.261179.372234.5010639.232292.846148.8432619.36
312749.00.001896.130.00763.060.000.000.000.000.00859.10572.590.000.004090.88
412820.00.000.000.00210.350.00170.460.000.000.000.000.00343.76217.77942.34

Selección de variables

Debes dividir las columnas en variable dependiente (o objetivo) e independientes (o de entrada). Selecciona los últimos 6 meses como variables independientes.

X=sale[['Dec-2011','Nov-2011', 'Oct-2011','Sep-2011','Aug-2011','Jul-2011']]
y=sale[['CLV']]

Dividir los datos

Para evaluar el rendimiento del modelo, es buena práctica dividir el dataset en entrenamiento y prueba.

Separamos el dataset con train_test_split(). Hay que pasar 3 parámetros: features, target y el tamaño del conjunto de prueba. Además, puedes usar random_state como semilla para garantizar reproducibilidad. Si random_state es None, el generador usa np.random y la partición variará en cada ejecución.

#split training set and test set
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,random_state=0)

Desarrollo del modelo

Importa el módulo de regresión lineal y crea el objeto. Ajusta el modelo al conjunto de entrenamiento con fit() y realiza predicciones en test con predict().

# import model
from sklearn.linear_model import LinearRegression

# instantiate
linreg = LinearRegression()

# fit the model to the training data (learn the coefficients)
linreg.fit(X_train, y_train)

# make predictions on the testing set
y_pred = linreg.predict(X_test)
# print the intercept and coefficients
print(linreg.intercept_)
print(linreg.coef_)
[208.50969617]
[[0.99880551 0.80381254 1.60226829 1.67433228 1.52860813 2.87959449]]

¿Qué tal se ajusta el modelo a los datos?

Para evaluar el ajuste global del modelo lineal usamos el R-cuadrado. Es la proporción de varianza explicada por el modelo y toma valores entre 0 y 1. Cuanto más alto, mejor, porque indica que el modelo explica mayor varianza.

from sklearn import metrics

# compute the R Square for model
print("R-Square:",metrics.r2_score(y_test, y_pred))
R-Square: 0.9666074402817512

Este modelo tiene un R-cuadrado alto (0,96), por lo que ajusta bien a los datos.

Evaluación del modelo

Para problemas de regresión se usan las siguientes métricas (Ritchie Ng):

  • MAE (error absoluto medio): media del valor absoluto de los errores.
  • MSE (error cuadrático medio): media de los errores al cuadrado.
  • RMSE (raíz del error cuadrático medio): raíz cuadrada del MSE.
# calculate MAE using scikit-learn
print("MAE:",metrics.mean_absolute_error(y_test,y_pred))

#calculate mean squared error
print("MSE",metrics.mean_squared_error(y_test, y_pred))
# compute the RMSE of our predictions
print("RMSE:",np.sqrt(metrics.mean_squared_error(y_test, y_pred)))
MAE: 595.0282284701234
MSE 2114139.8898678957
RMSE: 1454.0082151995896

El RMSE es más popular que MSE y MAE porque se interpreta en las mismas unidades que y.

Ventajas y limitaciones del CLTV

El CLTV te ayuda a diseñar un plan de negocio eficaz y a escalar tu actividad. Permite crear segmentos de clientes con sentido, que a su vez ayudan a identificar las necesidades de cada segmento.

El customer lifetime value es una herramienta, no una estrategia. El CLTV identifica a los clientes más rentables, pero cómo rentabilizarlos depende de tu estrategia. A menudo los modelos de CLTV se confunden o se usan mal. La obsesión por el CLTV puede generar puntos ciegos: las empresas se enfocan solo en el mejor segmento y en repetir negocio con él, pero también es importante atender al resto de clientes.

Conclusión

¡Enhorabuena, has llegado al final del tutorial!

En este recorrido has visto en detalle qué es el customer lifetime value, distintos enfoques para calcular el CLTV, su implementación desde cero en Python, un modelo de predicción y sus pros y contras. También repasaste conceptos básicos de pandas como groupby y pivot table para resumir columnas y filas seleccionadas.

Ojalá ahora puedas aplicar el concepto de CLTV para analizar tus propios datasets. ¡Gracias por leer!

Si quieres seguir aprendiendo a analizar datos de clientes en Python, haz el curso de DataCamp Customer Analytics & A/B Testing in Python.

Temas

Relacionado

Tutorial

Funciones en Python: cómo llamar y escribir funciones