Package: lucius-ltv
Lucius LTVA Python simple multi-cohort LTV calculator library for subscription-based products.Installationpipinstalllucius-ltvBuilding a cohort matrixTo use this library, the first step is to build aCohort retention matrixfor your product.To build a cohort matrix:For each period of interest (week/month/year - depending on your subscription)Retrieve the number of users that started paying for the first timeTrack those users over time to observe how many where still alive in the following periodsStack them in order to obtain a pd.DataFrame resembling this:202220212020201920182017201620151009582787571686312010999878067661019080777368130122115108999591856710290819080Alternatively if you are just trying out this library, you can generate a retention cohort matrix by callinggenerate_synthetic_cohort_matrix. An example of this is contained in the sample file,sample.pyshipped with this project.Fitting the modelOnce you have a cohort matrix, you can finally fit the sBG model that will predict LTV.
Behind the scenes this library implements "Fader, Peter and Hardie, Bruce, How to Project Customer Retention (May 2006)" (Available at SSRN:https://ssrn.com/abstract=801145orhttp://dx.doi.org/10.2139/ssrn.801145) using the powerful Bayesian Inference librarypymc3.To fit the pymc model, simply run:Basic fitinference_data,model=fit_sbg_model(cohort_matrix,periods=10,# Number of projected periods)Fit with "all_users"If your product offers free trials or initial offers of any kind, you can add a conversion layer to the model, by specifying the starting users:inference_data,model=fit_sbg_model(cohort_matrix,all_users=[150,147,180,160,130,140,160],periods=10,# Number of projected periods)Fit with true parametersIf you are testing the model with synthetic data you can specify the true alpha/beta sbg parameter values (those won't be used in fitting, but to generate reference/comparison timeseries)inference_data,model=fit_sbg_model(cohort_matrix,periods=10,# Number of projected periodstrue_a=1.5,true_b=2.3,)Extra pymc sampling parametersIf you want you can pass extra arguments that will be re-routed to the pymc sampler, liketarget_accept,steps,tune, etc..Analyzing resultsThe returnedinference_datais a standard Arviz InferenceData object will contain a posterior estimate of the modelled variables as returned by pymc3.
The most interesting variables are:inference_data.posterior['ltv']the LTV timeseriesinference_data.posterior['conversion_rate_by_cohort']the conversion rates if you did the fit withall_usersinference_data.posterior['ltv']the true LTV timeseries if you provided true values of alpha and betaPlease note that these are trace objects resulting from the sampling process,
therefore each variable will have multiple possible values representing the sampling of the posterior distribution.
From this sampling you can obtain high-density intervals usingnp.percentileoraz.hdi.Empirical LTV from cohort matrixYou can also compute the empirical LTV starting from the cohort matrix, allowing you to compare empirical LTV to the projected one.empirical_ltv=compute_empirical_ltv(cohort_matrix=cohort_matrix)PlotsThe library also includes a few methods for quickly plotting results.LTVPlot the user lifetime value with surrounding HDIfig,ax=plt.subplots(figsize=(20,10))plot_ltv(empirical_ltv,inference_data=inference_data,ax=ax)fig.suptitle(f'Lifetime value')ax.legend()ax.grid()Conversion RatePlot the conversion rate by cohort with sorrounding HDIfig,ax=plt.subplots(figsize=(20,10))plot_conversion_rate(inference_data,ax=ax)fig.suptitle(f'Conversion Rate')ax.grid()Cohort matrixPlot the cohort matrix retention ratesplot_cohort_matrix_retention(cohort_matrix,'Cohort Retention')Posterior distributions vs true valuesPlot the posterior distributions vs true valuesfig,(ax1,ax2,ax3)=plt.subplots(3,figsize=(12,12))az.plot_posterior(inference_data,var_names=("alpha",),ref_val=true_alpha,ax=ax1)az.plot_posterior(inference_data,var_names=("beta",),ref_val=true_beta,ax=ax2)az.plot_posterior(inference_data,var_names=("conversion_rate",),ref_val=1/true_conversion_rate,ax=ax3)fig.suptitle(f'True v Recovered values')