提问



我有一个data.frame,有些列有NA个值。我想用零替换NA s。我是怎么做到的

最佳参考


在@ 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


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


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





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



Locationally:



  • index 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



结果箱图(以对数刻度)



# 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]")


[76]


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



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))


[77]


关于其他高绩效者的说明



当数据集变大时, Tidyr s 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 [78] [79]


归因和赞赏



特别感谢:



  • Tyler Rinker和Akrun展示微型基准。

  • alexis_laz致力于帮助我理解local()的使用,以及(同弗兰克的耐心帮助)静音强制在加速许多这些方法中的作用。

  • ArthurYip捅戳添加更新的coalesce()函数并更新分析。

  • 格雷戈尔轻推,找出data.table的功能,以便最终将他们纳入阵容。

  • Base R For循环: alexis_laz

  • data.table 对于循环: Matt_Dowle



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


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

其它参考2


对于单个向量:


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


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


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


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

其它参考3


dplyr示例:


library(dplyr)

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


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

其它参考4


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


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

其它参考5


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


定义此功能:


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


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


na.zero(some.vector)

其它参考6


在矩阵或向量中使用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))

其它参考7


使用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

其它参考8


使用 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级别。

其它参考10


会不会评论@ianmunoz的帖子,但我没有足够的声誉。你可以结合dplyr [[s mutate_eachreplace来照顾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_的下划线。我们也使用lazyevals interp/~和[[.引用我们正在使用的一切,即数据框。现在有零!

其它参考11


你可以用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

其它参考12


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


从Datacamp中提取的这个简单函数可以提供帮助:[88]


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)