С самого первого занятия мы с вами говорили про пропущенные значения. В R они отображаются как NA
. В реальных данных часто приходится сталкиваться с ними. Например, если проводить опрос людей, то некоторые не захотят говорить о своей заработной плате. Это и есть пропущенное значение.
Мы уже знаем функцию is.na
. Она выдает логический ответ на вопрос: “Является ли значение пропущенным?”. Она может работать с массивами.
## [1] FALSE TRUE FALSE TRUE TRUE FALSE
Но есть и другие инструменты. Они находятся в пакете naniar
. Давайте установим и подгрузим его и остальные пакеты.
Наши переменные в наборе данных это массивы. И всегда хочется узнать, есть ли пропущенные значения в этой переменной. Для этого можно использовать функцию any_na
. Она выдает логический ответ на вопрос: “Есть ли в массиве пропущенные значения?”
Если пропущенные значения есть, то хотелось бы знать их количество, а лучше долю.
NaN
считается пропущенным значением, а бесконечность Inf
не считается.
Будет использовать функцию для генерации массива с пропущенными значениями.
generate <- function(prop_na, len = 1000){
x <- round(seq(-100, 100, length.out = len), 4)
n <- length(x)
y <- sample(c(NA, x), size = len, replace = TRUE, prob = c(prop_na, rep((1-prop_na)/n, n)))
return(y)
}
Сгенерим набор данных с 3 переменными.
С помощью функции n_miss
можно узнать количество пропущенных значений во всем наборе данных или в отдельной переменной.
Обратная функция n_complete
показывает сколько значений у нас есть о всем наборе данных или в отдельной переменной.
А функции prop_miss
и prop_complete
показывают долю пропущенных и имеющихся значений.
Не хотелось бы использовать эти функции в случае, когда у нас много переменныех. Есть готовые решения для этого. Функция miss_var_summary
показывает статистику для переменных, а функция miss_case_summary
для наблюдений. Давайте попробуем эти функции на датасете airquality
из пакета naniar
.
## Observations: 153
## Variables: 6
## $ Ozone <int> 41, 36, 12, 18, NA, 28, 23, 19, 8, NA, 7, 16, 11, 14, 18…
## $ Solar.R <int> 190, 118, 149, 313, NA, NA, 299, 99, 19, 194, NA, 256, 2…
## $ Wind <dbl> 7.4, 8.0, 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, 74, 69, 66, 68, …
## $ Month <int> 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,…
## $ Day <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1…
Функция miss_var_summary
выдает tibble- фрейм в котором есть переменные:
variable
– переменнаяn_miss
– количество пропущенных значенийpct_miss
– процент пропущенных значенийТакой фрейм отсортирован по столбцу n_miss
.
## # A tibble: 6 x 3
## variable n_miss pct_miss
## <chr> <int> <dbl>
## 1 Ozone 37 24.2
## 2 Solar.R 7 4.58
## 3 Wind 0 0
## 4 Temp 0 0
## 5 Month 0 0
## 6 Day 0 0
Функция miss_case_summary
выдает tibble- фрейм в котором есть переменные:
case
– номер наблюденияn_miss
– количество пропущенных значенийpct_miss
– процент пропущенных значенийТакой фрейм так же отсортирован по столбцу n_miss
.
## # A tibble: 153 x 3
## case n_miss pct_miss
## <int> <int> <dbl>
## 1 5 2 33.3
## 2 27 2 33.3
## 3 6 1 16.7
## 4 10 1 16.7
## 5 11 1 16.7
## 6 25 1 16.7
## 7 26 1 16.7
## 8 32 1 16.7
## 9 33 1 16.7
## 10 34 1 16.7
## # … with 143 more rows
Эти функции можно применять с pipe’ом (%>%
).
## # A tibble: 25 x 4
## # Groups: Month [5]
## Month variable n_miss pct_miss
## <int> <chr> <int> <dbl>
## 1 5 Ozone 5 16.1
## 2 5 Solar.R 4 12.9
## 3 5 Wind 0 0
## 4 5 Temp 0 0
## 5 5 Day 0 0
## 6 6 Ozone 21 70
## 7 6 Solar.R 0 0
## 8 6 Wind 0 0
## 9 6 Temp 0 0
## 10 6 Day 0 0
## # … with 15 more rows
## # A tibble: 153 x 4
## # Groups: Month [5]
## Month case n_miss pct_miss
## <int> <int> <int> <dbl>
## 1 5 5 2 40
## 2 5 27 2 40
## 3 5 6 1 20
## 4 5 10 1 20
## 5 5 11 1 20
## 6 5 25 1 20
## 7 5 26 1 20
## 8 5 1 0 0
## 9 5 2 0 0
## 10 5 3 0 0
## # … with 143 more rows
Функции miss_var_table
и miss_case_table
дают более общую информацию. Например, первая показывает количество переменных, в которых нет пропущенных значений или есть определенное количество.
## # A tibble: 3 x 3
## n_miss_in_var n_vars pct_vars
## <int> <int> <dbl>
## 1 0 4 66.7
## 2 7 1 16.7
## 3 37 1 16.7
## # A tibble: 3 x 3
## n_miss_in_case n_cases pct_cases
## <int> <int> <dbl>
## 1 0 111 72.5
## 2 1 40 26.1
## 3 2 2 1.31
Эти функции так же можно использовать с pipe’ом (%>%
).
## # A tibble: 12 x 4
## # Groups: Month [5]
## Month n_miss_in_var n_vars pct_vars
## <int> <int> <int> <dbl>
## 1 5 0 3 60
## 2 5 4 1 20
## 3 5 5 1 20
## 4 6 0 4 80
## 5 6 21 1 20
## 6 7 0 4 80
## 7 7 5 1 20
## 8 8 0 3 60
## 9 8 3 1 20
## 10 8 5 1 20
## 11 9 0 4 80
## 12 9 1 1 20
## # A tibble: 11 x 4
## # Groups: Month [5]
## Month n_miss_in_case n_cases pct_cases
## <int> <int> <int> <dbl>
## 1 5 0 24 77.4
## 2 5 1 5 16.1
## 3 5 2 2 6.45
## 4 6 0 9 30
## 5 6 1 21 70
## 6 7 0 26 83.9
## 7 7 1 5 16.1
## 8 8 0 23 74.2
## 9 8 1 8 25.8
## 10 9 0 29 96.7
## 11 9 1 1 3.33
Также есть функции miss_var_span
и miss_var_run
, которые можно изучить дополнительно.
Эта функция позволяет в прямом смысле посмотреть на ваши данные и на пропущенные значения.
Чтобы как-то сгрупиировать пропущенные значения можно установить параметр cluster
.
Попробуем использовать эту функцию на датасете riskfactors
, в котором больше переменных.
Можно так же отсортировать столбцы по количеству пропущенных значений с помощью параметра sort
.
Эти функции показывают наглядно количество пропущенных значений в виде гистограмм.
С помощью параметра facet
можно посмотреть эти графики в разрезе группы.
Функция gg_miss_upset
показывает количество пропущенных значений для различных комбинаций переменных.
Функция gg_miss_fct
позволяет увидеть в каких группах и по каким переменным пропущенных значений больше.
Рассмотрим разные случаи при работе с пропущенными значениями.
Сгенерим набор данных, в котором будет один столбец иметь много пропущенных значений.
C помощью функции miss_var_summary
и vis_miss
посмотрим какие переменные имеют пропущенные значения.
## # A tibble: 3 x 3
## variable n_miss pct_miss
## <chr> <int> <dbl>
## 1 z 701 70.1
## 2 x 0 0
## 3 y 0 0
Видно, что в переменной z
\(70.2\)% значений пропущены. В таких ситуациях лучше удалить всю такую переменную.
Сгенерим набор данных, в котором будет небольшое количество пропущенных значений во всех переменных.
C помощью функции miss_var_summary
и vis_miss
посмотрим какие переменные имеют пропущенные значения.
## # A tibble: 3 x 3
## variable n_miss pct_miss
## <chr> <int> <dbl>
## 1 y 74 7.4
## 2 x 42 4.2
## 3 z 20 2
В данному случае удалять переменные не логично. Можно удалить переменные, которые содержат пропущенные значения. Найдем количество таких наблюдений с помощью функции miss_case_summary
.
## # A tibble: 1 x 1
## n
## <int>
## 1 128
Таких наблюдений 131. Удалим их все с помощью функции drop_na
из пакета dplyr
.
В прошлом случае мы удалили 131 наблюдение. Данные очень ценный ресурс для построения моделей, поэтому нужно стараться не удалять их. Но для этого нужно заменить пропущенные значения какими-то дуругими значениями.
## # A tibble: 10 x 2
## x y
## <dbl> <dbl>
## 1 -100 11.1
## 2 -11.1 55.6
## 3 NA -33.3
## 4 33.3 -55.6
## 5 55.6 -55.6
## 6 NA 100
## 7 -11.1 NA
## 8 NA -33.3
## 9 -33.3 NA
## 10 NA -11.1
## [1] -11.1111
## [1] -100.0000 -11.1111 -11.1111 33.3333 55.5556 -11.1111 -11.1111
## [8] -11.1111 -33.3333 -11.1111
## # A tibble: 10 x 2
## x y
## <dbl> <dbl>
## 1 -100 11.1
## 2 -11.1 55.6
## 3 -11.1 -33.3
## 4 33.3 -55.6
## 5 55.6 -55.6
## 6 -11.1 100
## 7 -11.1 -2.78
## 8 -11.1 -33.3
## 9 -33.3 -2.78
## 10 -11.1 -11.1
## # A tibble: 10 x 2
## x y
## <dbl> <dbl>
## 1 -100 11.1
## 2 -11.1 55.6
## 3 -11.1 -33.3
## 4 33.3 -55.6
## 5 55.6 -55.6
## 6 -11.1 100
## 7 -11.1 NA
## 8 -11.1 -33.3
## 9 -33.3 NA
## 10 -11.1 -11.1
## # A tibble: 10 x 2
## x y
## <dbl> <dbl>
## 1 -100 11.1
## 2 -11.1 55.6
## 3 -11.1 -33.3
## 4 33.3 -55.6
## 5 55.6 -55.6
## 6 -11.1 100
## 7 -11.1 -2.78
## 8 -11.1 -33.3
## 9 -33.3 -2.78
## 10 -11.1 -11.1
## [1] -11.1111
## [1] -100.0000 -11.1111 -11.1111 33.3333 55.5556 -11.1111 -11.1111
## [8] -11.1111 -33.3333 -11.1111
## # A tibble: 10 x 2
## x y
## <dbl> <dbl>
## 1 -100 11.1
## 2 -11.1 55.6
## 3 -11.1 -33.3
## 4 33.3 -55.6
## 5 55.6 -55.6
## 6 -11.1 100
## 7 -11.1 -22.2
## 8 -11.1 -33.3
## 9 -33.3 -22.2
## 10 -11.1 -11.1
## # A tibble: 10 x 2
## x y
## <dbl> <dbl>
## 1 -100 11.1
## 2 -11.1 55.6
## 3 -11.1 -33.3
## 4 33.3 -55.6
## 5 55.6 -55.6
## 6 -11.1 100
## 7 -11.1 NA
## 8 -11.1 -33.3
## 9 -33.3 NA
## 10 -11.1 -11.1
## # A tibble: 10 x 2
## x y
## <dbl> <dbl>
## 1 -100 11.1
## 2 -11.1 55.6
## 3 -11.1 -33.3
## 4 33.3 -55.6
## 5 55.6 -55.6
## 6 -11.1 100
## 7 -11.1 -22.2
## 8 -11.1 -33.3
## 9 -33.3 -22.2
## 10 -11.1 -11.1
Если мы заменяем пропущенное значение средним или медианой, мы в какой-то степени ошибаемся, так как истинное значение могло быть совершенно другим. Чтобы это не мешало модели, можно ввести переменную-индикатор, которая будет равняться 1, если значение заменено.
Можно использовать следующую функцию.
ImputeInd <- function(df, strategy = 'mean', column){
if (strategy == 'mean') {
df1 <- df %>%
add_label_missings(column, missing = 1,
complete = 0) %>%
rename(!!paste0(column, '_IND', sep = ''):=any_missing) %>%
impute_mean_at(column)
return(df1)
}
if (strategy == 'median') {
df1 <- df %>%
add_label_missings(column, missing = 1,
complete = 0) %>%
rename(!!paste0(column, '_IND', sep = ''):=any_missing) %>%
impute_median_at(column)
return(df1)
}
stop('Введите правильно стратегию!')
}
Можно использовать даже с pipe :)
## # A tibble: 10 x 4
## x y x_IND y_IND
## <dbl> <dbl> <dbl> <dbl>
## 1 -100 11.1 0 0
## 2 -11.1 55.6 0 0
## 3 -11.1 -33.3 1 0
## 4 33.3 -55.6 0 0
## 5 55.6 -55.6 0 0
## 6 -11.1 100 1 0
## 7 -11.1 -22.2 0 1
## 8 -11.1 -33.3 1 0
## 9 -33.3 -22.2 0 1
## 10 -11.1 -11.1 1 0