Stampa tabelle “carine” per modelli h2o in R

Esistono più pacchetti per R che aiutano a stampare tabelle “graziose” (LaTeX / HTML / TEXT) dall’output di modelli statistici E per confrontare facilmente i risultati di specifiche di modelli alternativi.

Alcuni di questi pacchetti sono apsrtable , xtable , memisc , texreg , outreg e stargazer (per esempi vedere qui: https://www.r-statistics.com/2013/01/stargazer-package-for-beautiful-latex-tables -from-r-statistical-models-output / ).

Esiste un pacchetto R paragonabile che supporti i modelli del pacchetto h2o ?

Ecco un esempio di due semplici modelli GLM con h2o che mi piace stampare l’uno accanto all’altro come tabelle “belle”.

 # Load package and setup h2o library(h2o) localH2O <- h2o.init(ip = 'localhost', port = 54321, max_mem_size = '4g') # Load data prostatePath <- system.file("extdata", "prostate.csv", package = "h2o") prostate.hex <- h2o.importFile(path = prostatePath, destination_frame = "prostate.hex") # Run GLMs model.output.1 <- h2o.glm(y = "CAPSULE", x = c("RACE","PSA","DCAPS"), training_frame = prostate.hex,family = "binomial", nfolds = 0, alpha = 0.5, lambda_search = FALSE) model.output.2 <- h2o.glm(y = "CAPSULE", x = c("AGE","RACE","PSA","DCAPS"), training_frame = prostate.hex, family = "binomial", nfolds = 0, alpha = 0.5, lambda_search = FALSE) 

Ecco come apparirebbe con un normale object GLM usando screenreg() dal pacchetto texreg :

 library(data.table) library(texreg) d <- fread(prostatePath) model.output.1.glm <- glm(CAPSULE ~ RACE + PSA + DCAPS, data=d) model.output.2.glm <- glm(CAPSULE ~ AGE + RACE + PSA + DCAPS, data=d) screenreg(list(model.output.1.glm, model.output.2.glm)) 

inserisci la descrizione dell'immagine qui

autore di pacchetti texreg qui. texreg si basa su funzioni generiche, il che significa che qualsiasi utente può aggiungere metodi di extract personalizzati per modelli arbitrari e farli funzionare con texreg . Ti guiderò attraverso questo sotto. Spero che questa esposizione dettagliata possa aiutare altre persone che hanno fatto domande simili in passato a escogitare le proprie estensioni di texreg .

Vedere anche la sezione 6 del documento 2013 nel Journal of Statistical Software per un altro esempio. In primo luogo, tuttavia, descriverò in che modo l’architettura texreg funziona più in generale per darti un’idea di ciò che è ansible.

texreg e la funzione di extract generica

Ci sono tre funzioni: texreg (per output LaTeX), htmlreg (per output HTML, che può essere interpretato anche da Word o Markdown nella maggior parte degli scenari di utilizzo) e screenreg (per l’output di testo ASCII nella console). Queste tre funzioni servono a convertire alcune informazioni ripulite (coefficienti, errori standard, intervalli di confidenza, valori p, statistiche sulla bontà di adattamento, etichette modello ecc.) Nei rispettivi formati di output. Questo non è perfetto e potrebbe essere ancora più flessibile, ma penso che abbia alcuni argomenti per la personalizzazione a questo punto, inclusi cose come booktabs e supporto per dcolumn . Quindi la grande sfida è piuttosto quella di ottenere informazioni sul modello ripulite in primo luogo.

Questo viene fatto fornendo un object texreg a una di queste tre funzioni. Un object texreg è solo un contenitore per i coefficienti ecc. texreg è formalmente definito usando una class S4. Per creare un object texreg , puoi utilizzare la funzione di costruzione createTexreg (come documentato nelle pagine di aiuto), che accetta tutte le diverse informazioni come argomenti, come gli errori standard ecc. Oppure (meglio) puoi usare l’ extract funzione per estrarre tali informazioni da alcuni modelli stimati e restituire un object texreg da utilizzare con una qualsiasi delle tre funzioni. Il modo in cui lo fai tipicamente è semplicemente passando una lista di diversi modelli alla funzione texreg o screenreg , e questa funzione chiamerà internamente extract per creare oggetti texreg e quindi elaborare le informazioni da quegli oggetti.

Tuttavia, è ugualmente valido salvare semplicemente l’output di una chiamata della funzione di extract su un object, possibilmente manipolare questo object texreg e quindi chiamare la funzione texreg sull’object manipolato per visualizzarla come tabella. Ciò consente una certa flessibilità nel modificare i risultati.

In precedenza ho menzionato che il pacchetto utilizza funzioni generiche. Ciò significa che la funzione di extract è generica nel senso che è ansible registrare metodi che funzionano con classi arbitrarie di modelli. Ad esempio, se la funzione di extract non sa come gestire gli oggetti h2o e come estrarre le informazioni rilevanti da un tale object, puoi semplicemente scrivere un metodo per farlo e registrarlo con la funzione di extract . Di seguito, ti guiderò passo dopo passo nella speranza che le persone imparino da questa esposizione dettagliata e inizi a scrivere le loro estensioni. (Nota: se qualcuno sviluppa un metodo utile, per favore texreg una e-mail e io posso includerlo nella prossima versione di texreg .) Vale anche la pena sottolineare che i file sorgente del pacchetto contengono più di 70 esempi di metodi di extract che tu può usare come modelli. Questi esempi sono memorizzati nel file R/extract.R .

Identificazione dell’etichetta della class e impostazione di un metodo di extract

Il primo passo è identificare il nome della class dell’object. Nel tuo esempio, class(model.output.1) restituisce le seguenti etichette di class: “H2OBinomialModel” e “h2o”. La prima etichetta è la più specifica e la seconda è quella più generale. Se tutti gli oggetti del modello h2o sono strutturati in modo simile, avrà senso scrivere un’estensione per gli oggetti h2o e poi decidere all’interno del metodo come procedere con il modello specifico. Poiché non conosco praticamente nulla del pacchetto h2o , in questo caso preferisco iniziare con un metodo di extract più specifico per H2OBinomialModel oggetti H2OBinomialModel . Può essere regolato in un secondo momento, se lo desideriamo.

extract metodi di extract sono strutturati come segue: scrivi una funzione chiamata extract.xyz (sostituisci “xyz” con l’etichetta della class), hai almeno un argomento chiamato model , che accetta l’object model (ad es. model.output.1 nel tuo esempio ), inserire del codice nel corpo che estrae le informazioni rilevanti dall’object del model , creare un object texreg usando il costruttore createTexreg e restituire questo object. Ecco un contenitore vuoto:

 extract.H2OBinomialModel <- function(model, ...) { s <- summary(model) # extract information from model and summary object here # then create and return a texreg object (replace NULL with actual values): tr <- createTexreg( coef.names = NULL, # character vector of coefficient labels coef = NULL, # numeric vector with coefficients se = NULL, # numeric vector with standard error values pvalues = NULL, # numeric vector with p-values gof.names = NULL, # character vector with goodness-of-fit labels gof = NULL, # numeric vector of goodness-of-fit statistics gof.decimal = NULL # logical vector: GOF statistic has decimal points? ) return(tr) } 

Si noti che la definizione della funzione contiene anche l'argomento ... , che può essere utilizzato per argomenti personalizzati che devono essere trasferiti alle chiamate di funzione all'interno del metodo di extract .

Si noti inoltre che la prima riga nel corpo della definizione della funzione salva il riepilogo del modello in un object chiamato s . Questo è spesso utile perché molti scrittori di pacchetti decidono di archiviare alcune delle informazioni in una versione più semplice nel sumrio, quindi di solito dovremmo considerare sia il modello che il suo sumrio come utili fonti di informazione. In alcuni casi, potrebbe essere necessario esaminare la definizione effettiva del metodo di riepilogo nel rispettivo pacchetto per scoprire come vengono calcolate le informazioni visualizzate nella pagina di riepilogo quando viene chiamato il comando di summary perché non tutti i metodi di summary memorizzano i diversi elementi visualizzati nell'object di summary .

Individuazione delle informazioni corrette in un object H2OBinomialModel

Il prossimo passo è esaminare l'object e localizzare tutti i dettagli che dovrebbero essere visualizzati nella tabella finale. Osservando l'output di model.output.1 , suppongo che la parte seguente dovrebbe costituire il blocco GOF nella parte inferiore della tabella:

 MSE: 0.202947 R^2: 0.1562137 LogLoss: 0.5920097 Mean Per-Class Error: 0.3612191 AUC: 0.7185655 Gini: 0.4371311 Null Deviance: 512.2888 Residual Deviance: 449.9274 AIC: 457.9274 

E la parte seguente dovrebbe probabilmente costituire il blocco del coefficiente nel mezzo della tabella:

 Coefficients: glm coefficients names coefficients standardized_coefficients 1 Intercept -1.835223 -0.336428 2 RACE -0.625222 -0.193052 3 DCAPS 1.314428 0.408336 4 PSA 0.046861 0.937107 

In molti casi, il riepilogo contiene le informazioni pertinenti, ma qui la stampa del modello produce ciò di cui abbiamo bisogno. Dovremo trovare tutto questo nell'object model.output.1 (o nel suo sumrio se applicabile). Per fare ciò, ci sono diversi comandi utili. Tra questi ci sono str(model.output.1) , names(summary(model.output.1)) e comandi simili.

Iniziamo con il blocco del coefficiente. Chiamando str(model) scopre che c'è uno slot chiamato model nell'object S4. Possiamo esaminare il suo contenuto chiamando [email protected] . Il risultato è una lista con diversi elementi con nome, tra cui coefficients_table . Quindi possiamo accedere alla tabella dei coefficienti chiamando [email protected]$coefficients_table . Il risultato è un frame di dati con le colonne a cui è ansible accedere utilizzando l'operatore $ . In particolare, abbiamo bisogno dei nomi e dei coefficienti. Ci sono due tipi di coefficienti qui, standardizzati e non standardizzati, e possiamo aggiungere un argomento al nostro metodo di estrazione più tardi per consentire all'utente di decidere cosa vuole. Ecco come estraiamo i coefficienti e le loro etichette:

 coefnames <- [email protected]$coefficients_table$names coefs <- [email protected]$coefficients_table$coefficients coefs.std <- [email protected]$coefficients_table$standardized_coefficients 

Per quanto posso vedere, non ci sono errori standard o valori p memorizzati nell'object. Potremmo scrivere un codice aggiuntivo per calcolarli nel caso volessimo farlo, ma qui ci concentreremo su cose che sono prontamente fornite come parte dell'output del modello.

È importante non sovrascrivere alcun nome di funzione esistente in R , come names o coef . Mentre ciò dovrebbe funzionare tecnicamente perché il codice viene eseguito all'interno di una funzione in un secondo momento, questo può facilmente portare a confusione mentre si provano le cose, quindi è meglio evitarlo.

Successivamente, abbiamo bisogno di localizzare le statistiche della bontà di adattamento. Esaminando attentamente l'output di str(model.output.1) , vediamo che le statistiche sulla bontà di adattamento sono contenute in diversi slot sotto [email protected][email protected] . Salviamoli in alcuni oggetti a cui è più facile accedere:

 mse <- [email protected][email protected]$MSE r2 <- [email protected][email protected]$r2 logloss <- [email protected][email protected]$logloss mpce <- [email protected][email protected]$mean_per_class_error auc <- [email protected][email protected]$AUC gini <- [email protected][email protected]$Gini nulldev <- [email protected][email protected]$null_deviance resdev <- [email protected][email protected]$residual_deviance aic <- [email protected][email protected]$AIC 

In alcuni casi, ma non qui, l'autore di un pacchetto scrive metodi per funzioni generiche che possono essere utilizzate per estrarre alcune informazioni comuni come il numero di osservazioni ( nobs(model) ), AIC ( AIC(model) ), BIC ( BIC(model) ), devianza ( deviance(model) ) o probabilità di registro ( logLik(model)[[1]] ). Quindi queste sono le cose che potresti voler provare prima; ma il pacchetto h2o non sembra offrire tali metodi di convenienza.

Aggiunta delle informazioni alla funzione extract.H2OBinomialModel

Ora che abbiamo individuato tutte le informazioni necessarie, possiamo aggiungerle alla funzione extract.H2OBinomialModel definita in precedenza.

Tuttavia, vorremmo lasciare che l'utente decida se preferisce i coefficienti grezzi o standardizzati, e vorremmo che l'utente decida quali statistiche di idoneità devono essere riportate, quindi aggiungiamo vari argomenti logici all'intestazione della funzione e quindi utilizzare if-conditions all'interno della funzione per verificare se dovremmo incorporare le rispettive statistiche nell'object texreg risultante.

Rimuoviamo anche la riga s <- summary(model) in questo caso perché non abbiamo realmente bisogno di alcun tipo di informazione dal sumrio poiché abbiamo trovato tutto ciò di cui abbiamo bisogno nell'object del modello.

La funzione completa potrebbe essere simile a questa:

 # extension for H2OBinomialModel objects (h2o package) extract.H2OBinomialModel <- function(model, standardized = FALSE, include.mse = TRUE, include.rsquared = TRUE, include.logloss = TRUE, include.meanerror = TRUE, include.auc = TRUE, include.gini = TRUE, include.deviance = TRUE, include.aic = TRUE, ...) { # extract coefficient table from model: coefnames <- [email protected]$coefficients_table$names if (standardized == TRUE) { coefs <- [email protected]$coefficients_table$standardized_coefficients } else { coefs <- [email protected]$coefficients_table$coefficients } # create empty GOF vectors and subsequently add GOF statistics from model: gof <- numeric() gof.names <- character() gof.decimal <- logical() if (include.mse == TRUE) { mse <- [email protected][email protected]$MSE gof <- c(gof, mse) gof.names <- c(gof.names, "MSE") gof.decimal <- c(gof.decimal, TRUE) } if (include.rsquared == TRUE) { r2 <- [email protected][email protected]$r2 gof <- c(gof, r2) gof.names <- c(gof.names, "R^2") gof.decimal <- c(gof.decimal, TRUE) } if (include.logloss == TRUE) { logloss <- [email protected][email protected]$logloss gof <- c(gof, logloss) gof.names <- c(gof.names, "LogLoss") gof.decimal <- c(gof.decimal, TRUE) } if (include.meanerror == TRUE) { mpce <- [email protected][email protected]$mean_per_class_error gof <- c(gof, mpce) gof.names <- c(gof.names, "Mean Per-Class Error") gof.decimal <- c(gof.decimal, TRUE) } if (include.auc == TRUE) { auc <- [email protected][email protected]$AUC gof <- c(gof, auc) gof.names <- c(gof.names, "AUC") gof.decimal <- c(gof.decimal, TRUE) } if (include.gini == TRUE) { gini <- [email protected][email protected]$Gini gof <- c(gof, gini) gof.names <- c(gof.names, "Gini") gof.decimal <- c(gof.decimal, TRUE) } if (include.deviance == TRUE) { nulldev <- [email protected][email protected]$null_deviance resdev <- [email protected][email protected]$residual_deviance gof <- c(gof, nulldev, resdev) gof.names <- c(gof.names, "Null Deviance", "Residual Deviance") gof.decimal <- c(gof.decimal, TRUE, TRUE) } if (include.aic == TRUE) { aic <- [email protected][email protected]$AIC gof <- c(gof, aic) gof.names <- c(gof.names, "AIC") gof.decimal <- c(gof.decimal, TRUE) } # create texreg object: tr <- createTexreg( coef.names = coefnames, coef = coefs, gof.names = gof.names, gof = gof, gof.decimal = gof.decimal ) return(tr) } 

Per il blocco di adattamento, è ansible vedere che prima ho creato i vettori vuoti e successivamente li ho compilati con statistiche aggiuntive a condizione che la rispettiva statistica sia stata triggersta dall'utente utilizzando il rispettivo argomento.

Il vettore logico gof.decimal indica per ogni statistica GOF se ha cifre decimali ( TRUE ) o meno ( FALSE , come nel numero di osservazioni, ad esempio).

Infine, la nuova funzione deve essere registrata come metodo per la funzione di extract generica. Questo viene fatto usando un semplice comando:

 setMethod("extract", signature = className("H2OBinomialModel", "h2o"), definition = extract.H2OBinomialModel) 

Qui, il primo argomento di className è l'etichetta di class e il secondo è il pacchetto in cui è definita la class.

Riassumendo, le uniche due cose che devono essere fatte per scrivere un'estensione personalizzata sono: 1) scrivere un metodo di estrazione e 2) registrare il metodo. Cioè, questo codice può essere eseguito in fase di esecuzione e non deve essere inserito in alcun pacchetto.

Tuttavia, per maggiore praticità, ho aggiunto il metodo texreg alla versione 1.36.13 di texreg, disponibile su CRAN.

Si noti che la soluzione presentata qui non funziona con nessuna versione precedente di texreg quanto le versioni precedenti non potevano trattare modelli che non avevano né errori standard né intervalli di confidenza. Questa è una configurazione abbastanza specializzata, a mio parere, e non ho trovato un pacchetto che fornisca semplicemente le stime senza misure di incertezza. Ora ho risolto questo texreg in texreg .

Provando il nuovo metodo di extract

Una volta che la definizione della funzione e il comando setMethod sono stati eseguiti in fase di esecuzione, è ansible utilizzare il seguente comando per creare una tabella:

 screenreg(list(model.output.1, model.output.2), custom.note = "") 

Questo è l'output:

 ====================================== Model 1 Model 2 -------------------------------------- Intercept -1.84 -1.11 RACE -0.63 -0.62 DCAPS 1.31 1.31 PSA 0.05 0.05 AGE -0.01 -------------------------------------- MSE 0.20 0.20 R^2 0.16 0.16 LogLoss 0.59 0.59 Mean Per-Class Error 0.36 0.38 AUC 0.72 0.72 Gini 0.44 0.44 Null Deviance 512.29 512.29 Residual Deviance 449.93 449.51 AIC 457.93 459.51 ====================================== 

L' custom.note = "" ha senso qui perché non vogliamo una legenda di significatività poiché i modelli non riportano alcuna misura di incertezza.

È anche ansible sopprimere alcune delle misure del GOF e / o utilizzare coefficienti standardizzati:

 screenreg(list(model.output.1, model.output.2), custom.note = "", include.deviance = FALSE, include.auc = FALSE, standardized = TRUE) 

Il risultato:

 ====================================== Model 1 Model 2 -------------------------------------- Intercept -0.34 -0.34 RACE -0.19 -0.19 DCAPS 0.41 0.41 PSA 0.94 0.94 AGE -0.07 -------------------------------------- MSE 0.20 0.20 R^2 0.16 0.16 LogLoss 0.59 0.59 Mean Per-Class Error 0.36 0.38 Gini 0.44 0.44 AIC 457.93 459.51 ====================================== 

Altri slot che possono essere utilizzati con createTexreg

La funzione di costruzione createTexreg viene chiamata all'interno di qualsiasi metodo di extract . L'esempio sopra mostra alcune semplici informazioni. Oltre ai dettagli contenuti nell'esempio, i seguenti spazi sono disponibili in oggetti texreg :

  • se: errori standard
  • pvalues: i valori p
  • ci.low: limite inferiore di un intervallo di confidenza
  • ci.up: limite superiore di un intervallo di confidenza
  • model.name: il titolo del modello corrente

Si noti che devono essere utilizzati intervalli di confidenza o errori standard e valori p, non entrambi.

Manipolazione texreg oggetti texreg

Oltre a consegnare direttamente i modelli alle funzioni screenreg , texreg o htmlreg , è ansible salvare prima le informazioni estratte in un object texreg e manipolarle prima che la tabella venga visualizzata o salvata. Il valore aggiunto è che anche le modifiche complesse a una tabella sono facili da applicare in questo modo. Ad esempio, è ansible rinominare i coefficienti o le statistiche GOF, aggiungere nuove righe, rinominare un modello, modificare i valori o modificare l'ordine dei coefficienti o le statistiche GOF. Ecco come è ansible farlo: in primo luogo, si chiama la funzione di extract per salvare le informazioni su un object texreg :

 tr <- extract(model.output.1) 

Puoi visualizzare il contenuto dell'object texreg semplicemente chiamando tr , che mostra il seguente output:

 No standard errors and p-values were defined for this texreg object. coef. Intercept -1.83522343 RACE -0.62522179 DCAPS 1.31442834 PSA 0.04686106 GOF dec. places MSE 0.2029470 TRUE R^2 0.1562137 TRUE LogLoss 0.5920097 TRUE Mean Per-Class Error 0.3612191 TRUE AUC 0.7185655 TRUE Gini 0.4371311 TRUE Null Deviance 512.2888402 TRUE Residual Deviance 449.9273825 TRUE AIC 457.9273825 TRUE 

In alternativa, questa è la struttura dell'object come mostrato da str(tr) :

 Formal class 'texreg' [package "texreg"] with 10 slots [email protected] coef.names : chr [1:4] "Intercept" "RACE" "DCAPS" "PSA" [email protected] coef : num [1:4] -1.8352 -0.6252 1.3144 0.0469 [email protected] se : num(0) [email protected] pvalues : num(0) [email protected] ci.low : num(0) [email protected] ci.up : num(0) [email protected] gof.names : chr [1:9] "MSE" "R^2" "LogLoss" "Mean Per-Class Error" ... [email protected] gof : num [1:9] 0.203 0.156 0.592 0.361 0.719 ... [email protected] gof.decimal: logi [1:9] TRUE TRUE TRUE TRUE TRUE TRUE ... [email protected] model.name : chr(0) 

Ora puoi semplicemente manipolare questo object in modi arbitrari, ad esempio aggiungere una statistica GOF:

 [email protected] <- c([email protected], "new statistic") [email protected] <- c([email protected], 12) [email protected] <- c([email protected], FALSE) 

Oppure puoi cambiare l'ordine dei coefficienti:

 [email protected] <- [email protected][c(4, 1, 2, 3)] [email protected] <- [email protected][c(4, 1, 2, 3)] 

Quando hai finito con le manipolazioni, puoi passare l'object texreg posto del modello originale quando chiami, ad esempio, screenreg :

 screenreg(list(tr, model.output.2), custom.note = "") 

Il nuovo risultato sarà simile a questo:

 ====================================== Model 1 Model 2 -------------------------------------- PSA 0.05 0.05 Intercept -1.84 -1.11 RACE -0.63 -0.62 DCAPS 1.31 1.31 AGE -0.01 -------------------------------------- MSE 0.20 0.20 R^2 0.16 0.16 LogLoss 0.59 0.59 Mean Per-Class Error 0.36 0.38 AUC 0.72 0.72 Gini 0.44 0.44 Null Deviance 512.29 512.29 Residual Deviance 449.93 449.51 AIC 457.93 459.51 new statistic 12 ====================================== 

TL; DR

texreg può essere personalizzato dagli utenti. Basta scrivere un metodo di estrazione come quello mostrato sopra e registrarlo utilizzando una chiamata setMethods . Ho incluso il metodo H2OBinomialModel nell'ultima versione di texreg 1.36.13, insieme a un bugfix per l'utilizzo di modelli senza errori standard (come questo).

puoi usare il pacchetto R xtable con H2OTable di h2o (o knitr se converti H2OTable in H2OFrame usando as.h2o(your_H2OTable) ), se li estrai dall’output del modello.

per esempio per creare una bella tabella dai coefficienti di un modello è necessario prima estrarre la tabella dei coefficienti con [email protected]$coefficients_table , quindi è ansible utilizzare xtable: xtable([email protected]$coefficients_table) per stampare il codice Latex.

per le viste affiancate ci sono post multipli su come farlo in knitr o xtable , xtable e sweave

No, non c’è un pacchetto che lo faccia al momento. Il pacchetto di scopa non supporta ancora i modelli H2O – sarebbe fantastico! Forse potrebbe succedere in futuro. Una volta che c’è un modo per “riordinare” l’output del modello in un data.frame R. usando broom o funzionalità simili, allora xtable, ecc. Funzionerà bene.