如何在R数据帧中用零替换NA值?


Answers:


878

在@ 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包装。它具有许多出色的功能,可用于丢失数据分析。=)


2
我昨天已经在您发布它之前尝试了此代码,但没有用。因此,我发布了问题。但是我尽力而为,并且表现出色。我想我做错了什么。
Renato Dinhani

12
@RenatoDinhaniConceição:如果您已经尝试过某些东西,则在提出问题时共享该信息将很有帮助;它有助于缩小问题所在。
亚伦(Aaron)

2
d [is.na(d)] <-0对我来说没有意义。似乎倒退了吗?R如何处理此语句?
user798719 2015年

13
@ user798719-“ <-”是R的赋值运算符,可以理解为:在右侧进行一些操作,然后将其分配给左侧的位置/名称。在这种情况下,我们实际上并没有“做任何事情”,只是将它们设为零。左侧说:看一下d对象,在d对象内部(方括号),找到所有返回TRUE的元素(is.na(d)为每个元素返回一个逻辑)。找到它们后,将其替换为“ 0”(将它们赋值)。这将保留所有非NA,并仅将其替换为缺失项。
Twitch_City 2015年

3
并且...如果您有数据框,并且只想将替换应用于特定的数字矢量(不说...带有NA的字符串):df[19:28][is.na(df[19:28])] <- 0
jtdoud

298

dplyr杂交选项现在比Base R子集重新分配的速度快30%。在100M数据点上,数据帧的mutate_all(~replace(., is.na(.), 0))运行速度比基本R d[is.na(d)] <- 0选项快半秒。特别要避免的是使用ifelse()if_else()。(由于使用了这些方法,因此完整的600次试用分析要花4.5个小时以上。)有关完整结果,请参阅下面的基准分析。

如果您在海量数据帧中苦苦挣扎,那data.table是最快的选择:比标准Base 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
## dplyr
dplyr_if_else      <- function(x) { mutate_all(x, ~if_else(is.na(.), 0, .)) }
dplyr_coalesce     <- function(x) { mutate_all(x, ~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, ~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.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) }
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 columns
set.seed(42) # to recreate the exact dataframe
dfN <- 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: milliseconds
              expr       min        lq     mean   median       uq      max neval
      hybrd.ifelse 6171.0439 6339.7046 6425.221 6407.397 6496.992 7052.851   600
     dplyr_if_else 3737.4954 3877.0983 3953.857 3946.024 4023.301 4539.428   600
  hybrd.replace_na 1497.8653 1706.1119 1748.464 1745.282 1789.804 2127.166   600
  baseR.sbst.rssgn 1480.5098 1686.1581 1730.006 1728.477 1772.951 2010.215   600
     baseR.replace 1457.4016 1681.5583 1725.481 1722.069 1766.916 2089.627   600
    dplyr_coalesce 1227.6150 1483.3520 1524.245 1519.454 1561.488 1996.859   600
  tidyr_replace_na 1248.3292 1473.1707 1521.889 1520.108 1570.382 1995.768   600
     hybrd.replace  913.1865 1197.3133 1233.336 1238.747 1276.141 1438.646   600
 hybrd.rplc_at.ctn  916.9339 1192.9885 1224.733 1227.628 1268.644 1466.085   600
 hybrd.rplc_at.nse  919.0270 1191.0541 1228.749 1228.635 1275.103 2882.040   600
         baseR.for  869.3169 1180.8311 1216.958 1224.407 1264.737 1459.726   600
 hybrd.rplc_at.idx  839.8915 1189.7465 1223.326 1228.329 1266.375 1565.794   600
    DT.for.set.nms  761.6086  915.8166 1015.457 1001.772 1106.315 1363.044   600
   DT.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 “的replace_na历来在前面拉出。当前要收集100M个数据点,它的性能几乎与Base R For Loop一样好。我很好奇看到不同大小的数据帧会发生什么。

额外的例子mutate,并summarize _at_all功能变异可以在这里找到:https://rdrr.io/cran/dplyr/man/summarise_all.html 此外,我还发现有用的演示和实例集合在这里:HTTPS://blog.exploratory。 io / dplyr-0-5-很棒-为什么-be095fd4eb8a

归因与欣赏

特别感谢:

  • Tyler RinkerAkrun演示了微基准测试
  • alexis_laz致力于帮助我理解的使用local(),并且(在弗兰克的耐心帮助下)也发挥了沉默的强制作用在加速这些方法中的作用。
  • ArthurYip为戳添加更多内容 coalesce()功能并更新分析。
  • 要求微调充分了解这些data.table功能,以便最终将它们包括在阵容中。
  • Base R For循环:alexis_laz
  • data.table For循环:Matt_Dowle
  • Roman解释了is.numeric()真正的测试。

(当然,如果您发现这些方法有用,也请伸手给他们投票)。

关于我使用数字的注意事项: 如果确实有一个纯整数数据集,则所有函数的运行速度都会更快。有关更多信息,请参见 alexiz_laz的作品。IRL,我想不起遇到一个包含超过10-15%整数的数据集的情况,因此我正在全数字数据帧上运行这些测试。

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


2
@Frank-感谢您发现这种差异。所有引用均已清理,结果已完全在一台计算机上重新运行并重新过帐。
leerssej

好,谢谢。此外,我认为df1[j][is.na(df1[j])] = 0是错误的,应该是df1[[j]][is.na(df1[[j]])] = 0
Frank

哦,现在我看到您已经编写了两次,每个基准测试都不同。无论如何,forLp_Sbst似乎并不是任何人都应该考虑采用的方法vsforLp_smplfSbst
Frank

1
@UweBlock-一个好问题:它允许我对所有在完全相同的数据帧上运行的函数进行子集左分配操作。由于必须将局部函数包装在该函数周围,因此以科学的名义[一份工作,一份工作!]我将其包装在所有函数周围,以确保公平地竞争。有关更多信息,请参见此处:stackoverflow.com/questions/41604711/…我已经精简了先前颇为棘手的答案-但是,再次进行讨论的那部分内容将非常有用。谢谢!
leerssej

1
@ArthurYip-我已经添加了该coalesce()选项并一直重新运行。谢谢您的更新。
leerssej

128

对于单个向量:

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

对于data.frame,请根据上述内容创建一个函数,然后将apply其移至各列。

请下次提供可复制的示例,如下所示:

如何制作出色的R可复制示例?


18
is.na是通用函数,具有用于data.frame类对象的方法。所以这个也可以在data.frames上工作!
aL3xa

3
当我methods(is.na)第一次跑步时,我就像哇!??。我喜欢这样的事情发生时!=)
aL3xa 2011年

9
假设您有一个名为df的数据框,而不是一个矢量,并且只想替换一个名为X3的列中的缺失观测值。您可以使用以下行执行此操作:df $ X3 [is.na(df $ X3)] <-0
Mark Miller

8
假设您只想在名为my.df的数据帧的第4-6列中用0替换NA。您可以使用:my.df [,4:6] [is.na(my.df [,4:6])] <-0
Mark Miller

为什么将'x'传递给is.na(x),有没有办法告诉R中的哪些库例程是矢量化的?
uh_big_mike_boi,2016年

73

dplyr示例:

library(dplyr)

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

注意:这对每个选定的列都有效,如果我们需要对所有列进行此操作,请参阅@reidjax使用mutate_each的答案。


57

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

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

47

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

定义此功能:

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

现在,每当需要将向量中的NA转换为零时,都可以执行以下操作:

na.zero(some.vector)

22

dplyr0.5.0中,您可以使用coalesce可以轻松集成到%>%管道中的函数coalesce(vec, 0)。这将所有NA替换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

我测试了合并,它的性能与替换差不多。到目前为止,coalcece命令是最简单的!
亚瑟·叶

如果您将展示如何将其应用于2+列的所有列,那么这将很有用。
jangorecki

21

使用的更一般的方法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

这也是ifelse()dplyr

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

1
我的专栏文章很重要,因此我必须添加替换值levels(A$x) <- append(levels(A$x), "notAnswered") A$x <- replace(A$x,which(is.na(A$x)),"notAnswered")
Climbs_lika_Spyder

1
which这里不需要,您可以使用x1 <- replace(x,is.na(x),1)
lmo

我尝试了此线程中提出的许多方法来替换NA0大数据框中的特定列,并且此功能replace()最有效,同时也最简单。
德克(Duc)

19

也可以使用tidyr::replace_na

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

9

使用imputeTS包的另一个示例:

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

9

如果要替换因子变量中的NA,这可能会很有用:

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级别”的因子向量。


8

本来会对@ianmunoz的帖子发表评论,但我没有足够的声誉。您可以结合dplyrmutate_each,并replace要照顾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 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_” 下划线。我们还使用lazyevalinterp/ ~.引用“我们正在使用的所有工具”,即数据框。现在有零!


4

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

6
是的,但仅当您知道NA向量中s 的索引时才实用。像您的示例中的小矢量一样很好。
dardisco

4
@dardisco x1 <- replace(x,is.na(x),1)将在不显式列出索引值的情况下工作。
lmo

4

另一个与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)

4

专用功能(nafill/ setnafill)是最新data.table版本

install.packages("data.table", repos="https://Rdatatable.gitlab.io/data.table")
library(data.table)
ans_df = nafill(df, fill=0)
setnafill(df, fill=0) # this one updates in-place

对于那些反对的人,也请提供反馈,以便我的回答可以得到改善。
jangorecki

3

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)

3

一个简单的写法是if_nafrom hablar

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     1
2     2
3     3
4     0
5     5
6     6
7     8


1

如果要在此情况下在第V3列的特定列中更改NA后分配新名称,请使用您也可以这样

my.data.frame$the.new.column.name <- ifelse(is.na(my.data.frame$V3),0,1)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.