Задание 1. Сформировать датасет с помощью c() с числовыми данными и NA

vec <- c(5, 8, NA, 12, 15, NA, 20, 25)
vec
## [1]  5  8 NA 12 15 NA 20 25

Вывод: создан числовой вектор, содержащий значения и пропуски (NA).


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

is.na(vec)
## [1] FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE
vec_clean <- vec[!is.na(vec)]
vec_clean
## [1]  5  8 12 15 20 25

Вывод: is.na() возвращает логический вектор, по которому можно удалить NA и получить “чистые” данные.


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

df <- data.frame(
  id = c(101, 102, 103, 104, 105),
  score = c(4.5, NA, 3.8, 4.9, NA),
  name = c("Ivan", "Olga", NA, "Maria", "Alex")
)
df
##    id score  name
## 1 101   4.5  Ivan
## 2 102    NA  Olga
## 3 103   3.8  <NA>
## 4 104   4.9 Maria
## 5 105    NA  Alex
complete.cases(df)
## [1]  TRUE FALSE FALSE  TRUE FALSE
df_clean <- df[complete.cases(df), ]
df_clean
##    id score  name
## 1 101   4.5  Ivan
## 4 104   4.9 Maria

Вывод: complete.cases() оставляет строки без пропусков.


Задание 4. airquality (caret): заполнение пропусков средним и медианой

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
head(air_mean)
##      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 42.12931 185.9315 14.3   56     5   5
## 6 28.00000 185.9315 14.9   66     5   6

4.2 Заполнение пропусков медианой через preProcess (caret)

pre_median <- preProcess(airquality, method = "medianImpute")
air_median <- predict(pre_median, airquality)

colSums(is.na(air_median))
##   Ozone Solar.R    Wind    Temp   Month     Day 
##       0       0       0       0       0       0
head(air_median)
##   Ozone Solar.R Wind Temp Month Day
## 1  41.0     190  7.4   67     5   1
## 2  36.0     118  8.0   72     5   2
## 3  12.0     149 12.6   74     5   3
## 4  18.0     313 11.5   62     5   4
## 5  31.5     205 14.3   56     5   5
## 6  28.0     205 14.9   66     5   6

Вывод: после заполнения пропусков количество NA стало равно нулю. Замена медианой более устойчива к выбросам, чем замена средним.


Задание 5. Два числовых набора + выбросы + boxplot → обнаружить и удалить

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

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

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

Найдём выбросы:

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

out_a
## [1] 35 40
out_b
## [1]  70.0691 180.0000 220.0000

Удалим выбросы и построим boxplot повторно:

a_no_out <- a[!(a %in% out_a)]
b_no_out <- b[!(b %in% out_b)]

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

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

Вывод: boxplot позволяет выявить выбросы, а boxplot.stats()$out — получить их значения и удалить из данных.


Задание 6. Дубли строк: unique() и duplicated()

dup_df <- data.frame(
  id = c(1, 2, 2, 3, 4, 4, 5),
  name = c("Ann", "Bob", "Bob", "Kate", "Tom", "Tom", "Max")
)
dup_df
##   id name
## 1  1  Ann
## 2  2  Bob
## 3  2  Bob
## 4  3 Kate
## 5  4  Tom
## 6  4  Tom
## 7  5  Max

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

unique_df <- unique(dup_df)
unique_df
##   id name
## 1  1  Ann
## 2  2  Bob
## 4  3 Kate
## 5  4  Tom
## 7  5  Max

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

dup_removed <- dup_df[!duplicated(dup_df), ]
dup_removed
##   id name
## 1  1  Ann
## 2  2  Bob
## 4  3 Kate
## 5  4  Tom
## 7  5  Max

Сравнение:

identical(unique_df, dup_removed)
## [1] TRUE

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


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

library(mice)

df_mice <- data.frame(
  x1 = c(1, 2, NA, 4, 5),
  x2 = c(NA, 1, 2, 3, 4),
  group = c("A", "A", "B", "B", "B")
)
df_mice
##   x1 x2 group
## 1  1 NA     A
## 2  2  1     A
## 3 NA  2     B
## 4  4  3     B
## 5  5  4     B

Импутация:

imp <- mice(df_mice, m=3, method="pmm", seed=42)
## 
##  iter imp variable
##   1   1  x1
##   1   2  x1
##   1   3  x1
##   2   1  x1
##   2   2  x1
##   2   3  x1
##   3   1  x1
##   3   2  x1
##   3   3  x1
##   4   1  x1
##   4   2  x1
##   4   3  x1
##   5   1  x1
##   5   2  x1
##   5   3  x1
completed <- complete(imp, 1)
completed
##   x1 x2 group
## 1  1 NA     A
## 2  2  1     A
## 3  5  2     B
## 4  4  3     B
## 5  5  4     B

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


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

set.seed(100)
n <- 100
x1 <- rnorm(n)
x2 <- x1 * 0.95 + rnorm(n, sd=0.05)
x3 <- rnorm(n)
y <- 3*x1 + rnorm(n)

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

cor(mc_df[, c("x1","x2","x3")])
##           x1         x2         x3
## x1 1.0000000 0.99916444 0.10679137
## x2 0.9991644 1.00000000 0.09632181
## x3 0.1067914 0.09632181 1.00000000

Линейная модель:

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.6057 -0.6113 -0.0340  0.6866  3.4316 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.08296    0.10877  -0.763    0.447
## x1           1.51746    2.71255   0.559    0.577
## x2           1.62474    2.86576   0.567    0.572
## x3          -0.10681    0.10939  -0.976    0.331
## 
## Residual standard error: 1.087 on 96 degrees of freedom
## Multiple R-squared:  0.8938, Adjusted R-squared:  0.8905 
## F-statistic: 269.4 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 
## 641.816764 640.439156   1.082145

Вывод: при высокой корреляции признаков VIF становится большим (часто >5 или >10), что указывает на мультиколлинеарность и может ухудшать интерпретацию коэффициентов модели.