Pourquoi y a-t-il une valeur R ^ 2 (et qu'est-ce qui la détermine) quand lm n'a aucune variance dans la valeur prédite?

10

Considérez le code R suivant:

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)

Regarder http://svn.r-project.org/R/trunk/src/appl/dqrls.f ) ne m'a pas aidé à comprendre ce qui se passe, car je ne connais pas Fortran. Dans une autre question, il a été répondu que les erreurs de tolérance de la machine à virgule flottante étaient à blâmer pour les coefficients pour X qui sont proches, mais pas tout à fait de 0.

R2 est supérieur lorsque la valeur de coef(example(n))["X"]est plus proche de 0. Mais ...

  1. Pourquoi y a-t-il une valeur ? R2
  2. Qu'est-ce (spécifiquement) qui le détermine?
  3. Pourquoi la progression apparemment ordonnée des NaNrésultats?
  4. Pourquoi les violations de cette progression?
  5. Qu'en est-il du comportement «attendu»?
russellpierce
la source
Remarque: R ^ 2 de 7 devrait être 0,4542 pour voir quelque chose de plus constructif voir ma réponse. :-)
1
Eh bien, pour être juste, l'utilisateur est censé savoir quelque chose sur les méthodes statistiques avant d'utiliser des outils (contrairement, par exemple, aux utilisateurs d'Excel (ok, désolé pour la photo bon marché)). Comme il est assez évident que R ^ 2 s'approche de 1 lorsque l'erreur approche de zéro, nous savons mieux que de confondre une valeur NaN avec la limite d'une fonction. Maintenant, s'il y avait un problème avec R ^ 2 divergeant comme ynoise -> 0 (disons, remplacez la déclaration Y ci-dessus par Y <- rep(1,n)+runif(n)*ynoise), ce serait intéressant :-)
Carl Witthoft
@eznme: Je pense que les résultats sont spécifiques à la machine, ou au moins spécifiques à 32 ou 64 bits; J'ai une machine 32 bits qui donne 0,1963 pour 7, mais ma machine 64 bits donne NaN. Fait intéressant, sur la machine 64 bits, les R ^ 2 qui ne sont pas NaN sont tous très proches de 0,5. Ça a du sens quand j'y pense, mais ça m'a d'abord surpris.
Aaron a quitté Stack Overflow le
1
Vous étudiez l'erreur d'arrondi en double précision. Jetez un œil aux coefficients; par exemple apply(as.matrix(2:17), 1, function(n){example(n)$coefficients[-1]}). (Mes résultats, sur un Win 7 x64 Xeon, vont de -8e-17 à + 3e-16; environ la moitié sont de vrais zéros.) BTW, la source Fortran ne sert à rien: c'est juste un wrapper pour dqrdc; c'est le code que vous voulez regarder.
whuber
1
(Suite) Mais, en tant qu'utilisateur, le choix du CV est un meilleur site, pour la simple raison qu'une analyse statistique diligente relève de la responsabilité de l'utilisateur et non du développeur. Si l'utilisateur voit un erroné par rapport à l'ampleur du flux RSS, il doit alors effectuer son propre post-traitement avant de continuer. Côté programmation, j'aimerais savoir comment éviter autant que possible ces problèmes numériques, mais je pense qu'ils ne peuvent pas être échappés, et c'est là qu'il est important d'avoir un utilisateur assidu et d'éduquer les autres. R2
Iterator

Réponses:

6

Comme le dit Ben Bolker, la réponse à cette question se trouve dans le code de summary.lm().

Voici l'en-tête:

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

Alors, jetons x <- 1:1000; y <- rep(1,1000); z <- lm(y ~ x)un œil à cet extrait légèrement modifié:

    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)
    }

Notez que ans $ r.squared vaut ...0.4998923

Pour répondre à une question par une question: que pouvons-nous en tirer? :)

Je crois que la réponse réside dans la façon dont R gère les nombres à virgule flottante. Je pense que msset rsssont les sommes des très petites erreurs d'arrondi (au carré), d'où la raison pour laquelle est d'environ 0,5. Quant à la progression, je soupçonne que cela a à voir avec le nombre de valeurs nécessaires pour que les approximations +/- s'annulent à 0 (pour les deux et , comme c'est probablement la source de ces valeurs). Je ne sais pas pourquoi les valeurs diffèrent d'une progression, cependant.R2mssrss0/0NaN2^(1:k)


Mise à jour 1: voici un joli fil de discussion de R-help abordant certaines des raisons pour lesquelles les avertissements de dépassement de capacité ne sont pas traités dans R.

De plus, ce SO Q&A a un certain nombre de publications intéressantes et de liens utiles concernant le sous-dépassement, l'arithmétique de haute précision, etc.

Itérateur
la source
8

Je suis curieux de savoir pourquoi vous avez posé la question. Je ne peux pas penser à une raison pratique pour laquelle ce comportement devrait être important; la curiosité intellectuelle est une raison alternative (et beaucoup plus sensée à l'OMI). Je pense que vous n'avez pas besoin de comprendre FORTRAN pour répondre à cette question, mais je pense que vous devez connaître la décomposition QR et son utilisation dans la régression linéaire. Si vous traitez dqrlscomme une boîte noire qui calcule une décomposition QR et renvoie diverses informations à ce sujet, vous pourrez peut-être suivre les étapes ... ou simplement aller directement summary.lmet suivre pour voir comment le R ^ 2 est calculé. En particulier:

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)

Ensuite, vous devez revenir dans lm.fitet voir que les valeurs ajustées sont calculées comme r1 <- y - z$residuals(c'est-à-dire comme la réponse moins les résidus). Maintenant, vous pouvez déterminer ce qui détermine la valeur des résidus et si la valeur moins sa moyenne est exactement nulle ou non, et à partir de là, déterminer les résultats de calcul ...

Ben Bolker
la source
La curiosité intellectuelle est la majorité de la raison de ma question. Un collègue a signalé le comportement et je voulais fouiller et voir si je pouvais le comprendre. Après avoir tracé le problème au-delà de mes compétences, j'ai décidé de poser la question. En tant que problème pratique, parfois des analyses sont effectuées par lot, ou d'autres erreurs se produisent, et ce comportement me semble décidément «étrange».
russellpierce
1
mms et rss sont tous deux des résultats de z, qui est le nom de l'objet lm à l'intérieur de summary.lm. Donc, une réponse nécessite probablement une explication de la décomposition QR, son utilisation dans la régression linéaire, et spécifiquement certains détails de la décomposition QR telle qu'instanciée dans le code sous-jacent R afin d'expliquer pourquoi la décomposition QR se termine par des approximations de 0 plutôt que de 0 lui-même .
russellpierce
@drknexus Je ne suis pas d'accord. La décompression QR est l'un des nombreux algorithmes numériques; si le problème sous-jacent est la précision numérique, cela surgira dans QR, la multiplication matricielle, les solveurs non linéaires et tant d'autres endroits. La séquence essentielle est simple: les coefficients sont très légèrement décalés (devrait être (0,1)); ce n'est pas déraisonnable, mais produit le msset le rss"bruit". C'est le principe GIGO qui garantit que est précis, mais incorrect. Je préfère insérer un "détecteur de déchets" avant de calculer que de modifier l'algorithme QR, car je doute que sa validité puisse être améliorée. R 2R2R2
Iterator
Il me semble que le détecteur de déchets doit être au QR ou juste avant. Une simple vérification d'esprit sur la variance de Y et un avertissement que Y manque de variance serait bien (je peux écrire un wrapper lm pour mes amis qui fait juste cela). Il me semble qu'au moment où vous calculez , on est déjà trop loin dans le trou du lapin de calcul pour savoir si l'on regarde les ordures ou non. R2
russellpierce
0

R 2 = 1 - SS e r rR2 est défini comme ( http://en.wikipedia.org/wiki/R_squared ), donc si la somme des carrés-total est 0 alors elle n'est pas définie. À mon avis, R devrait afficher un message d'erreur.R2=1SSerrSStot

Bernd Elkemann
la source
1
Pouvez-vous donner une situation pratique où ce comportement aurait de l'importance?
Ben Bolker
3
@Brandon - Iterator a mis le smiley là-dedans et vous avez toujours reçu un whooshed!
Carl Witthoft
2
@eznme Bien qu'une erreur soit bonne, il est assez difficile d'attraper toutes sortes d'endroits où des problèmes de virgule flottante se posent, notamment dans le monde de l'arithmétique IEEE-754. La leçon ici est que même les calculs de pain et de beurre avec R doivent être traités délicatement.
Iterator
2
Ces considérations sont particulièrement importantes parce que dans ses écrits, John Chambers (l'un des auteurs de S et donc un "grand-père" de R) met fortement l'accent sur l'utilisation de R pour un calcul fiable. Par exemple, voir Chambers, Software for Data Analysis: Programming with R (Springer Verlag 2008): "les calculs et le logiciel d'analyse des données doivent être fiables: ils doivent faire ce qu'ils prétendent et être perçus comme tels." [À la p. 3.]
whuber
2
Le problème est que, pour le meilleur ou pour le pire, R-core est résistant (comme ils le voient) à festonner le code avec de très nombreux contrôles qui interceptent tous les cas d'angle et d'éventuelles erreurs utilisateur étranges - ils ont peur (je pense) qu'il (a) prendra énormément de temps, (b) rendra la base de code beaucoup plus grande et plus difficile à lire (car il y a littéralement des milliers de ces cas spéciaux), et (c) ralentira l'exécution en forçant de telles vérifications tout le temps même dans des situations où les calculs sont répétés plusieurs fois.
Ben Bolker