Изучить основные способы подготовки исходных данных к анализу: обнаружение и обработку пропущенных значений, удаление дубликатов, выявление выбросов, применение методов импутации и исследование мультиколлинеарности.
values <- c(12, 15, NA, 18, 21, NA, 25, 30)
data_numeric <- data.frame(
ID = 1:8,
Value = values
)
print(data_numeric)
## ID Value
## 1 1 12
## 2 2 15
## 3 3 NA
## 4 4 18
## 5 5 21
## 6 6 NA
## 7 7 25
## 8 8 30
В результате был создан набор данных из восьми наблюдений. Два
значения в столбце Value являются пропущенными.
is.na(data_numeric)
## ID Value
## [1,] FALSE FALSE
## [2,] FALSE FALSE
## [3,] FALSE TRUE
## [4,] FALSE FALSE
## [5,] FALSE FALSE
## [6,] FALSE TRUE
## [7,] FALSE FALSE
## [8,] FALSE FALSE
colSums(is.na(data_numeric))
## ID Value
## 0 2
data_clean <- data_numeric[
!is.na(data_numeric$Value),
]
print(data_clean)
## ID Value
## 1 1 12
## 2 2 15
## 4 4 18
## 5 5 21
## 7 7 25
## 8 8 30
colSums(is.na(data_clean))
## ID Value
## 0 0
После удаления строк с пропусками в наборе осталось шесть наблюдений. Проверка показывает, что в очищенном наборе отсутствуют пропущенные значения.
data_mixed <- data.frame(
ID = 1:6,
Age = c(20, 25, NA, 30, 22, 28),
City = c("Berlin", "Paris", "Rome", NA, "Madrid", "Moscow"),
Score = c(80, 90, 75, 85, NA, 95)
)
print(data_mixed)
## ID Age City Score
## 1 1 20 Berlin 80
## 2 2 25 Paris 90
## 3 3 NA Rome 75
## 4 4 30 <NA> 85
## 5 5 22 Madrid NA
## 6 6 28 Moscow 95
complete.cases(data_mixed)
## [1] TRUE TRUE FALSE FALSE FALSE TRUE
data_mixed_clean <- data_mixed[
complete.cases(data_mixed),
]
print(data_mixed_clean)
## ID Age City Score
## 1 1 20 Berlin 80
## 2 2 25 Paris 90
## 6 6 28 Moscow 95
Функция complete.cases() определяет строки, в которых
все значения заполнены. В результате остались наблюдения с ID 1, 2 и 6.
Строки, содержащие хотя бы один пропуск, были удалены.
data("airquality")
head(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
colSums(is.na(airquality))
## Ozone Solar.R Wind Temp Month Day
## 37 7 0 0 0 0
С помощью colSums(is.na()) определяем количество
пропущенных значений в каждом столбце.
airquality_mean <- airquality
columns_with_na <- names(airquality_mean)[
colSums(is.na(airquality_mean)) > 0
]
for (col in columns_with_na) {
airquality_mean[[col]][is.na(airquality_mean[[col]])] <-
mean(airquality_mean[[col]], na.rm = TRUE)
}
colSums(is.na(airquality_mean))
## Ozone Solar.R Wind Temp Month Day
## 0 0 0 0 0 0
library(caret)
## Loading required package: ggplot2
## Loading required package: lattice
median_model <- preProcess(
airquality,
method = "medianImpute"
)
airquality_median <- predict(
median_model,
airquality
)
colSums(is.na(airquality_median))
## Ozone Solar.R Wind Temp Month Day
## 0 0 0 0 0 0
Функция preProcess() настраивает процедуру обработки
данных, а predict() применяет её к исходному набору. Метод
medianImpute заменяет пропущенные значения медианами
соответствующих переменных.
Оба способа позволяют получить набор данных без пропусков. Медиана, в отличие от среднего, обычно менее чувствительна к экстремальным значениям.
set.seed(123)
data1 <- rnorm(100, mean = 50, sd = 10)
data2 <- rnorm(100, mean = 100, sd = 15)
data1 <- c(data1, 120, -30)
data2 <- c(data2, 200, 10)
Визуальное обнаружение выбросов
boxplot(
data1,
main = "Набор 1 до очистки",
ylab = "Значения"
)
boxplot(
data2,
main = "Набор 2 до очистки",
ylab = "Значения"
)
Диаграммы размаха позволяют визуально определить значения, которые значительно отличаются от основной массы наблюдений.
outliers1 <- boxplot.stats(data1)$out
outliers2 <- boxplot.stats(data2)$out
print(outliers1)
## [1] 26.90831 120.00000 -30.00000
print(outliers2)
## [1] 148.6156 200.0000 10.0000
data1_clean <- data1[!data1 %in% outliers1]
data2_clean <- data2[!data2 %in% outliers2]
cat("Размер первого набора до очистки:", length(data1), "\n")
## Размер первого набора до очистки: 102
cat("Размер первого набора после очистки:", length(data1_clean), "\n")
## Размер первого набора после очистки: 99
cat("Размер второго набора до очистки:", length(data2), "\n")
## Размер второго набора до очистки: 102
cat("Размер второго набора после очистки:", length(data2_clean), "\n")
## Размер второго набора после очистки: 99
Диаграммы после очистки
boxplot(
data1_clean,
main = "Набор 1 после очистки",
ylab = "Значения"
)
boxplot(
data2_clean,
main = "Набор 2 после очистки",
ylab = "Значения"
)
Для обнаружения потенциальных выбросов использовалась функция
boxplot.stats(). Выявленные значения были удалены, после
чего наборы данных были повторно визуализированы. Следует учитывать, что
статистически необычные значения не всегда являются ошибками и требуют
дополнительного анализа.
data_duplicates <- data.frame(
ID = c(1, 2, 3, 2, 4, 3),
Name = c("Anna", "Boris", "Olga", "Boris", "Ivan", "Olga"),
Score = c(85, 90, 78, 90, 88, 78)
)
print(data_duplicates)
## ID Name Score
## 1 1 Anna 85
## 2 2 Boris 90
## 3 3 Olga 78
## 4 2 Boris 90
## 5 4 Ivan 88
## 6 3 Olga 78
data_unique <- unique(data_duplicates)
data_no_duplicates <- data_duplicates[
!duplicated(data_duplicates),
]
print(data_unique)
## ID Name Score
## 1 1 Anna 85
## 2 2 Boris 90
## 3 3 Olga 78
## 5 4 Ivan 88
print(data_no_duplicates)
## ID Name Score
## 1 1 Anna 85
## 2 2 Boris 90
## 3 3 Olga 78
## 5 4 Ivan 88
cat("Исходное количество строк:", nrow(data_duplicates), "\n")
## Исходное количество строк: 6
cat("После unique():", nrow(data_unique), "\n")
## После unique(): 4
cat("После duplicated():", nrow(data_no_duplicates), "\n")
## После duplicated(): 4
Функция unique() возвращает уникальные строки таблицы.
Функция duplicated() отмечает повторяющиеся строки, а
логическое условие !duplicated() позволяет оставить только
первые вхождения. Оба способа дают одинаковый результат: четыре
уникальные строки.
.
library(mice)
##
## Attaching package: 'mice'
## The following object is masked from 'package:stats':
##
## filter
## The following objects are masked from 'package:base':
##
## cbind, rbind
data_mice <- airquality[
, c("Ozone", "Solar.R", "Wind", "Temp")
]
colSums(is.na(data_mice))
## Ozone Solar.R Wind Temp
## 37 7 0 0
set.seed(123)
imputed <- mice(
data_mice,
m = 5,
maxit = 5,
method = "pmm",
printFlag = FALSE
)
data_completed <- complete(imputed, 1)
head(data_completed)
## Ozone Solar.R Wind Temp
## 1 41 190 7.4 67
## 2 36 118 8.0 72
## 3 12 149 12.6 74
## 4 18 313 11.5 62
## 5 6 313 14.3 56
## 6 28 51 14.9 66
colSums(is.na(data_completed))
## Ozone Solar.R Wind Temp
## 0 0 0 0
Параметр m = 5 задаёт количество заполненных наборов
данных, а maxit = 5 — количество итераций алгоритма. Метод
pmm подбирает значения на основе наблюдений с похожими
предсказанными значениями.
Для дальнейшего анализа извлечён первый из пяти заполненных наборов.
set.seed(123)
x1 <- rnorm(100)
x2 <- x1 * 0.9 + rnorm(100, sd = 0.2)
x3 <- rnorm(100)
y <- 3 + 2 * x1 + 0.5 * x2 - x3 + rnorm(100)
data_multi <- data.frame(x1, x2, x3, y)
cor(data_multi[, c("x1", "x2", "x3")])
## x1 x2 x3
## x1 1.0000000 0.9728501 -0.1291760
## x2 0.9728501 1.0000000 -0.1200657
## x3 -0.1291760 -0.1200657 1.0000000
Построим линейную регрессионную модель.
model <- lm(y ~ x1 + x2 + x3, data = data_multi)
summary(model)
##
## Call:
## lm(formula = y ~ x1 + x2 + x3, data = data_multi)
##
## 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) 2.9807 0.1073 27.768 < 2e-16 ***
## x1 1.7366 0.5010 3.466 0.000791 ***
## x2 0.7311 0.5473 1.336 0.184752
## x3 -1.0574 0.1122 -9.422 2.6e-15 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.052 on 96 degrees of freedom
## Multiple R-squared: 0.8552, Adjusted R-squared: 0.8507
## F-statistic: 189.1 on 3 and 96 DF, p-value: < 2.2e-16
Расчёт показателя VIF Чем выше значение VIF, тем сильнее независимая переменная связана с остальными предикторами.
predictors <- data_multi[, c("x1", "x2", "x3")]
vif_values <- sapply(names(predictors), function(variable) {
other_variables <- predictors[
, names(predictors) != variable,
drop = FALSE
]
auxiliary_model <- lm(
predictors[[variable]] ~ .,
data = other_variables
)
1 / (1 - summary(auxiliary_model)$r.squared)
})
print(vif_values)
## x1 x2 x3
## 18.723996 18.680858 1.017576
В данном примере x1 и x2 специально созданы
зависимыми друг от друга. Корреляционная матрица и показатели VIF
позволяют оценить степень этой зависимости. При интерпретации VIF
следует учитывать контекст задачи: значения выше 5 или 10 часто
рассматривают как повод для дополнительной проверки, но это не
универсальные пороги.
В ходе лабораторной работы были изучены основные способы подготовки исходных данных к анализу.
Были созданы числовые и смешанные наборы данных с пропущенными
значениями, рассмотрены функции is.na() и
complete.cases() для обнаружения и удаления пропусков. На
наборе airquality выполнено заполнение пропущенных значений
средними и медианами с помощью пакета caret, а также
множественная импутация с использованием пакета mice.
Для обнаружения потенциальных выбросов применялись диаграммы размаха
и функция boxplot.stats(). Для удаления повторяющихся
записей использовались функции unique() и
duplicated(). Кроме того, был создан пример линейной
регрессии с мультиколлинеарностью и рассчитан показатель VIF.