将所有的0值替换为NA。

203

我有一个包含一些数字列的数据框。某些行具有值为0的值,在统计分析中应视为null。在R中,最快的方法是将所有0值替换为NULL?


18
我认为你不想/不能用NULL值来替换,但在R语言中,NA就是用来实现这个目的的。 - Chase
11个回答

343

替换所有的零为NA:

df[df == 0] <- NA



解释

1. 你不应该想要用 NULL 替换零值。正如在 ?'NULL' 中所述,

NULL 在 R 中代表 null 对象

它是唯一的,我想可以被看作是最没有信息和空的对象。1 那么就不足为奇了。

data.frame(x = c(1, NULL, 2))
#   x
# 1 1
# 2 2

换句话说,R并不为这个空对象保留任何空间。2 同时,查看'NA'的文档,我们可以看到:

  

NA是长度为1的逻辑常量,它包含一个缺失值指示符。 NA可以强制转换为除了原始类型之外的任何其他向量类型。

重要的是,NA的长度为1,因此R为其保留了一些空间。例如:

data.frame(x = c(1, NA, 2))
#    x
# 1  1
# 2 NA
# 3  2
此外,数据框架结构要求所有列具有相同数量的元素,以便没有“空洞”(即 NULL 值)。现在,您可以将数据框架中的零替换为 NULL ,意思是完全删除至少包含一个零的所有行。当使用例如 var cov cor 时,实际上等效于首先用 NA 替换零,并将 use 的值设置为“complete.obs”。然而,通常情况下,这是不令人满意的,因为会导致额外的信息丢失。 在解决方案中,我使用了 df == 0 向量化,而不是运行某种循环。 df == 0 返回(请尝试)与 df 大小相同的矩阵,具有条目 TRUE FALSE 。此外,我们也被允许将此矩阵传递给子集 [..] (请参见?'[')。最后,虽然 df [df == 0] 的结果完全直观,但 df [df == 0] <- NA 可能看起来很奇怪却能产生所需的效果。赋值运算符 <- 确实并不总是那么聪明,并且不能以这种方式与某些其他对象一起使用,但在数据框架中可以;请参见?'<-'

3
数据表对象的等价语法是什么? - itpetersen
6
我看到你已经获得了很多票,但我认为这并不适当地涵盖了非数值列的边缘情况,这些列的值为“0”,但未要求将其设置为<NA>。 - IRTFM
自己注意:如果代码不起作用,且数据框是从CSV解析而来的,请确保你的值不包含开头/结尾的空格,例如“?”。 - adroste

52

假设您的数据框包含不同的数据类型,且并非所有列都需要修改。

要仅修改第12到18列(共21列),只需执行以下操作

df[, 12:18][df[, 12:18] == 0] <- NA

1
这对我有效,而被接受的答案则无效。 - Patrick Coulombe
2
这个方法可行,而且比被接受的答案更灵活。 - drT
双重子集使用[]非常棒! 显而易见,但仍然隐藏得很好:) - bathyscapher
@userJT - 如果我想按列名而不是位置选择列怎么办?例如,有300个列名为:age1、age2、age3、age4...... age300。 - rais

42

dplyr::na_if()是一个选项:

library(dplyr)  

df <- data_frame(col1 = c(1, 2, 3, 0),
                 col2 = c(0, 2, 3, 4),
                 col3 = c(1, 0, 3, 0),
                 col4 = c('a', 'b', 'c', 'd'))

na_if(df, 0)
# A tibble: 4 x 4
   col1  col2  col3 col4 
  <dbl> <dbl> <dbl> <chr>
1     1    NA     1 a    
2     2     2    NA b    
3     3     3     3 c    
4    NA     4    NA d

在我的版本中,na_if 函数不接受 data.frame 或 tibble。 - undefined

23

不使用[<-函数的替代方法:

一个数据框样本dat(从@Chase的答案中无耻地复制):

dat

  x y
1 0 2
2 1 2
3 1 1
4 2 1
5 0 0

使用is.na<-函数可以将0替换为NA:

is.na(dat) <- !dat


dat

   x  y
1 NA  2
2  1  2
3  1  1
4  2  1
5 NA NA

14
#Sample data
set.seed(1)
dat <- data.frame(x = sample(0:2, 5, TRUE), y = sample(0:2, 5, TRUE))
#-----
  x y
1 0 2
2 1 2
3 1 1
4 2 1
5 0 0

#replace zeros with NA
dat[dat==0] <- NA
#-----
   x  y
1 NA  2
2  1  2
3  1  1
4  2  1
5 NA NA

13

因为有人要求使用Data.Table版本,且给定的data.frame解决方案在data.table中无法工作,因此我提供以下解决方案。

基本上,使用:=运算符 --> DT[x == 0, x := NA]

library("data.table")

status = as.data.table(occupationalStatus)

head(status, 10)
    origin destination  N
 1:      1           1 50
 2:      2           1 16
 3:      3           1 12
 4:      4           1 11
 5:      5           1  2
 6:      6           1 12
 7:      7           1  0
 8:      8           1  0
 9:      1           2 19
10:      2           2 40


status[N == 0, N := NA]

head(status, 10)
    origin destination  N
 1:      1           1 50
 2:      2           1 16
 3:      3           1 12
 4:      4           1 11
 5:      5           1  2
 6:      6           1 12
 7:      7           1 NA
 8:      8           1 NA
 9:      1           2 19
10:      2           2 40

3
或者 for (j in names(DT)); set(DT,which(DT[[j]] == 0),j,NA)。在这里查看使用data.table查找和替换值的更详细讨论。 - JWilliman

9

如果有人通过谷歌搜索到此处,寻找相反的问题(即如何将数据框中所有的NA替换为0),答案是:

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

或者

使用 dplyr / tidyverse 技术

library(dplyr)
mtcars %>% replace(is.na(.), 0)

7

以下是我的贡献,为那些在处理包含多种列类型且有多个值表示缺失数据的数据集时感到困难的人提供帮助。

dat <- data_frame(numA = c(1, 0, 3, 4),
             numB = c(NA, 2, 3, 4),
             strC = c("0", "1.2", "NA", "2.4"),
             strD = c("Yes", "Yes", "missing", "No"))

假设在这份数据中,我们想将数值列中的0替换为NA,字符串/字符类型的'NA'和'missing'值也替换为NA。请注意,strC列中的'NA'是字符类型的值,而不是所需的NA值。
dat
# A tibble: 4 x 4
  numA   numB  strC  strD   
  <dbl>  <dbl> <chr> <chr>  
1     1     NA 0     Yes    
2     0      2 1.2   Yes    
3     3      3 'NA'  missing
4     4      4 2.4   No 

首先,一个显而易见的情况是,注意将字符列转换为数字值时,任何非数字字符串值都会被强制转换为NA
as.numeric(dat$strC)
[1] 0.0 1.2  NA 2.4 

使用索引回答:

dat[dat == "NA" | dat =="missing"] <- NA

然而,对于数字和字符0都会被改变为NA,因此不要对0使用。这是因为在R中 "0" == 0返回的结果为TRUE.

dplyr::na_if方法:

library(dplyr)

dat %>%
  lapply(na_if, y = "missing") %>%
  lapply(na_if, y = "NA") %>%
  lapply(na_if, y = 0) %>%  # DONT DO THIS! It converts string 0s to NA as well!
  data.frame()

在这里,我们对数据的每一列应用 na_if 函数。由于 na_if 不接受多个值转换为 NA,因此我们需要为每个要转换为 NA 的值编写多行代码。然而,只需简单使用该函数将 0 转换为 NA 就可以同时转换数字和字符中的 0。我们需要做些别的事情!

使用 mutate across 方法和 na_if 函数:

这是我最喜欢的解决方案。在这里,我们检查列类型并根据需要应用 na_if 函数。字符型 0 不变,而所有需要转换的值都会被转换为 NA

dat %>%
  mutate(across(where(is.numeric), ~na_if(., 0))) %>%
  mutate(across(where(is.character), ~na_if(., "NA"))) %>%
  mutate(across(where(is.character), ~na_if(., "missing")))

# A tibble: 4 x 4
   numA  numB strC  strD 
  <dbl> <dbl> <chr> <chr>
1     1    NA 0     Yes  
2    NA     2 1.2   Yes  
3     3     3 NA    NA   
4     4     4 2.4   No 

终于,可以使用nariar包了

nariar是一个最近推出的包,引入了多种replace_with_函数。

library(naniar)

将所有的'NA'和'missing'值替换为NA:

dat %>%
  replace_with_na_all(~.x %in% c("NA", "missing"))

但是,如果您将此与0一起使用,则它仍会错误地将字符0转换为NA

dat %>%
  replace_with_na_all(~.x %in% c(0, "NA", "missing"))

# A tibble: 4 x 4
   numA  numB strC  strD 
  <dbl> <dbl> <chr> <chr>
1     1    NA NA    Yes  
2    NA     2 1.2   Yes  
3     3     3 NA    NA   
4     4     4 2.4   No
#strC's first element should not be NA here!

因此,我们必须使用 replace_with_na_if 来指定列类型:
dat %>%
  replace_with_na_if(is.character, ~.x %in% c("NA", "missing")) %>%
  replace_with_na_if(is.numeric, ~.x %in% c(0))

# A tibble: 4 x 4
   numA  numB strC  strD 
  <dbl> <dbl> <chr> <chr>
1     1    NA 0     Yes  
2    NA     2 1.2   Yes  
3     3     3 NA    NA   
4     4     4 2.4   No

我们达到了预期的结果。希望这一切都对你有帮助 :)

4

您只能在数值字段中(即排除分类型字段等)将0替换为NA,但它是基于列的方式工作:

col[col == 0 & is.numeric(col)] <- NA

利用一个函数,你可以将其应用到整个数据框中:

changetoNA <- function(colnum,df) {
    col <- df[,colnum]
    if (is.numeric(col)) {  #edit: verifying column is numeric
        col[col == -1 & is.numeric(col)] <- NA
    }
    return(col)
}
df <- data.frame(sapply(1:5, changetoNA, df))

虽然你可以用数据框中的列数或1:ncol(df)替换1:5


我不确定这是正确的解决方案。那么第6列及其后面的列会怎样呢?它们会被截断吗? - userJT
这就是为什么我建议在末尾用1:ncol(df)替换1:5。我不想让方程过于复杂或难以阅读。 - Alium Britt
但是如果第6列和第7列的数据类型是char,并且不需要进行替换怎么办?在我的问题中,我只需要在第12到15列进行替换,但整个df有21列(许多列根本不需要触摸)。 - userJT
对于您的数据框,您可以将“1:5”更改为要更改的列号,例如“12:15”,但是如果您想确认它只会影响数字列,则只需在函数的第二行中包装if语句,如下所示:“if (is.numeric(col)) { col[col == -1 & is.numeric(col)] <- NA }”。 - Alium Britt

1
另一个选项是使用mutate_all将所有的0替换为NA,代码如下:

replace

library(dplyr)
df <- data.frame(v1 = c(1,0,4,2),
                 v2 = c(3,1,0,0))
df
#>   v1 v2
#> 1  1  3
#> 2  0  1
#> 3  4  0
#> 4  2  0
mutate_all(df, ~replace(., .==0, NA))
#>   v1 v2
#> 1  1  3
#> 2 NA  1
#> 3  4 NA
#> 4  2 NA

reprex package (v2.0.1)于2022年7月10日创建


~na.if(.,0) - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接