Как заменить значения NA нулями в фрейме данных R?

у меня есть data.frame и некоторые столбцы имеют NA значения. Я хочу заменить NAs с нулями. Как я это делаю?

14 ответов


см. мой комментарий в ответе @gsk3. Простой пример:

> m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
> d <- as.data.frame(m)
   V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  3 NA  3  7  6  6 10  6   5
2   9  8  9  5 10 NA  2  1  7   2
3   1  1  6  3  6 NA  1  4  1   6
4  NA  4 NA  7 10  2 NA  4  1   8
5   1  2  4 NA  2  6  2  6  7   4
6  NA  3 NA NA 10  2  1 10  8   4
7   4  4  9 10  9  8  9  4 10  NA
8   5  8  3  2  1  4  5  9  4   7
9   3  9 10  1  9  9 10  5  3   3
10  4  2  2  5 NA  9  7  2  5   5

> d[is.na(d)] <- 0

> d
   V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  3  0  3  7  6  6 10  6   5
2   9  8  9  5 10  0  2  1  7   2
3   1  1  6  3  6  0  1  4  1   6
4   0  4  0  7 10  2  0  4  1   8
5   1  2  4  0  2  6  2  6  7   4
6   0  3  0  0 10  2  1 10  8   4
7   4  4  9 10  9  8  9  4 10   0
8   5  8  3  2  1  4  5  9  4   7
9   3  9 10  1  9  9 10  5  3   3
10  4  2  2  5  0  9  7  2  5   5

нет необходимости применять apply. =)

редактировать

вы также должны взглянуть на norm пакета. Он имеет много приятных функций для анализа отсутствующих данных. =)


гибрид dplyr / база R: mutate_all(funs(replace(., is.na(.), 0)))) более чем в два раза быстрее, чем база R . (см. ниже анализ контрольных показателей.)

если вы боретесь с массивными таблиц данных, data.table самый быстрый вариант всех: 30% меньше времени чем dplyr, и в 3 раза быстрее, чем База R подходы. Он также изменяет данные на месте, эффективно позволяя вам работать почти в два раза больше данных на однажды.


кластеризация других полезных подходов к замене tidyverse

Locationally:

  • индекс mutate_at(c(5:10), funs(replace(., is.na(.), 0)))
  • прямая ссылка mutate_at(vars(var5:var10), funs(replace(., is.na(.), 0)))
  • договорные матчи mutate_at(vars(contains("1")), funs(replace(., is.na(.), 0)))
    • или вместо contains() попробуй ends_with(),starts_with()
  • шаблон матч mutate_at(vars(matches("\d{2}")), funs(replace(., is.na(.), 0)))

условия:
(измените только числовые (столбцы) и оставьте строку (столбцы) в покое.)

  • чисел mutate_if(is.integer, funs(replace(., is.na(.), 0)))
  • двойники mutate_if(is.numeric, funs(replace(., is.na(.), 0)))
  • строки mutate_if(is.character, funs(replace(., is.na(.), 0)))

Полный Анализ -

подходы апробированы:

# Base R: 
baseR.sbst.rssgn   <- function(x) { x[is.na(x)] <- 0; x }
baseR.replace      <- function(x) { replace(x, is.na(x), 0) }
baseR.for          <- function(x) { for(j in 1:ncol(x))
                                    x[[j]][is.na(x[[j]])] = 0 }
# tidyverse
## dplyr
library(tidyverse)
dplyr_if_else      <- function(x) { mutate_all(x, funs(if_else(is.na(.), 0, .))) }
dplyr_coalesce     <- function(x) { mutate_all(x, funs(coalesce(., 0))) }

## tidyr
tidyr_replace_na   <- function(x) { replace_na(x, as.list(setNames(rep(0, 10), as.list(c(paste0("var", 1:10)))))) }

## hybrid 
hybrd.ifelse     <- function(x) { mutate_all(x, funs(ifelse(is.na(.), 0, .))) }
hybrd.rplc_all   <- function(x) { mutate_all(x, funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.idx<- function(x) { mutate_at(x, c(1:10), funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.nse<- function(x) { mutate_at(x, vars(var1:var10), funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.stw<- function(x) { mutate_at(x, vars(starts_with("var")), funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.ctn<- function(x) { mutate_at(x, vars(contains("var")), funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.mtc<- function(x) { mutate_at(x, vars(matches("\d+")), funs(replace(., is.na(.), 0))) }
hybrd.rplc_if    <- function(x) { mutate_if(x, is.numeric, funs(replace(., is.na(.), 0))) }

# data.table   
library(data.table)
DT.for.set.nms   <- function(x) { for (j in names(x))
                                    set(x,which(is.na(x[[j]])),j,0) }
DT.for.set.sqln  <- function(x) { for (j in seq_len(ncol(x)))
                                    set(x,which(is.na(x[[j]])),j,0) }

в код для этого анализа:

library(microbenchmark)
# 20% NA filled dataframe of 5 Million rows and 10 columns
set.seed(42) # to recreate the exact dataframe
dfN <- as.data.frame(matrix(sample(c(NA, as.numeric(1:4)), 5e6*10, replace = TRUE),
                            dimnames = list(NULL, paste0("var", 1:10)), 
                            ncol = 10))
# Running 250 trials with each replacement method 
# (the functions are excecuted locally - so that the original dataframe remains unmodified in all cases)
perf_results <- microbenchmark(
    hybrid.ifelse    = hybrid.ifelse(copy(dfN)),
    dplyr_if_else    = dplyr_if_else(copy(dfN)),
    baseR.sbst.rssgn = baseR.sbst.rssgn(copy(dfN)),
    baseR.replace    = baseR.replace(copy(dfN)),
    dplyr_coalesce   = dplyr_coalesce(copy(dfN)),
    hybrd.rplc_at.nse= hybrd.rplc_at.nse(copy(dfN)),
    hybrd.rplc_at.stw= hybrd.rplc_at.stw(copy(dfN)),
    hybrd.rplc_at.ctn= hybrd.rplc_at.ctn(copy(dfN)),
    hybrd.rplc_at.mtc= hybrd.rplc_at.mtc(copy(dfN)),
    hybrd.rplc_at.idx= hybrd.rplc_at.idx(copy(dfN)),
    hybrd.rplc_if    = hybrd.rplc_if(copy(dfN)),
    tidyr_replace_na = tidyr_replace_na(copy(dfN)),
    baseR.for        = baseR.for(copy(dfN)),
    DT.for.set.nms   = DT.for.set.nms(copy(dfN)),
    DT.for.set.sqln  = DT.for.set.sqln(copy(dfN)),
    times = 250L
)

подведение итогов

> perf_results
Unit: milliseconds
              expr       min        lq      mean    median        uq      max neval
     hybrid.ifelse 5250.5259 5620.8650 5809.1808 5759.3997 5947.7942 6732.791   250
     dplyr_if_else 3209.7406 3518.0314 3653.0317 3620.2955 3746.0293 4390.888   250
  baseR.sbst.rssgn 1611.9227 1878.7401 1964.6385 1942.8873 2031.5681 2485.843   250
     baseR.replace 1559.1494 1874.7377 1946.2971 1920.8077 2002.4825 2516.525   250
    dplyr_coalesce  949.7511 1231.5150 1279.3015 1288.3425 1345.8662 1624.186   250
 hybrd.rplc_at.nse  735.9949  871.1693 1016.5910 1064.5761 1104.9590 1361.868   250
 hybrd.rplc_at.stw  704.4045  887.4796 1017.9110 1063.8001 1106.7748 1338.557   250
 hybrd.rplc_at.ctn  723.9838  878.6088 1017.9983 1063.0406 1110.0857 1296.024   250
 hybrd.rplc_at.mtc  686.2045  885.8028 1013.8293 1061.2727 1105.7117 1269.949   250
 hybrd.rplc_at.idx  696.3159  880.7800 1003.6186 1038.8271 1083.1932 1309.635   250
     hybrd.rplc_if  705.9907  889.7381 1000.0113 1036.3963 1083.3728 1338.190   250
  tidyr_replace_na  680.4478  973.1395  978.2678 1003.9797 1051.2624 1294.376   250
         baseR.for  670.7897  965.6312  983.5775 1001.5229 1052.5946 1206.023   250
    DT.for.set.nms  496.8031  569.7471  695.4339  623.1086  861.1918 1067.640   250
   DT.for.set.sqln  500.9945  567.2522  671.4158  623.1454  764.9744 1033.463   250

Boxplot результатов (в масштабе журнала)

# adjust the margins to prepare for better boxplot printing
par(mar=c(8,5,1,1) + 0.1) 
# generate boxplot
boxplot(opN, las = 2, xlab = "", ylab = "log(time)[milliseconds]")

Boxplot Comparison of Elapsed Time

цветовая разметка испытаний (в масштабе журнала)

qplot(y=time/10^9, data=opN, colour=expr) + 
    labs(y = "log10 Scaled Elapsed Time per Trial (secs)", x = "Trial Number") +
    scale_y_log10(breaks=c(1, 2, 4))

Scatterplot of All Trial Times

заметка о других высоких исполнителях

когда данных становятся больше, Tidyr " s replace_na было исторически вытянули вперед. С текущей коллекцией 50M точек данных для выполнения, он выполняет почти точно так же, как База R Для Петли. Мне любопытно посмотреть, что происходит для фреймов данных разных размеров.

дополнительные примеры для mutate и summarize _at и _all вариантов функция может быть найден здесь: https://rdrr.io/cran/dplyr/man/summarise_all.html Кроме того, я нашел полезные демонстрации и коллекции примеров здесь: https://blog.exploratory.io/dplyr-0-5-is-awesome-heres-why-be095fd4eb8a

атрибуции и оценки

отдельное спасибо:

  • Тайлер Ринкер и Akrun для демонстрации microbenchmark.
  • alexis_laz для работы над тем, чтобы помочь мне понять использование local(), и (с терпеливой помощью Фрэнка, тоже) роль, которую играет молчаливое принуждение в ускорении многих из этих подходов.
  • ArthurYip для тыкать, чтобы добавить новый coalesce() функция внутри и уточняет анализ.
  • Грегор для толчка, чтобы выяснить data.table функции достаточно хорошо, чтобы, наконец, включить их в состав.
  • База R цикл for: alexis_laz
  • данные.таблица Для Петель: Matt_Dowle

(конечно, пожалуйста, протяните руку и дайте им upvotes, если вы найдете эти подходы полезными.)

обратите внимание на использование цифр: если у вас есть чистый целочисленный набор данных, все ваши функции будут работать быстрее. Пожалуйста, смотрите alexiz_laz это для получения дополнительной информации. IRL, я не могу вспомнить, что встретил набор данных, содержащий более 10-15% целые числа, поэтому я запускаю эти тесты на полностью числовых фреймах данных.


для одного вектора:

x <- c(1,2,NA,4,5)
x[is.na(x)] <- 0

для сведения.кадр, сделайте функцию из вышеизложенного, затем apply это к колоннам.

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

как сделать большой R воспроизводимый пример?


dplyr пример:

library(dplyr)

df1 <- df1 %>%
    mutate(myCol1 = if_else(is.na(myCol1), 0, myCol1))

Примечание: это работает на выбранный столбец, если нам нужно сделать это для всего столбца, см. @reidjaxответ с помощью mutate_each.


Если мы пытаемся заменить NAs при экспорте, например при записи в csv, мы можем использовать:

  write.csv(data, "data.csv", na = "0")

Я знаю, что на вопрос уже дан ответ, но делать это таким образом может быть более полезно для некоторых:

определите эту функцию:

na.zero <- function (x) {
    x[is.na(x)] <- 0
    return(x)
}

теперь, когда вам нужно преобразовать NA в вектор в ноль, вы можете сделать:

na.zero(some.vector)

более общий подход к использованию replace() в матрице или векторе для замены NA to 0

например:

> x <- c(1,2,NA,NA,1,1)
> x1 <- replace(x,is.na(x),0)
> x1
[1] 1 2 0 0 1 1

это также альтернатива использованию ifelse() на dplyr

df = data.frame(col = c(1,2,NA,NA,1,1))
df <- df %>%
   mutate(col = replace(col,is.na(col),0))

С dplyr 0.5.0, вы можете использовать coalesce функция, которая может быть легко интегрирована в делать coalesce(vec, 0). Это заменяет все NAs в vec С 0:

говорят, у нас есть фрейм данных с NAs:

library(dplyr)
df <- data.frame(v = c(1, 2, 3, NA, 5, 6, 8))

df
#    v
# 1  1
# 2  2
# 3  3
# 4 NA
# 5  5
# 6  6
# 7  8

df %>% mutate(v = coalesce(v, 0))
#   v
# 1 1
# 2 2
# 3 3
# 4 0
# 5 5
# 6 6
# 7 8

другой пример, с использованием imputeTS:

library(imputeTS)
na.replace(yourDataframe, 0)

Если вы хотите заменить NAs в факторных переменных, это может быть полезно:

n <- length(levels(data.vector))+1

data.vector <- as.numeric(data.vector)
data.vector[is.na(data.vector)] <- n
data.vector <- as.factor(data.vector)
levels(data.vector) <- c("level1","level2",...,"leveln", "NAlevel") 

он преобразует факторный вектор в числовой вектор и добавляет еще один искусственный числовой факторный уровень, который затем преобразуется обратно в факторный вектор с одним дополнительным "NA-уровнем" по вашему выбору.


прокомментировал бы пост @ianmunoz, но у меня недостаточно репутации. Вы можете объединить dplyr ' s mutate_each и replace заботиться о NA до 0 замена. Использование фрейма данных из ответа @aL3xa...

> m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
> d <- as.data.frame(m)
> d

    V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  8  1  9  6  9 NA  8  9   8
2   8  3  6  8  2  1 NA NA  6   3
3   6  6  3 NA  2 NA NA  5  7   7
4  10  6  1  1  7  9  1 10  3  10
5  10  6  7 10 10  3  2  5  4   6
6   2  4  1  5  7 NA NA  8  4   4
7   7  2  3  1  4 10 NA  8  7   7
8   9  5  8 10  5  3  5  8  3   2
9   9  1  8  7  6  5 NA NA  6   7
10  6 10  8  7  1  1  2  2  5   7

> d %>% mutate_each( funs_( interp( ~replace(., is.na(.),0) ) ) )

    V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  8  1  9  6  9  0  8  9   8
2   8  3  6  8  2  1  0  0  6   3
3   6  6  3  0  2  0  0  5  7   7
4  10  6  1  1  7  9  1 10  3  10
5  10  6  7 10 10  3  2  5  4   6
6   2  4  1  5  7  0  0  8  4   4
7   7  2  3  1  4 10  0  8  7   7
8   9  5  8 10  5  3  5  8  3   2
9   9  1  8  7  6  5  0  0  6   7
10  6 10  8  7  1  1  2  2  5   7

мы используем стандартную оценку (SE) здесь, поэтому нам нужно подчеркивание на "funs_.- Мы также используем lazyeval ' s interp/~ и . ссылки "все, с чем мы работаем", т. е. фрейм данных. Теперь есть нули!


можно использовать replace()

например:

> x <- c(-1,0,1,0,NA,0,1,1)
> x1 <- replace(x,5,1)
> x1
[1] -1  0  1  0  1  0  1  1

> x1 <- replace(x,5,mean(x,na.rm=T))
> x1
[1] -1.00  0.00  1.00  0.00  0.29  0.00 1.00  1.00

еще один dplyr вариант трубы совместимый с tidyrметод replace_na это работает для нескольких столбцов:

require(dplyr)
require(tidyr)

m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
d <- as.data.frame(m)

myList <- setNames(lapply(vector("list", ncol(d)), function(x) x <- 0), names(d))

df <- d %>% replace_na(myList)

вы можете легко ограничить, например, числовые столбцы:

d$str <- c("string", NA)

myList <- myList[sapply(d, is.numeric)]

df <- d %>% replace_na(myList)

эта простая функция извлекается из Datacamp могу помочь:

replace_missings <- function(x, replacement) {
  is_miss <- is.na(x)
  x[is_miss] <- replacement

  message(sum(is_miss), " missings replaced by the value ", replacement)
  x
}

затем

replace_missings(df, replacement = 0)