PyMC v4 Customer Lifetime Value (CLV) | Contractual-Discrete: The sBG Model

Hi everyone!

I am trying to model a customer lifetime value for a contractual discrete case as outlined here: Using MCMC to Fit the Shifted-Beta-Geometric Customer Lifetime Value Model . I can however not recreate this model in PyMC v4. Having read pm.DensityDist problem under PMYC 4 - #5 by zweli I have made sure all parameters are transferred. Here is my code and error. I was wondering if anyone could help:

import pymc as pm
import numpy as np

# ata prep
example_data = [1000, 869, 743, 653, 593, 551, 517, 491]

def n_lost(data):
    lost = [None]
    for i in range(1, len(data)):
        lost.append(data[i - 1] - data[i])
    return lost

example_data_n_lost = n_lost(example_data)

data = (example_data, example_data_n_lost)

num_periods = len(example_data)
active, lost = data

# bayesian model
with pm.Model() as bg_model:
    
    alpha = pm.Uniform('alpha', 0.00001, 1000) 
    beta = pm.Uniform('beta', 0.00001, 1000)

    def P_T_is_t(alpha=alpha, beta=beta, num_periods=num_periods):
        p = [None, alpha / (alpha + beta)]
        for t in range(2, num_periods):
            pt = (beta + t - 2) / (alpha + beta + t - 1) * p[t-1]
            p.append(pt)
        return p
    
    def survival_function(P_T_is_t=P_T_is_t, num_periods=num_periods):
        s = [None, 1 - P_T_is_t[1]]
        for t in range(2, num_periods):
            s.append(s[t-1] - P_T_is_t[t])
        return s

    def logp(active, lost, alpha, beta, P_T_is_t=P_T_is_t, survival_function = survival_function):
        
        # Those who've churned along the way...
        died = np.log(P_T_is_t[1:]) * lost[1:]
        
        # and those still active in last period
        still_active = np.log(survival_function[-1]) * active[-1]              
        return sum(died) + still_active

    # Custom distribution for BG likelihood function
    loglikelihood = pm.DensityDist("loglikelihood", lost, alpha, beta, logp=logp, observed= active)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/Documents/__RN/rn_predictions/venv_pred/lib/python3.9/site-packages/aesara/tensor/type.py:278, in TensorType.dtype_specs(self)
    277 try:
--> 278     return self.dtype_specs_map[self.dtype]
    279 except KeyError:

KeyError: 'object'

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
question_pymc.ipynb Cell 1 in <cell line: 21>()
         return sum(died) + still_active
     # Custom distribution for BG likelihood function
---> loglikelihood = pm.DensityDist("loglikelihood", lost, alpha, beta, logp=logp, observed= active)

File ~/Documents/__RN/rn_predictions/venv_pred/lib/python3.9/site-packages/pymc/distributions/distribution.py:753, in DensityDist.__new__(cls, name, logp, logcdf, random, moment, ndim_supp, ndims_params, dtype, *dist_params, **kwargs)
    745 elif len(dist_params) > 0 and callable(dist_params[0]):
    746     raise TypeError(
    747         "The DensityDist API has changed, you are using the old API "
    748         "where logp was the first positional argument. In the current API, "
   (...)
    751         "new DensityDist API."
    752     )
...
--> 280     raise TypeError(
    281         f"Unsupported dtype for {self.__class__.__name__}: {self.dtype}"
    282     )

TypeError: Unsupported dtype for TensorType: object

Just to be clear, the code you are trying to adapt was written for pymc version 2. As a consequence, the adaptation is going to need to be quite dramatic (e.g., much more substantial than adapting pymc v3 code for use in pymc v4). So the fact that you are seeing an error is not super surprising. Furthermore, I (personally) would not try and debug this error-by-error. Rather, I would try to re-implement the pieces of the model. But that’s just me.

1 Like