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

Ren*_*ani 673 r missing-data dataframe na imputation

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

如何NA用零替换这些值?

aL3*_*3xa 818

在@ 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
Run Code Online (Sandbox Code Playgroud)

没有必要申请apply.=)

编辑

您还应该看看norm包装.它有很多很好的功能,可用于缺少数据分析.=)

  • @ user798719 - "< - "是R的赋值运算符,可以理解为:在右侧执行某些操作,然后将其分配给左侧的位置/名称.在这种情况下,我们并没有真正"做"任何事情 - 只做零.左边是这样说:看看d对象,在d对象里面(方括号),找到所有返回TRUE的元素(is.na(d)为每个元素返回一个逻辑).一旦找到它们,用值0替换它们("赋值给它们").这样就留下了所有非NA,并且只替换了缺失的那些. (13认同)
  • @RenatoDinhaniConceição:如果你已经尝试了一些东西,当你提出问题时分享这些信息会很有帮助; 它有助于缩小问题的范围. (12认同)
  • 并且...如果您有数据框,并且只想将替换应用于特定的数字矢量(不说...带有NA的字符串):`df [19:28] [is.na(df [19:28] )] &lt;-0` (3认同)
  • 我在你发布它之前已经尝试过这段代码而没有工作.因为我发布了这个问题.但我尝试了解并且工作得很好.我想我做错了什么. (2认同)
  • d [is.na(d)] < - 0对我没有意义.好像倒退了?R如何处理这个陈述? (2认同)

lee*_*sej 262

混合dplyr/Base R选项:mutate_all(~replace(., is.na(.), 0))速度是基本R d[is.na(d)] <- 0选项的两倍多.(请参阅下面的基准分析.)

如果您正在努力应对海量数据帧,那么ifelse()它是最快的选择:比dplyr少30%的时间,比Base R方法快3倍.它还可以修改数据,有效地允许您同时处理几乎两倍的数据.


其他有用的tidyverse替换方法的集群

Locationally:

  • 指数 if_else()
  • 直接参考 data.table
  • 固定比赛 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))

完整分析 -

测试方法:

# 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)}
Run Code Online (Sandbox Code Playgroud)

此分析的代码:

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
)
Run Code Online (Sandbox Code Playgroud)

结果摘要

> 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
Run Code Online (Sandbox Code Playgroud)

结果箱图(以对数刻度)

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()
Run Code Online (Sandbox Code Playgroud)

Boxplot经过时间的比较

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

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)))
Run Code Online (Sandbox Code Playgroud)

所有审判时间的散点图

关于其他高绩效者的说明

当数据集变大时,Tidyrmutate_if(is.numeric, ~replace(., is.na(.), 0))历史就在前面退出.通过当前收集的50M数据点,它的表现几乎与Base R For Loop一样.我很想知道不同大小的数据帧会发生什么.

额外的例子mutate_if(is.character, ~replace(., is.na(.), 0)),并~ funs()replace_na功能变异可以在这里找到:https://rdrr.io/cran/dplyr/man/summarise_all.html 此外,我还发现有用的演示和实例集合在这里:HTTPS://blog.exploratory. IO/dplyr-0-5-是-真棒-继承人-为什么- be095fd4eb8a

归因和赞赏

特别感谢:

  • Tyler RinkerAkrun展示微型基准.
  • alexis_laz致力于帮助我理解使用mutate,以及(在Frank的患者帮助下)静音强制在加速许多这些方法中的作用.
  • ArthurYip为poke添加更新的summarize功能并更新分析.
  • 格雷戈尔轻推得出_at足够好的功能,最终将他们纳入阵容.
  • Base R For循环:alexis_laz
  • data.table For循环:Matt_Dowle

(当然,如果你发现这些方法有用的话,也请到达并给予他们投票.)

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

  • dplyr 1.0.2 的更新删除了 `mutate_at` 和 `mutate_all`: `function(x) { mutate(across(x, ~replace_na(., 0))) }` (5认同)
  • @Frank-感谢您发现这种差异。所有引用均已清理,结果已完全在一台计算机上重新运行并重新过帐。 (2认同)

Ari*_*man 118

对于单个向量:

x <- c(1,2,NA,4,5)
x[is.na(x)] <- 0
Run Code Online (Sandbox Code Playgroud)

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

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

如何制作一个很好的R可重复的例子?

  • `is.na`是泛型函数,并且具有`data.frame`类对象的方法.所以这个也将在`data.frame`s上工作! (18认同)
  • 假设您有一个名为df的数据框而不是单个向量,并且您只想在名为X3的单个列中替换缺少的观察.您可以使用以下行:df $ X3 [is.na(df $ X3)] < - 0 (9认同)
  • 假设您只想在名为my.df的数据框的4-6列中将NA替换为0.您可以使用:my.df [,4:6] [is.na(my.df [,4:6])] < - 0 (8认同)
  • 当我第一次运行`methods(is.na)`时,我就像_whaaa?!?_.我喜欢这样的事情发生!=) (3认同)

小智 68

dplyr示例:

library(dplyr)

df1 <- df1 %>%
    mutate(myCol1 = if_else(is.na(myCol1), 0, myCol1))
Run Code Online (Sandbox Code Playgroud)

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


mrs*_*tys 52

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

  write.csv(data, "data.csv", na = "0")
Run Code Online (Sandbox Code Playgroud)


小智 45

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

定义此功能:

na.zero <- function (x) {
    x[is.na(x)] <- 0
    return(x)
}
Run Code Online (Sandbox Code Playgroud)

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

na.zero(some.vector)
Run Code Online (Sandbox Code Playgroud)


Psi*_*dom 22

使用dplyr0.5.0,您可以使用coalesce可以轻松集成到%>%管道中的功能coalesce(vec, 0).这将取代vec0中的所有NA :

假设我们有一个带有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
Run Code Online (Sandbox Code Playgroud)

  • 如果您将展示如何将其应用于 2+ 列的所有列,这将很有用。 (2认同)

Cha*_*lmh 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
Run Code Online (Sandbox Code Playgroud)

这也是使用替代ifelse()dplyr

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

  • 我的专栏是一个因素,所以我不得不添加我的替换值 `levels(A$x) &lt;- append(levels(A$x), "notAnswered") A$x &lt;- replace(A$x,which(is. na(A$x)),"notAnswered")` (2认同)

Sas*_*sha 13

也可以使用tidyr::replace_na

    library(tidyr)
    df <- df %>% mutate_all(funs(replace_na(.,0)))
Run Code Online (Sandbox Code Playgroud)

  • `mutate_*` 动词现在被 `across()` 取代 (2认同)

sta*_*007 9

另一个使用imputeTS包的例子:

library(imputeTS)
na.replace(yourDataframe, 0)
Run Code Online (Sandbox Code Playgroud)


Oli*_*ver 9

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

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

  • 这不是一个新的解决方案 (3认同)

小智 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") 
Run Code Online (Sandbox Code Playgroud)

它将因子矢量转换为数字矢量,并添加另一个人工数字因子级别,然后将其转换回因子矢量,并选择一个额外的"NA级别".


LMc*_*LMc 8

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
Run Code Online (Sandbox Code Playgroud)

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

df %>% 
  mutate(across(where(~ anyNA(.) & is.character(.)), ~ replace_na(., "0")))
Run Code Online (Sandbox Code Playgroud)


小智 8

无需使用任何库。

df <- data.frame(a=c(1,3,5,NA))

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

df
Run Code Online (Sandbox Code Playgroud)


rei*_*jax 7

会对@ 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
Run Code Online (Sandbox Code Playgroud)

我们在这里使用标准评估(SE),这就是为什么我们需要" funs_." 的下划线.我们还使用lazyeval's interp/ ~.引用"我们正在使用的所有内容",即数据框.现在有零!


MS *_*nds 6

cleaner包有一个na_replace()通用的,默认情况下用零替换数字值,用 替换逻辑值FALSE,用今天替换日期等:

library(dplyr)
library(cleaner)

starwars %>% na_replace()
na_replace(starwars)
Run Code Online (Sandbox Code Playgroud)

它甚至支持矢量化替换:

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

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


小智 5

您可以使用 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
Run Code Online (Sandbox Code Playgroud)

  • 是的,但只有当您知道向量中 NA 的索引时才实用。对于您的示例中的小向量来说很好。 (6认同)
  • @dardisco `x1 &lt;- replace(x,is.na(x),1)` 无需明确列出索引值即可工作。 (4认同)

jan*_*cki 5

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

library(data.table)

ans_df <- nafill(df, fill=0)

# or even faster, in-place
setnafill(df, fill=0)
Run Code Online (Sandbox Code Playgroud)