web-dev-qa-db-fra.com

Ajustement d'une distribution normale dans R

J'utilise le code suivant pour s'adapter à la distribution normale. Le lien pour l'ensemble de données pour "b" (trop grand pour être publié directement) est:

lien pour b

setwd("xxxxxx")
library(fitdistrplus)

require(MASS)
tazur <-read.csv("b", header= TRUE, sep=",")
claims<-tazur$b
a<-log(claims)
plot(hist(a))

enter image description here

Après avoir tracé l'histogramme, il semble qu'une distribution normale devrait bien s'ajuster.

f1n <- fitdistr(claims,"normal")
summary(f1n)

#Length Class  Mode   
#estimate 2      -none- numeric
#sd       2      -none- numeric
#vcov     4      -none- numeric
#n        1      -none- numeric
#loglik   1      -none- numeric

plot(f1n)

Erreur dans xy.coords (x, y, xlabel, ylabel, log):

'x' est une liste, mais n'a pas les composants 'x' et 'y'

J'obtiens l'erreur ci-dessus lorsque j'essaie de tracer la distribution ajustée, et même les statistiques récapitulatives sont désactivées pour f1n.

J'apprécierais toute aide.

11
zkhan

On dirait que vous faites de la confusion entre MASS::fitdistr et fitdistrplus::fitdist.

  • MASS::fitdistr renvoie un objet de la classe "fitdistr", et il n'y a pas de méthode de tracé pour cela. Vous devez donc extraire les paramètres estimés et tracer vous-même la courbe de densité estimée.
  • Je ne sais pas pourquoi vous chargez le package fitdistrplus, car votre appel de fonction montre clairement que vous utilisez MASS. Quoi qu'il en soit, fitdistrplus a la fonction fitdist qui renvoie l'objet de la classe "fitdist". Il existe une méthode de tracé pour cette classe, mais elle ne fonctionnera pas pour "fitdistr" renvoyé par MASS.

Je vais vous montrer comment travailler avec les deux packages.

## reproducible example
set.seed(0); x <- rnorm(500)

Utilisation de MASS::fitdistr

Aucune méthode de tracé n'est disponible, alors faites-le nous-mêmes.

library(MASS)
fit <- fitdistr(x, "normal")
class(fit)
# [1] "fitdistr"

para <- fit$estimate
#         mean            sd 
#-0.0002000485  0.9886248515 

hist(x, prob = TRUE)
curve(dnorm(x, para[1], para[2]), col = 2, add = TRUE)

enter image description here


Utilisation de fitdistrplus::fitdist

library(fitdistrplus)
FIT <- fitdist(x, "norm")    ## note: it is "norm" not "normal"
class(FIT)
# [1] "fitdist"

plot(FIT)    ## use method `plot.fitdist`

enter image description here

16
李哲源

Examen de la réponse précédente

Dans la réponse précédente, je n'ai pas mentionné la différence entre deux méthodes. En général, si nous optons pour l'inférence du maximum de vraisemblance, je recommanderais d'utiliser MASS::fitdistr, car pour de nombreuses distributions de base, il effectue une inférence exacte au lieu d'une optimisation numérique. Doc de ?fitdistr l'a expliqué assez clairement:

For the Normal, log-Normal, geometric, exponential and Poisson
distributions the closed-form MLEs (and exact standard errors) are
used, and ‘start’ should not be supplied.

For all other distributions, direct optimization of the
log-likelihood is performed using ‘optim’.  The estimated standard
errors are taken from the observed information matrix, calculated
by a numerical approximation.  For one-dimensional problems the
Nelder-Mead method is used and for multi-dimensional problems the
BFGS method, unless arguments named ‘lower’ or ‘upper’ are
supplied (when ‘L-BFGS-B’ is used) or ‘method’ is supplied
explicitly.

D'autre part, fitdistrplus::fitdist effectue toujours l'inférence de manière numérique, même s'il existe une inférence exacte. Bien sûr, l'avantage de fitdist est que plus de principe d'inférence est disponible:

Fit of univariate distributions to non-censored data by maximum
likelihood (mle), moment matching (mme), quantile matching (qme)
or maximizing goodness-of-fit estimation (mge).

Objet de cette réponse

Cette réponse va explorer l'inférence exacte pour une distribution normale. Il aura une saveur théorique, mais il n'y a pas de principe de preuve de vraisemblance; seuls les résultats sont donnés. Sur la base de ces résultats, nous écrivons notre propre fonction R pour l'inférence exacte, qui peut être comparée à MASS::fitdistr. En revanche, pour comparer avec fitdistrplus::fitdist, nous utilisons optim pour minimiser numériquement la fonction de log-vraisemblance négative.

C'est une excellente occasion d'apprendre des statistiques et une utilisation relativement avancée de optim. Pour plus de commodité, je vais estimer le paramètre d'échelle: la variance, plutôt que l'erreur standard.


Inférence exacte de la distribution normale

enter image description here

enter image description here


Écriture de la fonction d'inférence nous-mêmes

Le code suivant est bien commenté. Il y a un interrupteur exact. Si défini FALSE, la solution numérique est choisie.

## fitting a normal distribution
fitnormal <- function (x, exact = TRUE) {
  if (exact) {
    ################################################
    ## Exact inference based on likelihood theory ##
    ################################################
    ## minimum negative log-likelihood (maximum log-likelihood) estimator of `mu` and `phi = sigma ^ 2`
    n <- length(x)
    mu <- sum(x) / n
    phi <- crossprod(x - mu)[1L] / n  # (a bised estimator, though)
    ## inverse of Fisher information matrix evaluated at MLE
    invI <- matrix(c(phi, 0, 0, phi * phi), 2L,
                   dimnames = list(c("mu", "sigma2"), c("mu", "sigma2")))
    ## log-likelihood at MLE
    loglik <- -(n / 2) * (log(2 * pi * phi) + 1)
    ## return
    return(list(theta = c(mu = mu, sigma2 = phi), vcov = invI, loglik = loglik, n = n))
    }
  else {
    ##################################################################
    ## Numerical optimization by minimizing negative log-likelihood ##
    ##################################################################
    ## negative log-likelihood function
    ## define `theta = c(mu, phi)` in order to use `optim`
    nllik <- function (theta, x) {
      (length(x) / 2) * log(2 * pi * theta[2]) + crossprod(x - theta[1])[1] / (2 * theta[2])
      }
    ## gradient function (remember to flip the sign when using partial derivative result of log-likelihood)
    ## define `theta = c(mu, phi)` in order to use `optim`
    gradient <- function (theta, x) {
      pl2pmu <- -sum(x - theta[1]) / theta[2]
      pl2pphi <- -crossprod(x - theta[1])[1] / (2 * theta[2] ^ 2) + length(x) / (2 * theta[2])
      c(pl2pmu, pl2pphi)
      }
    ## ask `optim` to return Hessian matrix by `hessian = TRUE`
    ## use "..." part to pass `x` as additional / further argument to "fn" and "gn"
    ## note, we want `phi` as positive so box constraint is used, with "L-BFGS-B" method chosen
    init <- c(sample(x, 1), sample(abs(x) + 0.1, 1))  ## arbitrary valid starting values
    z <- optim(par = init, fn = nllik, gr = gradient, x = x, lower = c(-Inf, 0), method = "L-BFGS-B", hessian = TRUE)
    ## post processing ##
    theta <- z$par
    loglik <- -z$value  ## flip the sign to get log-likelihood
    n <- length(x)
    ## Fisher information matrix (don't flip the sign as this is the Hessian for negative log-likelihood)
    I <- z$hessian / n  ## remember to take average to get mean
    invI <- solve(I, diag(2L))  ## numerical inverse
    dimnames(invI) <- list(c("mu", "sigma2"), c("mu", "sigma2"))
    ## return
    return(list(theta = theta, vcov = invI, loglik = loglik, n = n))
    }
  }

Nous utilisons toujours les données précédentes pour les tests:

set.seed(0); x <- rnorm(500)

## exact inference
fit <- fitnormal(x)

#$theta
#           mu        sigma2 
#-0.0002000485  0.9773790969 
#
#$vcov
#              mu    sigma2
#mu     0.9773791 0.0000000
#sigma2 0.0000000 0.9552699
#
#$loglik
#[1] -703.7491
#
#$n
#[1] 500

hist(x, prob = TRUE)
curve(dnorm(x, fit$theta[1], sqrt(fit$theta[2])), add = TRUE, col = 2)

enter image description here

La méthode numérique est également assez précise, sauf que la covariance de la variance n'a pas exactement 0 sur la diagonale:

fitnormal(x, FALSE)

#$theta
#[1] -0.0002235315  0.9773732277
#
#$vcov
#                 mu       sigma2
#mu     9.773826e-01 5.359978e-06
#sigma2 5.359978e-06 1.910561e+00
#
#$loglik
#[1] -703.7491
#
#$n
#[1] 500
6
李哲源