def bgnbd_est(rfm_data, guess={"r": 0.01, "alpha": 0.01, "a": 0.01, "b": 0.01}):
def log_likelihood(x):
r, alpha, a, b = x
p1x, t_x, T = rfm_data[:, 0], rfm_data[:, 1], rfm_data[:, 2]
# Logarithm calculations with numerical stability
log_alpha = np.log(
np.clip(alpha, 1e-10, None)
) # Avoid log(0) by clipping to a small value
log_alpha_t_x = np.log(np.clip(alpha + t_x, 1e-10, None))
# Components of the log-likelihood
D_1 = (
gammaln(r + p1x)
- gammaln(r)
+ gammaln(a + b)
+ gammaln(b + p1x)
- gammaln(b)
- gammaln(a + b + p1x)
)
D_2 = r * log_alpha - (r + p1x) * log_alpha_t_x
C_3 = ((alpha + t_x) / (alpha + T)) ** (r + p1x)
C_4 = a / (b + p1x - 1)
# Handle cases where p1x > 0 and apply log to valid values
log_term = np.log(np.clip(C_3 + C_4, 1e-10, None))
result = (
D_1 + D_2 + np.where(p1x > 0, log_term, np.log(np.clip(C_3, 1e-10, None)))
)
return -np.sum(result)
# Bounds for the optimization
bnds = [(1e-6, np.inf) for _ in range(4)]
# Optimization using minimize
return minimize(
log_likelihood,
x0=list(guess.values()),
bounds=bnds,
method="Nelder-Mead",
options={"maxiter": 20000},
)