簡體   English   中英

如何在 R dataframe 中用零替換 NA 值?

[英]How do I replace NA values with zeros in an R dataframe?

我有一個數據框,有些列有NA值。

如何用零替換這些NA值?

在@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包。 它具有許多用於缺失數據分析的不錯功能。 =)

dplyr 混合選項現在比 Base R 子集重新分配快約 30%。 在 100M 數據點數據幀上, mutate_all(~replace(., is.na(.), 0))比基本 R d[is.na(d)] <- 0選項快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 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)))

所有試驗時間的散點圖

關於其他高績效者的說明

當數據集變大時, Tidyrreplace_na歷來就排在前面。 通過當前要運行的 100M 數據點集合,它的性能幾乎與Base R For 循環一樣好。 我很想知道不同大小的數據幀會發生什么。

對於另外的例子mutate ,並summarize _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 為 poke 添加新的coalesce()函數並更新分析。
  • 格雷戈爾推動找出足夠data.table函數,最終將它們包含在陣容中。
  • Base R For 循環: alexis_laz
  • data.table For 循環: Matt_Dowle
  • Roman 用於解釋is.numeric()真正測試的內容。

(當然,如果你覺得這些方法有用,也請伸出手給他們點贊。)

關於我使用 Numerics 的注意事項:如果你有一個純整數數據集,你的所有函數都會運行得更快。 有關更多信息,請參閱alexiz_laz 的作品 IRL,我不記得遇到過包含超過 10-15% 整數的數據集,所以我在全數字數據幀上運行這些測試。

硬件 使用3.9 GHz CPU 和 24 GB RAM

對於單個向量:

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

對於 data.frame,從上面創建一個函數,然后apply其應用於列。

下次請提供一個可重現的示例,詳情如下:

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

dplyr 示例:

library(dplyr)

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

注意:這適用於每個選定的列,如果我們需要對所有列執行此操作,請參閱@reidjax使用mutate_each的答案。

如果我們在導出時嘗試替換NA ,例如在寫入 csv 時,那么我們可以使用:

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

我知道這個問題已經得到解答,但這樣做可能對某些人更有用:

定義這個函數:

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

現在,每當您需要將向量中的 NA 轉換為零時,您可以執行以下操作:

na.zero(some.vector)

也可以使用tidyr::replace_na

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

編輯(dplyr > 1.0.0):

df %>% mutate(across(everything(), .fns = ~replace_na(.,0))) 

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

這也是在dplyr使用ifelse()dplyr

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

使用dplyr 0.5.0,您可以使用coalesce函數,該函數可以通過執行coalesce(vec, 0)輕松集成到%>%管道中。 這將vec所有 NA 替換為 0:

假設我們有一個帶有NA的數據框:

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

會對@ianmunoz 的帖子發表評論,但我沒有足夠的聲譽。 您可以結合dplyrmutate_eachreplace來處理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 / ~. 引用“我們正在使用的一切”,即數據框。 現在有零了!

如果要替換因子變量中的 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 級別”的因子向量。

另一個使用imputeTS包的例子:

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

要替換數據框中的所有 NA,您可以使用:

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

setnafill ,專用函數nafillsetnafill位於data.table 只要可用,它們就會分布要在多個線程上計算的列。

library(data.table)

ans_df <- nafill(df, fill=0)

# or even faster, in-place
setnafill(df, fill=0)

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

tidyr方法replace_na另一個dplyr管道兼容選項適用於多個列:

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)

無需使用任何庫。

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

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

df

這個從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)

編寫它的一種簡單方法是使用來自hablar if_na

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

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

如果您想在這種情況下列 V3 中更改特定列中的 NA 后分配新名稱,請使用您也可以這樣做

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

dplyr >= 1.0.0

在較新版本的dplyr

cross() 取代了“范圍變體”系列,如 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")))

我想添加一個使用流行的Hmisc的下一個解決方案。

library(Hmisc)
data(airquality)
# imputing with 0 - all columns
# although my favorite one for simple imputations is Hmisc::impute(x, "random")
> dd <- data.frame(Map(function(x) Hmisc::impute(x, 0), airquality))
> str(dd[[1]])
 'impute' Named num [1:153] 41 36 12 18 0 28 23 19 8 0 ...
 - attr(*, "names")= chr [1:153] "1" "2" "3" "4" ...
 - attr(*, "imputed")= int [1:37] 5 10 25 26 27 32 33 34 35 36 ...
> dd[[1]][1:10]
  1   2   3   4   5   6   7   8   9  10 
 41  36  12  18  0*  28  23  19   8  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   

這並不是一個全新的解決方案,但我喜歡編寫內聯 lambda 表達式來處理我無法讓包完成的事情。 在這種情況下,

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

由於 R 永遠不會像您在 Python 中看到的那樣“通過對象”,因此該解決方案不會修改原始變量df ,因此與大多數其他解決方案的作用完全相同,但對復雜知識的需求要少得多特定的包。

注意函數定義周圍的括號! 雖然對我來說似乎有點多余,但由於函數定義被花括號括起來,因此需要在magrittr括號內定義內聯函數。

替換數據框中的 is.na 和 NULL。

  1. 帶列的數據框

A$name[is.na(A$name)]<-0

或者

A$name[is.na(A$name)]<-"NA"

  1. 與所有數據框

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

  1. 用數據框中的空白替換 na

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

  1. 將 NULL 替換為 NA

df[is.null(df)] <- 不適用

我個人使用它並且工作正常:

players_wd$APPROVED_WD[is.na(players_wd$APPROVED_WD)] <- 0

這是一個更靈活的解決方案。 無論您的數據框有多大,它都可以工作,或者用0zero或其他任何方式表示零。

library(dplyr) # make sure dplyr ver is >= 1.00

df %>%
    mutate(across(everything(), na_if, 0)) # if 0 is indicated by `zero` then replace `0` with `zero`

使用sapply將所有NA替換為零的另一種選擇。 這是一些可重現的代碼(來自@aL3xa 的數據):

set.seed(7) # for reproducibility
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   9  7  5  5  7  7  4  6  6   7
#> 2   2  5 10  7  8  9  8  8  1   8
#> 3   6  7  4 10  4  9  6  8 NA  10
#> 4   1 10  3  7  5  7  7  7 NA   8
#> 5   9  9 10 NA  7 10  1  5 NA   5
#> 6   5  2  5 10  8  1  1  5 10   3
#> 7   7  3  9  3  1  6  7  3  1  10
#> 8   7  7  6  8  4  4  5 NA  8   7
#> 9   2  1  1  2  7  5  9 10  9   3
#> 10  7  5  3  4  9  2  7  6 NA   5
d[sapply(d, \(x) is.na(x))] <- 0
d
#>    V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
#> 1   9  7  5  5  7  7  4  6  6   7
#> 2   2  5 10  7  8  9  8  8  1   8
#> 3   6  7  4 10  4  9  6  8  0  10
#> 4   1 10  3  7  5  7  7  7  0   8
#> 5   9  9 10  0  7 10  1  5  0   5
#> 6   5  2  5 10  8  1  1  5 10   3
#> 7   7  3  9  3  1  6  7  3  1  10
#> 8   7  7  6  8  4  4  5  0  8   7
#> 9   2  1  1  2  7  5  9 10  9   3
#> 10  7  5  3  4  9  2  7  6  0   5

創建於 2023-01-15,使用reprex v2.0.2


請注意:從 R 4.1.0 開始,您可以使用\(x)而不是function(x)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM