Perché esiste un valore R ^ 2 (e cosa lo sta determinando) quando lm non ha variazioni nel valore previsto?


10

Considera il seguente codice R:

example <- function(n) {
    X <- 1:n
    Y <- rep(1,n)
    return(lm(Y~X))
}
#(2.13.0, i386-pc-mingw32)
summary(example(7))    #R^2 = .1963
summary(example(62))   #R^2 = .4529
summary(example(4540)) #R^2 = .7832
summary(example(104))) #R^2 = 0
#I did a search for n 6:10000, the result for R^2 is NaN for
#n = 2, 4, 16, 64, 256, 1024, 2085 (not a typo), 4096, 6175 (not a typo), and 8340 (not a typo)

Guardare http://svn.r-project.org/R/trunk/src/appl/dqrls.f ) non mi ha aiutato a capire cosa sta succedendo, perché non conosco Fortran. In un'altra domanda è stato risposto che gli errori di tolleranza della macchina in virgola mobile sono la causa dei coefficienti per X che sono vicini, ma non del tutto 0.

R2 è maggiore quando il valore per coef(example(n))["X"]è più vicino a 0. Ma ...

  1. Perché esiste un valore ? R2
  2. Cosa (specificamente) lo sta determinando?
  3. Perché la progressione apparentemente ordinata dei NaNrisultati?
  4. Perché le violazioni di tale progressione?
  5. Che cosa è questo comportamento "previsto"?

Nota: R ^ 2 di 7 dovrebbe essere 0.4542 per vedere qualcosa di più costruttivo vedere la mia risposta. :-)

1
Bene, per essere onesti, l'utente dovrebbe effettivamente sapere qualcosa sui metodi statistici prima di utilizzare gli strumenti (a differenza, diciamo, degli utenti di Excel (ok, scusate per lo sparo economico)). Poiché è piuttosto ovvio che R ^ 2 si avvicina a 1 quando l'errore si avvicina a zero, sappiamo meglio che confondere un valore NaN con il limite di una funzione. Ora, se ci fosse un problema con R ^ 2 divergente come ynoise -> 0 (diciamo, sostituisci l'istruzione Y sopra con Y <- rep(1,n)+runif(n)*ynoise), sarebbe interessante :-)
— Carl Witthoft,

@eznme: penso che i risultati siano specifici della macchina, o almeno a 32 o 64 bit; Ho una macchina a 32 bit che fornisce 0,1963 per 7, ma la mia macchina a 64 bit fornisce NaN. È interessante notare che sulla macchina a 64 bit, gli R ^ 2 che non sono NaN sono tutti molto vicini a 0,5. Ha senso quando ci penso, ma all'inizio mi ha sorpreso.
— Aaron ha lasciato Stack Overflow il

1
Stai studiando un errore di arrotondamento a doppia precisione. Dai un'occhiata ai coefficienti; ad es apply(as.matrix(2:17), 1, function(n){example(n)$coefficients[-1]}). (I miei risultati, su Win 7 x64 Xeon, vanno da -8e-17 a + 3e-16; circa la metà sono veri zeri.) A proposito, la fonte Fortran non è di alcun aiuto: è solo un wrapper per dqrdc; questo è il codice che vuoi guardare.
— whuber

1
(Continua) Ma, come utente, la scelta del CV è un sito migliore, per la semplice ragione che l'analisi statistica diligente è responsabilità dell'utente, non dello sviluppatore. Se l'utente vede un errato rispetto all'entità dell'RSS, allora dovrebbe fare la propria post-elaborazione prima di riferire ulteriormente. Per quanto riguarda la programmazione, vorrei sapere come evitare il più possibile questi problemi numerici, ma penso che non possano essere sfuggiti, ed è qui che è importante avere un utente diligente ed educare gli altri. R2
— Iteratore

Risposte:


6

Come dice Ben Bolker, la risposta a questa domanda può essere trovata nel codice per summary.lm().

Ecco l'intestazione:

function (object, correlation = FALSE, symbolic.cor = FALSE, 
    ...) 
{

Quindi, diamo x <- 1:1000; y <- rep(1,1000); z <- lm(y ~ x)un'occhiata a questo estratto leggermente modificato:

    p <- z$rank
    rdf <- z$df.residual
    Qr <- stats:::qr.lm(z)
    n <- NROW(Qr$qr)
    r <- z$residuals
    f <- z$fitted.values
    w <- z$weights
    if (is.null(w)) {
        mss <- sum((f - mean(f))^2)
        rss <- sum(r^2)
    }
    ans <- z[c("call", "terms")]
    if (p != attr(z$terms, "intercept")) {
        df.int <- 1L
        ans$r.squared <- mss/(mss + rss)
        ans$adj.r.squared <- 1 - (1 - ans$r.squared) * ((n - 
            df.int)/rdf)
    }

Si noti che ans $ r.squared è ...0.4998923

Per rispondere a una domanda con una domanda: cosa traggiamo da questo? :)

Credo che la risposta stia nel modo in cui R gestisce i numeri in virgola mobile. Penso che msssia rssla somma di errori di arrotondamento molto piccoli (al quadrato), quindi la ragione è circa 0,5. Per quanto riguarda la progressione, sospetto che ciò abbia a che fare con il numero di valori necessari affinché le approssimazioni +/- si annullino su 0 (per entrambi e , come è probabile la fonte di questi valori). Non so perché i valori differiscano da una progressione, però.R2mssrss0/0NaN2^(1:k)


Aggiornamento 1: Ecco un bel thread di R-help che affronta alcuni dei motivi per cui gli avvisi di underflow non vengono risolti in R.

Inoltre, le domande e risposte SO contengono numerosi post interessanti e collegamenti utili relativi a underflow, aritmetica ad alta precisione, ecc.


8

Sono curioso della tua motivazione per porre la domanda. Non riesco a pensare a una ragione pratica che dovrebbe comportare questo comportamento; la curiosità intellettuale è una ragione alternativa (e l'IMO molto più ragionevole). Penso che non sia necessario comprendere FORTRAN per rispondere a questa domanda, ma penso che sia necessario conoscere la decomposizione QR e il suo utilizzo nella regressione lineare. Se trattate dqrlscome una scatola nera che calcola una decomposizione QR e restituisce varie informazioni su di essa, allora potreste essere in grado di tracciare i passaggi ... o semplicemente andare direttamente summary.lme rintracciare per vedere come viene calcolato R ^ 2. In particolare:

mss <- if (attr(z$terms, "intercept")) 
          sum((f - mean(f))^2)
       else sum(f^2)
rss <- sum(r^2)
## ... stuff ...
ans$r.squared <- mss/(mss + rss)

Quindi devi tornare indietro lm.fite vedere che i valori adattati vengono calcolati come r1 <- y - z$residuals(cioè come la risposta meno i residui). Ora puoi capire cosa determina il valore dei residui e se il valore meno la sua media è esattamente zero o no, e da lì capire i risultati computazionali ...


La curiosità intellettuale è la maggior parte del motivo della mia domanda. Un collega ha riferito il comportamento e volevo dare un'occhiata in giro e vedere se riuscivo a capirlo. Dopo aver tracciato il problema oltre il mio set di abilità, ho deciso di porre la domanda. Come problema pratico, a volte le analisi vengono eseguite in batch o si verificano altri errori e questo comportamento mi sembra decisamente "strano".
— Russellpierce,

1
mms e rss sono entrambi risultati di z, che è il nome dell'oggetto lm all'interno di summary.lm. Quindi, una risposta probabilmente richiede una spiegazione della decomposizione QR, il suo uso nella regressione lineare, e in particolare alcuni dettagli Decomposizione QR come istanziata nel codice sottostante R per spiegare perché la decomposizione QR finisce con approssimazioni di 0 anziché 0 .
— Russellpierce,

@drknexus Non sono d'accordo. QR decomp è uno dei molti algoritmi numerici; se il problema di fondo è la precisione numerica, questo comparirà in QR, moltiplicazione di matrici, solutori non lineari e così tanti altri posti. La sequenza essenziale è semplice: i coefficienti sono leggermente off (dovrebbe essere (0,1)); questo non è irragionevole, ma produce il msse rss"rumore". È il principio GIGO che assicura che è preciso, ma non corretto. Preferirei inserire un "rilevatore di immondizia" prima di calcolare piuttosto che modificare l'algo QR, perché dubito che la sua validità potrebbe essere migliorata. R2R2
— Iteratore,

Mi sembra che il rilevatore di immondizia dovrebbe essere al QR o proprio prima di esso. Un semplice controllo di integrità sulla varianza di Y e avvertire che Y manca di varianza andrebbe bene (potrei scrivere un lm wrapper per i miei amici che fa proprio questo). Mi sembra che quando stai calcolando , uno è già troppo lontano nella tana del coniglio computazionale per sapere se si sta guardando la spazzatura o no. R2
— Russellpierce,

0

R2 è definito come ( http://en.wikipedia.org/wiki/R_squared ), quindi se la somma dei quadrati è 0, allora non è definita. Secondo me R dovrebbe mostrare un messaggio di errore.R2=1-SSerrSStot


1
Puoi dare una situazione pratica in cui questo comportamento avrebbe importanza?
— Ben Bolker,

3
@Brandon - Iterator ha messo la faccina lì dentro e sei ancora sbronzo!
— Carl Witthoft,

2
@eznme Anche se un errore è buono, è abbastanza difficile individuare tutti i tipi di luoghi in cui sorgono problemi in virgola mobile, in particolare nel mondo dell'aritmetica IEEE-754. La lezione qui è che anche i calcoli di pane e burro con R dovrebbero essere gestiti delicatamente.
— Iteratore,

2
Queste considerazioni sono particolarmente importanti perché nei suoi scritti John Chambers (uno dei creatori di S e quindi un "nonno" di R) sottolinea fortemente l'uso di R per un calcolo affidabile. Ad esempio, vedi Chambers, Software per l'analisi dei dati: Programmazione con R (Springer Verlag 2008): "i calcoli e il software per l'analisi dei dati dovrebbero essere affidabili: dovrebbero fare ciò che sostengono e farsi vedere." [A pag. 3.]
— whuber

2
Il problema è che, nel bene o nel male, R-core è resistente (come lo vedono) a festoni il codice con molti, molti controlli che intercettano tutti i casi angolari e possibili strani errori dell'utente - hanno paura (penso) che (a) impiegherà enormi quantità di tempo, (b) renderà la base di codice molto più grande e più difficile da leggere (perché ci sono letteralmente migliaia di questi casi speciali), e (c) rallenterà l'esecuzione forzando tali controlli continuamente anche in situazioni in cui i calcoli vengono ripetuti molte, molte volte.
— Ben Bolker,
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.