Skip to content

Commit 783d5fd

Browse files
authored
Release v0.11.2
As went to CRAN: * Spending model new customer prediction: `newcustomer.spending()` (#277) * PNBD Dyncov: Improved optimx defaults (higher itnmax) (#279) * Bootstrapping: More tests (#281) * Uncertainty estimates: Move common docu to template (#280)
2 parents 38d23a3 + ca0ec5f commit 783d5fd

29 files changed

+423
-116
lines changed

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: CLVTools
22
Title: Tools for Customer Lifetime Value Estimation
3-
Version: 0.11.1
4-
Date: 2024-10-10
3+
Version: 0.11.2
4+
Date: 2024-12-01
55
Authors@R: c(
66
person(given="Patrick", family="Bachmann", email = "pbachma@ethz.ch", role = c("cre","aut")),
77
person(given="Niels", family="Kuebler", email = "niels.kuebler@uzh.ch", role = "aut"),

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export(clvdata)
4646
export(latentAttrition)
4747
export(newcustomer)
4848
export(newcustomer.dynamic)
49+
export(newcustomer.spending)
4950
export(newcustomer.static)
5051
export(spending)
5152
exportMethods(bgbb)

NEWS.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# CLVTools 0.11.2
2+
3+
### NEW FEATURES
4+
* `newcustomer.spending()`: Predict average spending per transaction for customers without order history
5+
* Improved optimizer defaults (higher iteration count) for PNBD dyncov
6+
7+
8+
19
# CLVTools 0.11.1
210

311
### NEW FEATURES

R/all_generics.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,10 @@ setGeneric(name="clv.model.process.newdata", def=function(clv.model, clv.fitted,
165165
setGeneric(name="clv.model.pmf", def=function(clv.model, clv.fitted, x)
166166
standardGeneric("clv.model.pmf"))
167167

168-
# .. New customer expectation -----------------------------------------------------------------------------------------------
169-
# predict unconditional expectation until individual t_i for all customers in clv.fitted@clv.data
170-
setGeneric("clv.model.predict.new.customer.unconditional.expectation", function(clv.model, clv.fitted, clv.newcustomer, t)
171-
standardGeneric("clv.model.predict.new.customer.unconditional.expectation"))
168+
# .. New customer prediction -----------------------------------------------------------------------------------------------
169+
setGeneric("clv.model.predict.new.customer", function(clv.model, clv.fitted, clv.newcustomer)
170+
standardGeneric("clv.model.predict.new.customer"))
171+
172172

173173

174174

R/class_clv_model_bgnbd.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,15 @@ setMethod("clv.model.expectation", signature(clv.model="clv.model.bgnbd.no.cov")
106106
fct.expectation = fct.bgnbd.expectation, clv.time = clv.fitted@clv.data@clv.time))
107107
})
108108

109-
# . clv.model.predict.new.customer.unconditional.expectation --------------------------------------------------------------------------------------------------------
110-
setMethod("clv.model.predict.new.customer.unconditional.expectation", signature = signature(clv.model="clv.model.bgnbd.no.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer, t){
109+
# . clv.model.predict.new.customer --------------------------------------------------------------------------------------------------------
110+
setMethod("clv.model.predict.new.customer", signature = signature(clv.model="clv.model.bgnbd.no.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer){
111111

112112
return(bgnbd_nocov_expectation(
113113
r = clv.fitted@prediction.params.model[["r"]],
114114
alpha = clv.fitted@prediction.params.model[["alpha"]],
115115
a = clv.fitted@prediction.params.model[["a"]],
116116
b = clv.fitted@prediction.params.model[["b"]],
117-
vT_i = t))
117+
vT_i = clv.newcustomer@num.periods))
118118
})
119119

120120

R/class_clv_model_bgnbd_staticcov.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ setMethod("clv.model.expectation", signature(clv.model="clv.model.bgnbd.static.c
130130
fct.expectation = fct.bgnbd.expectation, clv.time = clv.fitted@clv.data@clv.time))
131131
})
132132

133-
# . clv.model.predict.new.customer.unconditional.expectation -----------------------------------------------------------------------------------------------------
134-
setMethod("clv.model.predict.new.customer.unconditional.expectation", signature = signature(clv.model="clv.model.bgnbd.static.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer, t){
133+
# . clv.model.predict.new.customer -----------------------------------------------------------------------------------------------------
134+
setMethod("clv.model.predict.new.customer", signature = signature(clv.model="clv.model.bgnbd.static.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer){
135135

136136
m.cov.trans <- clv.newcustomer.static.get.matrix.cov.trans(clv.newcustomer=clv.newcustomer, clv.fitted=clv.fitted)
137137
m.cov.life <- clv.newcustomer.static.get.matrix.cov.life(clv.newcustomer=clv.newcustomer, clv.fitted=clv.fitted)
@@ -153,7 +153,7 @@ setMethod("clv.model.predict.new.customer.unconditional.expectation", signature
153153
vAlpha_i = alpha_i,
154154
vA_i = a_i,
155155
vB_i = b_i,
156-
vT_i = t
156+
vT_i = clv.newcustomer@num.periods
157157
))
158158
})
159159

R/class_clv_model_gg.R

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,18 @@ setMethod("clv.model.predict", signature(clv.model="clv.model.gg"), function(clv
115115
})
116116

117117

118+
# .clv.model.predict.newcustomer --------------------------------------------------------------------------------------------------------
119+
setMethod("clv.model.predict.new.customer", signature(clv.model="clv.model.gg"), function(clv.model, clv.fitted, clv.newcustomer){
120+
121+
p <- clv.fitted@prediction.params.model[["p"]]
122+
q <- clv.fitted@prediction.params.model[["q"]]
123+
gamma <- clv.fitted@prediction.params.model[["gamma"]]
124+
125+
# setting x=0 in the ordinary prediction function
126+
return( (gamma) * p/(q - 1) )
127+
})
128+
129+
118130
# .clv.model.vcov.jacobi.diag --------------------------------------------------------------------------------------------------------
119131
setMethod(f = "clv.model.vcov.jacobi.diag", signature = signature(clv.model="clv.model.gg"), definition = function(clv.model, clv.fitted, prefixed.params){
120132

R/class_clv_model_ggomnbd_nocov.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,16 @@ setMethod("clv.model.expectation", signature(clv.model="clv.model.ggomnbd.no.cov
118118
})
119119

120120

121-
# . clv.model.predict.new.customer.unconditional.expectation --------------------------------------------------------------------------------------------------------
122-
setMethod("clv.model.predict.new.customer.unconditional.expectation", signature = signature(clv.model="clv.model.ggomnbd.no.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer, t){
121+
# . clv.model.predict.new.customer --------------------------------------------------------------------------------------------------------
122+
setMethod("clv.model.predict.new.customer", signature = signature(clv.model="clv.model.ggomnbd.no.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer){
123123

124124
return(ggomnbd_nocov_expectation(
125125
r = clv.fitted@prediction.params.model[["r"]],
126126
alpha_0 = clv.fitted@prediction.params.model[["alpha"]],
127127
beta_0 = clv.fitted@prediction.params.model[["beta"]],
128128
b = clv.fitted@prediction.params.model[["b"]],
129129
s = clv.fitted@prediction.params.model[["s"]],
130-
vT_i = t))
130+
vT_i = clv.newcustomer@num.periods))
131131
})
132132

133133

R/class_clv_model_ggomnbd_staticcov.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ setMethod("clv.model.expectation", signature(clv.model="clv.model.ggomnbd.static
100100
fct.expectation = fct.expectation, clv.time = clv.fitted@clv.data@clv.time))
101101
})
102102

103-
# . clv.model.predict.new.customer.unconditional.expectation -----------------------------------------------------------------------------------------------------
104-
setMethod("clv.model.predict.new.customer.unconditional.expectation", signature = signature(clv.model="clv.model.ggomnbd.static.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer, t){
103+
# . clv.model.predict.new.customer -----------------------------------------------------------------------------------------------------
104+
setMethod("clv.model.predict.new.customer", signature = signature(clv.model="clv.model.ggomnbd.static.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer){
105105

106106
m.cov.trans <- clv.newcustomer.static.get.matrix.cov.trans(clv.newcustomer=clv.newcustomer, clv.fitted=clv.fitted)
107107
m.cov.life <- clv.newcustomer.static.get.matrix.cov.life(clv.newcustomer=clv.newcustomer, clv.fitted=clv.fitted)
@@ -122,7 +122,7 @@ setMethod("clv.model.predict.new.customer.unconditional.expectation", signature
122122
s = clv.fitted@prediction.params.model[["s"]],
123123
vAlpha_i= alpha_i,
124124
vBeta_i = beta_i,
125-
vT_i = t))
125+
vT_i = clv.newcustomer@num.periods))
126126
})
127127

128128

R/class_clv_model_pnbd.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,15 +269,15 @@ setMethod("clv.model.expectation", signature(clv.model="clv.model.pnbd.no.cov"),
269269

270270

271271

272-
# . clv.model.predict.new.customer.unconditional.expectation --------------------------------------------------------------------------------------------------------
273-
setMethod("clv.model.predict.new.customer.unconditional.expectation", signature = signature(clv.model="clv.model.pnbd.no.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer, t){
272+
# . clv.model.predict.new.customer --------------------------------------------------------------------------------------------------------
273+
setMethod("clv.model.predict.new.customer", signature = signature(clv.model="clv.model.pnbd.no.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer){
274274

275275
return(pnbd_nocov_expectation(
276276
r = clv.fitted@prediction.params.model[["r"]],
277277
s = clv.fitted@prediction.params.model[["s"]],
278278
alpha_0 = clv.fitted@prediction.params.model[["alpha"]],
279279
beta_0 = clv.fitted@prediction.params.model[["beta"]],
280-
vT_i = t))
280+
vT_i = clv.newcustomer@num.periods))
281281
})
282282

283283

R/class_clv_model_pnbd_dynamiccov.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ clv.model.pnbd.dynamic.cov <- function(){
1818
name.model = "Pareto/NBD with Dynamic Covariates",
1919
# Overwrite optimx default args
2020
optimx.defaults = list(method = "Nelder-Mead",
21-
itnmax = 3000,
21+
itnmax = 50000,
2222
control = list(
2323
kkt = TRUE,
2424
save.failures = TRUE,
@@ -175,11 +175,11 @@ setMethod("clv.model.expectation", signature(clv.model="clv.model.pnbd.dynamic.c
175175
})
176176

177177

178-
# . clv.model.predict.new.customer.unconditional.expectation -----------------------------------------------------------------------------------------------------
179-
setMethod("clv.model.predict.new.customer.unconditional.expectation", signature = signature(clv.model="clv.model.pnbd.dynamic.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer, t){
178+
# . clv.model.predict.new.customer -----------------------------------------------------------------------------------------------------
179+
setMethod("clv.model.predict.new.customer", signature = signature(clv.model="clv.model.pnbd.dynamic.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer){
180180
return(pnbd_dyncov_newcustomer_expectation(
181181
clv.fitted=clv.fitted,
182-
t=t,
182+
t=clv.newcustomer@num.periods,
183183
tp.first.transaction=clv.newcustomer@first.transaction,
184184
dt.cov.life=clv.newcustomer@data.cov.life,
185185
dt.cov.trans=clv.newcustomer@data.cov.trans))

R/class_clv_model_pnbd_staticcov.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ setMethod("clv.model.expectation", signature(clv.model="clv.model.pnbd.static.co
211211

212212

213213

214-
# . clv.model.predict.new.customer.unconditional.expectation -----------------------------------------------------------------------------------------------------
215-
setMethod("clv.model.predict.new.customer.unconditional.expectation", signature = signature(clv.model="clv.model.pnbd.static.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer, t){
214+
# . clv.model.predict.new.customer -----------------------------------------------------------------------------------------------------
215+
setMethod("clv.model.predict.new.customer", signature = signature(clv.model="clv.model.pnbd.static.cov"), definition = function(clv.model, clv.fitted, clv.newcustomer){
216216

217217

218218
m.cov.trans <- clv.newcustomer.static.get.matrix.cov.trans(clv.newcustomer=clv.newcustomer, clv.fitted=clv.fitted)
@@ -232,7 +232,7 @@ setMethod("clv.model.predict.new.customer.unconditional.expectation", signature
232232
s = clv.fitted@prediction.params.model[["s"]],
233233
vAlpha_i = alpha_i,
234234
vBeta_i = beta_i,
235-
vT_i = t
235+
vT_i = clv.newcustomer@num.periods
236236
))
237237
})
238238

R/f_clvfitted_inputchecks.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ check_user_data_predict_newcustomer_numperiods <- function(num.periods){
437437
check_user_data_predict_newcustomer_staticcov <- function(clv.fitted, clv.newcustomer){
438438

439439
# is exactly "clv.newcustomer.static.cov"
440-
if(!is(clv.newcustomer, "clv.newcustomer.static.cov") | is(clv.newcustomer, "clv.newcustomer.dynamic.cov")){
440+
if(!is(clv.newcustomer, "clv.newcustomer.static.cov")){
441441
return("Parameter newdata has to be output from calling `newcustomer.static()`!")
442442
}
443443

R/f_generics_clvfittedspending.R

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,23 @@ setMethod("clv.controlflow.check.newdata", signature(clv.fitted="clv.fitted.spen
4747
})
4848

4949

50+
# . clv.controlflow.predict.new.customer -----------------------------------------------------------------------
51+
setMethod("clv.controlflow.predict.new.customer", signature(clv.fitted="clv.fitted.spending"), definition = function(clv.fitted, clv.newcustomer){
52+
53+
54+
# Only newcustomer.spending() is allowed
55+
if(!is(clv.newcustomer, "clv.newcustomer.spending")){
56+
check_err_msg("To predict for new customers, 'newdata' has to be the output of 'newdata.spending()'!")
57+
}
58+
59+
return(drop(clv.model.predict.new.customer(
60+
clv.model = clv.fitted@clv.model,
61+
clv.fitted = clv.fitted,
62+
clv.newcustomer=clv.newcustomer
63+
)))
64+
})
65+
66+
5067
# . clv.controlflow.predict.build.result.table -----------------------------------------------------------------
5168
setMethod("clv.controlflow.predict.build.result.table", signature(clv.fitted="clv.fitted.spending"), definition = function(clv.fitted, verbose, ...){
5269
dt.predictions <- copy(clv.fitted@cbs[, "Id"])

R/f_generics_clvfittedtransactions.R

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -329,15 +329,14 @@ setMethod("clv.controlflow.predict.post.process.prediction.table", signature = s
329329
#' @include class_clv_fitted_transactions.R
330330
setMethod("clv.controlflow.predict.new.customer", signature = signature(clv.fitted="clv.fitted.transactions"), definition = function(clv.fitted, clv.newcustomer){
331331

332-
if(!is(clv.newcustomer, "clv.newcustomer.no.cov") | is(clv.newcustomer, "clv.newcustomer.static.cov")){
332+
if(!is(clv.newcustomer, "clv.newcustomer.no.cov")){
333333
check_err_msg("Parameter newdata has to be output from calling `newcustomer()`!")
334334
}
335335

336-
return(drop(clv.model.predict.new.customer.unconditional.expectation(
336+
return(drop(clv.model.predict.new.customer(
337337
clv.model = clv.fitted@clv.model,
338338
clv.fitted = clv.fitted,
339-
clv.newcustomer=clv.newcustomer,
340-
t=clv.newcustomer@num.periods)))
339+
clv.newcustomer=clv.newcustomer)))
341340
})
342341

343342

R/f_generics_clvfittedtransactionsdyncov.R

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,8 @@ setMethod(f = "clv.controlflow.predict.new.customer", signature = signature(clv.
107107
tp.prediction.end=tp.prediction.end))
108108

109109

110-
return(clv.model.predict.new.customer.unconditional.expectation(
110+
return(clv.model.predict.new.customer(
111111
clv.model = clv.fitted@clv.model,
112112
clv.fitted = clv.fitted,
113-
t=clv.newcustomer@num.periods,
114113
clv.newcustomer=clv.newcustomer))
115114
})

R/f_generics_clvfittedtransactionsstaticcov.R

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,10 @@ setMethod(f = "clv.controlflow.predict.new.customer", signature = signature(clv.
9191

9292
check_err_msg(check_user_data_predict_newcustomer_staticcov(clv.fitted=clv.fitted, clv.newcustomer=clv.newcustomer))
9393

94-
return(drop(clv.model.predict.new.customer.unconditional.expectation(
94+
return(drop(clv.model.predict.new.customer(
9595
clv.model = clv.fitted@clv.model,
9696
clv.fitted = clv.fitted,
97-
clv.newcustomer=clv.newcustomer,
98-
t=clv.newcustomer@num.periods)))
97+
clv.newcustomer=clv.newcustomer)))
9998
})
10099

101100

0 commit comments

Comments
 (0)