Задание 1: Создание датасета с NA значениями

Сформируйте свой собственный датасет с помощью функции c (конкатенация), в котором содержатся числовые данные и NA значения.

# Создаем датасет с числовыми данными и NA значениями
my_dataset <- c(10, 15, NA, 22, 18, NA, 30, 25, NA, 12, 8, NA, 45, 33, 27)

cat("Созданный датасет:\n")
## Созданный датасет:
print(my_dataset)
##  [1] 10 15 NA 22 18 NA 30 25 NA 12  8 NA 45 33 27
cat("\nДлина датасета:", length(my_dataset))
## 
## Длина датасета: 15
cat("\nКоличество NA значений:", sum(is.na(my_dataset)))
## 
## Количество NA значений: 4

Задание 2: Очистка данных с использованием is.na()

Проведите очистку данных с использованием функции is.na() и выведите “чистый” датасет.

# Определяем, какие элементы являются NA
na_positions <- is.na(my_dataset)
cat("Позиции NA значений:\n")
## Позиции NA значений:
print(na_positions)
##  [1] FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE  TRUE
## [13] FALSE FALSE FALSE
# Очищаем датасет (удаляем NA)
clean_dataset <- my_dataset[!na_positions]

cat("\nОчищенный датасет (без NA):\n")
## 
## Очищенный датасет (без NA):
print(clean_dataset)
##  [1] 10 15 22 18 30 25 12  8 45 33 27
cat("\nДлина очищенного датасета:", length(clean_dataset))
## 
## Длина очищенного датасета: 11
cat("\nПроверка на наличие NA:", any(is.na(clean_dataset)))
## 
## Проверка на наличие NA: FALSE

Задание 3: Таблица с числовыми и текстовыми столбцами

Сгенерируйте таблицу данных с числовыми и текстовыми столбцами. Очистите данные с помощью функции complete.cases().

# Создаем таблицу с числовыми и текстовыми столбцами
id <- c(1, 2, 3, 4, 5, 6)
name <- c("Иван", "Мария", "Петр", "Анна", "Сергей", "Елена")
age <- c(25, NA, 30, 22, NA, 35)
city <- c("Москва", "СПб", NA, "Казань", "Москва", NA)
score <- c(85, 92, NA, 78, 88, 95)

# Создаем data frame
df <- data.frame(ID = id, Name = name, Age = age, City = city, Score = score)

cat("Исходная таблица с пропусками:\n")
## Исходная таблица с пропусками:
print(df)
##   ID   Name Age   City Score
## 1  1   Иван  25 Москва    85
## 2  2  Мария  NA    СПб    92
## 3  3   Петр  30   <NA>    NA
## 4  4   Анна  22 Казань    78
## 5  5 Сергей  NA Москва    88
## 6  6  Елена  35   <NA>    95
# Очистка данных с помощью complete.cases()
complete_rows <- complete.cases(df)
cat("\nСтроки без пропусков (complete.cases):\n")
## 
## Строки без пропусков (complete.cases):
print(complete_rows)
## [1]  TRUE FALSE FALSE  TRUE FALSE FALSE
# Получаем чистый датасет
clean_df <- df[complete_rows, ]

cat("\nОчищенная таблица (только полные строки):\n")
## 
## Очищенная таблица (только полные строки):
print(clean_df)
##   ID Name Age   City Score
## 1  1 Иван  25 Москва    85
## 4  4 Анна  22 Казань    78
cat("\nСтатистика:\n")
## 
## Статистика:
cat("Всего строк:", nrow(df), "\n")
## Всего строк: 6
cat("Полных строк:", sum(complete_rows), "\n")
## Полных строк: 2
cat("Строк с пропусками:", sum(!complete_rows), "\n")
## Строк с пропусками: 4

Задание 4: Анализ датасета airquality с пропусками

Проанализируйте датасет airquality с пропусками. С использованием функции preProcess из пакета caret заполните пропуски предсказанными значениями (среднее, медиана).

library(caret)

# Загружаем датасет airquality
data(airquality)
cat("Датасет airquality:\n")
## Датасет airquality:
cat("Размерность:", dim(airquality), "\n")
## Размерность: 153 6
cat("Структура:\n")
## Структура:
str(airquality)
## 'data.frame':    153 obs. of  6 variables:
##  $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
##  $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
##  $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
##  $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
##  $ Month  : int  5 5 5 5 5 5 5 5 5 5 ...
##  $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...
# Анализируем пропуски
cat("\nАнализ пропусков:\n")
## 
## Анализ пропусков:
na_count <- colSums(is.na(airquality))
print(na_count)
##   Ozone Solar.R    Wind    Temp   Month     Day 
##      37       7       0       0       0       0
cat("\nПроцент пропусков:\n")
## 
## Процент пропусков:
na_percent <- round(colMeans(is.na(airquality)) * 100, 2)
print(na_percent)
##   Ozone Solar.R    Wind    Temp   Month     Day 
##   24.18    4.58    0.00    0.00    0.00    0.00
# Заполнение пропусков методом среднего
preProcess_mean <- preProcess(airquality, method = "medianImpute")
airquality_mean <- predict(preProcess_mean, airquality)

cat("\nДатасет после заполнения средним:\n")
## 
## Датасет после заполнения средним:
cat("Проверка наличия NA:", any(is.na(airquality_mean)), "\n")
## Проверка наличия NA: FALSE
# Сравнение результатов
cat("\nСравнение первых строк оригинала и после заполнения:\n")
## 
## Сравнение первых строк оригинала и после заполнения:
comparison_df <- data.frame(
  Оригинал = head(airquality$Ozone, 10),
  После_заполнения = head(airquality_mean$Ozone, 10)
)
print(comparison_df)
##    Оригинал После_заполнения
## 1        41             41.0
## 2        36             36.0
## 3        12             12.0
## 4        18             18.0
## 5        NA             31.5
## 6        28             28.0
## 7        23             23.0
## 8        19             19.0
## 9         8              8.0
## 10       NA             31.5

Задание 5: Обнаружение и удаление выбросов

Сгенерируйте два числовых набора данных и добавьте в них выбросы. С использованием функции boxplot обнаружьте выбросы и удалите их.

set.seed(123)

# Генерируем два набора данных с выбросами
data1 <- c(rnorm(50, mean = 10, sd = 2), 25, 30, 5, 2)
data2 <- c(rnorm(50, mean = 20, sd = 3), 40, 45, 10, 8)

cat("Набор данных 1:\n")
## Набор данных 1:
cat("Длина:", length(data1), "\n")
## Длина: 54
cat("Сводная статистика:\n")
## Сводная статистика:
print(summary(data1))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   2.000   8.782   9.855  10.471  11.521  30.000
cat("\nНабор данных 2:\n")
## 
## Набор данных 2:
cat("Длина:", length(data2), "\n")
## Длина: 54
cat("Сводная статистика:\n")
## Сводная статистика:
print(summary(data2))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    8.00   18.62   20.46   20.83   22.56   45.00
# Визуализация выбросов с помощью boxplot
par(mfrow = c(2, 2))

# Boxplot для первого набора
boxplot(data1, main = "Набор 1 - с выбросами", 
        col = "lightblue", horizontal = TRUE)

# Boxplot для второго набора
boxplot(data2, main = "Набор 2 - с выбросами", 
        col = "lightgreen", horizontal = TRUE)

# Функция для обнаружения и удаления выбросов
remove_outliers <- function(x, coef = 1.5) {
  qnt <- quantile(x, probs = c(0.25, 0.75), na.rm = TRUE)
  iqr <- qnt[2] - qnt[1]
  lower <- qnt[1] - coef * iqr
  upper <- qnt[2] + coef * iqr
  
  outliers <- x[x < lower | x > upper]
  clean_x <- x[x >= lower & x <= upper]
  
  return(list(
    clean = clean_x,
    outliers = outliers,
    lower_bound = lower,
    upper_bound = upper,
    removed_count = length(outliers)
  ))
}

# Удаляем выбросы
result1 <- remove_outliers(data1)
result2 <- remove_outliers(data2)

cat("\nРезультаты удаления выбросов:\n")
## 
## Результаты удаления выбросов:
cat("Набор 1 - удалено:", result1$removed_count, "выбросов\n")
## Набор 1 - удалено: 3 выбросов
cat("Выбросы в наборе 1:", result1$outliers, "\n")
## Выбросы в наборе 1: 25 30 2
cat("Набор 2 - удалено:", result2$removed_count, "выбросов\n")
## Набор 2 - удалено: 4 выбросов
cat("Выбросы в наборе 2:", result2$outliers, "\n")
## Выбросы в наборе 2: 40 45 10 8
# Визуализация после удаления выбросов
boxplot(result1$clean, main = "Набор 1 - после удаления", 
        col = "lightblue", horizontal = TRUE)
boxplot(result2$clean, main = "Набор 2 - после удаления", 
        col = "lightgreen", horizontal = TRUE)
Обнаружение и удаление выбросов

Обнаружение и удаление выбросов

par(mfrow = c(1, 1))

# Сравнительная таблица
cat("\nСравнительная таблица:\n")
## 
## Сравнительная таблица:
comparison <- data.frame(
  Показатель = c("До удаления", "После удаления", "Удалено"),
  Набор1 = c(length(data1), length(result1$clean), result1$removed_count),
  Набор2 = c(length(data2), length(result2$clean), result2$removed_count)
)
print(comparison)
##       Показатель Набор1 Набор2
## 1    До удаления     54     54
## 2 После удаления     51     50
## 3        Удалено      3      4

Задание 6: Удаление дубликатов

Сгенерируйте таблицу данных, в которой дублируются строки. Удалите строки с использованием функций unique(), duplicated(). Сравните результаты.

# Создаем таблицу с дубликатами
set.seed(123)
df_duplicates <- data.frame(
  ID = c(1, 2, 3, 1, 4, 2, 5, 3, 6, 1),
  Name = c("Анна", "Иван", "Мария", "Анна", "Петр", "Иван", "Елена", "Мария", "Сергей", "Анна"),
  Age = c(25, 30, 28, 25, 35, 30, 22, 28, 40, 25),
  Score = c(85, 90, 88, 85, 92, 90, 87, 88, 95, 85)
)

cat("Исходная таблица с дубликатами:\n")
## Исходная таблица с дубликатами:
print(df_duplicates)
##    ID   Name Age Score
## 1   1   Анна  25    85
## 2   2   Иван  30    90
## 3   3  Мария  28    88
## 4   1   Анна  25    85
## 5   4   Петр  35    92
## 6   2   Иван  30    90
## 7   5  Елена  22    87
## 8   3  Мария  28    88
## 9   6 Сергей  40    95
## 10  1   Анна  25    85
cat("\nРазмерность:", dim(df_duplicates), "\n")
## 
## Размерность: 10 4
# Использование duplicated() для поиска дубликатов
duplicated_rows <- duplicated(df_duplicates)
cat("\nДублированные строки (duplicated):\n")
## 
## Дублированные строки (duplicated):
print(duplicated_rows)
##  [1] FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE
# Использование duplicated() для удаления
df_no_duplicates_dup <- df_duplicates[!duplicated_rows, ]
cat("\nПосле удаления duplicated():\n")
## 
## После удаления duplicated():
print(df_no_duplicates_dup)
##   ID   Name Age Score
## 1  1   Анна  25    85
## 2  2   Иван  30    90
## 3  3  Мария  28    88
## 5  4   Петр  35    92
## 7  5  Елена  22    87
## 9  6 Сергей  40    95
cat("Размерность:", dim(df_no_duplicates_dup), "\n")
## Размерность: 6 4
# Использование unique() для удаления
df_no_duplicates_unique <- unique(df_duplicates)
cat("\nПосле удаления unique():\n")
## 
## После удаления unique():
print(df_no_duplicates_unique)
##   ID   Name Age Score
## 1  1   Анна  25    85
## 2  2   Иван  30    90
## 3  3  Мария  28    88
## 5  4   Петр  35    92
## 7  5  Елена  22    87
## 9  6 Сергей  40    95
cat("Размерность:", dim(df_no_duplicates_unique), "\n")
## Размерность: 6 4
# Сравнение результатов
cat("\n=== СРАВНЕНИЕ МЕТОДОВ ===\n")
## 
## === СРАВНЕНИЕ МЕТОДОВ ===
cat("Исходная таблица:", nrow(df_duplicates), "строк\n")
## Исходная таблица: 10 строк
cat("После duplicated():", nrow(df_no_duplicates_dup), "строк\n")
## После duplicated(): 6 строк
cat("После unique():", nrow(df_no_duplicates_unique), "строк\n")
## После unique(): 6 строк
cat("Методы дают одинаковый результат:", 
    identical(df_no_duplicates_dup, df_no_duplicates_unique), "\n")
## Методы дают одинаковый результат: TRUE

Задание 7: Обработка пропусков с использованием пакета mice

Обработайте пропуски в данных с использованием пакета mice.

library(mice)

# Создаем данные с пропусками
set.seed(123)
n <- 50
df_mice <- data.frame(
  x1 = rnorm(n, mean = 10, sd = 2),
  x2 = rnorm(n, mean = 20, sd = 3),
  x3 = rnorm(n, mean = 30, sd = 4),
  x4 = factor(sample(c("A", "B", "C"), n, replace = TRUE))
)

# Вносим пропуски
df_mice$x1[sample(1:n, 10)] <- NA
df_mice$x2[sample(1:n, 8)] <- NA
df_mice$x3[sample(1:n, 5)] <- NA
df_mice$x4[sample(1:n, 12)] <- NA

cat("Датасет с пропусками:\n")
## Датасет с пропусками:
cat("Размерность:", dim(df_mice), "\n")
## Размерность: 50 4
cat("Количество пропусков:\n")
## Количество пропусков:
na_count_mice <- colSums(is.na(df_mice))
print(na_count_mice)
## x1 x2 x3 x4 
## 10  8  5 12
# Визуализация пропусков
md.pattern(df_mice)

##    x3 x2 x1 x4   
## 24  1  1  1  1  0
## 8   1  1  1  0  1
## 5   1  1  0  1  1
## 1   1  1  0  0  2
## 3   1  0  1  1  1
## 2   1  0  0  1  2
## 2   1  0  0  0  3
## 3   0  1  1  1  1
## 1   0  1  1  0  2
## 1   0  0  1  1  2
##     5  8 10 12 35
# Применяем mice для множественной импутации
cat("\nЗапуск mice для множественной импутации...\n")
## 
## Запуск mice для множественной импутации...
imp <- mice(df_mice, m = 5, maxit = 10, method = 'pmm', seed = 123, printFlag = FALSE)

cat("\nМетоды импутации для каждой переменной:\n")
## 
## Методы импутации для каждой переменной:
print(imp$method)
##    x1    x2    x3    x4 
## "pmm" "pmm" "pmm" "pmm"
# Получаем первый заполненный датасет
df_complete_1 <- complete(imp, 1)
cat("\nПервый заполненный датасет (первые 10 строк):\n")
## 
## Первый заполненный датасет (первые 10 строк):
print(head(df_complete_1, 10))
##           x1       x2       x3 x4
## 1   8.879049 20.75996 27.15837  B
## 2  11.559930 20.54391 31.02753  A
## 3   7.864353 19.87139 29.01323  C
## 4  10.141017 24.10581 28.60983  B
## 5  10.258575 19.32269 26.19353  A
## 6  13.430130 20.54391 29.81989  A
## 7  10.921832 15.35374 26.86038  B
## 8   7.469878 21.75384 23.32823  C
## 9   8.626294 20.37156 28.47909  B
## 10  9.108676 20.64782 33.67599  C
# Проверка распределений
par(mfrow = c(2, 2))
for(i in 1:4) {
  if(is.numeric(df_mice[,i])) {
    # Оригинальные данные без NA
    orig <- df_mice[!is.na(df_mice[,i]), i]
    # Импутированные данные
    imp_data <- complete(imp, 1)[,i]
    
    plot(density(orig), col = "red", lwd = 2,
         main = paste("Распределение", names(df_mice)[i]),
         xlab = "Значения")
    lines(density(imp_data), col = "blue", lwd = 2)
    legend("topright", legend = c("Оригинал", "После импутации"),
           col = c("red", "blue"), lwd = 2)
  } else {
    # Для факторных переменных - столбчатая диаграмма
    barplot(table(df_mice[,i]), main = paste("Распределение", names(df_mice)[i]),
            col = "lightblue", xlab = "Категории", ylab = "Частота")
  }
}

par(mfrow = c(1, 1))

Задание 8: Мультиколлинеарность

Разберите пример с мультиколлинеарностью.

library(caret)
library(corrplot)
library(car)

# Создаем данные с мультиколлинеарностью
set.seed(123)
n <- 100

# Основные переменные
x1 <- rnorm(n, mean = 10, sd = 2)
x2 <- rnorm(n, mean = 20, sd = 3)
x3 <- rnorm(n, mean = 30, sd = 4)

# Создаем сильно коррелированные переменные
x4 <- 2 * x1 + rnorm(n, 0, 0.5)
x5 <- 0.5 * x2 + rnorm(n, 0, 0.3)
x6 <- x3 + x1 + rnorm(n, 0, 1)

# Целевая переменная
y <- 5 + 2*x1 + 3*x2 + 1.5*x3 + rnorm(n, 0, 2)

df_multicoll <- data.frame(x1, x2, x3, x4, x5, x6, y)

cat("Датасет с мультиколлинеарностью:\n")
## Датасет с мультиколлинеарностью:
cat("Размерность:", dim(df_multicoll), "\n")
## Размерность: 100 7
# Анализ корреляционной матрицы
cat("\n=== КОРРЕЛЯЦИОННАЯ МАТРИЦА ===\n")
## 
## === КОРРЕЛЯЦИОННАЯ МАТРИЦА ===
cor_matrix <- cor(df_multicoll[, 1:6])
print(round(cor_matrix, 3))
##        x1     x2     x3     x4     x5    x6
## x1  1.000 -0.050 -0.129  0.990 -0.089 0.313
## x2 -0.050  1.000  0.031 -0.043  0.979 0.033
## x3 -0.129  0.031  1.000 -0.135  0.026 0.874
## x4  0.990 -0.043 -0.135  1.000 -0.083 0.300
## x5 -0.089  0.979  0.026 -0.083  1.000 0.020
## x6  0.313  0.033  0.874  0.300  0.020 1.000
# Визуализация корреляционной матрицы
corrplot(cor_matrix, method = "color", type = "upper", 
         tl.col = "black", tl.srt = 45,
         title = "Корреляционная матрица")

# VIF анализ
model_full <- lm(y ~ ., data = df_multicoll)
cat("\n=== VIF АНАЛИЗ ===\n")
## 
## === VIF АНАЛИЗ ===
vif_values <- vif(model_full)
print(vif_values)
##       x1       x2       x3       x4       x5       x6 
## 57.25092 25.93986 18.79531 50.60383 26.36414 20.53319
# Удаление коррелированных переменных
highly_correlated <- findCorrelation(cor_matrix, cutoff = 0.7)
cat("\nПеременные для удаления:", names(df_multicoll)[highly_correlated], "\n")
## 
## Переменные для удаления: x1 x6 x5
df_reduced <- df_multicoll[, -highly_correlated]
cat("\nДатасет после удаления:\n")
## 
## Датасет после удаления:
cat("Переменные:", names(df_reduced), "\n")
## Переменные: x2 x3 x4 y
# Сравнение моделей
model_reduced <- lm(y ~ ., data = df_reduced)
cat("\n=== СРАВНЕНИЕ МОДЕЛЕЙ ===\n")
## 
## === СРАВНЕНИЕ МОДЕЛЕЙ ===
cat("Полная модель R²:", round(summary(model_full)$r.squared, 4), "\n")
## Полная модель R²: 0.9662
cat("Уменьшенная модель R²:", round(summary(model_reduced)$r.squared, 4), "\n")
## Уменьшенная модель R²: 0.9626
# Визуализация VIF до и после
par(mfrow = c(1, 2))

# VIF до удаления
barplot(vif_values, main = "VIF до удаления", 
        col = ifelse(vif_values > 10, "red", "lightblue"),
        ylab = "VIF", las = 2, ylim = c(0, max(vif_values) * 1.1))
abline(h = 10, col = "red", lty = 2)

# VIF после удаления
vif_reduced <- vif(model_reduced)
barplot(vif_reduced, main = "VIF после удаления", 
        col = "lightgreen",
        ylab = "VIF", las = 2, ylim = c(0, max(vif_reduced) * 1.1))
abline(h = 10, col = "red", lty = 2)

par(mfrow = c(1, 1))

cat("\n=== ВЫВОДЫ ===\n")
## 
## === ВЫВОДЫ ===
cat("Мультиколлинеарность успешно обнаружена и устранена.\n")
## Мультиколлинеарность успешно обнаружена и устранена.

Заключение

В ходе выполнения работы были освоены основные методы обработки и очистки данных в R:

  1. Работа с пропусками: выявление (is.na, complete.cases) и заполнение (mice, preProcess)
  2. Обнаружение выбросов: визуализация (boxplot) и удаление на основе квантилей
  3. Удаление дубликатов: функции unique() и duplicated()
  4. Множественная импутация: пакет mice для сложных случаев
  5. Мультиколлинеарность: обнаружение (корреляция, VIF) и устранение

Все методы были успешно применены на практике с соответствующими примерами и визуализацией результатов.