library(PLNmodels)
library(tidymodels)
library(poissonreg)
Construire un modèle parsnip
Introduction
Notre objectif ici est de construire un nouveau modèle {parsnip}
à partir d’une implémentation existante dans un package R.
Intérêts : éliminer des dépendances, du code dupliqué, pouvoir profiter du cadre {tidymodels}
.
Ressources
Un modèle {parsnip}
Il se définit par
un
mode
(régression linéaire, logistique, …),un
type
(régression, classification),un moteur de calcul
engine
(lm
,stan
, …).
Qunad on ajoute un modèle, on doit donc spécifier son mode
et son engine
. On peut aussi ajouter un nouveau modèle différent d’un pré-existant seulement par son type
(ie même combinaison engine
et mode
).
Il est possible de voir l’ensemble des modèles déjà disponibles ici.
Intégration de la régression PLN
Données
Nous utiliserons le jeu de données de la vignette de PLN.
data(trichoptera)
Il existe une fonction prepare_data
dans {parsnip}
également.
Différentes étapes pour intégrer PLNmodels::PLN
dans parsnip
Etape 1 : Spécification du modèle et de ses arguments
On va ajouter la fonction PLNmodels::PLN
à la liste des fonctions utilisables pour faire de la régression de Poisson.
show_model_info("poisson_reg")
Information for `poisson_reg`
modes: unknown, regression
engines:
regression: glm¹, glmnet¹, hurdle¹, stan¹, zeroinfl¹
¹The model can use case weights.
arguments:
glmnet:
penalty --> lambda
mixture --> alpha
fit modules:
engine mode
glm regression
hurdle regression
zeroinfl regression
glmnet regression
stan regression
prediction modules:
mode engine methods
regression glm numeric, raw
regression glmnet numeric, raw
regression hurdle numeric, raw
regression stan conf_int, numeric, pred_int, raw
regression zeroinfl numeric, raw
set_model_mode(model = "poisson_reg", mode = "regression")
set_model_engine(
"poisson_reg",
mode = "regression",
eng = "PLN"
)set_dependency("poisson_reg", eng = "PLN", pkg = "PLNmodels")
On peut vérifier ce qu’on a ajouté.
show_model_info("poisson_reg")
Information for `poisson_reg`
modes: unknown, regression
engines:
regression: glm¹, glmnet¹, hurdle¹, PLNNA, stan¹, zeroinfl¹
¹The model can use case weights.
arguments:
glmnet:
penalty --> lambda
mixture --> alpha
fit modules:
engine mode
glm regression
hurdle regression
zeroinfl regression
glmnet regression
stan regression
prediction modules:
mode engine methods
regression glm numeric, raw
regression glmnet numeric, raw
regression hurdle numeric, raw
regression stan conf_int, numeric, pred_int, raw
regression zeroinfl numeric, raw
Si notre modèle a des arguments supplémentaires que ceux existants, on peut les ajouter.
Etape 2 : Créer la fonction principale associée au modèle le cas échéant
Ici comme nous avons seulement ajouté un moteur à un modèle préexistant ce n’est pas nécessaire.
todo: regarder comment ajouter l’information possible d’utilisation de poids
Etape 3 : Préciser le module d’ajustement (fit
)
L’argument
interface
, peut prendre les valeursformula
,data
oumatrices
.protect
est une liste optionnelle d’arguments qui ne devraient pas être modifié par l’utilisateur.func
est un vecteur précisant le package et la fonction qui sera appelé pour l’ajustement.defaults
est une liste optionnelle d’arguments que l’utilisateur peut modifier mais pour laquelle on peut spécifier des valeurs par défaut.
set_fit(
model = "poisson_reg",
eng = "PLN",
mode = "regression",
value = list(
interface = "formula",
protect = c("formula", "data"),
func = c(pkg = "PLNmodels", fun = "PLN"),
defaults = list(control = PLN_param(), weights = NULL, subset = NULL)
) )
On peut ajouter des traitements par défaut pour les variables explicatives, tels que calculs de variables indicatrices, calcul d’une constante etc…
set_encoding(
model = "poisson_reg",
eng = "PLN",
mode = "regression",
options = list(
predictor_indicators = "traditional",
compute_intercept = TRUE,
remove_intercept = TRUE,
allow_sparse_x = FALSE
) )
Etape 4 : Ajouter un module pour la prédiction (predict
)
set_pred(
model = "poisson_reg",
eng = "PLN",
mode = "regression",
type = "numeric",
value = list(
pre = NULL,
post = NULL,
func = c(fun = "predict"),
args =
list(
object = expr(object$fit),
newdata = expr(new_data),
type = "response"
)
) )
Application sur les données trichoptera
<- PLNmodels::prepare_data(trichoptera$Abundance, trichoptera$Covariate)
prep_trichoptera <- PLN(Abundance ~ 1 , data = prep_trichoptera) myPLN
Initialization...
Adjusting a full covariance PLN model with nlopt optimizer
Post-treatments...
DONE!
myPLN
A multivariate Poisson Lognormal fit with full covariance model.
==================================================================
nb_param loglik BIC ICL
170 -1129.909 -1460.714 -2230.658
==================================================================
* Useful fields
$model_par, $latent, $latent_pos, $var_par, $optim_par
$loglik, $BIC, $ICL, $loglik_vec, $nb_param, $criteria
* Useful S3 methods
print(), coef(), sigma(), vcov(), fitted()
predict(), predict_cond(), standard_error()
<- poisson_reg() %>%
resPLN set_engine("PLN") %>%
fit(Abundance ~ 1 , data = prep_trichoptera)
Initialization...
Adjusting a full covariance PLN model with nlopt optimizer
Post-treatments...
DONE!
resPLN
parsnip model object
A multivariate Poisson Lognormal fit with full covariance model.
==================================================================
nb_param loglik BIC ICL
170 -1129.909 -1460.714 -2230.658
==================================================================
* Useful fields
$model_par, $latent, $latent_pos, $var_par, $optim_par
$loglik, $BIC, $ICL, $loglik_vec, $nb_param, $criteria
* Useful S3 methods
print(), coef(), sigma(), vcov(), fitted()
predict(), predict_cond(), standard_error()
summary(resPLN)
Length Class Mode
lvl 0 -none- NULL
spec 8 poisson_reg list
fit 32 PLNfit environment
preproc 1 -none- list
elapsed 1 -none- list
censor_probs 0 -none- list
$spec resPLN
Poisson Regression Model Specification (regression)
Computational engine: PLN
Model fit template:
PLNmodels::PLN(formula = missing_arg(), data = missing_arg(),
control = list(backend = "nlopt", trace = 1, covariance = "full",
Omega = NULL, config_post = list(jackknife = FALSE, bootstrap = 0L,
rsquared = TRUE, variational_var = FALSE, sandwich_var = FALSE,
trace = 1), config_optim = list(algorithm = "CCSAQ",
maxeval = 10000, ftol_rel = 1e-08, xtol_rel = 1e-06,
ftol_abs = 0, xtol_abs = 0, maxtime = -1, trace = 1),
inception = NULL), weights = NULL, subset = NULL)
# Pour recuperer les coefficients
coef(resPLN$fit)
Che Hyc Hym Hys Psy Aga Glo
(Intercept) -2.833822 -4.049306 1.051399 -2.17906 3.291263 -0.3560404 -2.217591
Ath Cea Ced Set All Han
(Intercept) -1.661659 -3.36122 0.3593471 -0.6285713 -0.8500234 -1.351443
Hfo Hsp Hve Sta
(Intercept) -2.190792 -0.3070371 -2.758038 0.9621415
all.equal(coef(myPLN), coef(resPLN$fit))
[1] TRUE
# Pour faire de la prediction
<- predict(myPLN, newdata = prep_trichoptera, type = "response")
pred_pln %>% head(n=3) pred_pln
Che Hyc Hym Hys Psy Aga Glo Ath
1 0.06157233 0.0588531 3.813198 0.1225141 89.53902 2.888241 0.251068 0.2913597
2 0.06157233 0.0588531 3.813198 0.1225141 89.53902 2.888241 0.251068 0.2913597
3 0.06157233 0.0588531 3.813198 0.1225141 89.53902 2.888241 0.251068 0.2913597
Cea Ced Set All Han Hfo Hsp Hve
1 0.1053727 2.54603 3.594756 1.074918 11.25454 1.024682 5.592037 0.1974294
2 0.1053727 2.54603 3.594756 1.074918 11.25454 1.024682 5.592037 0.1974294
3 0.1053727 2.54603 3.594756 1.074918 11.25454 1.024682 5.592037 0.1974294
Sta
1 7.385873
2 7.385873
3 7.385873
# On peut appliquer les methodes
#pred_parsnipfit <- predict(resPLN$fit, newdata = prep_trichoptera)
<- predict(resPLN, new_data = trichoptera$Abundance)
pred_parsnip %>% head(n=3) pred_parsnip
# A tibble: 3 × 17
.pred_Che .pred_Hyc .pred_Hym .pred_Hys .pred_Psy .pred_Aga .pred_Glo
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 0.0616 0.0589 3.81 0.123 89.5 2.89 0.251
2 0.0616 0.0589 3.81 0.123 89.5 2.89 0.251
3 0.0616 0.0589 3.81 0.123 89.5 2.89 0.251
# ℹ 10 more variables: .pred_Ath <dbl>, .pred_Cea <dbl>, .pred_Ced <dbl>,
# .pred_Set <dbl>, .pred_All <dbl>, .pred_Han <dbl>, .pred_Hfo <dbl>,
# .pred_Hsp <dbl>, .pred_Hve <dbl>, .pred_Sta <dbl>
Test d’un workflow
# separation du jeu de donnees en test et apprentissage
set.seed(123)
<- initial_split(data = prep_trichoptera, prop = 0.9)
tri_split #tri_train <- training(tri_split)
#tri_test <- testing(tri_split)
<-
p_recipe recipe(Abundance ~ 1 + Temperature, data = training(tri_split))
<-
pln_model poisson_reg()%>%
set_engine("PLN") %>%
set_mode("regression")
<- workflow() %>%
pln_workflow add_recipe(p_recipe) %>%
add_model(pln_model)
<- pln_workflow %>%
fitted_workflow fit(training(tri_split))
# Predicition sur le jeu d'entrainement
<- fitted_workflow %>% predict(new_data = testing(tri_split)) test_pred
Pour la suite du workflow, calcul de performance sur un rééchantillonnage etc., ce serait possible mais nécessite un peu de code. Il faudrait ici que la sortie de prédiction soit en format plus ‘tidy’ ou réécrire le calcul de métrique sur des matrices.
Autre jeu de données, exemple en régression univariée
<- read.csv("https://stats.idre.ucla.edu/stat/data/poisson_sim.csv")
p <- within(p, {
p <- factor(prog, levels=1:3, labels=c("General", "Academic",
prog "Vocational"))
<- factor(id)
id })
On essaie maintenant en utilisant {parsnip}
.
summary(m1 <- glm(num_awards ~ prog + math, family="poisson", data=p))
Call:
glm(formula = num_awards ~ prog + math, family = "poisson", data = p)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -5.24712 0.65845 -7.969 1.60e-15 ***
progAcademic 1.08386 0.35825 3.025 0.00248 **
progVocational 0.36981 0.44107 0.838 0.40179
math 0.07015 0.01060 6.619 3.63e-11 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 287.67 on 199 degrees of freedom
Residual deviance: 189.45 on 196 degrees of freedom
AIC: 373.5
Number of Fisher Scoring iterations: 6
poisson_reg() %>%
set_engine("glm") %>%
set_mode("regression") %>%
fit(num_awards ~ prog + math, data = p) %>%
predict(p)
# A tibble: 200 × 1
.pred
<dbl>
1 0.135
2 0.0934
3 0.167
4 0.145
5 0.126
6 0.100
7 0.192
8 0.126
9 0.0771
10 0.192
# ℹ 190 more rows
<- PLN(as.matrix(num_awards) ~ prog + math, data = p) mod_pln
Initialization...
Adjusting a full covariance PLN model with nlopt optimizer
Post-treatments...
DONE!
%>%
mod_pln predict(p, type = "response") %>%
head(n = 10)
Y
1 0.13408541
2 0.09317098
3 0.16585222
4 0.14393335
5 0.12491126
6 0.10001395
7 0.19110900
8 0.12491126
9 0.07605752
10 0.19110900
poisson_reg() %>%
set_engine("PLN") %>%
set_mode("regression") %>%
fit(as.matrix(num_awards) ~ prog + math, data = p) %>%
predict(p)
Initialization...
Adjusting a full covariance PLN model with nlopt optimizer
Post-treatments...
DONE!
# A tibble: 200 × 1
.pred_Y
<dbl>
1 0.134
2 0.0932
3 0.166
4 0.144
5 0.125
6 0.100
7 0.191
8 0.125
9 0.0761
10 0.191
# ℹ 190 more rows
On peut vérifier la commande associée au modèle programmé.
poisson_reg() %>% translate(engine = "PLN")
Poisson Regression Model Specification (regression)
Computational engine: PLN
Model fit template:
PLNmodels::PLN(formula = missing_arg(), data = missing_arg(),
control = list(backend = "nlopt", trace = 1, covariance = "full",
Omega = NULL, config_post = list(jackknife = FALSE, bootstrap = 0L,
rsquared = TRUE, variational_var = FALSE, sandwich_var = FALSE,
trace = 1), config_optim = list(algorithm = "CCSAQ",
maxeval = 10000, ftol_rel = 1e-08, xtol_rel = 1e-06,
ftol_abs = 0, xtol_abs = 0, maxtime = -1, trace = 1),
inception = NULL), weights = NULL, subset = NULL)
Pour aller plus loin
Dans le cadre tidymodels
, tout est assez modulaire et personnalisable : on peut ajouter de nouvelles recettes de pré-traitement, personnaliser son rééchantillonnage, le calcul de métrique etc.
Un peu de documentation sur le site de {tidymodels]
dans la section Develop custom modeling tools.