web-dev-qa-db-fra.com

Façon correcte d'obtenir un intervalle de confiance avec scipy

J'ai un tableau de données à 1 dimension:

a = np.array([1,2,3,4,4,4,5,5,5,5,4,4,4,6,7,8])

pour lequel je veux obtenir l'intervalle de confiance de 68% (ie: le 1 sigma ).

Le premier commentaire dans cette réponse indique que cela peut être réalisé en utilisant scipy.stats.norm.interval À partir de la fonction scipy.stats.norm , via:

from scipy import stats
import numpy as np
mean, sigma = np.mean(a), np.std(a)

conf_int = stats.norm.interval(0.68, loc=mean, 
    scale=sigma)

Mais un commentaire dans ce post indique que la manière correcte réelle d'obtenir l'intervalle de confiance est:

conf_int = stats.norm.interval(0.68, loc=mean, 
    scale=sigma / np.sqrt(len(a)))

c'est-à-dire que sigma est divisé par la racine carrée de la taille de l'échantillon: np.sqrt(len(a)).

La question est: quelle version est la bonne?

30
Gabriel

L'intervalle de confiance de 68% pour un seul tirage à partir d'une distribution normale avec un écart moyen mu et std sigma est

stats.norm.interval(0.68, loc=mu, scale=sigma)

L'intervalle de confiance de 68% pour la moyenne de N tirages à partir d'une distribution normale avec un écart moyen mu et std sigma est

stats.norm.interval(0.68, loc=mu, scale=sigma/sqrt(N))

Intuitivement, ces formules ont du sens, car si vous tenez un pot de bonbons à la gelée et demandez à un grand nombre de personnes de deviner le nombre de bonbons, chaque individu peut être très éloigné - la même déviation standard sigma - mais la moyenne des suppositions fera un travail remarquablement fin d'estimation du nombre réel et cela se reflète par l'écart type de la moyenne rétrécissant par un facteur de 1/sqrt(N).


Si un seul tirage a une variance sigma**2, Alors par la formule de Bienaymé , la somme de N non corrélée les tirages ont une variance N*sigma**2.

La moyenne est égale à la somme divisée par N. Lorsque vous multipliez une variable aléatoire (comme la somme) par une constante, la variance est multipliée par la constante au carré. C'est

Var(cX) = c**2 * Var(X)

Ainsi, la variance de la moyenne est égale

(variance of the sum)/N**2 = N * sigma**2 / N**2 = sigma**2 / N

et donc l'écart-type de la moyenne (qui est la racine carrée de la variance) est égal à

sigma/sqrt(N).

Il s'agit de l'origine de la sqrt(N) dans le dénominateur.


Voici un exemple de code, basé sur le code de Tom, qui illustre les affirmations ci-dessus:

import numpy as np
from scipy import stats

N = 10000
a = np.random.normal(0, 1, N)
mean, sigma = a.mean(), a.std(ddof=1)
conf_int_a = stats.norm.interval(0.68, loc=mean, scale=sigma)

print('{:0.2%} of the single draws are in conf_int_a'
      .format(((a >= conf_int_a[0]) & (a < conf_int_a[1])).sum() / float(N)))

M = 1000
b = np.random.normal(0, 1, (N, M)).mean(axis=1)
conf_int_b = stats.norm.interval(0.68, loc=0, scale=1 / np.sqrt(M))
print('{:0.2%} of the means are in conf_int_b'
      .format(((b >= conf_int_b[0]) & (b < conf_int_b[1])).sum() / float(N)))

impressions

68.03% of the single draws are in conf_int_a
67.78% of the means are in conf_int_b

Attention, si vous définissez conf_int_b Avec les estimations pour mean et sigma basées sur l'échantillon a, la moyenne peut ne pas tomber dans conf_int_b avec la fréquence souhaitée.


Si vous prenez un échantillon d'une distribution et calculez la moyenne de l'échantillon et l'écart std,

mean, sigma = a.mean(), a.std()

attention à noter qu'il n'y a aucune garantie que celles-ci seront égales à population moyenne et écart type et que nous sommes en supposant la population est normalement distribuée - celles ne sont pas des données automatiques!

Si vous prenez un échantillon et que vous voulez estimer la moyenne de la population et l'écart-type, vous devez utiliser

mean, sigma = a.mean(), a.std(ddof=1)

puisque cette valeur pour sigma est estimateur sans biais pour l'écart type de la population.

60
unutbu

J'ai testé vos méthodes en utilisant un tableau avec un intervalle de confiance connu. numpy.random.normal (mu, std, size) renvoie un tableau centré sur mu avec un écart-type de std (dans les documents , il est défini comme Standard deviation (spread or “width”) of the distribution.).

from scipy import stats
import numpy as np
from numpy import random
a = random.normal(0,1,10000)
mean, sigma = np.mean(a), np.std(a)
conf_int_a = stats.norm.interval(0.68, loc=mean, scale=sigma)
conf_int_b = stats.norm.interval(0.68, loc=mean, scale=sigma / np.sqrt(len(a)))


conf_int_a
(-1.0011149125527312, 1.0059797764202412)
conf_int_b
(-0.0076030415111100983, 0.012467905378619625)

Comme la valeur sigma doit être comprise entre -1 et 1, la méthode / np.sqrt(len(a)) semble incorrecte.

Modifier

Comme je n'ai pas la réputation de commenter ci-dessus, je clarifierai comment cette réponse est liée à la réponse complète d'unutbu. Si vous remplissez un tableau aléatoire avec une distribution normale, 68% du total se situera à moins de 1-σ de la moyenne. Dans le cas ci-dessus, si vous vérifiez que vous voyez

b = a[np.where((a>-1)&(a <1))]
len(a)
> 6781

soit 68% de la population appartient à 1σ. Eh bien, environ 68%. Comme vous utilisez un tableau de plus en plus grand, vous approcherez de 68% (dans un essai de 10, 9 se situaient entre -1 et 1). C'est parce que le 1-σ est la distribution inhérente des données, et plus vous avez de données, mieux vous pouvez les résoudre.

Fondamentalement, mon interprétation de votre question était Si j'ai un échantillon de données que je veux utiliser pour décrire la distribution dont elles ont été tirées, quelle est la méthode pour trouver l'écart-type de ces données? alors que l'interprétation d'unutbu semble être plus Quel est l'intervalle auquel je peux placer la moyenne avec une confiance de 68%? . Ce qui signifierait, pour les bonbons à la gelée, j'ai répondu Comment sont-ils en train de deviner et unutbu a répondu Que nous disent leurs suppositions sur les bonbons à la gelée.

5
Tom

Je viens de vérifier comment R et GraphPad calculent les intervalles de confiance, et ils augmentent l'intervalle en cas de petite taille d'échantillon (n). Par exemple, plus de 6 fois pour n = 2 par rapport à un grand n. Ce code (basé sur shasan réponse ) correspond à leurs intervalles de confiance:

import numpy as np, scipy.stats as st

# returns confidence interval of mean
def confIntMean(a, conf=0.95):
  mean, sem, m = np.mean(a), st.sem(a), st.t.ppf((1+conf)/2., len(a)-1)
  return mean - m*sem, mean + m*sem

Pour R, j'ai vérifié contre t.test (a). La page intervalle de confiance d'une moyenne de GraphPad contient des informations de "niveau utilisateur" sur la dépendance de la taille de l'échantillon.

Voici la sortie de l'exemple de Gabriel:

In [2]: a = np.array([1,2,3,4,4,4,5,5,5,5,4,4,4,6,7,8])

In [3]: confIntMean(a, 0.68)
Out[3]: (3.9974214366806184, 4.877578563319382)

In [4]: st.norm.interval(0.68, loc=np.mean(a), scale=st.sem(a))
Out[4]: (4.0120010966037407, 4.8629989033962593)

Notez que la différence entre les intervalles confIntMean() et st.norm.interval() est relativement faible ici; len (a) == 16 n'est pas trop petit.

5
Ulrich Stern