Здесь приведён мой код на языке программирования R для проведения статистических расчётов по всем примерам из книги «Занимательная статистика. Манга», опубликованной издательством Додэка-XXI. Документ был подготовлен при помощи языка разметки R Markdown, который позволяет представлять в красивом виде код на языке R вместе с результатами его выполнения. Все числа округлены так же, как и в книге.
Я хочу поблагодарить Джеймса Чёрча (James Church) за разрешение использовать свой код в качестве отправной точки для моего варианта. Я практически полностью переработал его код на свой лад, но кое-какие элементы ещё остались. Его код к американскому изданию книги, содержащий только базовые функции R без использования дополнительных пакетов, опубликован на сайте издательства No Starch Press: www.nostarch.com/download/MangaGuideToStatistics.R.
В моём варианте кода я использовал три довольно популярных в последнее время пакета, не входящих в базовую поставку R:
%>%
(forward pipe) и %$%
(exposition pipe);Эти пакеты можно установить при помощи функции install.packages
или, например, при помощи графического интерфейса RStudio. Подключить же пакеты можно следующим образом:
library(magrittr)
library(dplyr)
library(ggplot2)
В этой главе содержится только теория без каких-либо статистических расчётов.
Стр. 33: Таблицу с ценами на лапшу рамэн можно представить в виде простого вектора (так в R называются одномерные массивы):
ramen.prices <- c(700, 850, 600, 650, 980, 750, 500, 890, 880, 700,
890, 720, 680, 650, 790, 670, 680, 900, 880, 720,
850, 700, 780, 850, 750, 780, 590, 650, 580, 750,
800, 550, 750, 700, 600, 800, 800, 880, 790, 790,
780, 600, 670, 680, 650, 890, 930, 650, 777, 700)
Стр. 38: Группировку данных по интервалам можно получить при помощи функции hist
(её можно использовать не только для графического изображения гистограммы). Попробуем сделать таблицу, аналогичную приведённой на стр. 38.
Названия столбцов в R всё-таки лучше задавать латинскими буквами. Для наглядности, перед выводом на экран я буду менять названия на русские и округлять числовые значения. Для этого будет служить функция Output:
# функция для смены названий столбцов и округления численных значений перед выводом на экран
Output <- function(df, newnames, digits = 1){
nums <- sapply(df, is.numeric)
df[nums] <- round(df[nums], digits)
names(df) <- newnames[names(df)]
return(df)
}
# функция для создания таблицы аналогичной приведённым на стр. 38 и 56
DistributionTable <- function(hist.data){
interval.lower <- hist.data$breaks[-length(hist.data$breaks)]
interval.upper <- hist.data$breaks[-1]
hist.table <- data.frame(
Interval = paste(interval.lower, "-", interval.upper),
Midpoint = hist.data$mids,
Frequency = hist.data$counts,
Rel.Frequency = hist.data$counts / sum(hist.data$counts)
)
return(hist.table)
}
ramen.hist <- hist(ramen.prices, breaks = 5, right = FALSE, plot = FALSE)
# используем вектор соответсвий названий столбцов для возможности лёгкого переименования
hist.table.rus <- c(Interval = "Интервал", Midpoint = "Середина интервала", Frequency = "Частота", Rel.Frequency = "Относительная частота")
DistributionTable(ramen.hist) %>% Output(hist.table.rus, 2)
Интервал | Середина интервала | Частота | Относительная частота |
---|---|---|---|
500 - 600 | 550 | 4 | 0.08 |
600 - 700 | 650 | 13 | 0.26 |
700 - 800 | 750 | 18 | 0.36 |
800 - 900 | 850 | 12 | 0.24 |
900 - 1000 | 950 | 3 | 0.06 |
Стр. 39: Гистограмма цен на рамэн построена при помощи функции ggplot
из пакета ggplot2. График был построен таким образом, чтобы он был похожим на книжный вариант.
ggplot(NULL, aes(x = ramen.prices)) + theme_bw() +
geom_histogram(binwidth = 100, colour = "black", fill = "gray") +
ggtitle("Гистограмма цен на рамэн 50-ти лучших ресторанов") +
xlab("Цена на рамэн") + ylab("Частота (количество ресторанов") +
coord_cartesian(xlim = c(490, 1010), ylim = c(0, 20)) +
scale_x_continuous(breaks = seq(550, 950, 100))
Стр. 41: Введём таблицу (фрейм) данных, содержащую результаты игры в боулинг для трёх команд:
bowling <- data.frame(
team.A = c(86, 73, 124, 111, 90, 38),
team.B = c(84, 71, 103, 85, 90, 89),
team.C = c(229, 77, 59, 95, 70, 88)
)
teams.rus <- c(team.A = "Команда А", team.B = "Команда Б", team.C = "Команда В")
bowling %>% Output(teams.rus)
Команда А | Команда Б | Команда В |
---|---|---|
86 | 84 | 229 |
73 | 71 | 77 |
124 | 103 | 59 |
111 | 85 | 95 |
90 | 90 | 70 |
38 | 89 | 88 |
Стр. 42: Средние значения можно рассчитать при помощи встроенной функции mean
. А функция summarise_each
из пакета dplyr позволяет применить какую-либо функцию для каждого столбца таблицы. Поэтому можно легко получить средние результаты сразу для всех трёх команд:
bowling %>% summarise_each(funs(mean)) %>% Output(teams.rus)
Команда А | Команда Б | Команда В |
---|---|---|
87 | 87 | 103 |
Стр. 43: В R нет встроенной функции для расчёта cредней геометрической, но её очень легко можно написать самим:
GeometricMean <- function(values) {
gmean <- prod(values) ^ (1 / length(values))
return(gmean)
}
bowling %>% summarise_each(funs(GeometricMean)) %>% Output(teams.rus, 2)
Команда А | Команда Б | Команда В |
---|---|---|
81.61 | 86.48 | 92.06 |
Стр. 43: Средние гармонические результатов игры в боулинг для трёх команд:
HarmonicMean <- function(values) {
hmean <- 1 / (sum(1 / values) / length(values))
return(hmean)
}
bowling %>% summarise_each(funs(HarmonicMean)) %>% Output(teams.rus, 2)
Команда А | Команда Б | Команда В |
---|---|---|
75.16 | 85.95 | 85.13 |
Стр. 45: Медианные значения результатов трёх команд (для расчёта медианы существует встроенная функция median
):
bowling %>% summarise_each(funs(median)) %>% Output(teams.rus)
Команда А | Команда Б | Команда В |
---|---|---|
88 | 87 | 82.5 |
Стр. 50: Стандартное отклонение (\(\sigma\)). Существует встроенная функция sd
для расчёта несмещённой оценки стандарного отклонения генеральной совокупности по данным выборки: \(\sigma = \sqrt{\frac{1}{N-1} \sum_{i=1}^N (x_i-\overline{x})^2}\) (см. стр. 52). Но опять же можно легко написать функцию для расчётов по формуле для генеральной совокупности \(\sigma = \sqrt{\frac{1}{N} \sum_{i=1}^N (x_i-\overline{x})^2}\):
StdDev <- function(values) {
m <- mean(values)
stddev <- sqrt(sum((values - m) ^ 2) / length(values))
return(stddev)
}
bowling %>% summarise_each(funs(StdDev)) %>% Output(teams.rus, 1)
Команда А | Команда Б | Команда В |
---|---|---|
27.5 | 9.5 | 57.5 |
Стр. 55: Количество интервалов группировки по правилу Стерджеса (используя встроенную функцию nclass.Sturges
) и величина интервала:
cat("Применим правило Стерджеса на данные о ценах на рамэн в 50-ти лучших ресторанах:\n")
nclass.Sturges(ramen.prices) %>% paste("Количество интервалов:", ., "\n") %>% cat()
BreakRange <- function(values) {
return(ceiling((max(values) - min(values)) / nclass.Sturges(values)))
}
BreakRange(ramen.prices) %>% paste("Величина интервала:", ., "\n") %>% cat()
Применим правило Стерджеса на данные о ценах на рамэн в 50-ти лучших ресторанах:
Количество интервалов: 7
Величина интервала: 69
Стр. 56: Таблица распределения цены на рамэн в 50-ти лучших ресторанах (группировка по правилу Стерджеса):
# вектор с границами интервалов группировки
ramen.breaks <- seq(min(ramen.prices), by = BreakRange(ramen.prices),
length.out = nclass.Sturges(ramen.prices) + 1)
ramen.breaks
[1] 500 569 638 707 776 845 914 983
ramen.histS <- hist(ramen.prices, breaks = ramen.breaks, right = FALSE, plot = FALSE)
DistributionTable(ramen.histS) %>% Output(hist.table.rus, 2)
Интервал | Середина интервала | Частота | Относительная частота |
---|---|---|---|
500 - 569 | 534.5 | 2 | 0.04 |
569 - 638 | 603.5 | 5 | 0.10 |
638 - 707 | 672.5 | 15 | 0.30 |
707 - 776 | 741.5 | 6 | 0.12 |
776 - 845 | 810.5 | 10 | 0.20 |
845 - 914 | 879.5 | 10 | 0.20 |
914 - 983 | 948.5 | 2 | 0.04 |
Стр. 57: Упражнение. Данные по результатам забега на 100 метров:
race.times <- c(16.3, 22.4, 18.5, 18.7, 20.1);
mean(race.times) %>% paste("Среднее значение:", ., "\n") %>% cat()
median(race.times) %>% paste("Медиана:", ., "\n") %>% cat()
StdDev(race.times) %>% round(2) %>% paste("Стандартное отклонение:", ., "\n") %>% cat()
Среднее значение: 19.2
Медиана: 18.7
Стандартное отклонение: 2.01
Стр. 61: Результаты школьного анкетирования по вопросу «Нравится ли вам новая форма?»:
school.uniform.survey <- factor(
c("Нравится", "Так себе", "Нравится", "Так себе", "Не нравится",
"Нравится", "Нравится", "Нравится", "Нравится", "Нравится",
"Нравится", "Нравится", "Так себе", "Нравится", "Нравится",
"Так себе", "Нравится", "Нравится", "Нравится", "Нравится",
"Нравится", "Нравится", "Не нравится", "Так себе", "Нравится",
"Нравится", "Не нравится", "Нравится", "Нравится", "Нравится",
"Так себе", "Так себе", "Нравится", "Не нравится", "Нравится",
"Нравится", "Нравится", "Нравится", "Так себе", "Нравится"))
Стр. 62: Частотную таблицу можно получить при помощи функции table
. Итоговые суммы можно добавить, используя функцию addmargins
:
school.uniform.table <- table(school.uniform.survey)
school.uniform.table %>% addmargins(FUN=list(`Итого` = sum))
Не нравится | Нравится | Так себе | Итого |
---|---|---|---|
4 | 28 | 8 | 40 |
Для получения процентной таблицы применим функцию prop.table
:
school.uniform.ptable <- prop.table(school.uniform.table) * 100
school.uniform.ptable %>% addmargins(FUN=list(`Итого` = sum))
Не нравится | Нравится | Так себе | Итого |
---|---|---|---|
10 | 70 | 20 | 100 |
Пользуясь этими функциями, можно сделать таблицу, аналогичную таблице на стр. 62:
CrossTab <- function(fact){
tab <- addmargins(table(fact), FUN=list(`Итого` = sum))
ptab <- addmargins(prop.table(table(fact)), FUN=list(`Итого` = sum))
ctab <- data.frame(Response = unlist(unname(dimnames(tab))),
Frequency = as.vector(tab),
Procent = as.vector(ptab) * 100)
return(ctab)
}
ctab.rus <- c(Response = "Вариант ответа", Frequency = "Количество", Procent = "Процент")
CrossTab(school.uniform.survey) %>% Output(ctab.rus)
Вариант ответа | Количество | Процент |
---|---|---|
Не нравится | 4 | 10 |
Нравится | 28 | 70 |
Так себе | 8 | 20 |
Итого | 40 | 100 |
Графическое представление качественной переменной (гистограмма):
ggplot(NULL, aes(x = school.uniform.survey)) + theme_bw() +
geom_bar(colour = "black", fill = "gray") +
ggtitle("Результаты анкетирования о новой школьной форме") +
xlab("Ответ") + ylab("Частота") +
coord_cartesian(ylim = c(0, 30))
Стр. 64: Упражнение. Какая из двух политических партий победит на следующих выборах?
party.victory <- factor(c("Победит Б", "Победит Б", "Победит Б", "Не знаю", "Победит А", "Победит Б", "Победит А", "Не знаю", "Победит Б", "Победит Б"))
CrossTab(party.victory) %>% Output(ctab.rus)
Вариант ответа | Количество | Процент |
---|---|---|
Не знаю | 2 | 20 |
Победит А | 2 | 20 |
Победит Б | 6 | 60 |
Итого | 10 | 100 |
Стр. 67-69: Результаты тестов в баллах по четырём школьным предметам:
RUS.LETTERS <- c("А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "К", "Л",
"М", "Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц",
"Ч", "Ш", "Щ", "Ы", "Э", "Ю", "Я")
test.scores <- data.frame(
Student = c("Руи", "Юми", RUS.LETTERS[1:16]),
English = c(90, 81, 73, 97, 85, 60, 74, 64, 72, 67, 87, 78, 85, 96, 77 ,100 ,92 ,86),
Literature = c(71, 90, 79, 70, 67, 66, 60, 83, 57, 85, 93, 89, 78, 74, 65, 78, 53, 80),
History = c(73, 61, 14, 41, 49, 87, 69, 65, 36, 7, 53, 100, 57, 45, 56, 34, 37, 70),
Biology = c(59, 73, 47, 38, 63, 56, 15, 53, 80, 50, 41, 62, 44, 26, 91, 35, 53, 68)
)
# вектор для переименования названий столбцов на русский перед выводом на экран
test.rus <- c(Student = "Ученик", English = "Английский язык", Literature = "Литература",
History = "История", Biology = "Биология")
test.scores %>% Output(test.rus)
Ученик | Английский язык | Литература | История | Биология |
---|---|---|---|---|
Руи | 90 | 71 | 73 | 59 |
Юми | 81 | 90 | 61 | 73 |
А | 73 | 79 | 14 | 47 |
Б | 97 | 70 | 41 | 38 |
В | 85 | 67 | 49 | 63 |
Г | 60 | 66 | 87 | 56 |
Д | 74 | 60 | 69 | 15 |
Е | 64 | 83 | 65 | 53 |
Ж | 72 | 57 | 36 | 80 |
З | 67 | 85 | 7 | 50 |
И | 87 | 93 | 53 | 41 |
К | 78 | 89 | 100 | 62 |
Л | 85 | 78 | 57 | 44 |
М | 96 | 74 | 45 | 26 |
Н | 77 | 65 | 56 | 91 |
О | 100 | 78 | 34 | 35 |
П | 92 | 53 | 37 | 53 |
Р | 86 | 80 | 70 | 68 |
Стр. 68: Средний балл по каждому предмету можно рассчитать при помощи функции summarise_each
, отбрасывая при этом столбец с именами учеников (-Student
):
test.scores %>% summarise_each(funs(mean), -Student) %>% Output(test.rus)
Английский язык | Литература | История | Биология |
---|---|---|---|
81.3 | 74.3 | 53 | 53 |
Стр. 72: Для нормирования вектора значений применяется функция scale
, но по умолчанию она использует выборочное стандарное отклонение (со знаменателем \(n-1\)), поэтому в данном случае придётся поменять параметр scale:
Normalize <- function(values) {
norm.v <- scale(values, scale = StdDev(values))
return(norm.v)
}
# Normalize each column except Student
test.scores %>% mutate_each(funs(Normalize), -Student) %>% Output(test.rus, 2)
Ученик | Английский язык | Литература | История | Биология |
---|---|---|---|---|
Руи | 0.77 | -0.30 | 0.88 | 0.33 |
Юми | -0.03 | 1.39 | 0.35 | 1.09 |
А | -0.74 | 0.41 | -1.71 | -0.33 |
Б | 1.39 | -0.39 | -0.53 | -0.82 |
В | 0.33 | -0.65 | -0.18 | 0.55 |
Г | -1.90 | -0.74 | 1.49 | 0.16 |
Д | -0.65 | -1.27 | 0.70 | -2.08 |
Е | -1.54 | 0.77 | 0.53 | 0.00 |
Ж | -0.83 | -1.54 | -0.75 | 1.48 |
З | -1.27 | 0.95 | -2.02 | -0.16 |
И | 0.50 | 1.66 | 0.00 | -0.66 |
К | -0.30 | 1.30 | 2.07 | 0.49 |
Л | 0.33 | 0.33 | 0.18 | -0.49 |
М | 1.30 | -0.03 | -0.35 | -1.48 |
Н | -0.39 | -0.83 | 0.13 | 2.08 |
О | 1.66 | 0.33 | -0.84 | -0.98 |
П | 0.95 | -1.90 | -0.70 | 0.00 |
Р | 0.41 | 0.50 | 0.75 | 0.82 |
Стр. 74: Рейтинг успеваемости:
DeviationScore <- function(values){
return(Normalize(values) * 10 + 50)
}
test.scores %>% mutate_each(funs(DeviationScore), -Student) %>% Output(test.rus)
Ученик | Английский язык | Литература | История | Биология |
---|---|---|---|---|
Руи | 57.7 | 47.0 | 58.8 | 53.3 |
Юми | 49.7 | 63.9 | 53.5 | 60.9 |
А | 42.6 | 54.1 | 32.9 | 46.7 |
Б | 63.9 | 46.1 | 44.7 | 41.8 |
В | 53.3 | 43.5 | 48.2 | 55.5 |
Г | 31.0 | 42.6 | 64.9 | 51.6 |
Д | 43.5 | 37.3 | 57.0 | 29.2 |
Е | 34.6 | 57.7 | 55.3 | 50.0 |
Ж | 41.7 | 34.6 | 42.5 | 64.8 |
З | 37.3 | 59.5 | 29.8 | 48.4 |
И | 55.0 | 66.6 | 50.0 | 43.4 |
К | 47.0 | 63.0 | 70.7 | 54.9 |
Л | 53.3 | 53.3 | 51.8 | 45.1 |
М | 63.0 | 49.7 | 46.5 | 35.2 |
Н | 46.1 | 41.7 | 51.3 | 70.8 |
О | 66.6 | 53.3 | 41.6 | 40.2 |
П | 59.5 | 31.0 | 43.0 | 50.0 |
Р | 54.1 | 55.0 | 57.5 | 58.2 |
Стр. 78: Упражнение. Среднее значение и стандартное отклонение, рассчитанные по нормированным отклонениям результатов забега на 100 метров:
mean(Normalize(race.times)) %>% round(4) %>% paste("Среднее значение:", ., "\n") %>% cat()
Среднее значение: 0
StdDev(Normalize(race.times)) %>% paste("Стандартное отклонение:", ., "\n") %>% cat()
Стандартное отклонение: 1
Стр. 93: Найдём вероятность того, что случайная величина, распределённая по стандартному нормальному закону попадёт в интервал от \(0\) до \(1.96\). В таблице стандартного нормального распределения из книги затабулирована площадь под кривой от \(0\) до \(x\), а функция pnorm
в R по умолчанию использует интервал от \(-\infty\) до \(x\). Чтобы получить правильный ответ можно просто вычесть pnorm(0)
из pnorm(1.96)
.
(pnorm(1.96) - pnorm(0)) %>% round(4)
[1] 0.475
Стр. 97: Необходимо найти площадь под стандартной нормальной кривой от \(1.8\) to \(\infty\). Чтобы использовать интервал от \(x\) до \(\infty\) нужно использовать параметр lower.tail = FALSE
.
pnorm(1.8, lower.tail = FALSE) %>% round(4)
[1] 0.0359
Стр. 104: Найдём \(\chi^2\)-значение для соответсвующей вероятности \(0.05\) с \(1\) степенью свободы. Функция qchisq
по умолчанию возвращает такое значение \(q\), для которого \(P(X < q)=p\). А таблица, приведённая в книге подразумевает \(P(X \geq q)=p\). Поэтому опять необходимо использовать параметр lower.tail = FALSE
:
qchisq(0.05, df = 1, lower.tail = FALSE) %>% round(4)
[1] 3.8415
Стр. 108: Упражнение. Найдём площадь под стандартной нормальной кривой в интервале от \(-\infty\) до \(-0.29\):
pnorm(-0.29) %>% round(4)
[1] 0.3859
Стр. 108: Упражнение. Найдём величину \(\chi^2\) для вероятности \(P=0.05\) и \(2\) степенями свободы:
qchisq(0.05, df = 2, lower.tail = FALSE) %>% round(4)
[1] 5.9915
Стр. 116: Результаты анкетирования о расходах на косметику и одежду из журнала P.girls:
street.survey <- data.frame(
Respondent = c(RUS.LETTERS[1:10]),
Makeup = c(3000, 5000, 12000, 2000, 7000, 15000, 5000, 6000, 8000, 10000),
Clothes = c(7000, 8000, 25000, 5000, 12000, 30000, 10000, 15000, 20000, 18000)
)
street.rus <- c(Respondent = "Респондент", Makeup = "Расходы на косметику, йены", Clothes = "Расходы на одежду, йены")
street.survey %>% Output(street.rus)
Респондент | Расходы на косметику, йены | Расходы на одежду, йены |
---|---|---|
А | 3000 | 7000 |
Б | 5000 | 8000 |
В | 12000 | 25000 |
Г | 2000 | 5000 |
Д | 7000 | 12000 |
Е | 15000 | 30000 |
Ж | 5000 | 10000 |
З | 6000 | 15000 |
И | 8000 | 20000 |
К | 10000 | 18000 |
Стр. 116: Точечная диаграмма результатов анкетирования (при помощи пакета ggplot2):
ggplot(street.survey, aes(Makeup, Clothes)) + geom_point(size=3) + theme_bw() +
ggtitle("Точечная диаграмма расходов на косметику и одежду (в месяц)") +
xlab("Расходы на косметику (¥)") + ylab("Расходы на одежду (¥)") +
coord_cartesian(xlim = c(0, 30000), ylim = c(0, 32000))
Стр. 118: Коэффициент линейной корреляции между расходами на косметику и расходами на одежду можно вычислить, используя функцию cor
:
street.survey %$% cor(Makeup, Clothes) %>% round(4)
[1] 0.968
Стр. 121: Результаты анкетирования «Возраст и любимый бренд одежды»:
brand.survey <- data.frame(
Respondent = c(RUS.LETTERS[1:15]),
Age = c(27, 33, 16, 29, 32, 23, 25, 28, 22, 18, 26, 26, 15, 29, 26),
Brand = as.factor(c("Benetton", "Zara", "O'STIN", "O'STIN", "Zara",
"Benetton", "Zara", "Benetton", "O'STIN", "O'STIN",
"Zara", "Benetton", "O'STIN", "Zara", "O'STIN"))
)
brand.rus <- c(Respondent = "Респондент", Age = "Возраст", Brand = "Бренд")
brand.survey %>% Output(brand.rus)
Респондент | Возраст | Бренд |
---|---|---|
А | 27 | Benetton |
Б | 33 | Zara |
В | 16 | O’STIN |
Г | 29 | O’STIN |
Д | 32 | Zara |
Е | 23 | Benetton |
Ж | 25 | Zara |
З | 28 | Benetton |
И | 22 | O’STIN |
К | 18 | O’STIN |
Л | 26 | Zara |
М | 26 | Benetton |
Н | 15 | O’STIN |
О | 29 | Zara |
П | 26 | O’STIN |
Стр. 122: Точечная диаграмма по результатам опроса:
ggplot(brand.survey, aes(Brand, Age)) + geom_point(size=3) +
theme_bw() + ggtitle("Точечная диаграмма «Любимый бренд одежды» и «Возраст»") +
xlab("Предпочитаемый бренд одежды") + ylab("Возраст")
Стр. 123-124: Вычислим корреляционное отношение между возрастом девушек и предпочитаемым брендом одежды. В R нет встроенной функции для расчёта корреляционного отношения, так что выполним все вычисления по формуле. Здесь хорошо проявляются преимущества пакетов dplyr и magrittr, при помощи которых для вычисления внутригрупповой и межгрупповой дисперсий потребуется буквально по одной строчке кода:
# Внутригрупповая дисперсия
intraclass.var <- brand.survey %>% group_by(Brand) %>%
summarise(S = sum((Age - mean(Age))^2)) %>%
summarise(sum(S)) %>% as.numeric()
# Средний возраст респондентов
age.mean <- brand.survey %>% summarise(mean(Age))
# Межгрупповая дисперсия
interclass.var <- brand.survey %>% group_by(Brand) %>%
summarise(S = n()*(mean(Age) - age.mean)^2) %>%
summarise(sum(S)) %>% as.numeric()
# Корреляционное отношение
cor.ratio <- interclass.var / (intraclass.var + interclass.var)
Осталось только вывести результаты вычислений:
intraclass.var %>% round(4) %>% paste("Внутригрупповая дисперсия:", ., "\n") %>% cat()
interclass.var %>% round(4) %>% paste("Межгрупповая дисперсия:", ., "\n") %>% cat()
cor.ratio %>% round(4) %>% paste("Корреляционное отношение:", ., "\n") %>% cat()
Внутригрупповая дисперсия: 224
Межгрупповая дисперсия: 180
Корреляционное отношение: 0.4455
Стр. 128: Таблица взаимной сопряжённости, полученная по результатам опроса 300 школьников о предпочитаемом способе признания в любви:
asked.out.table <- matrix(c(34, 61, 53, 38, 40, 74), byrow=T, nrow=2)
dimnames(asked.out.table) = list(Sex=c("Женский", "Мужской"),
DesiredWay = c("По телефону", "По SMS", "При встрече"))
asked.out.table %>% addmargins(FUN=list(`Итого` = sum), quiet = TRUE)
По телефону | По SMS | При встрече | Итого | |
---|---|---|---|---|
Женский | 34 | 61 | 53 | 148 |
Мужской | 38 | 40 | 74 | 152 |
Итого | 72 | 101 | 127 | 300 |
Стр. 132: Критерий согласия Пирсона (\(\chi^2_0\)), применяемый для расчёта коэффициента корреляции Крамера, можно вычислить при помощи функции chisq.test
:
asked.out.chisq <- chisq.test(asked.out.table)$statistic
asked.out.chisq %>% round(4)
X-squared
8.0091
Стр. 133: Осталось лишь определить величину самого коэффициента корреляции Крамера:
Cramers <- function(ctable) {
chi2 <- as.numeric(chisq.test(ctable)$statistic)
s <- sum(ctable)
d <- min(dim(ctable))
return(sqrt(chi2 / (s * (d - 1))))
}
Cramers(asked.out.table) %>% round(4)
[1] 0.1634
Стр. 138: Упражнение. Вычислим коэффициент корреляции Крамера, чтобы установить взаимосвязь между «обычно заказываемой кухней» и «предпочитаемым напитком»:
drink.table <- matrix(c(43, 33, 51, 53, 29, 41), byrow=T, nrow=3)
dimnames(drink.table) = list(FoodType=c("Японская", "Европейская", "Китайская"),
Preference = c("Кофе", "Чай"))
drink.table %>% addmargins(FUN=list(`Итого` = sum), quiet = TRUE)
Кофе | Чай | Итого | |
---|---|---|---|
Японская | 43 | 33 | 76 |
Европейская | 51 | 53 | 104 |
Китайская | 29 | 41 | 70 |
Итого | 123 | 127 | 250 |
Стр. 141: Упражнение. Рассчитаем значение критерия согласия Пирсона \(\chi^2_0\):
chisq.test(drink.table)$statistic %>% round(4)
X-squared
3.3483
Стр. 141: Упражнение. Определим коэффициент корреляции Крамера:
Cramers(drink.table) %>% round(4)
[1] 0.1157
Попробуем провести эксперимент, описанный на стр. 152-153 и касающийся критерия согласия Пирсона (\(\chi^2_0\)) и распределения \(\chi^2\):
Стр. 153: Предполагаемая таблица взаимной сопряжённости о предпочитаемом способе признания в любви для всех школьников Японии:
asked.out.table.all <- as.table(matrix(c(400, 1600, 2000, 600, 2400, 3000),
byrow=T, nrow=2))
dimnames(asked.out.table.all) = list(Sex=c("Женский", "Мужской"),
DesiredWay = c("По телефону", "По SMS", "При встрече"))
asked.out.table.all %>% addmargins(FUN=list(`Итого` = sum), quiet = TRUE)
По телефону | По SMS | При встрече | Итого | |
---|---|---|---|---|
Женский | 400 | 1600 | 2000 | 4000 |
Мужской | 600 | 2400 | 3000 | 6000 |
Итого | 1000 | 4000 | 5000 | 10000 |
Сначала сделаем таблицу с частотами всех возможных комбинаций заначений двух признаков:
asked.out.freq <- as.data.frame(asked.out.table.all)
asked.out.rus <- c(Sex = "Пол респондента", DesiredWay = "Способ признания в любви",
Freq = "Частота")
asked.out.freq %>% Output(asked.out.rus)
Пол респондента | Способ признания в любви | Частота |
---|---|---|
Женский | По телефону | 400 |
Мужской | По телефону | 600 |
Женский | По SMS | 1600 |
Мужской | По SMS | 2400 |
Женский | При встрече | 2000 |
Мужской | При встрече | 3000 |
Теперь, чтобы получить таблицу по всем 10000 школьникам (аналогичную таблице 7.1. на стр. 153) необходимо «повторить» все строки с соответсвующей частотой:
FreqToCases <- function(row) {
n <- row$Freq
df <- data.frame(
Sex = rep(row$Sex, n),
DesiredWay = rep(row$DesiredWay, n)
)
return(df)
}
# Вызываем функцию FreqToCases для размножения каждой строки
# таблицы частот asked.out.freq и "склеиваем" результаты
# в одну большую таблицу на 10000 строк
asked.out.data <- asked.out.freq %>% rowwise() %>% do(FreqToCases(.))
Теперь нужно получить 20000 различных случайных выборок по 300 школьников и рассчитать для каждой из них величину критерия согласия Пирсона \(\chi^2_0\):
chisq.data <- data.frame(Sample = 1:20000)
SampleChiSq <- function(n) {
# случайная выборка из n школьников
asked.out.sample <- sample_n(asked.out.data, n)
# таблица взаимной сопряжённости для этой выборки
asked.out.sample.table <- table(asked.out.sample)
# возвращаем величину критерия согласия Пирсона
return(chisq.test(asked.out.sample.table)$statistic)
}
# Векторизируем функцию, чтобы не нужно было вызывать функцию отдельно для каждой из 20000 выборок
SampleChiSq %<>% Vectorize()
# Вычисляем 20000 критериев согласия Пирсона по различным выборкам из 300 школьников
chisq.data$ChiSq <- SampleChiSq(rep(300, 20000))
# Выведем первые 10 строк полученной таблицы
chisq.rus <- c(Sample = "Выборка", ChiSq = "Критерий согласия Пирсона")
chisq.data %>% head(10) %>% Output(chisq.rus, 5)
Выборка | Критерий согласия Пирсона |
---|---|
1 | 1.62330 |
2 | 2.89592 |
3 | 1.13590 |
4 | 1.06070 |
5 | 2.53846 |
6 | 0.39047 |
7 | 0.70536 |
8 | 0.60186 |
9 | 0.11794 |
10 | 0.58609 |
Стр. 154: Гистограмма распределения полученных 20000 значений \(\chi^2_0\) по 20000 различным случайным выборкам (величина интервала равна 0.5 для большей наглядности):
# название графика с математическами аннотациями
title <- expression(paste("Гистограмма распределения значений ", chi[0]^2, " по данным 20000 выборок"))
# рисуем график при помощи пакета ggplot2
chi.plot1 <- ggplot(chisq.data, aes(x = ChiSq)) + theme_bw() +
geom_histogram(aes(y = ..density..), binwidth = 0.5, colour = "black", fill = "gray") +
xlab(expression(paste(chi[0]^2, "-значение"))) + ylab("Плотность распределения") +
ggtitle(title) + coord_cartesian(xlim = c(0, 15), ylim = c(0, 0.52))
chi.plot1
Стр. 158: Критическое значение \(\chi^2_{крит}\) для уровня значимости \(0.05\) и двумя степенями свободы:
chi.crit <- qchisq(0.05, df = 2, lower.tail = FALSE)
chi.crit
[1] 5.991465
Стр. 159: Добавим график плотности вероятности \(\chi^2\)-распределения к полученной гистограмме и покажем критическую область:
# название графика с математическами аннотациями
title <- expression(atop(paste("Гистограмма распределения значений ", chi[0]^2, " по данным 20000 выборок"),
paste("и график ", chi^2, "-распределения с 2 степенями свободы")))
# добавляем элементы к графику chi.plot1
chi.plot2 <- chi.plot1 + ggtitle(title) +
stat_function(fun = dchisq, args = list(df=2), colour = "blue") +
geom_vline(aes(xintercept = chi.crit), colour = "red", linetype = "dashed") +
annotate("text", label = "{chi[крит]^2}(alpha == 0.05)", parse = TRUE, x = chi.crit + 2,
y = 0.07, color = "red", size = 6)
chi.plot2
Гистограмма распределения практически совпадает с графиком \(\chi^2\)-распределения, так как коэффициент корреляции Крамера равен нулю:
Cramers(asked.out.table.all) %>% cat()
0
Посмотрим, для какого числа выборок значение \(\chi^2_0\) оказалось больше или равно \(\chi^2_{крит}\):
chisq.data %>% filter(ChiSq >= chi.crit) %>% nrow()
[1] 897
Это составляет 4.5% от всех 20000 случайных выборок, т.е. около 5%.
Стр. 177: P-значение:
chisq.test(asked.out.table)$p.value %>% round(4)
[1] 0.0182
Или можно получить сразу полную сводку по критерию согласия Пирсона:
chisq.test(asked.out.table)
Pearson's Chi-squared test
data: asked.out.table
X-squared = 8.0091, df = 2, p-value = 0.01823
Стр. 188: Упражнение. Проверить гипотезу о независимости по данным анкетирования об обычно заказываемой кухне и предпочитаемым напитком:
chisq.test(drink.table)
Pearson's Chi-squared test
data: drink.table
X-squared = 3.3483, df = 2, p-value = 0.1875
qchisq(0.01, df = 2, lower.tail = FALSE) %>% round(4) %>%
paste("Критическое χ²-значение:", ., "\n") %>% cat()
Критическое χ²-значение: 9.2103