Заменить отсутствующие значения средним по столбцу

Я не уверен, как перебирать каждый столбец, чтобы заменить значения NA на среднее значение столбца. Когда я пытаюсь заменить один столбец следующим, он работает хорошо.

Column1[is.na(Column1)] <- round(mean(Column1, na.rm = TRUE))

Код для перебора столбцов не работает:

for(i in 1:ncol(data)){
    data[i][is.na(data[i])] <- round(mean(data[i], na.rm = TRUE))
}

значения не заменяются. Может кто-нибудь помочь мне с этим?


person Nikita    schedule 14.09.2014    source источник
comment
Замена пропущенных значений средним значением столбца является статистической халатностью.   -  person IRTFM    schedule 14.09.2014
comment
@BondedDust Причина, по которой я это сделал, заключалась в том, что, если я игнорировал эти значения NA, мой набор данных сжимается до очень небольшого числа. Можете ли вы подсказать, как лучше всего справиться с такими проблемами. Если бы вы могли дать ссылку на блог, было бы здорово   -  person Nikita    schedule 14.09.2014
comment
Если вы хотите заменить что-то в качестве быстрого взлома, вы можете попробовать заменить NA, например mean(x) +rnorm(length(missing(x)))*sd(x). Это не будет учитывать корреляции между ошибками (или корреляции измеренных значений), но, по крайней мере, не будет серьезно увеличивать значимость результатов. Лучше было бы получить опыт работы с пакетами, которые обрабатывают вменение пропущенных значений. В основе проблемы немало тонкостей.   -  person IRTFM    schedule 15.09.2014
comment
@ 42- Я понимаю, что этому комментарию уже пару лет. Однако был ли код буквально означал mean(x)+rnorm(length(missing(x)))*sd(x)? Когда запускаю, получаю Error in missing(x) : invalid use of 'missing'. Я предполагаю, что намерение состояло в том, чтобы взять среднее из доступных значений для x, а затем добавить rnorm (длина NA) * sd (доступные значения для x). Верный? Мне понравилась линия противозаконного врачебного поведения :-). Я лично ищу быстрый взлом, потому что я работаю с набором данных чашки KDD '98, который имеет более 120 атрибутов с NA. Я бы хотел отбросить большинство из них, и инструкции должны исключить только ›= .995 NA. . .   -  person Daniel Fletcher    schedule 28.08.2016
comment
Между прочим, я предположил, что предполагаемый код был следующим: mean(x, na.rm = T)+rnorm(sum(is.na(x)))*sd(x, na.rm = T)   -  person Daniel Fletcher    schedule 28.08.2016
comment
Подразумевался скорее как псевдокод. Потребуется правильная индексация, но, возможно, rnorm( n=sum(is.na(x)) , mean=mean(x), sd=sd(x) ) будет ближе к рабочему коду.   -  person IRTFM    schedule 28.08.2016


Ответы (11)


Относительно простая модификация вашего кода должна решить проблему:

for(i in 1:ncol(data)){
  data[is.na(data[,i]), i] <- mean(data[,i], na.rm = TRUE)
}
person Thomas    schedule 14.09.2014
comment
Большое спасибо. У меня есть еще одна проблема с обработкой недостающих дат в данных. Я не могу заменить даты указанным выше подходом. не могли бы вы мне что-нибудь предложить? - person Nikita; 14.09.2014
comment
тогда ты должен задать этот вопрос - person Monica Heddneck; 13.07.2017

Если DF - ваш фрейм данных из числовых столбцов:

library(zoo)
na.aggregate(DF)

ДОБАВЛЕН:

Используя только базу R, определите функцию, которая делает это для одного столбца, а затем для каждого столбца:

NA2mean <- function(x) replace(x, is.na(x), mean(x, na.rm = TRUE))
replace(DF, TRUE, lapply(DF, NA2mean))

Последнюю строку можно заменить следующей, если можно перезаписать ввод:

DF[] <- lapply(DF, NA2mean)
person G. Grothendieck    schedule 14.09.2014
comment
Странно, здесь нет большего количества голосов или лучшего варианта ответа в этом отношении. Очень емкая реализация. Спасибо. - person Ekaba Bisong; 19.11.2016

Чтобы добавить к альтернативам, используя образцы данных @ akrun, я бы сделал следующее:

d1[] <- lapply(d1, function(x) { 
  x[is.na(x)] <- mean(x, na.rm = TRUE)
  x
})
d1
person A5C1D2H2I1M1N2O1R2T1    schedule 14.09.2014
comment
@ Тележка и мохер. Вероятно, это связано с моим ограниченным опытом программирования: что означает включение x в третьей строке? - person Daniel Fletcher; 28.08.2016
comment
Немного запустив код, я предполагаю, что здесь главное - вернуть весь вектор x, а не только значения замены, а затем перезаписать весь df d1 (в открытых скобках []), а не перезаписывать только NAс. - person Daniel Fletcher; 28.08.2016
comment
@DanielFletcher, это почти все. - person A5C1D2H2I1M1N2O1R2T1; 28.08.2016

Существует также быстрое решение с использованием пакета imputeTS:

library(imputeTS)
na_mean(yourDataFrame)
person Steffen Moritz    schedule 04.05.2018
comment
Честно говоря, я считаю, что это лучший ответ. Знал, что для выполнения этой общей задачи в другом пакете должна быть какая-то функция. - person wordsforthewise; 27.09.2019
comment
На мой взгляд, ImputeTS дает хорошие результаты. В этот пакет входит еще один вариант, основанный на фильтрах Калмана. Разработчики ImputeTS также рекомендуют его на своем информационные страницы. Вы можете использовать его с кодом. na_kalman(yourDataFrame) - person NCC1701; 26.09.2020

mutate_all или mutate_at dplyr могут быть здесь полезны:

library(dplyr)                                                             

set.seed(10)                                                               
df <- data.frame(a = sample(c(NA, 1:3)    , replace = TRUE, 10),           
                 b = sample(c(NA, 101:103), replace = TRUE, 10),                            
                 c = sample(c(NA, 201:203), replace = TRUE, 10))                            

df         

#>     a   b   c
#> 1   2 102 203
#> 2   1 102 202
#> 3   1  NA 203
#> 4   2 102 201
#> 5  NA 101 201
#> 6  NA 101 202
#> 7   1  NA 203
#> 8   1 101  NA
#> 9   2 101 203
#> 10  1 103 201

df %>% mutate_all(~ifelse(is.na(.x), mean(.x, na.rm = TRUE), .x))          

#>        a       b        c
#> 1  2.000 102.000 203.0000
#> 2  1.000 102.000 202.0000
#> 3  1.000 101.625 203.0000
#> 4  2.000 102.000 201.0000
#> 5  1.375 101.000 201.0000
#> 6  1.375 101.000 202.0000
#> 7  1.000 101.625 203.0000
#> 8  1.000 101.000 202.1111
#> 9  2.000 101.000 203.0000
#> 10 1.000 103.000 201.0000

df %>% mutate_at(vars(a, b),~ifelse(is.na(.x), mean(.x, na.rm = TRUE), .x))

#>        a       b   c
#> 1  2.000 102.000 203
#> 2  1.000 102.000 202
#> 3  1.000 101.625 203
#> 4  2.000 102.000 201
#> 5  1.375 101.000 201
#> 6  1.375 101.000 202
#> 7  1.000 101.625 203
#> 8  1.000 101.000  NA
#> 9  2.000 101.000 203
#> 10 1.000 103.000 201
person zack    schedule 16.11.2018

lapply можно использовать вместо for цикла.

d1[] <- lapply(d1, function(x) ifelse(is.na(x), mean(x, na.rm = TRUE), x))

На самом деле у этого нет никаких преимуществ перед циклом for, хотя, возможно, это проще, если у вас также есть нечисловые столбцы, и в этом случае

d1[sapply(d1, is.numeric)] <- lapply(d1[sapply(d1, is.numeric)], function(x) ifelse(is.na(x), mean(x, na.rm = TRUE), x))

почти так же просто.

person Ista    schedule 17.12.2016
comment
Интересно, что после lapply мои команды сбора из dplyr не работают. :( Я разместил это по другому вопросу. - person fiacobelli; 26.06.2017

Вы также можете попробовать:

 cM <- colMeans(d1, na.rm=TRUE)
 indx <- which(is.na(d1), arr.ind=TRUE)
 d1[indx] <- cM[indx[,2]]
 d1  

данные

set.seed(42)
d1 <- as.data.frame(matrix(sample(c(NA,0:5), 5*10, replace=TRUE), ncol=10))
person akrun    schedule 14.09.2014

Однострочник с использованием replace_na tidyr является

library(tidyr)
replace_na(mtcars,as.list(colMeans(mtcars,na.rm=T)))

Если в вашем df столбцы не являются числовыми, это требует немного больше работы, чем однострочный.

mean_to_fill <- select_if(ungroup(df), is.numeric) %>%
 colMeans(na.rm=T)

bind_cols(select(df, group1, group2, group3),
          select_if(ungroup(df), is.numeric) %>% 
            tidyr::replace_na(as.list(mean_to_fill))
          ) 
person Marcus Ritt    schedule 26.05.2019

Просто используйте Zoo, он просто заменит все значения NA на среднее значение столбца:

library(zoo)
na.aggregate(data) 
person pari    schedule 20.06.2019

Подобно ответу, указанному @Thomas, это также можно сделать с помощью ifelse() метода R:

for(i in 1:ncol(data)){
  data[,i]=ifelse(is.na(data[,i]),
                  ave(data[,i],FUN=function(y) mean(y, na.rm = TRUE)),
                  data[,i])
}

где аргументы для ifelse(TEST, YES , NO): -

ТЕСТ - логическое условие для проверки

ДА - выполняется, если условие истинно

НЕТ - иначе, когда условие ложно

и ave(x, ..., FUN = mean) - метод в R, используемый для вычисления средних значений подмножеств x []

person Aseem Yadav    schedule 28.03.2017

С пакетом data.table вы можете использовать функцию set() и перебирать столбцы и заменять NAs или что угодно другим агрегатом или значением по вашему выбору (здесь: означает):

require(data.table)

# data
dt = copy(iris[ ,-5])
setDT(dt)
dt[1:4, Sepal.Length := NA] # introduce NAs

# replace NAs with mean (or whatever function you like)
for (j in seq_along(names(dt))) {
  set(dt,
      i = which(is.na(dt[[j]])),
      j = j, 
      value = mean(dt[[j]], na.rm = TRUE))
}
person andschar    schedule 10.10.2020