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

时间:2011-11-17 03:45:45

标签: r dataframe na

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

如何用零替换这些NA值?

23 个答案:

答案 0 :(得分:747)

在@ 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包。它有很多很好的功能,可用于缺少数据分析。 =)

答案 1 :(得分:209)

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替换方法的聚类

位置:

  • index 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.fnafill       <- function(x) { fnafill(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()

Boxplot Comparison of Elapsed Time

试验的颜色编码散点图(对数刻度为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)))

Scatterplot of All Trial Times

关于其他高绩效者的说明

当数据集变大时, Tidyr '的replace_na历史上已经被淘汰出局。通过当前收集的50M数据点,它的执行几乎与 Base R For循环一样好。我很想知道不同大小的数据帧会发生什么。

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()的使用,以及(在Frank的患者帮助下)静音强制在加速许多这些方法中所起的作用。
  • ArthurYip,用于添加较新的coalesce()函数并更新分析。
  • 格雷戈尔轻推,弄清楚data.table的功能,最终将他们列入阵容。
  • Base R For循环:alexis_laz
  • data.table For Loops:Matt_Dowle

(当然,如果您发现这些方法有用,请与他们联系并给予他们投票。)

关于我使用Numerics的注意事项: 如果你有一个纯整数数据集,你的所有函数都会运行得更快。有关详细信息,请参阅alexiz_laz's work。 IRL,我不记得遇到包含超过10-15%整数的数据集,所以我在完全数字数据帧上运行这些测试。

使用的硬件 具有24 GB RAM的3.9 GHz CPU

答案 2 :(得分:111)

对于单个载体:

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

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

请在下次详细说明下提供可重复的示例:

How to make a great R reproducible example?

答案 3 :(得分:66)

dplyr示例:

library(dplyr)

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

注意:这适用于所选列,如果我们需要对所有列执行此操作,请参阅 @reidjax 使用mutate_each的答案

答案 4 :(得分:50)

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

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

答案 5 :(得分:44)

我知道问题已经得到解答,但这样做对某些人来说可能更有用:

定义此功能:

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

现在,无论何时需要将向量中的NA转换为零,您都可以:

na.zero(some.vector)

答案 6 :(得分:20)

使用dplyr 0.5.0,您可以使用coalesce功能,可以通过%>%轻松集成到coalesce(vec, 0)管道中。这将vec中的所有NA替换为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

答案 7 :(得分:19)

在矩阵或向量中使用replace()NA替换为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))

答案 8 :(得分:8)

使用 imputeTS 包的另一个例子:

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

答案 9 :(得分:8)

如果要在因子变量中替换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级别”。

答案 10 :(得分:7)

会对@ ianmunoz的帖子发表评论,但我没有足够的声誉。您可以合并dplyr的{​​{1}}和mutate_each来处理replaceNA的替换。使用来自@ aL3xa答案的数据框...

0

我们在这里使用标准评估(SE),这就是为什么我们需要“> 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 ”的下划线。我们还使用funs_的{​​{1}} / lazyevalinterp引用“我们正在使用的所有内容”,即数据框。现在有零!

答案 11 :(得分:5)

也可以使用tidyr::replace_na

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

答案 12 :(得分: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)

答案 13 :(得分: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

答案 14 :(得分:4)

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

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

答案 15 :(得分: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)

答案 16 :(得分:3)

用于此目的的专用功能(Collectors.toList() / nafill)是最新的setnafill版本

data.table

答案 17 :(得分:3)

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

starwars %>% na_replace()
na_replace(starwars)

它甚至支持矢量替换:

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

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

答案 18 :(得分:0)

一种简单的编写方法是使用if_na中的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

答案 19 :(得分:0)

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

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

答案 20 :(得分:0)

在 data.frame 中,不需要通过 mutate 创建新列。

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   j
1   0           
2   0           
80  3           
NA  31          
NA  12          
51  0   

答案 21 :(得分:0)

dplyr >= 1.0.0

在较新版本的 dplyr 中:

<块引用>

across() 取代了诸如 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 b
1 A 0
2 B 1
3 C 2
4 0 3

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

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

答案 22 :(得分:0)

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

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

因为 R 不会像您在 Python 中看到的那样“通过对象传递”,该解决方案不会修改原始变量 df,因此与大多数其他解决方案的作用完全相同,但是对特定包的复杂知识的需求要少得多。

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