# Создаем датасет с числовыми данными и NA
set.seed(123)
dataset1 <- c(10, 15, NA, 25, 30, NA, 45, 50, 55, NA, 65, 70)
cat("Исходный датасет с пропусками:\n")
## Исходный датасет с пропусками:
print(dataset1)
## [1] 10 15 NA 25 30 NA 45 50 55 NA 65 70
# Проверяем наличие NA
na_check <- is.na(dataset1)
cat("\nПроверка на NA (TRUE - пропуск):\n")
##
## Проверка на NA (TRUE - пропуск):
print(na_check)
## [1] FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE TRUE FALSE FALSE
# Количество пропусков
cat("\nКоличество пропусков:", sum(na_check), "\n")
##
## Количество пропусков: 3
# Очистка данных - удаляем NA
clean_dataset1 <- dataset1[!is.na(dataset1)]
cat("\nОчищенный датасет (без NA):\n")
##
## Очищенный датасет (без NA):
print(clean_dataset1)
## [1] 10 15 25 30 45 50 55 65 70
# Альтернатива - замена NA на среднее
mean_value <- mean(dataset1, na.rm = TRUE)
dataset1_filled <- ifelse(is.na(dataset1), mean_value, dataset1)
cat("\nДатасет с заполненными пропусками (среднее =", round(mean_value, 2), "):\n")
##
## Датасет с заполненными пропусками (среднее = 40.56 ):
print(dataset1_filled)
## [1] 10.00000 15.00000 40.55556 25.00000 30.00000 40.55556 45.00000 50.00000
## [9] 55.00000 40.55556 65.00000 70.00000
Вывод: Функция is.na() позволяет
обнаружить пропущенные значения. Их можно удалить или заменить на
среднее/медиану.
# Создаем таблицу с числовыми и текстовыми данными
df2 <- data.frame(
ID = 1:10,
Name = c("Alice", "Bob", NA, "David", "Eva", NA, "George", "Hannah", NA, "Jack"),
Age = c(25, 30, NA, 35, 28, NA, 32, 29, 40, 27),
Score = c(85, 90, 78, NA, 88, 92, NA, 85, 91, 89),
City = c("NY", "LA", "NY", NA, "LA", "NY", "LA", NA, "NY", "LA")
)
cat("Исходная таблица с пропусками:\n")
## Исходная таблица с пропусками:
print(df2)
## ID Name Age Score City
## 1 1 Alice 25 85 NY
## 2 2 Bob 30 90 LA
## 3 3 <NA> NA 78 NY
## 4 4 David 35 NA <NA>
## 5 5 Eva 28 88 LA
## 6 6 <NA> NA 92 NY
## 7 7 George 32 NA LA
## 8 8 Hannah 29 85 <NA>
## 9 9 <NA> 40 91 NY
## 10 10 Jack 27 89 LA
# Используем complete.cases() для поиска полных строк
complete_rows <- complete.cases(df2)
cat("\nПолные строки (TRUE - нет пропусков):\n")
##
## Полные строки (TRUE - нет пропусков):
print(complete_rows)
## [1] TRUE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE
# Очищаем данные - оставляем только полные строки
clean_df2 <- df2[complete.cases(df2), ]
cat("\nОчищенная таблица (только полные строки):\n")
##
## Очищенная таблица (только полные строки):
print(clean_df2)
## ID Name Age Score City
## 1 1 Alice 25 85 NY
## 2 2 Bob 30 90 LA
## 5 5 Eva 28 88 LA
## 10 10 Jack 27 89 LA
# Статистика
cat("\nСтатистика очистки:\n")
##
## Статистика очистки:
cat("Исходное количество строк:", nrow(df2), "\n")
## Исходное количество строк: 10
cat("Удалено строк с пропусками:", sum(!complete_rows), "\n")
## Удалено строк с пропусками: 6
cat("Осталось строк:", nrow(clean_df2), "\n")
## Осталось строк: 4
Вывод: complete.cases() возвращает
логический вектор, указывающий на строки без пропусков, что удобно для
фильтрации данных.
library(caret)
# Загружаем датасет airquality
data(airquality)
cat("Исходные данные airquality:\n")
## Исходные данные airquality:
print(head(airquality, 10))
## 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_count <- colSums(is.na(airquality))
cat("\nКоличество пропусков в каждом столбце:\n")
##
## Количество пропусков в каждом столбце:
print(na_count)
## Ozone Solar.R Wind Temp Month Day
## 37 7 0 0 0 0
# Визуализация пропусков
na_plot <- data.frame(
Variable = names(na_count),
NA_Count = na_count
)
ggplot(na_plot, aes(x = Variable, y = NA_Count, fill = Variable)) +
geom_bar(stat = "identity") +
theme_minimal() +
labs(title = "Количество пропусков в данных airquality",
x = "Переменные", y = "Количество NA") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Заполнение пропусков методом bagImpute (не требует RANN)
preProcess_bag <- preProcess(airquality, method = "bagImpute")
airquality_bag <- predict(preProcess_bag, airquality)
cat("\nДанные после заполнения пропусков (bagImpute):\n")
##
## Данные после заполнения пропусков (bagImpute):
print(head(airquality_bag, 10))
## Ozone Solar.R Wind Temp Month Day
## 1 41.00000 190.0000 7.4 67 5 1
## 2 36.00000 118.0000 8.0 72 5 2
## 3 12.00000 149.0000 12.6 74 5 3
## 4 18.00000 313.0000 11.5 62 5 4
## 5 14.76573 194.5703 14.3 56 5 5
## 6 28.00000 234.7085 14.9 66 5 6
## 7 23.00000 299.0000 8.6 65 5 7
## 8 19.00000 99.0000 13.8 59 5 8
## 9 8.00000 19.0000 20.1 61 5 9
## 10 23.50119 194.0000 8.6 69 5 10
# Заполнение пропусков медианой (через простую замену)
airquality_median <- airquality
for(i in 1:ncol(airquality_median)) {
if(is.numeric(airquality_median[, i])) {
airquality_median[is.na(airquality_median[, i]), i] <- median(airquality_median[, i], na.rm = TRUE)
}
}
cat("\nПроверка пропусков после заполнения:\n")
##
## Проверка пропусков после заполнения:
print(colSums(is.na(airquality_median)))
## Ozone Solar.R Wind Temp Month Day
## 0 0 0 0 0 0
Вывод: preProcess из пакета
caret предоставляет различные методы для заполнения
пропусков (knnImpute, medianImpute, bagImpute и др.).
# Генерация двух наборов данных с выбросами
set.seed(123)
data1 <- c(rnorm(50, mean = 10, sd = 2), 50, 55, 60) # нормальные данные + выбросы
data2 <- c(rnorm(50, mean = 20, sd = 3), 80, 85, 90, 95) # нормальные данные + выбросы
# Создаем dataframe
df_outliers <- data.frame(
Group = rep(c("Group1", "Group2"), c(length(data1), length(data2))),
Value = c(data1, data2)
)
# Визуализация до удаления выбросов
par(mfrow = c(1, 2))
boxplot(data1, main = "Group 1 - Boxplot", ylab = "Values", col = "lightblue")
boxplot(data2, main = "Group 2 - Boxplot", ylab = "Values", col = "lightgreen")
par(mfrow = c(1, 1))
# Обнаружение выбросов
boxplot_stats1 <- boxplot(data1, plot = FALSE)
boxplot_stats2 <- boxplot(data2, plot = FALSE)
cat("Выбросы в Group 1:", boxplot_stats1$out, "\n")
## Выбросы в Group 1: 50 55 60
cat("Выбросы в Group 2:", boxplot_stats2$out, "\n")
## Выбросы в Group 2: 80 85 90 95
# Функция для удаления выбросов
remove_outliers <- function(x) {
q1 <- quantile(x, 0.25)
q3 <- quantile(x, 0.75)
iqr <- q3 - q1
lower_bound <- q1 - 1.5 * iqr
upper_bound <- q3 + 1.5 * iqr
return(x[x >= lower_bound & x <= upper_bound])
}
# Удаляем выбросы
clean_data1 <- remove_outliers(data1)
clean_data2 <- remove_outliers(data2)
# Визуализация после удаления выбросов
par(mfrow = c(1, 2))
boxplot(clean_data1, main = "Group 1 - After Outlier Removal", ylab = "Values", col = "lightblue")
boxplot(clean_data2, main = "Group 2 - After Outlier Removal", ylab = "Values", col = "lightgreen")
par(mfrow = c(1, 1))
# Статистика
cat("\n=== СТАТИСТИКА УДАЛЕНИЯ ВЫБРОСОВ ===\n")
##
## === СТАТИСТИКА УДАЛЕНИЯ ВЫБРОСОВ ===
cat("Group 1:\n")
## Group 1:
cat(" Исходно элементов:", length(data1), "\n")
## Исходно элементов: 53
cat(" Удалено выбросов:", length(data1) - length(clean_data1), "\n")
## Удалено выбросов: 3
cat(" Осталось элементов:", length(clean_data1), "\n")
## Осталось элементов: 50
cat("Group 2:\n")
## Group 2:
cat(" Исходно элементов:", length(data2), "\n")
## Исходно элементов: 54
cat(" Удалено выбросов:", length(data2) - length(clean_data2), "\n")
## Удалено выбросов: 4
cat(" Осталось элементов:", length(clean_data2), "\n")
## Осталось элементов: 50
Вывод: Boxplot эффективно визуализирует выбросы (точки за пределами усов). Выбросы можно удалить, используя межквартильный размах (IQR).
# Генерация таблицы с дубликатами
df_duplicates <- data.frame(
ID = c(1, 2, 3, 2, 4, 5, 3, 6, 1, 7),
Name = c("John", "Mary", "Peter", "Mary", "Anna", "John", "Peter", "Mike", "John", "Lisa"),
Age = c(25, 30, 35, 30, 28, 25, 35, 32, 25, 29),
Score = c(85, 90, 88, 90, 92, 85, 88, 87, 85, 91)
)
cat("Исходная таблица с дубликатами:\n")
## Исходная таблица с дубликатами:
print(df_duplicates)
## ID Name Age Score
## 1 1 John 25 85
## 2 2 Mary 30 90
## 3 3 Peter 35 88
## 4 2 Mary 30 90
## 5 4 Anna 28 92
## 6 5 John 25 85
## 7 3 Peter 35 88
## 8 6 Mike 32 87
## 9 1 John 25 85
## 10 7 Lisa 29 91
# Поиск дубликатов
duplicated_rows <- duplicated(df_duplicates)
cat("\nДубликаты строк (TRUE - дубликат):\n")
##
## Дубликаты строк (TRUE - дубликат):
print(duplicated_rows)
## [1] FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE
# Удаление дубликатов с помощью duplicated()
df_unique_duplicated <- df_duplicates[!duplicated(df_duplicates), ]
cat("\nУдаление дубликатов через duplicated():\n")
##
## Удаление дубликатов через duplicated():
print(df_unique_duplicated)
## ID Name Age Score
## 1 1 John 25 85
## 2 2 Mary 30 90
## 3 3 Peter 35 88
## 5 4 Anna 28 92
## 6 5 John 25 85
## 8 6 Mike 32 87
## 10 7 Lisa 29 91
# Удаление дубликатов с помощью unique()
df_unique_function <- unique(df_duplicates)
cat("\nУдаление дубликатов через unique():\n")
##
## Удаление дубликатов через unique():
print(df_unique_function)
## ID Name Age Score
## 1 1 John 25 85
## 2 2 Mary 30 90
## 3 3 Peter 35 88
## 5 4 Anna 28 92
## 6 5 John 25 85
## 8 6 Mike 32 87
## 10 7 Lisa 29 91
# Сравнение результатов
identical(df_unique_duplicated, df_unique_function)
## [1] TRUE
cat("\nРезультаты duplicated() и unique() совпадают:",
identical(df_unique_duplicated, df_unique_function), "\n")
##
## Результаты duplicated() и unique() совпадают: TRUE
# Поиск дубликатов по конкретному столбцу
duplicated_by_id <- duplicated(df_duplicates$ID)
cat("\nДубликаты по ID:\n")
##
## Дубликаты по ID:
print(df_duplicates[duplicated_by_id, ])
## ID Name Age Score
## 4 2 Mary 30 90
## 7 3 Peter 35 88
## 9 1 John 25 85
# Удаление дубликатов по ID (оставляем первое вхождение)
df_unique_id <- df_duplicates[!duplicated(df_duplicates$ID), ]
cat("\nУдаление дубликатов по ID (оставляем первое вхождение):\n")
##
## Удаление дубликатов по ID (оставляем первое вхождение):
print(df_unique_id)
## ID Name Age Score
## 1 1 John 25 85
## 2 2 Mary 30 90
## 3 3 Peter 35 88
## 5 4 Anna 28 92
## 6 5 John 25 85
## 8 6 Mike 32 87
## 10 7 Lisa 29 91
Вывод: unique() и
duplicated() эффективно удаляют дубликаты строк.
duplicated() дает больше контроля (можно выбрать, какие
дубликаты удалять).
# Создаем данные с пропусками
set.seed(123)
data_mice <- data.frame(
Age = c(25, 30, NA, 35, 28, NA, 32, 29, 40, 27),
Income = c(50000, 60000, 55000, NA, 65000, 58000, NA, 62000, 70000, 52000),
Score = c(85, 90, 78, 88, NA, 92, 86, 85, 91, 89)
)
cat("Исходные данные с пропусками:\n")
## Исходные данные с пропусками:
print(data_mice)
## Age Income Score
## 1 25 50000 85
## 2 30 60000 90
## 3 NA 55000 78
## 4 35 NA 88
## 5 28 65000 NA
## 6 NA 58000 92
## 7 32 NA 86
## 8 29 62000 85
## 9 40 70000 91
## 10 27 52000 89
# Визуализация пропусков (VIM закомментирован, используем базовую статистику)
cat("\nКоличество пропусков:\n")
##
## Количество пропусков:
print(colSums(is.na(data_mice)))
## Age Income Score
## 2 2 1
# Применяем mice для множественного восстановления
imputed_data <- mice(data_mice, m = 5, method = 'pmm', seed = 123, printFlag = FALSE)
cat("\nМетод импутации:", imputed_data$method, "\n")
##
## Метод импутации: pmm pmm pmm
cat("Количество импутаций:", imputed_data$m, "\n")
## Количество импутаций: 5
# Получаем заполненные данные
completed_data <- complete(imputed_data, 1)
cat("\nЗаполненные данные (первая импутация):\n")
##
## Заполненные данные (первая импутация):
print(completed_data)
## Age Income Score
## 1 25 50000 85
## 2 30 60000 90
## 3 27 55000 78
## 4 35 62000 88
## 5 28 65000 92
## 6 35 58000 92
## 7 32 58000 86
## 8 29 62000 85
## 9 40 70000 91
## 10 27 52000 89
# Сравнение оригинальных и восстановленных значений
cat("\n=== СРАВНЕНИЕ ОРИГИНАЛЬНЫХ И ВОССТАНОВЛЕННЫХ ЗНАЧЕНИЙ ===\n")
##
## === СРАВНЕНИЕ ОРИГИНАЛЬНЫХ И ВОССТАНОВЛЕННЫХ ЗНАЧЕНИЙ ===
for(i in 1:ncol(data_mice)) {
na_pos <- is.na(data_mice[, i])
if(sum(na_pos) > 0) {
cat("\nПеременная:", names(data_mice)[i], "\n")
cat("Пропуски в строках:", which(na_pos), "\n")
cat("Восстановленные значения:", completed_data[na_pos, i], "\n")
}
}
##
## Переменная: Age
## Пропуски в строках: 3 6
## Восстановленные значения: 27 35
##
## Переменная: Income
## Пропуски в строках: 4 7
## Восстановленные значения: 62000 58000
##
## Переменная: Score
## Пропуски в строках: 5
## Восстановленные значения: 92
# Визуализация результатов
par(mfrow = c(1, 3))
for(i in 1:ncol(data_mice)) {
plot(density(data_mice[, i], na.rm = TRUE),
main = paste("Плотность -", names(data_mice)[i]),
col = "blue", lwd = 2)
lines(density(completed_data[, i]), col = "red", lwd = 2, lty = 2)
legend("topright", legend = c("Original", "Imputed"),
col = c("blue", "red"), lty = 1:2, lwd = 2)
}
par(mfrow = c(1, 1))
Вывод: Пакет mice (Multiple Imputation
by Chained Equations) предоставляет продвинутые методы для
множественного восстановления пропущенных значений.
# Создаем данные с мультиколлинеарностью
set.seed(123)
n <- 100
X1 <- rnorm(n, mean = 10, sd = 2)
X2 <- X1 * 0.8 + rnorm(n, mean = 0, sd = 0.5) # Сильно коррелирует с X1
X3 <- X1 * 0.3 + X2 * 0.5 + rnorm(n, mean = 0, sd = 0.3) # Комбинация X1 и X2
X4 <- rnorm(n, mean = 5, sd = 1) # Независимая переменная
Y <- 2*X1 + 3*X2 + 1.5*X3 + 0.5*X4 + rnorm(n, mean = 0, sd = 2)
df_corr <- data.frame(Y = Y, X1 = X1, X2 = X2, X3 = X3, X4 = X4)
# Корреляционная матрица
cor_matrix <- cor(df_corr)
cat("Корреляционная матрица:\n")
## Корреляционная матрица:
print(round(cor_matrix, 3))
## Y X1 X2 X3 X4
## Y 1.000 0.965 0.966 0.961 0.011
## X1 0.965 1.000 0.948 0.956 -0.044
## X2 0.966 0.948 1.000 0.968 -0.028
## X3 0.961 0.956 0.968 1.000 -0.046
## X4 0.011 -0.044 -0.028 -0.046 1.000
# Визуализация корреляций
library(corrplot)
corrplot(cor_matrix, method = "color", type = "upper",
addCoef.col = "black", tl.col = "black",
title = "Корреляционная матрица признаков")
# Выявление мультиколлинеарности через VIF (Variance Inflation Factor)
library(car)
model <- lm(Y ~ X1 + X2 + X3 + X4, data = df_corr)
vif_values <- vif(model)
cat("\nVIF значения:\n")
##
## VIF значения:
print(vif_values)
## X1 X2 X3 X4
## 12.868932 17.538239 20.761554 1.006429
# Интерпретация VIF
cat("\n=== ИНТЕРПРЕТАЦИЯ VIF ===\n")
##
## === ИНТЕРПРЕТАЦИЯ VIF ===
for(i in 1:length(vif_values)) {
vif_val <- vif_values[i]
interpretation <- ifelse(vif_val < 5, "Нет мультиколлинеарности",
ifelse(vif_val < 10, "Умеренная мультиколлинеарность",
"Сильная мультиколлинеарность"))
cat(names(vif_values)[i], ":", round(vif_val, 2), "-", interpretation, "\n")
}
## X1 : 12.87 - Сильная мультиколлинеарность
## X2 : 17.54 - Сильная мультиколлинеарность
## X3 : 20.76 - Сильная мультиколлинеарность
## X4 : 1.01 - Нет мультиколлинеарности
# Создаем альтернативные данные без мультиколлинеарности
set.seed(456)
X1_ind <- rnorm(n, mean = 10, sd = 2)
X2_ind <- rnorm(n, mean = 15, sd = 3)
X3_ind <- rnorm(n, mean = 20, sd = 4)
X4_ind <- rnorm(n, mean = 5, sd = 1)
Y_ind <- 2*X1_ind + 3*X2_ind + 1.5*X3_ind + 0.5*X4_ind + rnorm(n, mean = 0, sd = 2)
df_no_corr <- data.frame(Y = Y_ind, X1 = X1_ind, X2 = X2_ind, X3 = X3_ind, X4 = X4_ind)
# Сравнение VIF
model_no_corr <- lm(Y ~ X1 + X2 + X3 + X4, data = df_no_corr)
vif_no_corr <- vif(model_no_corr)
cat("\n=== СРАВНЕНИЕ С ДАННЫМИ БЕЗ МУЛЬТИКОЛЛИНЕАРНОСТИ ===\n")
##
## === СРАВНЕНИЕ С ДАННЫМИ БЕЗ МУЛЬТИКОЛЛИНЕАРНОСТИ ===
comparison <- data.frame(
Variable = names(vif_values),
With_Multicollinearity = round(vif_values, 2),
Without_Multicollinearity = round(vif_no_corr, 2)
)
print(comparison)
## Variable With_Multicollinearity Without_Multicollinearity
## X1 X1 12.87 1.02
## X2 X2 17.54 1.01
## X3 X3 20.76 1.02
## X4 X4 1.01 1.01
# Визуализация сравнения
comparison_long <- reshape2::melt(comparison, id.vars = "Variable")
ggplot(comparison_long, aes(x = Variable, y = value, fill = variable)) +
geom_bar(stat = "identity", position = "dodge") +
theme_minimal() +
labs(title = "Сравнение VIF значений",
x = "Переменные", y = "VIF значение") +
geom_hline(yintercept = 5, linetype = "dashed", color = "red") +
geom_hline(yintercept = 10, linetype = "dashed", color = "orange") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Решение проблемы - удаление коррелирующих признаков
df_corr_reduced <- df_corr[, !names(df_corr) %in% c("X3")] # Удаляем X3
model_reduced <- lm(Y ~ X1 + X2 + X4, data = df_corr_reduced)
vif_reduced <- vif(model_reduced)
cat("\nVIF после удаления коррелирующего признака X3:\n")
##
## VIF после удаления коррелирующего признака X3:
print(round(vif_reduced, 2))
## X1 X2 X4
## 9.88 9.87 1.00
Вывод: Мультиколлинеарность возникает при сильной корреляции между признаками. VIF (Variance Inflation Factor) > 5-10 указывает на проблему. Решение: удаление коррелирующих признаков или использование регуляризации (Ridge, Lasso).
Обнаружение пропусков: is.na() и
complete.cases() - базовые инструменты для выявления
пропущенных значений.
Заполнение пропусков: Методы включают удаление,
замену на среднее/медиану (preProcess), множественную
импутацию (mice).
Выбросы: Boxplot и IQR метод эффективно выявляют и удаляют аномальные значения.
Дубликаты: unique() и
duplicated() удаляют повторяющиеся строки с разной степенью
контроля.
Мультиколлинеарность: VIF анализ помогает выявить сильные корреляции между признаками для улучшения моделей.
Практические выводы: Комплексная очистка данных - критически важный этап перед любым анализом или моделированием. ```