Как стать автором
Обновить

Проверка нормальности распределения с использованием критерия Эппса-Палли средствами Python

Время на прочтение16 мин
Количество просмотров13K

Постановка задачи

Критерий Эппса-Палли - один из критериев проверки нормальности распределения, основанный на сравнении эмпирической и теоретической характеристических функций.

Критерий предложен в 1983 г. (см. Epps, T. W., and Pulley, L. B. (1983). A test for normality based on the empirical characteristic function. Biometrika 70, 723–726). В настоящее время данный критерий рекомендован к использованию в ГОСТ Р ИСО 5479-2002 [1, с.13], однако среди тестов, включенных в модуль scipy.stats, данный критерий, к сожалению, отсутствует.

Детальное исследование критерия Эппса-Палли проведено в работах Б.Ю. Лемешко ([2], [3] и др.). Так, было установлено [3, с.31, 80], что по мощности критерий Эппса-Палли превосходит критерии Шапиро-Уилка, Д'Агостино, Дэвида-Хартли-Пирсона, однако имеет недостаток - при малых объемах выборки неспособен отличать от нормального закона распределения с более плоскими плотностями распределений (с коэффициентом эксцесса Es < 2); впрочем этот недостаток также свойственен и критерию Шапиро-Уилка.

Также про достоинства критерия Эппса-Палли - см., например, Bahadur efficiencies of the Epps–Pulley test for normality.

В ГОСТ Р ИСО 5479-2002 критерий Эппса-Палли рекомендуется применять при n ≥ 8, табличные значения приведены для 8 ≤ n ≤ 200. В работе Б.Ю. Лемешко [3, с.33] критерий рекомендуется применять при n > 15, табличные значения приведены для 8 ≤ n ≤ 1000.

Представляет интерес рассмотреть способы реализации критерия Эппса-Палли средствами python, добавив это весьма неплохой критерий в инструментарий специалиста DataScience.

Формирование исходных данных

В качестве исходных данных рассмотрим примеры, разобранные в работе Лемешко Б.Ю. [3, с.138-146], а именно:

  • результаты измерения средней плотности Земли (эксперимент Кавендиша);

  • результаты измерения заряда электрона (эксперимент Милликена);

  • результаты измерения скорости света (эксперимент Майкельсона);

  • результаты измерения скорости света (эксперимент Ньюкомба).

Рассмотрим для начала результаты измерения средней плотности Земли, полученные Генри Кавендишем, (г/см3) [3, с.139, табл.5.1]:

X = np.array([
    5.50, 5.55, 5.57, 5.34, 5.42, 5.30, 5.61, 5.36, 5.53, 5.79,
    5.47, 5.75, 4.88, 5.29, 5.62, 5.10, 5.63, 5.68, 5.07, 5.58,
    5.29, 5.27, 5.34, 5.85, 5.26, 5.65, 5.44, 5.39, 5.46
    ])

Выполним предварительную визуализацию с помощью пользовательской функции graph_hist_boxplot_probplot_sns, которая позволяет визуализировать исходную выборку путем одновременного построения гистограммы, коробчатой диаграммы и вероятностного графика, тем самым давая возможность исследователю одним взглядом оценить свойства выборки. Данная функция загружается из пользовательского модуля my_module__stat.py (функция graph_hist_boxplot_probplot_sns и модуль my_module__stat.py доступны в моем репозитории на GitHub https://github.com/AANazarov/MyModulePython).

graph_hist_boxplot_probplot_sns(
    data=X,
    graph_inclusion='hbp',    # выбор вида визуализации: h - hist, b - boxplot, p - probplot
    title_axes='Эксперимент Кавендиша', title_axes_fontsize=16,
    data_label='Средняя плотность Земли (г/см3)')    

Проверка нормальности распределения по критерию Эппса-Палли

1. Расчет статистики критерия Эппса-Палли

Определим расчетное значение статистики критерия Эппса-Палли по методике, приведенной в ГОСТ Р ИСО 5479-2002.

Объем выборки:

N = len(X)
print(f'N = {N}')

Находим характеристики выборки - среднее арифметическое и центральный момент 2-го порядка (дисперсия для генеральной совокупности, без поправки на смещенность):

# среднее арифметическое
X_mean = X.mean()
print(f'Xmean = {X_mean}')

# центральный момент 2-го порядка
m2 = np.var(X, ddof = 0)
print(f'm2 = {m2}')

Вычислим величину A:

A = sqrt(2) * np.sum([exp(-(X[i] - X_mean)**2 / (4*m2)) for i in range(N)])
print(f'A = {A}')

Вычислим величину B:

B = 2/N * np.sum(
    [np.sum([exp(-(X[j] - X[k])**2 / (2*m2)) for j in range(0, k)]) 
     for k in range(1, N)])
print(f'B = {B}')

Расчетное значение статистики критерия Эппса-Палли:

TEP_calc = 1 + N / sqrt(3) + B - A
print(f'Расчетное значение статистики критерия Эппса-Палли: TEP_calc = {TEP_calc}')

Рассчитанное нами значение совпадает с приведенным в источнике [3, с.143].

2. Определение табличных значений статистики критерия Эппса-Палли

В табл.12 ГОСТ Р ИСО 5479-2002 приведены табличные значения статистики критерия Эппса-Палли для 8 ≤ n ≤ 200:

В работе Б.Ю. Лемешко [3, с.185] приведены табличные значения статистики критерия Эппса-Палли для 8 ≤ n ≤ 1000:

Сформируем csv-файл с табличными значениями (для дальнейшего использования), поместим его в папку table в том же каталоге, что и наш рабочий файл *.ipynb:

Tep_table_df = pd.read_csv(
    filepath_or_buffer='table/Tep_table.csv',
    sep=';',
    index_col='n')
display(Tep_table_df)

Промежуточные значения определим с помощью интерполяции - для этого создадим пользовательскую функцию:

def Tep_table(n, p_level=0.95):
    # загружаем табличные значения статистики критерия Эппса-Палли
    Tep_table_df = pd.read_csv(
        filepath_or_buffer='table/Tep_table.csv',
        sep=';',
        index_col='n')
    # выбираем величину доверительной вероятности
    p_level_dict = {
        0.9:   Tep_table_df.columns[0],
        0.95:  Tep_table_df.columns[1],
        0.975: Tep_table_df.columns[2],
        0.99:  Tep_table_df.columns[3]}
    # линейная интерполяция
    N = Tep_table_df.index
    T = Tep_table_df[p_level_dict[p_level]]
    f_lin = sci.interpolate.interp1d(N, T)
    result = float(f_lin(n))    
    return result

Визуализация табличных значений статистики критерия Эппса-Палли:

fig, axes = plt.subplots(figsize=(297/INCH, 210/INCH))
axes.set_title('Табличные значения статистики критерия Эппса-Палли', fontsize=16)
axes.plot(Tep_table_df.index, Tep_table_df['p=0.9'].values, 'ob-', label='p=0.9')
axes.plot(Tep_table_df.index, Tep_table_df['p=0.95'].values, 'og-', label='p=0.95')
axes.plot(Tep_table_df.index, Tep_table_df['p=0.975'].values, 'oc-', label='p=0.975')
axes.plot(Tep_table_df.index, Tep_table_df['p=0.99'].values, 'om-', label='p=0.99')
axes.legend(loc="best", fontsize=12)
axes.set_xlim(0, 1000)
axes.set_ylim(0.2, 0.7)
axes.set_xlabel('n')
axes.set_ylabel('Tep(n)')

3. Аппроксимация предельных распределений статистики критерия Эппса-Палли

В работе Б.Ю. Лемешко [3, с.32] указано, что распределение статистики критерия Эппса-Палли достаточно хорошо аппроксимируется бета-распределением III рода, плотность распределения которого имеет вид:

Там же указано, что при 15 < n < 50 можно использовать бета-распределениями III рода с параметрами:

θ0 = 1.8645    θ1 = 2.5155    θ2 = 5.8256    θ3 = 0.9216    θ4 = 0.0008

При n ≥ 50 следует пользоваться предельным распределением с параметрами:

θ0 = 1.7669    θ1 = 2.1668    θ2 = 6.7594    θ3 = 0.91    θ4 = 0.0016

Теперь для выполнения расчетов нам необходимо сопоставить встроенные возможности python с указанным выше распределением.

Библиотека scipy дает нам возможность работать с бета-распределением (https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.beta.html), плотность которого имеет вид:

Так как бета-функция выражается через гамма-функцию:

то очевидно, что мы имеем дело с классическим бета-распределением, разобранным в множестве источников (например, [4, 5, 6]). При этом во всех указанных источниках упоминаний про бета-распределение III рода нет. Соответственно, и python не имеет встроенных возможностей для работы с данным распределением.

Следует разобраться более подробно с этим распределением. Это представляет интерес еще и потому, что в работах [2], [3], посвященных исследованию распределений подобная ситуация встречается часто - распределения статистик различных критериев описываются малораспространенными видами распределений, которые не реализованы в python, то есть с подобной проблемой исследователь может столкнуться при решении других задач. Ну об этом напишем в другой раз, в этой области огромное количество интересных и полезных методов и задач.

Кстати, на основании исследований Б.Ю. Лемешко и его коллектива были разработаны рекомендации по стандартизации:

  • Р 50.1.033-2001. Рекомендации по стандартизации. Прикладная статистика. Правила проверки согласия опытного распределения с теоретическим. Часть I. Критерии типа хи-квадрат.

  • Р 50.1.037-2002. Рекомендации по стандартизации. Прикладная статистика. Правила проверки согласия опытного распределения с теоретическим. Часть II. Непараметрические критерии.

На сегодняшний день они имеют статус действующих.

Итак, в отечественной литературе по прикладной статистике понятие бета-распределения III рода (а, соответственно I и II рода) до начала 1990-х гг., мне обнаружить не удалось.

В интернете информации тоже немного, хотя кое-что найти можно (например, https://www.r-bloggers.com/2019/07/the-beta-distribution-of-the-third-kind-or-generalised-beta-prime/).

Более или менее подробный теоретический материал удалось обнаружить в кандидатской диссертации Постовалова С.Н. [7, с.92-95, 111-115], научным руководителем которого был Б.Ю. Лемешко. Постовалов С.Н. в своей работе [7, с.113] ссылается на работу Губарева В.В. [8], в которой и приводятся справочные данные о бета-распределения III рода [8, табл.4.3, п.27, с.164], при этом данной распределение отмечено примечанием "введено автором". То есть получается, что понятие бета-распределения III рода предложено Губаревым В.В. в 1992 г. и, очевидно, характерно только для отечественной, в частности, новосибирской, школы прикладной статистики? Так это или иначе, но неудивительно, что в python это распределение не реализовано. Реализуем сами.

Итак, Постовалов С.Н. в своей работе [7, с.92] дает следующее определение бета-распределения III рода: если рассмотреть обобщенное семейство бета-распределений, функция распределения которых имеет вид:

где:

то, используя генерирующую функцию вида:

мы и получим бета-распределение III рода.

То есть мы видим, что с помощью генерирующей функции мы можем перейти от обычного бета-распределения (или бета-распределения I рода, как оно именуется в [7]), которое реализовано в библиотеке scipy, к бета-распределению III рода, что нам и требуется.

4. Реализация бета-распределения III рода средствами python

Функция обычного бета-распределения (I рода) реализована в модуле scipy.stat следующим образом:

сdf_beta_I = lambda x, a, b: sci.stats.beta.cdf(x, a, b, loc=0, scale=1)

Перейдем к функции бета-распределения III рода с помощью генерирующей функции [7, с.92]:

g_beta_III = lambda z, δ: δ*z / (1+(δ-1)*z)

Подставляя g_beta_III в сdf_beta_I, получим функцию бета-распределения III рода:

cdf_beta_III = lambda x, θ0, θ1, θ2, θ3, θ4: сdf_beta_I(g_beta_III((x - θ4)/θ3, θ2), θ0, θ1)

При этом важно идентифицировать параметры функции распределения, так как в разных источниках используются различные обозначения. В рассматриваемом нами источнике у Б.Ю. Лемешко [3] используются параметры θ0, θ1, θ2, θ3, θ4, будем идентифицировать их:

График функции бета-распределения III рода:

# набор параметров распределения для 15 < n < 50
θ_1 = (1.8645, 2.5155, 5.8256, 0.9216, 0.0008)
# набор параметров распределения для n >= 50
θ_2 = (1.7669, 2.1668, 6.7594, 0.91, 0.0016)
fig, axes = plt.subplots(figsize=(210/INCH, 297/INCH/2))
axes.set_title('Функция бета-распределения III рода \nпри аппроксимации распределения статистики критерия Эппса-Палли', fontsize=14)
xmin = 0
xmax = 1
x = np.linspace(xmin, xmax, 100)
axes.plot(x, cdf_beta_III(x, θ_1[0], θ_1[1], θ_1[2], θ_1[3], θ_1[4]), label='для 15 < n < 50')
axes.plot(x, cdf_beta_III(x, θ_2[0], θ_2[1], θ_2[2], θ_2[3], θ_2[4]), label='для n >= 50')
axes.legend(loc="best", fontsize=12)
axes.set_xlim(xmin, xmax)
axes.set_xlabel('x')
axes.set_ylabel('cdf_beta_III(x)')

5. Плотность бета-распределения III рода

Для использования критерия Эппса-Палли плотность плотности распределения нам, по сути, не нужна, но, тем не менее, найдем ее, так сказать, для общей демонстрации возможностей.

Для нахождения плотности бета-распределения III рода мы не можем воспользоваться встроенными возможностями модуля scipy.stat. Придется воспользоваться определением плотности распределения как первой производной от функции распределения. При этом у нас не получится найти эту производную в общем виде, пользуясь библиотекой символьных вычислений sympy, поэтому дифференцировать будем численно, с помощью функции scipy.misc.derivative:

# для 15 < n < 50
cdf_beta_III_part1 = lambda x: cdf_beta_III(x, θ_1[0], θ_1[1], θ_1[2], θ_1[3], θ_1[4])
pdf_beta_III_part1 = lambda x: sci.misc.derivative(cdf_beta_III_part1, x, dx=1e-6)

# для n >= 50
cdf_beta_III_part2 = lambda x: cdf_beta_III(x, θ_2[0], θ_2[1], θ_2[2], θ_2[3], θ_2[4])
pdf_beta_III_part2 = lambda x: sci.misc.derivative(cdf_beta_III_part2, x, dx=1e-6)

fig, axes = plt.subplots(figsize=(210/INCH, 297/INCH/2))
axes.set_title('Плотность бета-распределения III рода \nпри аппроксимации распределения статистики критерия Эппса-Палли', fontsize=14)
xmin = 0
xmax = 1
x = np.linspace(xmin, xmax, 100)
axes.plot(x, pdf_beta_III_part1(x), label='для 15 < n < 50')
axes.plot(x, pdf_beta_III_part2(x), label='для n >= 50')
axes.legend(loc="best", fontsize=12)
axes.set_xlim(xmin, xmax)
axes.set_xlabel('x')
axes.set_ylabel('pdf_beta_III(x)')

Небольшой Offtop - про тестирование математических расчетов

Самопроверка при выполнении математических расчетов - это, как говорится, основа основ... Ошибиться может каждый. На мой взгляд, лучший способ самопроверки - при выполнении какого-либо тестового расчета выполнить его разными способами, или в разных математических пакетах. Это сильно повышает вероятность отследить ошибки.

В нашем случае, мы выполняем не такие уж сложные расчеты, но с использованием специальных функций и встроенных возможностей python. Функции имеют большое количество параметров, кроме того, в разных математических пакетах методика расчета специальных функций может отличаться. Проверить себя необходимо.

Рассчитаем значение функции бета-распределения III рода для некой условной точки x=0.45, a=0.65, b=0.85 различными способами:

  • с использованием встроенных возможностей модуля scipy.stat

  • по определению бета-функции с использованием модуля специальных функций scipy.special (https://docs.scipy.org/doc/scipy/reference/special.html), в котором имеется функция scipy.special.betainc.

Сравним результаты:

# с использованием встроенных возможностей модуля scipy.stat
print(сdf_beta_I(x=0.45, a=0.65, b=0.85))
# с использованием модуля специальных функций scipy.special
print(sci.special.betainc(0.65, 0.85, 0.45))

Как видим, результаты практически совпадают.

Рассчитаем теперь значение плотности бета-распределения III рода для точки x=0.45 в двух вариантах: для 15 < n < 50 и для n >= 50. Эта проверка уже сложнее, так как средствами python мы получили плотность распределения только одним способом - через численное дифференцирование:

print(pdf_beta_III_part1(0.45))
print(pdf_beta_III_part2(0.45))

Второе решение получим с помощью альтернативного математического пакета - системы компьютерной алгебры Maxima. Далее представлен фрагмент программного кода:

Как видим, результаты расчетов также совпадают.

В системе Maxima продублирован полный комплекс расчетов из данного разбора (программный код доступен в моем репозитории на GitHub).

При этом, как я писал выше, мы сталкиваемся с отличием в методике расчетов специальных функций в различных математических пакетах: в системе Maxima неполная бета-функция рассчитывается, в отличие от scipy, без умножения на множитель

В общем, будьте бдительны, товарищи!

Кстати, о системе компьютерной алгебры Maxima (https://sourceforge.net/projects/maxima/files/), ее возможностях, достоинствах, недостатках можно написать отдельно. Этот математический пакет с открытой лицензией нельзя сказать, что очень сильно распространен, хотя о нем написано немало книг, статей, методических указаний, и их количество в последние годы растет.

Из своего собственного опыта я абсолютно убежден, что любому специалисту, деятельность которого так или иначе связана с математическими расчетами, владеть какой-либо системой компьютерной алгебры необходимо, а Maxima имеет для этого немало достоинств:

  • открытая лицензия;

  • очень большой математический инструментарий;

  • обширные возможности для символьных вычислений (например, выше было продемонстрировано, как Maxima позволяет путем подстановки получить символьное выражение для рассматриваемой нами выше функции бета-распределения III рода, а затем, продифференцировав ее, получить символьное выражение для плотности распределения);

  • удобство использования (лучше, чем в Mathlab или Scilab, но хуже, чем в Mathcad);

  • компактность кода (одна страница кода в Maxima гораздо информативней, чем, например, в Scilab);

  • встроенный язык программирования.

Среди недостатков можно отметить:

  • низкое быстродействие;

  • необходимость адаптироваться к несколько специфическому синтаксису (например, в Maxima вместо символа операции присваивания значения переменной "=" используется ":");

  • неприспособленность для решения задач высокой размерности (при решении отдельных задач прикладной статистики с объемами выборки в несколько сотен Maxima начинает вполне ощутимо "тормозить", а при объеме данных в нескольких тысяч - Maxima лучше не применять вовсе).

По-моему личному мнению, Maxima прекрасно подходит как средство тестирования математических расчетов. Впрочем, каждый исследователь сам выбирает для себя инструменты по душе.

Ну, подробнее об этом, напишем отдельно, в другой раз, а пока вернемся к нашей задаче.

6. Проверка гипотезы о нормальности распределения по критерию Эппса-Палли

Проверка гипотезы на основании расчетного и табличного значения статистики критерия:

DecPlace = 5    # точность вывода

print(f'Расчетное значение статистики критерия Эппса-Палли: TEP_calc = {round(TEP_calc, DecPlace)}')

TEP_table = Tep_table(N, p_level=0.95)
print(f'Табличное значение статистики критерия Эппса-Палли: TEP_table = {round(TEP_table, DecPlace)}')

if TEP_calc <= TEP_table:
        conclusion_EP_test = f"Так как TEP_calc = {round(TEP_calc, DecPlace)} <= TEP_table = {round(TEP_table, DecPlace)}" + \
            ", то гипотеза о нормальности распределения по критерию Эппса-Палли ПРИНИМАЕТСЯ"
else:
    conclusion_EP_test = f"Так как TEP_calc = {round(TEP_calc, DecPlace)} > TEP_table = {round(TEP_table, DecPlace)}" + \
            ", то гипотеза о нормальности распределения по критерию Эппса-Палли ОТВЕРГАЕТСЯ"
    
print(conclusion_EP_test)

Проверка гипотезы на основании расчетного и заданного уровня значимости:

if 15 < N < 50:
    a_TEP_calc = 1 - cdf_beta_III(TEP_calc, θ_1[0], θ_1[1], θ_1[2], θ_1[3], θ_1[4])
elif N >= 50:
    a_TEP_calc = 1 - cdf_beta_III(TEP_calc, θ_2[0], θ_2[1], θ_2[2], θ_2[3], θ_2[4])    
print(f'Расчетное (достигнутое) значение уровня значимости: a_TEP_calc = {round(a_TEP_calc, DecPlace)}')

print(f'Заданное значение уровня значимости: a_level = {round(a_level, DecPlace)}')

if a_TEP_calc > a_level:
        conclusion_EP_test = f"Так как a_calc = {round(a_TEP_calc, DecPlace)} > a_level = {round(a_level, DecPlace)}" + \
            ", то гипотеза о нормальности распределения по критерию Эппса-Палли ПРИНИМАЕТСЯ"
else:
    conclusion_EP_test = f"Так как a_calc = {round(a_TEP_calc, DecPlace)} <= a_level = {round(a_level, DecPlace)}" + \
            ", то гипотеза о нормальности распределения по критерию Эппса-Палли ОТВЕРГАЕТСЯ"

print(conclusion_EP_test)

Видим, что расчетное значение уровня значимости a_TEP_calc = 0.75339 практически совпадает с примером в первоисточнике ([3, с.143]): 0.734.

Создание пользовательской функции для реализации теста Эппса-Палли

Для практической работы целесообразно все вышеприведенные расчеты реализовать в виде пользовательской функции Epps_Pulley_test.

Данная функция выводят результаты анализа в виде DataFrame, что удобно для визуального восприятия и дальнейшего использования результатов анализа (впрочем, способ вывода - на усмотрение каждого исследователя).

def Epps_Pulley_test(data, p_level=0.95):
    a_level = 1 - p_level
    X = np.array(data)
    N = len(X)
    
    # аппроксимация предельных распределений статистики критерия
    сdf_beta_I = lambda x, a, b: sci.stats.beta.cdf(x, a, b, loc=0, scale=1)
    g_beta_III = lambda z, δ: δ*z / (1+(δ-1)*z)
    cdf_beta_III = lambda x, θ0, θ1, θ2, θ3, θ4: сdf_beta_I(g_beta_III((x - θ4)/θ3, θ2), θ0, θ1)
    # набор параметров распределения
    θ_1 = (1.8645, 2.5155, 5.8256, 0.9216, 0.0008)    # для 15 < n < 50
    θ_2 = (1.7669, 2.1668, 6.7594, 0.91, 0.0016)    # для n >= 50
    
    if N >= 8:
        # среднее арифметическое
        X_mean = X.mean()
        # центральный момент 2-го порядка
        m2 = np.var(X, ddof = 0)
        # расчетное значение статистики критерия
        A = sqrt(2) * np.sum([exp(-(X[i] - X_mean)**2 / (4*m2)) for i in range(N)])
        B = 2/N * np.sum(
            [np.sum([exp(-(X[j] - X[k])**2 / (2*m2)) for j in range(0, k)]) 
             for k in range(1, N)])
        s_calc_EP = 1 + N / sqrt(3) + B - A
        # табличное значение статистики критерия
        Tep_table_df = pd.read_csv(
            filepath_or_buffer='table/Tep_table.csv',
            sep=';',
            index_col='n')
        p_level_dict = {
            0.9:   Tep_table_df.columns[0],
            0.95:  Tep_table_df.columns[1],
            0.975: Tep_table_df.columns[2],
            0.99:  Tep_table_df.columns[3]}
        f_lin = sci.interpolate.interp1d(Tep_table_df.index, Tep_table_df[p_level_dict[p_level]])
        s_table_EP = float(f_lin(N))
        # проверка гипотезы
        if 15 < N < 50:
            a_calc_EP = 1 - cdf_beta_III(s_calc_EP, θ_1[0], θ_1[1], θ_1[2], θ_1[3], θ_1[4])
            conclusion_EP = 'gaussian distribution' if a_calc_EP > a_level else 'not gaussian distribution'            
        elif N >= 50:
            a_calc_EP = 1 - cdf_beta_III(s_calc_EP, θ_2[0], θ_2[1], θ_2[2], θ_2[3], θ_2[4])
            conclusion_EP = 'gaussian distribution' if a_calc_EP > a_level else 'not gaussian distribution'            
        else:
            a_calc_EP = ''              
            conclusion_EP = 'gaussian distribution' if s_calc_EP <= s_table_EP else 'not gaussian distribution'            
                
    else:
        s_calc_EP = '-'
        s_table_EP = '-'
        a_calc_EP = '-'
        conclusion_EP = 'count less than 8'
    
    result = pd.DataFrame({
        'test': ('Epps-Pulley test'),
        'p_level': (p_level),
        'a_level': (a_level),
        'a_calc': (a_calc_EP),
        'a_calc >= a_level': (a_calc_EP >= a_level if N > 15 else '-'),
        'statistic': (s_calc_EP),
        'critical_value': (s_table_EP),
        'statistic < critical_value': (s_calc_EP < s_table_EP if N >= 8 else '-'),
        'conclusion': (conclusion_EP)},
        index=[1])  
        
    return result
Epps_Pulley_test(X, p_level=0.95)

Другие примеры

Рассматривая остальные примеры из первоисточника [3] - эксперименты Милликена, Майкельсона и Ньюкомба - получим аналогичные результаты расчетов, совпадающие с данными из первоисточника.

Для примера рассмотрим результаты измерения скорости света, полученные Саймоном Ньюкомбом, (миллионные доли секунды) [3, с.139, табл.5.4]:

X = 24.8 + 10**-3 * np.array([
    28,  26,  33,  24,  34, -44, 27,  16,  40,  -2,
    29,  22,  24,  21,  25,  30,  23,  29,  31,  19,
    24,  20,  36,  32,  36,  28,  25,  21,  28,  29,
    37,  25,  28,  26,  30,  32,  36,  26,  30,  22,
    36,  23,  27,  27,  28,  27,  31,  27,  26,  33,
    26,  32,  32,  24,  39,  28,  24,  25,  32,  25,
    29,  27,  28,  29,  16,  23                
    ])

Выполним предварительную визуализацию:

graph_hist_boxplot_probplot_sns(
    data=X,
    data_min=min(X)*0.9995, data_max=max(X)*1.001,
    graph_inclusion='hbp',
    title_axes='Эксперимент Ньюкомба', title_axes_fontsize=16,
    data_label='Скорость света (миллионные доли секунды)')    

Видим, что в выборке присутствуют очевидно аномальные значения (выбросы), искажающие результат.
Гипотеза о нормальном распределении исходных данных ОТВЕРГАЕТСЯ:

Epps_Pulley_test(X, p_level=0.95)

Исключим аномальные значения (выбросы) и повторим процедуру:

mask = (X >= 24.81)
X = X[mask]
print(X)
graph_hist_boxplot_probplot_sns(
    data=X,
    data_min=min(X)*0.9995, data_max=max(X)*1.001,
    graph_inclusion='hbp',
    title_axes='Эксперимент Ньюкомба', title_axes_fontsize=16,
    data_label='Скорость света (миллионные доли секунды)')    
    
Epps_Pulley_test(X, p_level=0.95)    

Видим, что после исключения выбросов рассчитанные нами параметры практически совпадают с примером в первоисточнике ([3, с.144]):

  • расчетное значение уровня значимости: наш результат a_calc = 0.4539, результат в первоисточнике 0.461;

  • расчетное значение статистики критерия Эппса-Палли: наш результат statistic = 0.1074, результат в первоисточнике 0.1074.

Гипотеза о нормальном распределении исходных данных ПРИНИМАЕТСЯ.

В программном коде в моем репозитории на GitHub (https://github.com/AANazarov/Statistical-methods/tree/master/Epps-Pally test) также разобран пример из ГОСТ Р ИСО 5479-2002 - результаты измерения прочности вискозной нити (в условных единицах) [1, с.14, пример 5].

Итоги

Итак, мы рассмотрели способы реализации критерия Эппса-Палли средствами python, разобрали подход к определению расчетного уровня значимости, который можно использовать при реализации других статистических критериев, отсутствующих в наборе стандартных функций python, также предложены пользовательские функции, уменьшающие размер кода. Кроме того, выполнено тестирование математических расчетов альтернативным способом - в системе компьютерной алгебры Maxima.

Исходный код находится в моем репозитории на GitHub.

Отдельную благодарность хочу выразить профессору Новосибирского государственного технического университета Борису Юрьевичу Лемешко, методические разработки которого использовались при написании данного разбора - за отклик, ответы на вопросы и комментарии, данные по электронной почте.

Литература

1. ГОСТ Р ИСО 5479-2002. Статистические методы. Проверка отклонения распределения вероятностей от нормального распределения.

2. Лемешко Б.Ю. и др. Статистический анализ данных, моделирование и исследование вероятностных закономерностей. Компьютерный подход. - Новосибирск: Изд-во НГТУ, 2011. - 888 с.

3. Лемешко Б.Ю. Критерии проверки отклонения распределения от нормального закона. Руководство по применению. - Новосибирск: Изд-во НГТУ, 2014. - 192 с.

4. Хан Г., Шапиро С. Статистические модели в инженерных задачах / пер. с англ. - М.: Мир, 1969. - 395 с.

5. Айвазян С.А. и др. Прикладная статистика: основы моделирования и первичная обработка данных. - М.: Финансы и статистика, 1983. - 471 с.

6. Джонсон Н.Л. и др. Одномерные непрерывные распределения. В 2-х ч. - Ч.2 / пер. с англ. - М.: БИНОМ. Лаборатория знаний, 2012. - 600 с.

7. Постовалов С.Н. Статистический анализ интервальных наблюдений одномерных непрерывных случайных величин: дис. ... канд.тех.наук 05.13.16. - Новосибирск.гос.тех.ун-т: Новосибирск, 1997. - 188 с.

8. Губарев В.В. Вероятностные модели: Справочник. В 2-х ч. - Ч.1. - Новосиб. электротех. ин-т: Новосибирск, 1992. - 198 с.

Теги:
Хабы:
Всего голосов 11: ↑11 и ↓0+11
Комментарии1

Публикации

Истории

Работа

Data Scientist
63 вакансии

Ближайшие события

25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань