Введение

Цель работы — изучить методы обработки пропусков, удаления выбросов, устранения дубликатов и выявления мультиколлинеарности в языке R.


1. Формирование собственного датасета с NA

Создадим числовой вектор, содержащий пропущенные значения.

data_vec <- c(5, 12, NA, 18, 21, NA, 30, 42)
data_vec
## [1]  5 12 NA 18 21 NA 30 42

Пояснение:
Функция c() объединяет значения в один вектор. Значения NA обозначают пропуски.


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

Определим позиции пропусков и удалим их.

is.na(data_vec)
## [1] FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE
clean_vec <- data_vec[!is.na(data_vec)]
clean_vec
## [1]  5 12 18 21 30 42

Пояснение:
is.na() возвращает TRUE для пропусков.
Используя логическое отрицание !, мы оставляем только реальные значения.


3. Таблица с числовыми и текстовыми столбцами + complete.cases()

Сформируем таблицу, содержащую пропуски.

df_mixed <- data.frame(
  id = c(1, 2, 3, 4, 5, 6),
  score = c(80, NA, 95, 88, NA, 77),
  name = c("Anna", "Bob", "Chris", NA, "Diana", "Eva")
)

df_mixed
##   id score  name
## 1  1    80  Anna
## 2  2    NA   Bob
## 3  3    95 Chris
## 4  4    88  <NA>
## 5  5    NA Diana
## 6  6    77   Eva

Удалим строки с пропусками:

complete.cases(df_mixed)
## [1]  TRUE FALSE  TRUE FALSE FALSE  TRUE
df_mixed_clean <- df_mixed[complete.cases(df_mixed), ]
df_mixed_clean
##   id score  name
## 1  1    80  Anna
## 3  3    95 Chris
## 6  6    77   Eva

Пояснение:
complete.cases() возвращает TRUE только для строк без NA.
Таким образом остаются полностью заполненные строки.


4. Анализ датасета airquality и заполнение пропусков

Загрузим данные и посмотрим количество пропусков.

library(caret)
data(airquality)

summary(airquality)
##      Ozone           Solar.R           Wind             Temp      
##  Min.   :  1.00   Min.   :  7.0   Min.   : 1.700   Min.   :56.00  
##  1st Qu.: 18.00   1st Qu.:115.8   1st Qu.: 7.400   1st Qu.:72.00  
##  Median : 31.50   Median :205.0   Median : 9.700   Median :79.00  
##  Mean   : 42.13   Mean   :185.9   Mean   : 9.958   Mean   :77.88  
##  3rd Qu.: 63.25   3rd Qu.:258.8   3rd Qu.:11.500   3rd Qu.:85.00  
##  Max.   :168.00   Max.   :334.0   Max.   :20.700   Max.   :97.00  
##  NA's   :37       NA's   :7                                       
##      Month            Day      
##  Min.   :5.000   Min.   : 1.0  
##  1st Qu.:6.000   1st Qu.: 8.0  
##  Median :7.000   Median :16.0  
##  Mean   :6.993   Mean   :15.8  
##  3rd Qu.:8.000   3rd Qu.:23.0  
##  Max.   :9.000   Max.   :31.0  
## 
colSums(is.na(airquality))
##   Ozone Solar.R    Wind    Temp   Month     Day 
##      37       7       0       0       0       0

4.1 Заполнение пропусков средним

air_mean <- airquality
num_cols <- sapply(air_mean, is.numeric)

for (col in names(air_mean)[num_cols]) {
  air_mean[[col]][is.na(air_mean[[col]])] <- mean(air_mean[[col]], na.rm = TRUE)
}

colSums(is.na(air_mean))
##   Ozone Solar.R    Wind    Temp   Month     Day 
##       0       0       0       0       0       0

4.2 Заполнение пропусков медианой

air_median <- airquality

for (col in names(air_median)[num_cols]) {
  air_median[[col]][is.na(air_median[[col]])] <- median(air_median[[col]], na.rm = TRUE)
}

colSums(is.na(air_median))
##   Ozone Solar.R    Wind    Temp   Month     Day 
##       0       0       0       0       0       0

Пояснение:
Среднее чувствительно к выбросам.
Медиана более устойчива и часто используется при наличии экстремальных значений.


5. Выбросы и их удаление

Создадим два набора данных и добавим искусственные выбросы.

set.seed(100)
a <- c(rnorm(40, mean=10, sd=2), 35, 40)
b <- c(rnorm(40, mean=100, sd=8), 180, 220)

Построим boxplot:

boxplot(a, main="Набор A с выбросами")

boxplot(b, main="Набор B с выбросами")

Пояснение графика:
Точки за пределами “усов” boxplot являются выбросами.

Определим выбросы:

out_a <- boxplot.stats(a)$out
out_b <- boxplot.stats(b)$out

out_a
## [1] 35 40
out_b
## [1] 180 220

Удалим выбросы:

a_clean <- a[!(a %in% out_a)]
b_clean <- b[!(b %in% out_b)]

boxplot(a_clean, main="Набор A без выбросов")

boxplot(b_clean, main="Набор B без выбросов")

Вывод:
После удаления экстремальных значений распределение становится более компактным и симметричным.


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

Создадим таблицу с повторяющимися строками.

dup_table <- data.frame(
  id = c(1,2,2,3,4,4,5),
  name = c("A","B","B","C","D","D","E")
)

dup_table
##   id name
## 1  1    A
## 2  2    B
## 3  2    B
## 4  3    C
## 5  4    D
## 6  4    D
## 7  5    E

Удаление через unique():

unique_rows <- unique(dup_table)
unique_rows
##   id name
## 1  1    A
## 2  2    B
## 4  3    C
## 5  4    D
## 7  5    E

Удаление через duplicated():

no_dups <- dup_table[!duplicated(dup_table), ]
no_dups
##   id name
## 1  1    A
## 2  2    B
## 4  3    C
## 5  4    D
## 7  5    E

Сравнение:

identical(unique_rows, no_dups)
## [1] TRUE

Вывод:
Обе функции дают одинаковый результат — остаются только уникальные строки.


7. Обработка пропусков с помощью mice

library(mice)

df_na <- data.frame(
  x1 = c(10, 15, NA, 20, 25),
  x2 = c(NA, 5, 7, 9, 11)
)

df_na
##   x1 x2
## 1 10 NA
## 2 15  5
## 3 NA  7
## 4 20  9
## 5 25 11

Импутация методом pmm:

imp <- mice(df_na, m=3, method="pmm", seed=100)
## 
##  iter imp variable
##   1   1  x1  x2
##   1   2  x1  x2
##   1   3  x1  x2
##   2   1  x1  x2
##   2   2  x1  x2
##   2   3  x1  x2
##   3   1  x1  x2
##   3   2  x1  x2
##   3   3  x1  x2
##   4   1  x1  x2
##   4   2  x1  x2
##   4   3  x1  x2
##   5   1  x1  x2
##   5   2  x1  x2
##   5   3  x1  x2
completed <- complete(imp, 1)
completed
##   x1 x2
## 1 10  9
## 2 15  5
## 3 15  7
## 4 20  9
## 5 25 11

Пояснение:
mice использует множественную импутацию, восстанавливая пропуски на основе других переменных.


8. Пример мультиколлинеарности

Создадим переменные с высокой корреляцией.

set.seed(123)
n <- 100
x1 <- rnorm(n)
x2 <- x1 * 0.9 + rnorm(n, sd=0.1)
x3 <- rnorm(n)
y <- 2*x1 + rnorm(n)

mc_df <- data.frame(y, x1, x2, x3)

cor(mc_df[,2:4])
##           x1         x2         x3
## x1  1.000000  0.9930810 -0.1291760
## x2  0.993081  1.0000000 -0.1254392
## x3 -0.129176 -0.1254392  1.0000000

Построим модель:

model <- lm(y ~ x1 + x2 + x3, data=mc_df)
summary(model)
## 
## Call:
## lm(formula = y ~ x1 + x2 + x3, data = mc_df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.49138 -0.65392  0.05664  0.67033  2.53210 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.01933    0.10734  -0.180    0.858
## x1           1.52859    0.98666   1.549    0.125
## x2           0.46218    1.09456   0.422    0.674
## x3          -0.05739    0.11223  -0.511    0.610
## 
## Residual standard error: 1.052 on 96 degrees of freedom
## Multiple R-squared:  0.7474, Adjusted R-squared:  0.7395 
## F-statistic: 94.69 on 3 and 96 DF,  p-value: < 2.2e-16

Проверим VIF:

if (!requireNamespace("car", quietly = TRUE)) install.packages("car")
library(car)
vif(model)
##        x1        x2        x3 
## 72.628674 72.558466  1.017576

Пояснение:
Если VIF > 5 (или 10), это указывает на мультиколлинеарность.
В нашем случае x1 и x2 сильно коррелируют, что подтверждается высоким VIF.


Заключение

В ходе работы были изучены методы: - очистки пропусков, - удаления выбросов, - устранения дубликатов, - импутации с помощью mice, - выявления мультиколлинеарности.

Полученные результаты демонстрируют важность предварительной обработки данных перед построением моделей.