Simulazione dell'analisi della potenza della regressione logistica - esperimenti progettati


39

Questa domanda è in risposta a una risposta data da @Greg Snow in merito a una domanda che ho posto in merito all'analisi di potenza con regressione logistica e SAS Proc GLMPOWER.

Se sto progettando un esperimento e analizzerò i risultati in una regressione logistica fattoriale, come posso usare la simulazione (e qui ) per condurre un'analisi di potenza?

Ecco un semplice esempio in cui ci sono due variabili, la prima assume tre possibili valori {0,03, 0,06, 0,09} e la seconda è un indicatore fittizio {0,1}. Per ciascuno stimiamo il tasso di risposta per ciascuna combinazione (numero di responder / numero di persone commercializzate). Inoltre, desideriamo avere 3 volte più della prima combinazione di fattori rispetto alle altre (che possono essere considerate uguali) perché questa prima combinazione è la nostra versione provata e vera. Questa è una configurazione come quella fornita nel corso SAS menzionato nella domanda collegata.

inserisci qui la descrizione dell'immagine

Il modello che verrà utilizzato per analizzare i risultati sarà una regressione logistica, con effetti e interazione principali (la risposta è 0 o 1).

mod <- glm(response ~ Var1 + Var2 + I(Var1*Var2))

Come posso simulare un set di dati da utilizzare con questo modello per condurre un'analisi di potenza?

Quando eseguo questo tramite SAS Proc GLMPOWER(usando STDDEV =0.05486016 quale corrisponde a sqrt(p(1-p))dove p è la media ponderata dei tassi di risposta mostrati):

data exemplar;
  input Var1 $ Var2 $ response weight;
  datalines;
    3 0 0.0025  3
    3 1 0.00395 1
    6 0 0.003   1
    6 1 0.0042  1
    9 0 0.0035  1
    9 1 0.002   1;
run;

proc glmpower data=exemplar;
  weight weight;
  class Var1 Var2;
  model response = Var1 | Var2;
  power
    power=0.8
    ntotal=.
    stddev=0.05486016;
run;

Nota: GLMPOWERutilizzerà solo le variabili di classe (nominali) in modo che 3, 6, 9 sopra siano trattati come caratteri e avrebbero potuto essere bassi, medi e alti o altre tre stringhe. Quando viene condotta l'analisi reale, Var1 verrà utilizzato un valore numerico (e includeremo un termine polinomiale Var1 * Var1) per tenere conto di qualsiasi curvatura.

L'output di SAS è

inserisci qui la descrizione dell'immagine

Quindi vediamo che abbiamo bisogno di 762.112 come dimensione del campione (l'effetto principale Var2 è il più difficile da stimare) con potenza pari a 0,80 e alfa uguale a 0,05. Assegneremmo questi in modo che 3 volte il numero fosse la combinazione di base (cioè 0,375 * 762112) e il resto cadesse ugualmente nelle altre 5 combinazioni.


Questo è facile da fare in R. 1a domanda: ho ragione che vuoi che il 75% di tutti i casi sia {var1 = .03, var2 = 0} e il 25% per tutte le altre combo, e non 3 unità lì ogni 1 unità in ciascuna delle altre combo (cioè il 37,5%)? 2a domanda, puoi specificare gli effetti che ti interessano rilevare? Cioè, quali sarebbero le probabilità del log di 1 vs 0? Come dovrebbero cambiare le probabilità del log di successo se var1 aumenta di .01? Pensi che potrebbe esserci un'interazione (in tal caso, quanto è grande)? (NB, questi Q possono essere difficili da rispondere, 1 strategia è quella di specificare la proporzione di 1 che pensi possa essere in ogni combo.)
gung - Reinstalla Monica

1 °: il peso di 3 per il caso di base è che ci sono 3 volte il numero di casi in cui {var1 = 0,03, var2 = 0}. Quindi, i risultati di SAS (che afferma che abbiamo bisogno di 762.112 dimensioni totali del campione per avere l'80% di potenza di rifiuto dell'effetto principale var2 = 0, quindi questa è la dimensione totale del campione di cui abbiamo bisogno) verrebbero assegnati al 37,5% in questo caso di riferimento.
B_Miner,

2 °: Beh, tutto ciò che abbiamo sono i tassi di risposta (che è il rapporto atteso tra il numero di successi rispetto al numero di prove). Quindi, se inviamo 1.000 lettere con Var1 = 0,03 e Var2 = 0 che potrebbero corrispondere a un'offerta di tasso di interesse su un'offerta di direct mail con carta di credito di 0,03 (3%) e nessun adesivo sulla busta (dove Var2 = 1 significa che c'è un adesivo), ci aspettiamo 1000 * 0,0025 risposte.
B_Miner,

2 ° cont: ci aspettiamo un'interazione, quindi i tassi di risposta. Si noti che esiste una diversa percentuale di risposta per Var2 = 0 a seconda del valore di Var1. Non sono sicuro di come tradurli per registrare le probabilità e quindi il set di dati simulato.
B_Miner,

Un'ultima cosa, però. Vedo che i tassi di risposta sono lineari per var1 quando var2 = 0 (ovvero, 0,25%, .30%, .35%). Intendevi che questo fosse un effetto lineare o curvilineo? Dovresti sapere che le probabilità possono sembrare abbastanza lineari per piccoli sottoinsiemi del loro intervallo, ma in realtà non possono essere lineari. La regressione logistica è lineare nelle probabilità del log, non nella probabilità (discuto cose del genere nella mia risposta qui ).
gung - Ripristina Monica

Risposte:


43

Preliminari:

  • NESα

    • Nα=.05
    • N
  • Oltre all'eccellente post di @ GregSnow, qui puoi trovare un'altra guida davvero eccezionale alle analisi di potenza basate su simulazione su CV: Calcolo della potenza statistica . Per riassumere le idee di base:

    1. capire l'effetto che si desidera essere in grado di rilevare
    2. generare N dati da quel mondo possibile
    3. eseguire l'analisi che si intende condurre su tali dati falsi
    4. memorizza se i risultati sono "significativi" in base all'alfa scelta
    5. BN
    6. N
  • ppBpB

  • In R, il modo principale per generare dati binari con una data probabilità di "successo" è ? Rbinom

    • Ad esempio, per ottenere il numero di successi su 10 prove di Bernoulli con probabilità p, il codice sarebbe rbinom(n=10, size=1, prob=p): (probabilmente vorrai assegnare il risultato a una variabile per l'archiviazione)
    • puoi anche generare tali dati in modo meno elegante usando ? runif , ad es.ifelse(runif(1)<=p, 1, 0)
    • se ritieni che i risultati siano mediati da una variabile gaussiana latente, potresti generare la variabile latente in funzione delle tue covariate con ? rnorm , quindi convertirle in probabilità con pnorm()e utilizzare quelle nel tuo rbinom()codice.
  • var12var1var2var12var2

  • Sebbene scritta nel contesto di una domanda diversa, la mia risposta qui: La differenza tra i modelli logit e probit ha molte informazioni di base su questi tipi di modelli.
  • Così come ci sono diversi tipi di tassi di errore di tipo I quando ci sono più ipotesi (ad esempio, per contrasto tasso di errore , il tasso di errore familywise , e per famiglia tasso di errore ), così Ci sono diversi tipi di potere * (ad esempio, per un singolo effetto predefinito , per qualsiasi effetto e per tutti gli effetti ). Potresti anche cercare il potere di rilevare una specifica combinazione di effetti, o il potere di un test simultaneo del modello nel suo insieme. La mia ipotesi dalla tua descrizione del tuo codice SAS è che sta cercando quest'ultimo. Tuttavia, dalla tua descrizione della tua situazione, presumo che tu voglia rilevare gli effetti di interazione al minimo.

  • Per un modo diverso di pensare alle questioni relative al potere, vedi la mia risposta qui: Come segnalare la precisione generale nella stima delle correlazioni in un contesto di giustificazione della dimensione del campione.

Semplice potenza post-hoc per la regressione logistica in R:

Supponiamo che i tassi di risposta postulati rappresentino la vera situazione nel mondo e che tu abbia inviato 10.000 lettere. Qual è il potere di rilevare quegli effetti? (Nota che sono famoso per aver scritto codice "comicamente inefficiente", il seguente è destinato a essere facile da seguire piuttosto che ottimizzato per l'efficienza; in effetti, è piuttosto lento.)

set.seed(1)

repetitions = 1000
N = 10000
n = N/8
var1  = c(   .03,    .03,    .03,    .03,    .06,    .06,    .09,   .09)
var2  = c(     0,      0,      0,      1,      0,      1,      0,     1)
rates = c(0.0025, 0.0025, 0.0025, 0.00395, 0.003, 0.0042, 0.0035, 0.002)

var1    = rep(var1, times=n)
var2    = rep(var2, times=n)
var12   = var1**2
var1x2  = var1 *var2
var12x2 = var12*var2

significant = matrix(nrow=repetitions, ncol=7)

startT = proc.time()[3]
for(i in 1:repetitions){
  responses          = rbinom(n=N, size=1, prob=rates)
  model              = glm(responses~var1+var2+var12+var1x2+var12x2, 
                           family=binomial(link="logit"))
  significant[i,1:5] = (summary(model)$coefficients[2:6,4]<.05)
  significant[i,6]   = sum(significant[i,1:5])
  modelDev           = model$null.deviance-model$deviance
  significant[i,7]   = (1-pchisq(modelDev, 5))<.05
}
endT = proc.time()[3]
endT-startT

sum(significant[,1])/repetitions      # pre-specified effect power for var1
[1] 0.042
sum(significant[,2])/repetitions      # pre-specified effect power for var2
[1] 0.017
sum(significant[,3])/repetitions      # pre-specified effect power for var12
[1] 0.035
sum(significant[,4])/repetitions      # pre-specified effect power for var1X2
[1] 0.019
sum(significant[,5])/repetitions      # pre-specified effect power for var12X2
[1] 0.022
sum(significant[,7])/repetitions      # power for likelihood ratio test of model
[1] 0.168
sum(significant[,6]==5)/repetitions   # all effects power
[1] 0.001
sum(significant[,6]>0)/repetitions    # any effect power
[1] 0.065
sum(significant[,4]&significant[,5])/repetitions   # power for interaction terms
[1] 0.017

Quindi vediamo che 10.000 lettere non raggiungono realmente l'80% di potenza (di alcun tipo) per rilevare questi tassi di risposta. (Non sono sufficientemente sicuro di cosa stia facendo il codice SAS per essere in grado di spiegare la netta discrepanza tra questi approcci, ma questo codice è concettualmente semplice - se lento - e ho passato un po 'di tempo a controllarlo, e penso che questi i risultati sono ragionevoli.)

Potere a priori basato sulla simulazione per la regressione logistica:

NNNN

NN

sum(significant[,1])/repetitions      # pre-specified effect power for var1
[1] 0.115
sum(significant[,2])/repetitions      # pre-specified effect power for var2
[1] 0.091
sum(significant[,3])/repetitions      # pre-specified effect power for var12
[1] 0.059
sum(significant[,4])/repetitions      # pre-specified effect power for var1X2
[1] 0.606
sum(significant[,5])/repetitions      # pre-specified effect power for var12X2
[1] 0.913
sum(significant[,7])/repetitions      # power for likelihood ratio test of model
[1] 1
sum(significant[,6]==5)/repetitions   # all effects power
[1] 0.005
sum(significant[,6]>0)/repetitions    # any effect power
[1] 0.96
sum(significant[,4]&significant[,5])/repetitions   # power for interaction terms
[1] 0.606

var12significant

N


Gung - WOW, grazie mille per una risposta così dettagliata e ponderata! Scrivendo il mio e giocando con il tuo codice, i termini quadratici sembrano essere il problema, poiché almeno l'80% della potenza viene raggiunta con una dimensione del campione molto più piccola senza considerarla nel modello.
B_Miner

1
È fantastico, @B_Miner, è il tipo di cosa che vuoi fare. Inoltre, è la ragione per cui penso che l'approccio basato sulla simulazione sia superiore al software analitico che sputa solo un numero (R ha anche questo, il pwrpacchetto). Questo approccio ti dà l'opportunità di pensare in modo molto più chiaro (e / o perfezionare il tuo pensiero) su cosa ti aspetti che accada, su come gestiresti con quello, ecc. NB, tuttavia, che hai bisogno dei termini quadratici, o qualcosa del genere analogo, se i tassi proposti sono corretti, b / c non sono lineari e l'interazione da sola non consente di acquisire relazioni curvilinee.
gung - Ripristina Monica

Penso che dovresti dimostrare l'uso polypiuttosto che mostrare ai nuovi utenti di R la strategia più soggetta a errori di quadratura dei valori grezzi. Penso che il modello completo avrebbe dovuto essere proposto come glm(responses~ poly(var1, 2) * var2, family=binomial(link="logit"). Sarebbe sia meno soggetto a errori statistici nell'interpretazione sia molto più compatto. Potrebbe non essere importante in questo preciso esempio se stai solo osservando una soluzione generale, ma potresti facilmente fuorviare gli utenti meno sofisticati che potrebbero essere tentati di guardare termini individuali.
DWin,

@DWin, quando uso R per illustrare le cose qui su CV, lo faccio in un modo non-R. L'idea è quella di essere il più trasparente possibile per coloro che non hanno familiarità con R. Ad esempio, non sto usando le possibilità vettorializzate, sto usando loop =, ecc. Le persone avranno più familiarità con la quadratura delle variabili da una regressione di base classe e meno consapevole di cosa poly()sia, se non sono utenti R.
gung - Ripristina Monica

17

La risposta di @ Gung è ottima per la comprensione. Ecco l'approccio che vorrei usare:

mydat <- data.frame( v1 = rep( c(3,6,9), each=2 ),
    v2 = rep( 0:1, 3 ), 
    resp=c(0.0025, 0.00395, 0.003, 0.0042, 0.0035, 0.002) )

fit0 <- glm( resp ~ poly(v1, 2, raw=TRUE)*v2, data=mydat,
    weight=rep(100000,6), family=binomial)
b0 <- coef(fit0)


simfunc <- function( beta=b0, n=10000 ) {
    w <- sample(1:6, n, replace=TRUE, prob=c(3, rep(1,5)))
    mydat2 <- mydat[w, 1:2]
    eta <- with(mydat2,  cbind( 1, v1, 
                v1^2, v2,
                v1*v2,
                v1^2*v2 ) %*% beta )
    p <- exp(eta)/(1+exp(eta))
    mydat2$resp <- rbinom(n, 1, p)

    fit1 <- glm( resp ~ poly(v1, 2)*v2, data=mydat2,
        family=binomial)
    fit2 <- update(fit1, .~ poly(v1,2) )
    anova(fit1,fit2, test='Chisq')[2,5]
}

out <- replicate(100, simfunc(b0, 10000))
mean( out <= 0.05 )
hist(out)
abline(v=0.05, col='lightgrey')

Questa funzione verifica l'effetto complessivo di v2, i modelli possono essere modificati per esaminare altri tipi di test. Mi piace scriverlo come una funzione in modo che quando voglio testare qualcosa di diverso posso semplicemente cambiare gli argomenti della funzione. Puoi anche aggiungere una barra di avanzamento o utilizzare il pacchetto parallelo per velocizzare le cose.

Qui ho appena eseguito 100 repliche, di solito inizio da quel livello per trovare la dimensione approssimativa del campione, quindi aumento le iterazioni quando mi trovo nel giusto parco di palla (non c'è bisogno di perdere tempo con 10.000 iterazioni quando hai il 20% di potenza).


Grazie Greg! Mi chiedevo di questo stesso approccio (se sto capendo correttamente cosa hai fatto). Per confermare: stai creando un set di dati (in effetti, ma facendolo con i pesi invece della forza bruta creando singoli record dei valori di Var1 e Var2 e quindi 1 e 0 per la risposta) che è molto grande in base a "mydat" , adattando una regressione logistica e quindi usando quei coefficienti per campionare dal modello proposto nella simulazione? Sembra che questo sia un modo generale per elaborare i coefficienti, quindi è proprio come la tua risposta al potere di regressione ordinale a cui ho collegato.
B_Miner,

Il modello iniziale utilizza i pesi per ottenere i coefficienti da utilizzare, ma nella simulazione sta creando un frame di dati con nrighe. Potrebbe essere più efficiente fare pesi anche nella funzione.
Greg Snow,

Ho ragione che stai usando i dati inizialmente (rendendoli grandi per ottenere stime molto buone) allo scopo di ottenere i coefficienti utilizzati?
B_Miner

Sì, beh il grande è in modo che la proporzione per il peso dia un numero intero.
Greg Snow,

2
@B_Miner, sto programmando un articolo, non so che ce n'è abbastanza per un libro completo o no. Ma grazie per l'incoraggiamento.
Greg Snow,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.