Загружаем необходимые пакеты, говорим knitr’у, что надо кэшировать куски для ускорения работы, и устанавливаем чёрно-белую тему оформления графиков:
library("knitr") # создание отчётов
library("reshape2") # структуризация данных
library("ggplot2") # графики
library("vcd") # графики для качественных переменных
# opts_chunk$set(cache=TRUE)
# theme_set(theme_gray())
theme_set(theme_bw())
Загружаем данные по стоимости квартир в Москве и проверяем, что они загрузились:
filename <- "../datasets/flats_moscow.txt"
flats <- read.table(filename, header = TRUE)
str(flats)
## 'data.frame': 2040 obs. of 11 variables:
## $ n : int 1 2 3 4 5 6 7 8 9 10 ...
## $ price : int 81 75 128 95 330 137 98 88 225 140 ...
## $ totsp : int 58 44 70 61 104 76 59 55 80 86 ...
## $ livesp : int 40 28 42 37 60 50 39 36 56 51 ...
## $ kitsp : num 6 6 6 6 11 9 6 6 9 10 ...
## $ dist : num 12.5 13.5 14.5 13.5 10.5 11 7.5 9 9 12.7 ...
## $ metrdist: int 7 7 3 7 7 7 10 5 5 10 ...
## $ walk : int 1 1 1 1 0 1 0 1 1 1 ...
## $ brick : int 1 0 1 0 1 1 0 1 1 0 ...
## $ floor : int 1 1 1 1 1 1 1 0 1 1 ...
## $ code : int 3 6 3 1 3 8 8 4 3 5 ...
Оцениваем logit модель, полином 5-ой степени, \(P(y_i=1)=F(\beta_1+\beta_2 x_i+\ldots+\beta_6 x_i^5)\)
m1 <- glm(brick ~ poly(price, 5), data = flats, family = binomial)
summary(m1)
##
## Call:
## glm(formula = brick ~ poly(price, 5), family = binomial, data = flats)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.9784 -0.7769 -0.6932 1.0781 1.7766
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.78768 0.05063 -15.558 < 2e-16 ***
## poly(price, 5)1 25.97300 3.11629 8.335 < 2e-16 ***
## poly(price, 5)2 -9.29670 5.66507 -1.641 0.1008
## poly(price, 5)3 5.71829 5.71520 1.001 0.3170
## poly(price, 5)4 6.05547 3.21044 1.886 0.0593 .
## poly(price, 5)5 -13.74891 2.67952 -5.131 2.88e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 2566.9 on 2039 degrees of freedom
## Residual deviance: 2354.5 on 2034 degrees of freedom
## AIC: 2366.5
##
## Number of Fisher Scoring iterations: 6
Строим графики:
ggplot(data = flats, aes(x = price, y = brick)) +
geom_point(position = position_jitter(height = 0.05), alpha = 0.2) +
stat_smooth(method.args = list(family = "binomial"),
method = "glm",
formula = y ~ poly(x, 4)) +
labs(x = "Цена квартиры, $1000",
y = "Кирпичный дом или нет",
title = "График logit модели")
Две гистограммки:
ggplot(data = flats, aes(x = price, fill = factor(brick))) + geom_density(alpha = 0.5) +
scale_fill_discrete(name = "Материал",
breaks = c("1", "0"),
labels = c("Кирпич", "Другой")) +
labs(x = "Цена квартиры, $1000",
y = "Плотность",
title = "Цены квартир в кирпичных и не кирпичных домах")
Виолончельки:
ggplot(data = flats, aes(y = price, x = factor(brick))) + geom_violin() +
scale_x_discrete(labels = c("Другой", "Кирпич")) +
labs(x = "Материал",
y = "Цена квартиры, $1000",
title = "Цены квартир в кирпичных и не кирпичных домах")
Прогнозируем вероятности по нашей модели. Сравниваем их с настоящими \(y_i\)
flats$prob.brick <- fitted(m1)
head(flats[c("brick", "prob.brick")])
## brick prob.brick
## 1 1 0.2225129
## 2 0 0.2444560
## 3 1 0.2960600
## 4 0 0.2065029
## 5 1 0.5002794
## 6 1 0.3450575
Создаём функцию, которая по порогу считает количество true positive, true negative и т.д.
tfnp <- function(cut = 0.5, y.true, prob) {
y.pred <- (prob > cut)
tp <- sum((y.true == 1) & (y.pred == 1))
tn <- sum((y.true == 0) & (y.pred == 0))
fp <- sum((y.true == 0) & (y.pred == 1))
fn <- sum((y.true == 1) & (y.pred == 0))
return(c(tp, tn, fp, fn))
}
tfnp(0.5, flats$brick, flats$prob.brick) # проверяем
## [1] 185 1275 106 474
Для разных порогов считаем специфичность и чувствительность
t <- data.frame(cuts = seq(0,1, len = 33), tp = 0, tn = 0, fp = 0, fn = 0)
for (i in 1:nrow(t)) {
t[i, 2:5] <- tfnp(t$cuts[i], flats$brick, flats$prob.brick)
}
t$spec <- t$tn / (t$tn + t$fp)
t$sens <- t$tp / (t$tp + t$fn)
t$correct <- (t$tp + t$tn) / (t$tp + t$tn + t$fn + t$fp)
head(t)
## cuts tp tn fp fn spec sens correct
## 1 0.00000 659 0 1381 0 0 1 0.3230392
## 2 0.03125 659 0 1381 0 0 1 0.3230392
## 3 0.06250 659 0 1381 0 0 1 0.3230392
## 4 0.09375 659 0 1381 0 0 1 0.3230392
## 5 0.12500 659 0 1381 0 0 1 0.3230392
## 6 0.15625 659 0 1381 0 0 1 0.3230392
Зависимость специфичности от порога
matplot(t$cuts, cbind(t$spec, t$sens, t$correct), type = "l")
molten.t <- melt(t, id.vars = "cuts", measure.vars = c("spec", "sens", "correct"))
ggplot(data = molten.t, aes(x = cuts, y = value, color = variable)) + geom_line() +
labs(title = "График зависимости специфичности от порога")
ggplot(data = t, aes(x = 1 - spec, y = sens)) + geom_line()
Красивый график для качественных переменных:
mosaic(data = flats, ~ brick + walk + floor, shade = TRUE)