如何在R数据框中将NA值替换为零?

我有一个数据框,有些列有NA值。

如何将这些NA值替换为零?

1939620 次浏览

对于单个向量:

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

对于data.frame,从上面创建一个函数,然后将其apply到列。

下次请提供一个可重现的例子,详见此处:

如何制作一个伟大的R可重复的例子?

见我在@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 V101   4  3 NA  3  7  6  6 10  6   52   9  8  9  5 10 NA  2  1  7   23   1  1  6  3  6 NA  1  4  1   64  NA  4 NA  7 10  2 NA  4  1   85   1  2  4 NA  2  6  2  6  7   46  NA  3 NA NA 10  2  1 10  8   47   4  4  9 10  9  8  9  4 10  NA8   5  8  3  2  1  4  5  9  4   79   3  9 10  1  9  9 10  5  3   310  4  2  2  5 NA  9  7  2  5   5
> d[is.na(d)] <- 0
> dV1 V2 V3 V4 V5 V6 V7 V8 V9 V101   4  3  0  3  7  6  6 10  6   52   9  8  9  5 10  0  2  1  7   23   1  1  6  3  6  0  1  4  1   64   0  4  0  7 10  2  0  4  1   85   1  2  4  0  2  6  2  6  7   46   0  3  0  0 10  2  1 10  8   47   4  4  9 10  9  8  9  4 10   08   5  8  3  2  1  4  5  9  4   79   3  9 10  1  9  9 10  5  3   310  4  2  2  5  0  9  7  2  5   5

没有必要应用apply。=)

编辑

你还应该看看norm包。它有很多很好的缺失数据分析功能。=)

您可以使用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

如果我们尝试在导出时替换NA,例如写入csv时,那么我们可以使用:

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

dplyr示例:

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

备注:这适用于每个选定的列,如果我们需要对所有列执行此操作,请参阅@陈志立使用mutate_each的答案。

我知道这个问题已经回答了,但这样做可能对一些人更有用:

定义这个函数:

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

现在,每当你需要将向量中的NA转换为零时,你可以这样做:

na.zero(some.vector)

在矩阵或向量中使用replace()来替换NA0的更通用方法

例如:

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

这也是在dplyr中使用ifelse()的替代方法

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

如果你想在因子变量中替换NAs,这可能很有用:

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

它将一个因子向量转换为一个数值向量,并添加另一个人工数值因子级别,然后将其转换回一个因子向量,其中包含您选择的一个额外的“NA级别”。

会对@ian免疫z的帖子发表评论,但我没有足够的声誉。您可以将dplyrmutate_eachreplace组合起来,以处理NA0的替换。使用@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 V101   4  8  1  9  6  9 NA  8  9   82   8  3  6  8  2  1 NA NA  6   33   6  6  3 NA  2 NA NA  5  7   74  10  6  1  1  7  9  1 10  3  105  10  6  7 10 10  3  2  5  4   66   2  4  1  5  7 NA NA  8  4   47   7  2  3  1  4 10 NA  8  7   78   9  5  8 10  5  3  5  8  3   29   9  1  8  7  6  5 NA NA  6   710  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 V101   4  8  1  9  6  9  0  8  9   82   8  3  6  8  2  1  0  0  6   33   6  6  3  0  2  0  0  5  7   74  10  6  1  1  7  9  1 10  3  105  10  6  7 10 10  3  2  5  4   66   2  4  1  5  7  0  0  8  4   47   7  2  3  1  4 10  0  8  7   78   9  5  8 10  5  3  5  8  3   29   9  1  8  7  6  5  0  0  6   710  6 10  8  7  1  1  2  2  5   7

我们在这里使用标准评估(SE),这就是为什么我们需要在“funs_”上下划线。我们还使用lazyevalinterp/~.引用“我们正在使用的一切”,即数据帧。现在有零了!

使用dplyr 0.5.0,您可以使用coalesce函数,该函数可以通过执行coalesce(vec, 0)轻松集成到%>%管道中。这将vec中的所有NAs替换为0:

假设我们有一个带有NA的数据帧:

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

另一个与tidyr方法replace_na兼容的dplyr管道选项,适用于多个列:

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)

另一个使用缺陷包的示例:

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

dplyr混合选项现在比Base R子集重新分配快30%左右。在100Mdatapoint dataframemutate_all(~replace(., is.na(.), 0))比base Rd[is.na(d)] <- 0选项快半秒。需要特别避免的是使用ifelse()if_else()。(完整的600次试验分析运行超过4.5小时,主要是因为包括了这些方法。)请参阅下面的基准分析以获取完整的结果。

如果你正在处理大量的数据帧,data.table是最快的选择:比标准的基地R方法快40%。它还修改了现有的数据,有效地允许你一次处理近两倍的数据。


其他有用的tidyverse替换方法的聚类

地点:

  • 索引mutate_at(c(5:10), ~replace(., is.na(.), 0))
  • 直接引用mutate_at(vars(var5:var10), ~replace(., is.na(.), 0))
  • 固定匹配mutate_at(vars(contains("1")), ~replace(., is.na(.), 0))
    • 或者代替contains(),尝试ends_with()starts_with()
  • 模式匹配mutate_at(vars(matches("\\d{2}")), ~replace(., is.na(.), 0))

有条件:
(只改变单一类型,而不管其他类型。)

  • 整数mutate_if(is.integer, ~replace(., is.na(.), 0))
  • 数字mutate_if(is.numeric, ~replace(., is.na(.), 0))
  • 字符串mutate_if(is.character, ~replace(., is.na(.), 0))

完整的分析-

为dplyr 0.8.0更新:函数使用purrr格式~符号:替换不建议使用的funs()参数。

测试的方法:

# 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## dplyrdplyr_if_else      <- function(x) { mutate_all(x, ~if_else(is.na(.), 0, .)) }dplyr_coalesce     <- function(x) { mutate_all(x, ~coalesce(., 0)) }
## tidyrtidyr_replace_na   <- function(x) { replace_na(x, as.list(setNames(rep(0, 10), as.list(c(paste0("var", 1:10)))))) }
## hybridhybrd.ifelse     <- function(x) { mutate_all(x, ~ifelse(is.na(.), 0, .)) }hybrd.replace_na <- function(x) { mutate_all(x, ~replace_na(., 0)) }hybrd.replace    <- function(x) { mutate_all(x, ~replace(., is.na(.), 0)) }hybrd.rplc_at.idx<- function(x) { mutate_at(x, c(1:10), ~replace(., is.na(.), 0)) }hybrd.rplc_at.nse<- function(x) { mutate_at(x, vars(var1:var10), ~replace(., is.na(.), 0)) }hybrd.rplc_at.stw<- function(x) { mutate_at(x, vars(starts_with("var")), ~replace(., is.na(.), 0)) }hybrd.rplc_at.ctn<- function(x) { mutate_at(x, vars(contains("var")), ~replace(., is.na(.), 0)) }hybrd.rplc_at.mtc<- function(x) { mutate_at(x, vars(matches("\\d+")), ~replace(., is.na(.), 0)) }hybrd.rplc_if    <- function(x) { mutate_if(x, is.numeric, ~replace(., is.na(.), 0)) }
# data.tablelibrary(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) }DT.nafill        <- function(x) { nafill(df, fill=0)}DT.setnafill     <- function(x) { setnafill(df, fill=0)}

此分析的代码:

library(microbenchmark)# 20% NA filled dataframe of 10 Million rows and 10 columnsset.seed(42) # to recreate the exact dataframedfN <- as.data.frame(matrix(sample(c(NA, as.numeric(1:4)), 1e7*10, replace = TRUE),dimnames = list(NULL, paste0("var", 1:10)),ncol = 10))# Running 600 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)),hybrd.replace_na = hybrd.replace_na(copy(dfN)),baseR.sbst.rssgn = baseR.sbst.rssgn(copy(dfN)),baseR.replace    = baseR.replace(copy(dfN)),dplyr_coalesce   = dplyr_coalesce(copy(dfN)),tidyr_replace_na = tidyr_replace_na(copy(dfN)),hybrd.replace    = hybrd.replace(copy(dfN)),hybrd.rplc_at.ctn= hybrd.rplc_at.ctn(copy(dfN)),hybrd.rplc_at.nse= hybrd.rplc_at.nse(copy(dfN)),baseR.for        = baseR.for(copy(dfN)),hybrd.rplc_at.idx= hybrd.rplc_at.idx(copy(dfN)),DT.for.set.nms   = DT.for.set.nms(copy(dfN)),DT.for.set.sqln  = DT.for.set.sqln(copy(dfN)),times = 600L)

结果摘要

> print(perf_results)Unit: millisecondsexpr       min        lq     mean   median       uq      max nevalhybrd.ifelse 6171.0439 6339.7046 6425.221 6407.397 6496.992 7052.851   600dplyr_if_else 3737.4954 3877.0983 3953.857 3946.024 4023.301 4539.428   600hybrd.replace_na 1497.8653 1706.1119 1748.464 1745.282 1789.804 2127.166   600baseR.sbst.rssgn 1480.5098 1686.1581 1730.006 1728.477 1772.951 2010.215   600baseR.replace 1457.4016 1681.5583 1725.481 1722.069 1766.916 2089.627   600dplyr_coalesce 1227.6150 1483.3520 1524.245 1519.454 1561.488 1996.859   600tidyr_replace_na 1248.3292 1473.1707 1521.889 1520.108 1570.382 1995.768   600hybrd.replace  913.1865 1197.3133 1233.336 1238.747 1276.141 1438.646   600hybrd.rplc_at.ctn  916.9339 1192.9885 1224.733 1227.628 1268.644 1466.085   600hybrd.rplc_at.nse  919.0270 1191.0541 1228.749 1228.635 1275.103 2882.040   600baseR.for  869.3169 1180.8311 1216.958 1224.407 1264.737 1459.726   600hybrd.rplc_at.idx  839.8915 1189.7465 1223.326 1228.329 1266.375 1565.794   600DT.for.set.nms  761.6086  915.8166 1015.457 1001.772 1106.315 1363.044   600DT.for.set.sqln  787.3535  918.8733 1017.812 1002.042 1122.474 1321.860   600

结果框图

ggplot(perf_results, aes(x=expr, y=time/10^9)) +geom_boxplot() +xlab('Expression') +ylab('Elapsed Time (Seconds)') +scale_y_continuous(breaks = seq(0,7,1)) +coord_flip()

经过时间的箱线图比较

试验的颜色编码散点图(对数刻度上的y轴)

qplot(y=time/10^9, data=perf_results, colour=expr) +labs(y = "log10 Scaled Elapsed Time per Trial (secs)", x = "Trial Number") +coord_cartesian(ylim = c(0.75, 7.5)) +scale_y_log10(breaks=c(0.75, 0.875, 1, 1.25, 1.5, 1.75, seq(2, 7.5)))

所有审判时间的散点图

关于其他高绩效者的说明

当数据集变大时,Tidyr"sreplace_na历来在前面拉出。随着当前100M数据点的收集,它的性能几乎与基地R For Loop一样好。我很想知道不同大小的数据帧会发生什么。

可以在这里找到mutatesummarize_at_all函数变体的其他示例:https://rdrr.io/cran/dplyr/man/summarise_all.html此外,我在这里找到了有用的演示和示例集合:https://blog.exploratory.io/dplyr-0-5-is-awesome-heres-why-be095fd4eb8a

表彰和赞赏

特别感谢:

  • Tyler RinkerAkrun用于演示微基准测试。
  • alexis_laz感谢你帮助我理解local()的使用,以及(在弗兰克的耐心帮助下)沉默胁迫在加速许多这些方法中所起的作用。
  • ArthurYep为poke添加更新的coalesce()函数并更新分析。
  • 格雷戈尔推动找出data.table功能足够好,最终将它们包括在阵容中。
  • 基地R For循环:alexis_laz
  • data.table对于循环:Matt_Dowle
  • 罗曼解释了is.numeric()真正测试的是什么。

(当然,如果你觉得这些方法有用,也请伸出手给他们投票。

关于我使用数字的说明:如果你有一个纯整数数据集,你所有的函数都会运行得更快。有关更多信息,请参阅alexiz_laz的工作。IRL,我不记得遇到过包含超过10-15%整数的数据集,所以我在全数字数据帧上运行这些测试。

使用的硬件3.9 GHz CPU,24 GB RAM

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)

也可以使用tidyr::replace_na

    library(tidyr)df <- df %>% mutate_all(funs(replace_na(.,0)))

编辑(dplyr>1.0.0):

df %>% mutate(across(everything(), .fns = ~replace_na(.,0)))

专用函数nafillsetnafill,为此目的在data.table中。只要可用,它们就会在多个线程上分发要计算的列。

library(data.table)
ans_df <- nafill(df, fill=0)
# or even faster, in-placesetnafill(df, fill=0)

写它的一个简单方法是从hablar开始的if_na

library(dplyr)library(hablar)
df <- tibble(a = c(1, 2, 3, NA, 5, 6, 8))
df %>%mutate(a = if_na(a, 0))

它返回:

      a<dbl>1     12     23     34     05     56     67     8

如果您想在更改特定列(在本例中为列V3)中的NAs后分配新名称,请使用您也可以这样做

my.data.frame$the.new.column.name <- ifelse(is.na(my.data.frame$V3),0,1)

要替换数据框中的所有NAs,您可以使用:

df %>% replace(is.na(.), 0)

cleaner包有一个na_replace()泛型,默认将数值替换为零,逻辑替换为FALSE,日期替换为今天,等等:

library(dplyr)library(cleaner)
starwars %>% na_replace()na_replace(starwars)

它甚至支持矢量化替换:

mtcars[1:6, c("mpg", "hp")] <- NAna_replace(mtcars, mpg, hp, replacement = c(999, 123))

文档:https://msberends.github.io/cleaner/reference/na_replace.html

我想添加一个使用流行#0包的下一个解决方案。

library(Hmisc)data(airquality)# imputing with 0 - all columns# although my favorite one for simple imputations is Hmisc::impute(x, "random")> dd <- data.frame(Map(function(x) Hmisc::impute(x, 0), airquality))> str(dd[[1]])'impute' Named num [1:153] 41 36 12 18 0 28 23 19 8 0 ...- attr(*, "names")= chr [1:153] "1" "2" "3" "4" ...- attr(*, "imputed")= int [1:37] 5 10 25 26 27 32 33 34 35 36 ...> dd[[1]][1:10]1   2   3   4   5   6   7   8   9  1041  36  12  18  0*  28  23  19   8  0*

可以看到,所有元数据都被分配为属性。因此它可以稍后使用。

data.frame不需要通过突变创建新列。

library(tidyverse)k <- c(1,2,80,NA,NA,51)j <- c(NA,NA,3,31,12,NA)        
df <- data.frame(k,j)%>%replace_na(list(j=0))#convert only column j, for example    

结果

k   j1   02   080  3NA  31NA  1251  0

dplyr>=1.0.0

在较新版本的dplyr中:

跨()取代了“作用域变体”家族,如summarise_at(),summarise_if()和summarise_all()。

df <- data.frame(a = c(LETTERS[1:3], NA), b = c(NA, 1:3))
library(tidyverse)
df %>%mutate(across(where(anyNA), ~ replace_na(., 0)))
a b1 A 02 B 13 C 24 0 3

此代码将强制0成为第一列中的字符。要根据列类型替换NA,您可以在where中使用类似Purrr的公式:

df %>%mutate(across(where(~ anyNA(.) & is.character(.)), ~ replace_na(., "0")))

这并不是一个新的解决方案,但我喜欢编写内联lambda来处理我无法让包完成的事情。在这种情况下,

df %>%(function(x) { x[is.na(x)] <- 0; return(x) })

因为R不会像你在Python中看到的那样“通过对象传递”,所以这个解决方案不会修改原始变量df,因此与大多数其他解决方案一样,但对特定包的复杂知识的需求要少得多。

注意函数定义周围的括号!虽然对我来说似乎有点多余,但由于函数定义被花括号包围,因此需要在magrittr的括号内定义内联函数。

不需要使用任何图书馆。

df <- data.frame(a=c(1,3,5,NA))
df$a[is.na(df$a)] <- 0
df

替换数据帧中的is.na&NULL。

  1. 列数据帧

A$name[is.na(A$name)]<-0

A$name[is.na(A$name)]<-"NA"

  1. 与所有数据帧

df[is.na(df)]<-0

  1. 将数据帧中的na替换为空白

df[is.na(df)]<-""

  1. 将NULL替换为NA

df[is.null(df)]<-NA

我亲自使用它,效果很好:

players_wd$APPROVED_WD[is.na(players_wd$APPROVED_WD)] <- 0