Задание: Сформируйте собственный датасет с помощью
функции c(), содержащий числовые данные и NA
значения.
# c() — "concatenate", склеивает элементы в вектор
# NA — специальное значение "данных нет" (Not Available)
my_vector <- c(10, 25, NA, 47, NA, 63, 8, NA, 99, 12)
print(my_vector)## [1] 10 25 NA 47 NA 63 8 NA 99 12
## Количество элементов: 10
## Min. 1st Qu. Median Mean 3rd Qu. Max. NAs
## 8.00 11.00 25.00 37.71 55.00 99.00 3
is.na()Задание: Проведите очистку данных с использованием
функции is.na() и выведите “чистый” датасет.
## [1] FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE
# ! означает "НЕ". Так берём только те элементы, где НЕ NA
clean_vector <- my_vector[!is.na(my_vector)]
cat("=== Исходный вектор (с пропусками) ===\n")## === Исходный вектор (с пропусками) ===
## [1] 10 25 NA 47 NA 63 8 NA 99 12
##
## === Чистый вектор (без NA) ===
## [1] 10 25 47 63 8 99 12
##
## Было элементов: 10
##
## Стало элементов: 7
##
## Удалено NA: 3
complete.cases()Задание: Сгенерируйте таблицу данных с числовыми и
текстовыми столбцами. Очистите данные с помощью
complete.cases().
# data.frame() создаёт таблицу — основной формат данных в R
df_raw <- data.frame(
name = c("Alex", "Maria", NA, "Ivan", "Olga", NA, "Petr"),
age = c(25, NA, 30, 45, NA, 28, 33),
salary = c(50000, 60000, NA, 80000, 55000, NA, 70000)
)
cat("=== Исходная таблица ===\n")## === Исходная таблица ===
## name age salary
## 1 Alex 25 50000
## 2 Maria NA 60000
## 3 <NA> 30 NA
## 4 Ivan 45 80000
## 5 Olga NA 55000
## 6 <NA> 28 NA
## 7 Petr 33 70000
# complete.cases() возвращает TRUE для строк, где ВСЕ столбцы заполнены (нет ни одного NA)
complete_mask <- complete.cases(df_raw)
cat("\n=== Маска полных строк (TRUE = строка без NA) ===\n")##
## === Маска полных строк (TRUE = строка без NA) ===
## [1] TRUE FALSE FALSE TRUE FALSE FALSE TRUE
##
## === Чистая таблица ===
## name age salary
## 1 Alex 25 50000
## 4 Ivan 45 80000
## 7 Petr 33 70000
##
## Исходно строк: 7
##
## После очистки: 3
preProcess (пакет caret)Задание: Проанализируйте датасет
airquality с пропусками. Заполните пропуски предсказанными
значениями (среднее, медиана) с помощью preProcess из
пакета caret.
# install.packages("caret") # раскомментируй если пакет не установлен
library(caret)
# airquality — встроенный датасет R, данные о качестве воздуха в Нью-Йорке
# Содержит пропуски в столбцах Ozone и Solar.R
cat("=== Первые 10 строк airquality ===\n")## === Первые 10 строк airquality ===
## Ozone Solar.R Wind Temp Month Day
## 1 41 190 7.4 67 5 1
## 2 36 118 8.0 72 5 2
## 3 12 149 12.6 74 5 3
## 4 18 313 11.5 62 5 4
## 5 NA NA 14.3 56 5 5
## 6 28 NA 14.9 66 5 6
## 7 23 299 8.6 65 5 7
## 8 19 99 13.8 59 5 8
## 9 8 19 20.1 61 5 9
## 10 NA 194 8.6 69 5 10
##
## === Количество NA по каждому столбцу ===
## Ozone Solar.R Wind Temp Month Day
## 37 7 0 0 0 0
# --- Заполнение МЕДИАНОЙ ---
# preProcess() создаёт объект обработки, который запоминает параметры трансформации
# method = "medianImpute" — заполнить пропуски медианой (устойчивее к выбросам, чем среднее)
prep_median <- preProcess(airquality, method = "medianImpute")
# predict() применяет объект к данным и возвращает заполненный датафрейм
data_median <- predict(prep_median, airquality)
cat("\n=== После заполнения МЕДИАНОЙ — NA остались? ===\n")##
## === После заполнения МЕДИАНОЙ — NA остались? ===
## Ozone Solar.R Wind Temp Month Day
## 0 0 0 0 0 0
# --- Заполнение СРЕДНИМ (вручную) ---
# caret не имеет отдельного "meanImpute", поэтому делаем сами
data_mean <- airquality
for (col in names(data_mean)) {
if (is.numeric(data_mean[[col]])) {
col_mean <- mean(data_mean[[col]], na.rm = TRUE) # na.rm=TRUE — игнорируем NA при расчёте
data_mean[[col]][is.na(data_mean[[col]])] <- col_mean
}
}
cat("\n=== После заполнения СРЕДНИМ — NA остались? ===\n")##
## === После заполнения СРЕДНИМ — NA остались? ===
## Ozone Solar.R Wind Temp Month Day
## 0 0 0 0 0 0
# Сравниваем результаты на первых 10 строках столбца Ozone
cat("\n=== Сравнение значений Ozone (первые 10 строк) ===\n")##
## === Сравнение значений Ozone (первые 10 строк) ===
comparison <- data.frame(
Original = airquality$Ozone[1:10],
Median = data_median$Ozone[1:10],
Mean = round(data_mean$Ozone[1:10], 1)
)
print(comparison)## Original Median Mean
## 1 41 41.0 41.0
## 2 36 36.0 36.0
## 3 12 12.0 12.0
## 4 18 18.0 18.0
## 5 NA 31.5 42.1
## 6 28 28.0 28.0
## 7 23 23.0 23.0
## 8 19 19.0 19.0
## 9 8 8.0 8.0
## 10 NA 31.5 42.1
boxplotЗадание: Сгенерируйте два числовых набора данных с
выбросами. Обнаружьте и удалите выбросы с помощью
boxplot.
# set.seed() фиксирует случайность — чтобы каждый раз получались одинаковые числа
set.seed(42)
# rnorm() генерирует числа из нормального распределения
# Специально добавляем несколько очень больших и маленьких значений — это выбросы
set1 <- c(rnorm(50, mean = 100, sd = 10), 200, 250, -50)
set2 <- c(rnorm(50, mean = 50, sd = 5), 150, -20, 160)
# Рисуем боксплоты ДО очистки
par(mfrow = c(1, 2)) # два графика рядом
boxplot(set1, main = "Набор 1 (до очистки)", ylab = "Значение",
col = "lightblue", border = "navy")
boxplot(set2, main = "Набор 2 (до очистки)", ylab = "Значение",
col = "lightyellow", border = "darkorange")# boxplot.stats()$out возвращает список выбросов
# Выброс = значение за пределами 1.5 * IQR от краёв ящика
outliers1 <- boxplot.stats(set1)$out
outliers2 <- boxplot.stats(set2)$out
cat("=== Выбросы в наборе 1 ===\n")## === Выбросы в наборе 1 ===
## [1] 200 250 -50
##
## === Выбросы в наборе 2 ===
## [1] 35.03455 150.00000 -20.00000 160.00000
# %in% проверяет вхождение в список; ! инвертирует — берём те, кого НЕТ в списке выбросов
clean1 <- set1[!set1 %in% outliers1]
clean2 <- set2[!set2 %in% outliers2]
cat("\nНабор 1: было", length(set1), "-> стало", length(clean1), "элементов\n")##
## Набор 1: было 53 -> стало 50 элементов
## Набор 2: было 53 -> стало 49 элементов
# Боксплоты ПОСЛЕ очистки
boxplot(clean1, main = "Набор 1 (после очистки)", ylab = "Значение",
col = "lightgreen", border = "darkgreen")
boxplot(clean2, main = "Набор 2 (после очистки)", ylab = "Значение",
col = "lightpink", border = "red")Задание: Сгенерируйте таблицу с дублирующимися
строками. Удалите их с помощью unique() и
duplicated(). Сравните результаты.
df_dupes <- data.frame(
product = c("Apple", "Banana", "Apple", "Pear", "Banana", "Orange", "Apple"),
price = c(30, 50, 30, 40, 50, 60, 30),
quantity = c(5, 3, 5, 7, 3, 2, 5)
)
cat("=== Исходная таблица (с дублями) ===\n")## === Исходная таблица (с дублями) ===
## product price quantity
## 1 Apple 30 5
## 2 Banana 50 3
## 3 Apple 30 5
## 4 Pear 40 7
## 5 Banana 50 3
## 6 Orange 60 2
## 7 Apple 30 5
# --- Метод 1: unique() ---
# Оставляет только уникальные строки. Просто и коротко.
df_unique <- unique(df_dupes)
cat("\n=== После unique() ===\n")##
## === После unique() ===
## product price quantity
## 1 Apple 30 5
## 2 Banana 50 3
## 4 Pear 40 7
## 6 Orange 60 2
# --- Метод 2: duplicated() ---
# Возвращает TRUE для строк, которые уже встречались раньше.
# Первое вхождение — FALSE, все повторы — TRUE.
dup_mask <- duplicated(df_dupes)
cat("\n=== Маска дублей (TRUE = строка-дубль) ===\n")##
## === Маска дублей (TRUE = строка-дубль) ===
## [1] FALSE FALSE TRUE FALSE TRUE FALSE TRUE
##
## === После duplicated() ===
## product price quantity
## 1 Apple 30 5
## 2 Banana 50 3
## 4 Pear 40 7
## 6 Orange 60 2
##
## === Сравнение методов ===
## Исходно строк: 7
## После unique(): 4
## После duplicated(): 4
# Вывод: результат одинаковый.
# unique() — короче, проще
# duplicated() — гибче: сначала смотришь где дубли, потом решаешь что делатьmiceЗадание: Обработайте пропуски в данных с
использованием пакета mice.
# install.packages("mice")
library(mice)
# nhanes — встроенный датасет из пакета mice с пропусками, удобен для примера
cat("=== Датасет nhanes ===\n")## === Датасет nhanes ===
## age bmi hyp chl
## 1 1 NA NA NA
## 2 2 22.7 1 187
## 3 1 NA 1 187
## 4 3 NA NA NA
## 5 1 20.4 1 113
## 6 3 NA NA 184
##
## === NA по столбцам ===
## age bmi hyp chl
## 0 9 8 10
# md.pattern() показывает паттерны пропусков: какие комбинации NA встречаются
cat("\n=== Паттерны пропусков ===\n")##
## === Паттерны пропусков ===
## age hyp bmi chl
## 13 1 1 1 1 0
## 3 1 1 1 0 1
## 1 1 1 0 1 1
## 1 1 0 0 1 2
## 7 1 0 0 0 3
## 0 8 9 10 27
# --- Множественное вменение (Multiple Imputation) ---
# mice() создаёт несколько (m=5) версий датасета, в каждой пропуски заполнены
# по-разному на основе статистических моделей. Умнее, чем просто среднее!
#
# method = "pmm" — predictive mean matching, хорошо для числовых данных
# seed = 123 — фиксируем случайность
# printFlag = FALSE — не захламляем консоль логом
imp <- mice(nhanes, m = 5, method = "pmm", seed = 123, printFlag = FALSE)
summary(imp)## Class: mids
## Number of multiple imputations: 5
## Imputation methods:
## age bmi hyp chl
## "" "pmm" "pmm" "pmm"
## PredictorMatrix:
## age bmi hyp chl
## age 0 1 1 1
## bmi 1 0 1 1
## hyp 1 1 0 1
## chl 1 1 1 0
# complete() извлекает один из 5 заполненных датасетов
filled_data <- complete(imp, action = 1)
cat("\n=== Заполненные данные ===\n")##
## === Заполненные данные ===
## age bmi hyp chl
## 1 1 22.0 1 238
## 2 2 22.7 1 187
## 3 1 30.1 1 187
## 4 3 26.3 2 218
## 5 1 20.4 1 113
## 6 3 27.5 1 184
## 7 1 22.5 1 118
## 8 1 30.1 1 187
## 9 2 22.0 1 238
## 10 2 27.4 1 186
## 11 1 33.2 1 187
## 12 2 27.4 2 229
## 13 3 21.7 1 206
## 14 2 28.7 2 204
## 15 1 29.6 1 187
## 16 1 35.3 1 187
## 17 3 27.2 2 284
## 18 2 26.3 2 199
## 19 1 35.3 1 218
## 20 3 25.5 2 218
## 21 1 33.2 1 187
## 22 1 33.2 1 229
## 23 1 27.5 1 131
## 24 3 24.9 1 284
## 25 2 27.4 1 186
##
## === Остались ли NA? ===
## age bmi hyp chl
## 0 0 0 0
# Синие точки = реальные данные, красные = вмененные алгоритмом
stripplot(imp, pch = 20, cex = 1.5,
main = "Реальные (синие) vs вмененные (красные) значения")Задание: Разберите пример с мультиколлинеарностью.
# Мультиколлинеарность — когда два или больше предиктора сильно коррелируют между собой.
# Проблема: модель не понимает, кто из них "виновен" в результате.
# install.packages("car")
library(car)
# mtcars — встроенный датасет с характеристиками автомобилей
# Предсказываем расход топлива (mpg) через несколько переменных
model_full <- lm(mpg ~ wt + hp + disp + cyl, data = mtcars)
summary(model_full)##
## Call:
## lm(formula = mpg ~ wt + hp + disp + cyl, data = mtcars)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.0562 -1.4636 -0.4281 1.2854 5.8269
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 40.82854 2.75747 14.807 1.76e-14 ***
## wt -3.85390 1.01547 -3.795 0.000759 ***
## hp -0.02054 0.01215 -1.691 0.102379
## disp 0.01160 0.01173 0.989 0.331386
## cyl -1.29332 0.65588 -1.972 0.058947 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.513 on 27 degrees of freedom
## Multiple R-squared: 0.8486, Adjusted R-squared: 0.8262
## F-statistic: 37.84 on 4 and 27 DF, p-value: 1.061e-10
# VIF = Variance Inflation Factor (коэффициент инфляции дисперсии)
# Показывает, насколько дисперсия коэффициента раздута из-за корреляции с другими
#
# VIF < 5 — всё ок
# VIF 5-10 — есть проблема
# VIF > 10 — серьёзная мультиколлинеарность
cat("=== VIF исходной модели ===\n")## === VIF исходной модели ===
## wt hp disp cyl
## 4.85 3.41 10.37 6.74
##
## Предикторы с VIF > 5:
## disp cyl
## 10.373286 6.737707
# Матрица корреляций — смотрим кто с кем коррелирует
cat("\n=== Матрица корреляций предикторов ===\n")##
## === Матрица корреляций предикторов ===
## wt hp disp cyl
## wt 1.00 0.66 0.89 0.78
## hp 0.66 1.00 0.79 0.83
## disp 0.89 0.79 1.00 0.90
## cyl 0.78 0.83 0.90 1.00
# Решение: убираем сильно коррелирующие переменные (disp и cyl коррелируют с wt)
model_clean <- lm(mpg ~ wt + hp, data = mtcars)
summary(model_clean)##
## Call:
## lm(formula = mpg ~ wt + hp, data = mtcars)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.941 -1.600 -0.182 1.050 5.854
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 37.22727 1.59879 23.285 < 2e-16 ***
## wt -3.87783 0.63273 -6.129 1.12e-06 ***
## hp -0.03177 0.00903 -3.519 0.00145 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.593 on 29 degrees of freedom
## Multiple R-squared: 0.8268, Adjusted R-squared: 0.8148
## F-statistic: 69.21 on 2 and 29 DF, p-value: 9.109e-12
##
## === VIF после удаления коллинеарных переменных ===
## wt hp
## 1.77 1.77
## Теперь все VIF < 5 — мультиколлинеарность устранена!
# Матрица графиков — визуально видна корреляция между disp, cyl, wt
pairs(mtcars[, c("mpg", "wt", "hp", "disp", "cyl")],
main = "Матрица корреляций (видна мультиколлинеарность)",
col = "steelblue", pch = 19)