Постановка задачи
Критерий Эппса-Палли — один из критериев проверки нормальности распределения, основанный на сравнении эмпирической и теоретической характеристических функций.
Критерий предложен в 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); впрочем этот недостаток также свойственен и критерию Шапиро-Уилка.
Также про достоинства критерия Эппса-Палли — см., например, http://www.mathnet.ru/php/archive.phtml?wshow=paper&jrnid=znsl&paperid=7090&option_lang=rus.
В ГОСТ Р ИСО 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 (https://github.com/AANazarov/Statistical-methods/tree/master/Epps-Pally%20test).
При этом, как я писал выше, мы сталкиваемся с отличием в методике расчетов специальных функций в различных математических пакетах: в системе 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 (https://github.com/AANazarov/Statistical-methods/tree/master/Epps-Pally%20test).
Отдельную благодарность хочу выразить профессору Новосибирского государственного технического университета Борису Юрьевичу Лемешко, методические разработки которого использовались при написании данного разбора — за отклик, ответы на вопросы и комментарии, данные по электронной почте.
Литература
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 с.
ссылка на оригинал статьи https://habr.com/ru/post/685582/
Добавить комментарий