Как заменить значения NA нулями в фрейме данных R?
у меня есть data.frame
и некоторые столбцы имеют NA
значения. Я хочу заменить NA
s с нулями. Как я это делаю?
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]")
цветовая разметка испытаний (в масштабе журнала)
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))
заметка о других высоких исполнителях
когда данных становятся больше, 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
это к колоннам.
просьба предоставить воспроизводимый пример в следующий раз, как описано здесь:
dplyr пример:
library(dplyr)
df1 <- df1 %>%
mutate(myCol1 = if_else(is.na(myCol1), 0, myCol1))
Примечание: это работает на выбранный столбец, если нам нужно сделать это для всего столбца, см. @reidjaxответ с помощью mutate_each.
Если мы пытаемся заменить NA
s при экспорте, например при записи в 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:
говорят, у нас есть фрейм данных с NA
s:
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
Если вы хотите заменить 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)