Вычислить доверительный интервал из выборочных данных
У меня есть выборочные данные, для которых я хотел бы рассчитать доверительный интервал, предполагая нормальное распределение.
Я нашел и установил пакеты numpy и scipy и получил numpy для возврата среднего значения и стандартного отклонения (numpy.mean(data) со списком данных). Любой совет по получению доверительного интервала образца будет принята с благодарностью.
7 ответов
import numpy as np
import scipy.stats
def mean_confidence_interval(data, confidence=0.95):
a = 1.0 * np.array(data)
n = len(a)
m, se = np.mean(a), scipy.stats.sem(a)
h = se * scipy.stats.t.ppf((1 + confidence) / 2., n-1)
return m, m-h, m+h
Вы можете рассчитать так
Здесь сокращенная версия кода Шасана, рассчитывающая 95% доверительный интервал среднего значения массива a
:
import numpy as np, scipy.stats as st
st.t.interval(0.95, len(a)-1, loc=np.mean(a), scale=st.sem(a))
Но использование StatsModels ' tconfint_mean возможно даже лучше:
import statsmodels.stats.api as sms
sms.DescrStatsW(a).tconfint_mean()
Основополагающие предположения для обоих являются то, что образец (массив a
) был составлен независимо от нормального распределения с неизвестным стандартным отклонением (см. MathWorld или Wikipedia).
Для большого размера выборки n среднее значение выборки обычно распределяется, и можно рассчитать его доверительный интервал, используя st.norm.interval()
(как предложено в комментарии Хайме). Но приведенные решения верны и для малых n, где st.norm.interval()
дает доверительные интервалы, которые являются слишком узкими (то есть, "ложная уверенность") Смотрите мой ответ на аналогичный вопрос для более подробной информации (и один из комментариев Русса здесь).
Вот пример, где правильные параметры дают (по существу) идентичные доверительные интервалы:
In [9]: a = range(10,14)
In [10]: mean_confidence_interval(a)
Out[10]: (11.5, 9.4457397432391215, 13.554260256760879)
In [11]: st.t.interval(0.95, len(a)-1, loc=np.mean(a), scale=st.sem(a))
Out[11]: (9.4457397432391215, 13.554260256760879)
In [12]: sms.DescrStatsW(a).tconfint_mean()
Out[12]: (9.4457397432391197, 13.55426025676088)
И, наконец, неверный результат с помощью st.norm.interval()
:
In [13]: st.norm.interval(0.95, loc=np.mean(a), scale=st.sem(a))
Out[13]: (10.23484868811834, 12.76515131188166)
Запуск Python 3.8
стандартная библиотека предоставляет NormalDist
объект как часть statistics
модуль:
from statistics import NormalDist
def confidence_interval(data, confidence=0.95):
dist = NormalDist.from_samples(data)
z = NormalDist().inv_cdf((1 + confidence) / 2.)
h = dist.stdev * z / ((len(data) - 1) ** .5)
return dist.mean - h, dist.mean + h
Эта:
Создает
NormalDist
объект из выборки данных (NormalDist.from_samples(data)
, что дает нам доступ к среднему и стандартному отклонению выборки черезNormalDist.mean
а такжеNormalDist.stdev
.Вычислить
Z-score
на основе стандартного нормального распределения (представленногоNormalDist()
) для данной достоверности с использованием обратной функции кумулятивного распределения (inv_cdf
).Выдает доверительный интервал на основе стандартного отклонения и среднего значения выборки.
Это предполагает, что размер выборки достаточно велик (скажем, более ~100 баллов), чтобы использовать стандартное нормальное распределение, а не распределение Стьюдента для вычисления z
ценность.
Начните с поиска значения z для желаемого доверительного интервала из справочной таблицы. Доверительный интервал тогда mean +/- z*sigma
, где sigma
является расчетным стандартным отклонением среднего значения для вашей выборки, определяемым как sigma = s / sqrt(n)
, где s
стандартное отклонение, рассчитанное на основе ваших выборочных данных и n
ваш размер выборки.
На основе оригинала, но с некоторыми конкретными примерами:
import numpy as np
def mean_confidence_interval(data, confidence: float = 0.95) -> tuple[float, np.ndarray]:
"""
Returns (tuple of) the mean and confidence interval for given data.
Data is a np.arrayable iterable.
ref:
- https://stackoverflow.com/a/15034143/1601580
- https://github.com/WangYueFt/rfs/blob/f8c837ba93c62dd0ac68a2f4019c619aa86b8421/eval/meta_eval.py#L19
"""
import scipy.stats
import numpy as np
a: np.ndarray = 1.0 * np.array(data)
n: int = len(a)
if n == 1:
import logging
logging.warning('The first dimension of your data is 1, perhaps you meant to transpose your data? or remove the'
'singleton dimension?')
m, se = a.mean(), scipy.stats.sem(a)
tp = scipy.stats.t.ppf((1 + confidence) / 2., n - 1)
h = se * tp
return m, h
def ci_test_float():
import numpy as np
# - one WRONG data set of size 1 by N
data = np.random.randn(1, 30) # gives an error becuase len sets n=1, so not this shape!
m, ci = mean_confidence_interval(data)
print('-- you should get a mean and a list of nan ci (since data is in wrong format, it thinks its 30 data sets of '
'length 1.')
print(m, ci)
# right data as N by 1
data = np.random.randn(30, 1)
m, ci = mean_confidence_interval(data)
print('-- gives a mean and a list of length 1 for a single CI (since it thinks you have a single dat aset)')
print(m, ci)
# multiple data sets (7) of size N (=30)
data = np.random.randn(30, 7)
print('-- gives 7 CIs for the 7 data sets of length 30. 30 is the number ud want large if you were using z(p)'
'due to the CLT.')
m, ci = mean_confidence_interval(data)
print(m, ci)
ci_test_float()
выход:
-- you should get a mean and a list of nan ci (since data is in wrong format, it thinks its 30 data sets of length 1.
0.1431623130952463 [nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan]
-- gives a mean and a list of length 1 for a single CI (since it thinks you have a single dat aset)
0.04947206018132864 [0.40627264]
-- gives 7 CIs for the 7 data sets of length 30. 30 is the number ud want large if you were using z(p)due to the CLT.
-0.03585104402718902 [0.31867309 0.35619134 0.34860011 0.3812853 0.44334033 0.35841138
0.40739732]
Я думаю, что Num_samples от Num_datasets правильный, но если это не так, дайте мне знать в разделе комментариев.
В качестве бонуса реализация факела, которая почти использует только факел:
def torch_compute_confidence_interval(data: Tensor,
confidence: float = 0.95
) -> Tensor:
"""
Computes the confidence interval for a given survey of a data set.
"""
n: int = len(data)
mean: Tensor = data.mean()
# se: Tensor = scipy.stats.sem(data) # compute standard error
# se, mean: Tensor = torch.std_mean(data, unbiased=True) # compute standard error
se: Tensor = data.std(unbiased=True) / (n ** 0.5)
t_p: float = float(scipy.stats.t.ppf((1 + confidence) / 2., n - 1))
ci = t_p * se
return mean, ci
Некоторые комментарии по CI (или см. https://stats.stackexchange.com/questions/554332/confidence-interval-given-the-population-mean-and-standard-deviation?noredirect=1&lq=1):
"""
Review for confidence intervals. Confidence intervals say that the true mean is inside the estimated confidence interval
(the r.v. the user generates). In particular it says:
Pr[mu^* \in [mu_n +- t.val(p) * std_n / sqrt(n) ] ] >= p
e.g. p = 0.95
This does not say that for a specific CI you compute the true mean is in that interval with prob 0.95. Instead it means
that if you surveyed/sampled 100 data sets D_n = {x_i}^n_{i=1} of size n (where n is ideally >=30) then for 95 of those
you'd expect to have the truee mean inside the CI compute for that current data set. Note you can never check for which
ones mu^* is in the CI since mu^* is unknown. If you knew mu^* you wouldn't need to estimate it. This analysis assumes
that the the estimator/value your estimating is the true mean using the sample mean (estimator). Since it usually uses
the t.val or z.val (second for the standardozed r.v. of a normal) then it means the approximation that mu_n ~ gaussian
must hold. This is most likely true if n >= 0. Note this is similar to statistical learning theory where we use
the MLE/ERM estimator to choose a function with delta, gamma etc reasoning. Note that if you do algebra you can also
say that the sample mean is in that interval but wrt mu^* but that is borning, no one cares since you do not know mu^*
so it's not helpful.
An example use could be for computing the CI of the loss (e.g. 0-1, CE loss, etc). The mu^* you want is the expected
risk. So x_i = loss(f(x_i), y_i) and you are computing the CI for what is the true expected risk for that specific loss
function you choose. So mu_n = emperical mean of the loss and std_n = (unbiased) estimate of the std and then you can
simply plug in the values.
Assumptions for p-CI:
- we are making a statement that mu^* is in mu+-pCI = mu+-t_p * sig_n / sqrt n, sig_n ~ Var[x] is inside the CI
p% of the time.
- we are estimating mu^, a mean
- since the quantity of interest is mu^, then the z_p value (or p-value, depending which one is the unknown), is
computed using the normal distribution.
- p(mu) ~ N(mu; mu_n, sig_n/ sqrt n), vial CTL which holds for sample means. Ideally n >= 30.
- x ~ p^*(x) are iid.
Std_n vs t_p*std_n/ sqrt(n)
- std_n = var(x) is more pessimistic but holds always. Never shrinks as n->infity
- but if n is small then pCI might be too small and your "lying to yourself". So if you have very small data
perhaps doing std_n for the CI is better. That holds with prob 99.9%. Hopefuly std is not too large for your
experiments to be invalidated.
ref:
- https://stats.stackexchange.com/questions/554332/confidence-interval-given-the-population-mean-and-standard-deviation?noredirect=1&lq=1
- https://stackoverflow.com/questions/70356922/what-is-the-proper-way-to-compute-95-confidence-intervals-with-pytorch-for-clas
- https://www.youtube.com/watch?v=MzvRQFYUEFU&list=PLUl4u3cNGP60hI9ATjSFgLZpbNJ7myAg6&index=205
"""
Что касается ответа Ульриха - это использование t-значения. Мы используем это, когда истинная дисперсия неизвестна. Это когда единственными данными, которые у вас есть, являются данные выборки.
Для ответа богатрона это касается z-таблиц. Z-таблицы используются, когда дисперсия уже известна и предоставлена. Тогда у вас также есть образцы данных. Сигма не является расчетным стандартным отклонением выборочного среднего. Это уже известно.
Итак, предположим, что дисперсия известна, и нам нужна достоверность 95%:
from scipy.stats import norm
alpha = 0.95
# Define our z
ci = alpha + (1-alpha)/2
#Lower Interval, where n is sample siz
c_lb = sample_mean - norm.ppf(ci)*((sigma/(n**0.5)))
c_ub = sample_mean + norm.ppf(ci)*((sigma/(n**0.5)))
Только с выборочными данными и неизвестной дисперсией (это означает, что дисперсию нужно будет рассчитывать исключительно на основе выборочных данных), ответ Ульриха работает отлично. Однако вы, вероятно, захотите указать доверительный интервал. Если ваши данные представляют собой и вам нужен доверительный интервал 0,95:
import statsmodels.stats.api as sms
conf = sms.DescrStatsW(a).tconfint_mean(alpha=0.05)
conf