Иногда в процессе анализа данных мы сталкиваемся с необходимостью определить тип распределения данных. Решить эту задачу непросто: нет такого универсального статистического теста, который позволил бы однозначно определить тип распределения, за исключением случаев, когда оно является нормальным. Но распределение данных можно сравнить с нормальным распределением. Требование нормальности распределения данных лежит в основе некоторых статистических тестов и моделей; плюс, при визуальном сравнении с нормальным распределением удобно отмечать всякие особенности распределения (скошенность, наличие «длинных хвостов» и прочее).
Начнем с визуального анализа. Например, наложим на гистограмму, построенную для показателя, график плотности нормального распределения с соответствующими параметрами.
Напоминание 1. О графике плотности распределения можно думать как о «сглаженной» гистограмме с большим числов столбцов.
Напоминание 2. Нормальное распределение задается двумя параметрами: математическим ожиданием и стандартным отклонением. Математическое ожидание отвечает за среднее значение распределения (значение, относительно которого симметричен график плотности распределения), стандартное отклонение — за разброс значений вокруг среднего.
Для примера — графики плотности нормального распределения с разными параметрами.
# add = TRUE - чтобы добавлять графики к уже нарисованным
curve(dnorm(x, mean = 2, sd = 1), xlim = c(-10, 10), col = "green" )
curve(dnorm(x, mean = -1, sd = 1), xlim = c(-10, 10), col = "blue", add = TRUE)
curve(dnorm(x, mean = 2, sd = 3), xlim = c(-10, 10), col = "red", add = TRUE)
Теперь попробуем совместить на графике гистограмму и график плотности нормального распределения с соответствующими параметрами. Загрузим датафрейм с данными проекта Comparative Political Data Set, с которым мы уже работали.
df <- read.csv("http://math-info.hse.ru/f/2018-19/pep/hw/CPDS.csv", dec = ",")
df <- subset(df, df$year >= 2014) # выберем данные
Построим гистограмму для показателя vturn
(явка на выборы) и наложим на неё график плотности нормального распределения с соответствующими параметрами. Какие параметры считать соответствующими? Среднее значение, равное среднему значению показателя vturn
, и стандартное отклонение, равное стандартному отклонению vturn
.
# freq = FALSE - обязательно, так как нужны не абсолютные частоты, а нормализованные
hist(df$vturn, main = "Histogram of turnout", col = "tomato", freq = FALSE)
# na.rm = TRUE - не учитываем пропуски (NA)
# lwd - line width, толщина линии
curve(dnorm(x, mean = mean(df$vturn, na.rm = TRUE),
sd = sd(df$vturn, na.rm = TRUE)),
col = "blue", lwd = 2, add = TRUE)
Пока кажется, что распределение явки не очень похоже на нормальное из-за высокого, сильно выделяющегося столбца на участке от 50 до 60. А теперь проверим формально.
Один из статистических критериев, позволяющих проверить нормальность распределения данных, это критерий Шапиро-Уилка. С помощью этого критерия проверяется нулевая гипотеза, которая состоит в том, что данные распределены нормально.
shapiro.test(df$vturn)
##
## Shapiro-Wilk normality test
##
## data: df$vturn
## W = 0.96857, p-value = 0.01167
P-value < 0.05, следовательно, «жизнеспособность» нулевой гипотезы, оценённая на основе имеющихся данных, мала. На имеющихся данных на уровне значимости 5% (0.05) есть основания отвергнуть нулевую гипотезу о том, что данные распределены нормально. Показатель явки не распределён нормально.
С таблицами частот мы уже знакомы. Познакомимся с таблицами сопряжённости (contingency tables) — таблицами, которые иллюстрируют совместное распределение переменных. Построим таблицу сопряженности для двух признаков: poco
(принадлежность к пост-коммунистическим странам) и gov_party
(тип партийной системы).
ctab <- table(df$poco, df$gov_party)
# View(ctab)
По полученной таблице сопряжённости можно определить, например, что число пост-коммунистических стран с гегемонией правых/центристских партий равно 4.
Связь между качественными переменными можно визуализировать с помощью мозаичного графика (mosaic plot). Подробнее о мозаичном графике см. здесь и здесь. Для этого потребуется библиотека vcd
(от visualising categorical data).
install.packages("vcd")
library(vcd)
mosaic(data = df, poco ~ gov_party)
С помощью мозаичного графика мы можем визуализировать таблицу сопряжённости. Тёмно-серый цвет соответствует пост-коммунистическим странам, светло-серый — всем остальным. Разбивка на пять блоков по горизонтали — разбивка по значениям переменной gov_party
(гегемония правых/центристских партий, доминирование левых партий и прочие).
Чтобы всё совсем стало понятно, поправим подписи по осям. Создадим список (list
) с поименованными векторами, один для подписей к poco
, другой — к gov_party
.
args <- list(poco = c("No", "Yes"),
gov_party = c("Hegemony R", "Dominance R/C", "Balance R/L", "Dominance L", "Hegemony L"))
А теперь добавим полученные подписи — запишем их в аргумент set_labels
:
mosaic(data = df, poco ~ gov_party, set_labels = args)
Проблему длинных подписей, которые накладываются друг на друга, можно решать по-разному. Мы пока на время воспользуемся простым, но не самым красивым: повернём подписи и сделаем их горизонтальными.
mosaic(data = df, poco ~ gov_party, set_labels = args, rot_labels = 0)
Можем сократить подписи до аббревиатур, используя функцию abbreviate()
. Заодно повернём подписи на 45 градусов и уберём подписи самих осей:
args <- list(poco = c("No", "Yes"),
gov_party = abbreviate(c("Hegemony R", "Dominance R/C", "Balance R/L", "Dominance L", "Hegemony L")))
axis_labs <- list(set_varnames = c(poco = "", gov_party = "")) # убираем подписи
mosaic(data = df, poco ~ gov_party,
set_labels = args,
rot_labels = 45,
labeling_args = axis_labs)
А теперь проверим формально, есть ли связь между этими признаками (принадлежность к пост-коммунистическим странам и тип партийной системы). Воспользуемся критерием хи-квадрат. Нулевая гипотеза: признаки не связаны (независимы).
\[ H_0: \text{признаки независимы (не связаны)} \] \[ H_1: \text{признаки не независимы (связаны)} \]
chisq.test(table(df$poco, df$gov_party))
## Warning in chisq.test(table(df$poco, df$gov_party)): Chi-squared
## approximation may be incorrect
##
## Pearson's Chi-squared test
##
## data: table(df$poco, df$gov_party)
## X-squared = 8.3005, df = 4, p-value = 0.08117
P-value > 0.05, следовательно, вероятность того, что мы получим результаты такие, какие получили и выше, при условии, что нулевая гипотеза верна, не мала. На имеющихся данных на уровне значимости 5% (0.05) нет оснований отвергнуть нулевую гипотезу о том, что признаки независимы. Тип партийной системы и принадлежность к пост-коммунистическим странам не связаны.
Замечание. R выдал предупреждение Chi-squared approximation may be incorrect
. (Пояснение, возможно, будет понятно не всем, но его можно смело пропустить и посмотреть, как решается эта проблема). При расчете ожидаемых частот для расчета наблюдаемого значения статистики хи-квадрат получилось, что некоторые ожидаемые частоты в ячейках таблицы сопряженности меньше 5, и таких ячеек много. В такой ситуации p-value не может быть посчитан точно. Для решения проблемы есть два способа: объединить ячейки (укрупнить группы, если это уместно) или воспользоваться точным тестом Фишера (Fisher’s Exact test). Мы пойдём вторым путём:
fisher.test(table(df$poco, df$gov_party))
##
## Fisher's Exact Test for Count Data
##
## data: table(df$poco, df$gov_party)
## p-value = 0.07206
## alternative hypothesis: two.sided
Логика проверки гипотезы и выводы — те же самые, что и в критерии хи-квадрат.
Напоминание про корреляции. Коэффициент корреляции К.Пирсона — показатель линейной связи между двумя переменными, измеренными в количественной шкале. Коэффициент корреляции принимает значения от \(-1\) до \(1\). Отрицательные значения коэффициента корреляции свидетельствуют об обратной связи между переменными (с ростом значений одной переменной значения другой переменной уменьшаются), положительные значения коэффициента корреляции — о прямой связи между переменными (с ростом значений одной переменной значения другой переменной увеличиваются). Если коэффициент корреляции Пирсона между переменными равен 0, это не всегда означает, что связи между ними нет — связь между ними может просто быть нелинейной (например, квадратичной). Коэффициент корреляции показывает только связь между переменными, а не зависимость (Y
зависит от X
) и не влияние (X
влияет на Y
) и, конечно, ничего не сообщает о причинно-следственной связи.
Коэффициент корреляции Ч.Спирмена также используется для измерения связи между двумя переменными, измеренными в количественной шкале, преимущественно в порядковой (ординальной). Коэффициент корреляции Спирмена, в отличие от коэффициента Пирсона, является устойчивым к наличию нетипичных значений.
Связи между количественными переменными можно представить в виде корреляционной матрицы. Корреляционная матрица всегда симметрична (коэффициент корреляции между переменными X
и Y
равен коэффициенту корреляции между переменными Y
и X
), и на главной диагонали такой матрицы стоят 1 (корреляция переменной самой с собой равна 1).
Допустим, мы хотим посмотреть на связь между переменными gov_left1
и gov_right1
. Построим диаграмму рассеяния (scatterplot).
plot(df$gov_left1, df$gov_right1)
По диаграмме рассеяния видно, что связь между переменными обратная (чем больше x
, тем меньше y
) и, скорее всего, сильная.
Как можно заметить, особой красотой этот график не отличается. График скучный. Что мы можем сделать? Во-первых, подписать оси и изменить тип маркера для точек.
Все типы маркеров мы можем посмотреть, запросив help
по аргументу pch
, он как раз отвечает за тип точек (pch
— от point character).
?pch
plot(df$gov_left1, df$gov_right1,
xlab = "Left parties (% of total)",
ylab = "Right parties (% of total)",
pch = 19)
Во-вторых, мы можем добавить цвета, причем вполне содержательно. Допустим, мы хотим разделить страны на пост-коммунистические и не пост-коммунистические и отразить это на графике. То есть, точки, соответствующие пост-коммунистическим странам и точки, соответствующие всем остальным странам будут отличаться по цвету.
str(df$poco) # проверим, какие значения принимает poco
## int [1:108] 0 0 0 0 0 0 0 0 0 1 ...
Показатель poco
числовой, но по смыслу он качественный, то есть факторный. Поправим тип:
df$poco <- factor(df$poco)
colors <- c("blue", "red")[df$poco] # устанавливаем цвета по группирующей переменной
plot(df$gov_left1, df$gov_right1,
xlab = "Left parties (% of total)",
ylab = "Right parties (% of total)",
pch = 19,
col = colors)
Иногда в ходе предварительного анализа бывает нужно посмотреть на связь «всего со всем». Для этого удобно использовать матрицу диаграмм рассеяния.
Построим диаграммы рассеяния для процентов голосов за разные партии.
pairs(df[10:12], col = colors) # выбираем столбцы 10-12
На пересечении названий переменных находятся диаграммы рассеяния, соответствующие парам показателей.
А теперь проиллюстрируем то же самое, но более красочно. Для этого потребуется библиотека car
.
install.packages("car")
library(car)
scatterplotMatrix(df[10:12])
На диагонали этой матрицы диаграмм рассеяния добавляются графики плотности распределения («сглаженные» гистограммы). На сами диаграммы рассеяния добавляется регрессионная прямая (прямая вида \(y = kx+b\), при \(k < 0\) наклон прямой отрицательный, связь между \(x\) и \(y\) обратная, при \(k > 0\) наклон прямой положительный, связь между \(x\) и \(y\) прямая). Можно также добавить кривую взвешенной регрессии (loess regression от locally weigthed regression). Логика её построения (в очень упрощённом виде) такая:
Чтобы добавить линию взвешенной регресии, нужно убрать обычную регрессионную прямую (reg.line = FALSE
) и добавить новую сглаженную (smooth = TRUE
):
scatterplotMatrix(df[10:12], regLine = FALSE, smooth = TRUE)
Наведем красоту на графике выше, создадим вектор с названиями переменных в более внятном виде и чуть-чуть увеличим шрифт у подписей на диагонали:
labs <- c("Right", "Center", "Left")
# var.labels - названия переменных на диагонали
# cex.labels - размер шрифта для labels
# main - название графика
scatterplotMatrix(df[10:12], regLine = FALSE, smooth = TRUE,
var.labels = labs,
cex.labels = 1.3,
main = "Correlations of parties' share")
А теперь поменяем графики плотности на диагонали на гистограммы (при желании можно поменять на ящики с усами, вписав "boxplot"
):
# diagonal=list()
scatterplotMatrix(df[10:12], regLine = FALSE, smooth = TRUE,
var.labels = labs,
cex.labels = 1.3,
main = "Correlations of parties' share",
diagonal = list(method = "histogram"))
Ещё один вариант симпатичного графика для корреляций - разноцветный график, созданный с помощью пакета gclus
.
library(gclus)
## Warning: package 'gclus' was built under R version 3.5.2
# получим вектор коэффициентов корреляции (по модулю)
coeffs <- abs(cor(df[10:12]))
# зададим цвета (автоматическое разбиение по вектору коэффициентов)
colors <- dmat.color(coeffs)
# отсортируем так, чтобы графики, где связь переменных наибольшая,
# были ближе к диагонали
order <- order.single(coeffs)
# строим сам график
# gap - расстояние между графиками в матрице
cpairs(df[10:12], order, panel.colors = colors, gap = .5,
main = "Correlations of parties' shares" )
Для начала посмотрим на коэффициент корреляции между какими-нибудь двумя переменными:
cor(df$gov_left1, df$gov_right1)
## [1] -0.6594741
Если бы в одной из переменных были пропущенные значения (NA
), коэффициент корреляции бы не рассчитался. Тут можно действовать по аналогии с расчетом среднего значения:
# использовать всё, кроме NA (complete observations)
cor(df$gov_left1, df$gov_right1, use = "complete.obs")
## [1] -0.6594741
Как известно, существуют разные коэффициенты корреляции. Самые распространенные - линейный коэффициент корреляции Пирсона, коэффициент ранговой корреляции Спирмена и коэффициент ранговой корреляции Кендалла. По умолчанию считается коэффициент Пирсона, остальные можно получить, прописав дополнительный аргумент:
cor(df$gov_left1, df$gov_right1, method = "spearman") # коэфф. Спирмена
## [1] -0.6544615
Проверить значимость коэффициента корреляции — проверить нулевую гипотезу о том, что истинный коэффициент корреляции равен 0.
\[ H_0: r = 0 \text{ (связи нет)} \]
\[ H_1: r \ne 0 \text{ (связь есть)} \]
corr <- cor.test(df$gov_left1, df$gov_right1)
corr
##
## Pearson's product-moment correlation
##
## data: df$gov_left1 and df$gov_right1
## t = -9.0321, df = 106, p-value = 8.441e-15
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.7544286 -0.5374832
## sample estimates:
## cor
## -0.6594741
В выдаче R мы видим две важные вещи: значение коэффициента корреляции (sample estimates
) и pvalue
. В нашем случае p-value < 0.05, следовательно, на 5% уровне значимости есть основания отвергнуть нулевую гипотезу о равенстве коэффициента корреляции нулю. Раз эту гипотезу отвергаем, считаем, что коэффициент корреляции не 0, а следовательно, связь между процентом левых и правых партий действительно есть.
Выдача R представляет собой список (list
):
str(corr)
## List of 9
## $ statistic : Named num -9.03
## ..- attr(*, "names")= chr "t"
## $ parameter : Named int 106
## ..- attr(*, "names")= chr "df"
## $ p.value : num 8.44e-15
## $ estimate : Named num -0.659
## ..- attr(*, "names")= chr "cor"
## $ null.value : Named num 0
## ..- attr(*, "names")= chr "correlation"
## $ alternative: chr "two.sided"
## $ method : chr "Pearson's product-moment correlation"
## $ data.name : chr "df$gov_left1 and df$gov_right1"
## $ conf.int : num [1:2] -0.754 -0.537
## ..- attr(*, "conf.level")= num 0.95
## - attr(*, "class")= chr "htest"
А значит, из него можно вызывать отдельные элементы.
coeff <- corr$estimate # коэффициент
pvalue <- corr$p.value # p-value
coeff; pvalue
## cor
## -0.6594741
## [1] 8.440678e-15
Если хотим посмотреть на корреляцию «всего со всем», можем указать столбцы в базе (переменные) и получить корреляционную матрицу:
cor(df[10:12])
## gov_right1 gov_cent1 gov_left1
## gov_right1 1.0000000 -0.4397581 -0.6594741
## gov_cent1 -0.4397581 1.0000000 -0.2777804
## gov_left1 -0.6594741 -0.2777804 1.0000000
Для того, чтобы получить корреляционную матрицу и значимость коэффициентов в ней, нужно постараться. Загрузим библиотеку Hmisc
.
# install.packages("Hmisc")
library(Hmisc)
# Внимание: функция привередничает - требует матрицу, а не просто столбцы из базы
rcorr(as.matrix(df[10:12]))
## gov_right1 gov_cent1 gov_left1
## gov_right1 1.00 -0.44 -0.66
## gov_cent1 -0.44 1.00 -0.28
## gov_left1 -0.66 -0.28 1.00
##
## n= 108
##
##
## P
## gov_right1 gov_cent1 gov_left1
## gov_right1 0.0000 0.0000
## gov_cent1 0.0000 0.0036
## gov_left1 0.0000 0.0036
Но то, что мы увидели, немного не похоже на то, что хотелось бы показать другим. Единой таблички с коэффициентами и значимостью нет. Действительно, в R есть некоторые проблемы с корреляционными матрицами.
Воспользуемся уже написанной функцией, доступной по ссылке, и немного модифицируем её. Чтобы воспользоваться этой функцией, помимо уже установленного нами пакета Hmisc
потребуется библиотека xtable
. Как и stargazer
, она используется для выгрузки выдач R в html или LaTeX.
install.packages("xtable")
Скопируем код для функции с сайта в R-файл (New RScript
) и назовем его correlation.R
. Модифицируем код: в качестве аргумента функции corstars
добавим file
(про написание собственных функций поговорим позже).
corstars <-function(x, method=c("pearson", "spearman"), removeTriangle=c("upper", "lower"),
result=c("none", "html", "latex"), file)
А также допишем в строки 46 и 47 file = file
:
if(result[1]=="html") print(xtable(Rnew), type = "html", file = file)
else print(xtable(Rnew), type="latex", file = file)
Это нужно для того, чтобы R не просто выводил результат в консоль, но и сохранял код для таблички в отдельный файл.
Добавим также строку require(xtable)
, например, после require(Hmisc)
, иначе R не поймет, откуда брать функцию xtable
. Теперь сохраним все изменения в файле correlation.R
и загрузим его сюда:
source("correlation.R")
Теперь R знает, что из этого файла можно брать функции, и не будет ругаться, если встретит незнакомое (не встроенное в базовые библиотеки) название.
Финальный аккорд:
# сохраняем результат в файл corrtable.htm - можем открыть через браузер иди Word
corstars(x = df[10:12], method = "pearson", removeTriangle = "upper",
result = "html", file = "corrtable.htm")
А вот и итоговая таблица со звёздочками:
gov_right1 | gov_cent1 | |
---|---|---|
gov_right1 | ||
gov_cent1 | -0.44*** | |
gov_left1 | -0.71**** | -0.24* |
На этом всё.