如何在 R 中替换缺失值 (NA):na.omit 和 na.rm
⚡ 智能摘要
R 语言中的“替换缺失值”部分涵盖了检测 NA 值、使用 `na.omit()` 删除不完整行以及使用 `mutate()` 函数用均值或中位数填充缺失值。本教程使用泰坦尼克号数据集,其中年龄和票价数据均存在缺失值。

R语言中的缺失值是什么?
当观测值在某一列中没有记录值,或者本应是数字的位置被非数字占位符占据时,就会出现缺失值。在进行任何计算之前,必须删除或替换这些缺失值,因为大多数 R 函数在遇到缺失值时都会返回 NA。
本教程展示了如何使用 dplyr 库(tidyverse 数据分析生态系统的一部分)处理缺失值。
第一步总是要找出缺失的值有多少以及缺失的位置。
如何在 R 语言中检测和统计缺失值
在决定如何处理缺失值之前,你需要知道缺失值的数量以及它们位于哪些列中。R 提供了四种检查方法,从简单的“是”或“否”判断到逐列统计。
# 1. Is there any NA at all? anyNA(df_titanic) # 2. How many in total? sum(is.na(df_titanic)) # 3. How many per column, the most useful view colSums(is.na(df_titanic)) # 4. Percentage missing per column round(colMeans(is.na(df_titanic)) * 100, 1)
在实际应用中,应该优先使用 `colSums()` 函数。它返回一个命名向量,每列包含一个计数,可以立即显示变量是否缺少少量值或大部分为空。
计算完整行数。 complete.cases() 对于没有任何缺失值的行返回 TRUE,这可以提前告诉你 na.omit() 会丢弃多少数据:
sum(complete.cases(df_titanic)) # rows that would survive sum(!complete.cases(df_titanic)) # rows that would be dropped
关于占位符的警告。 R 语言只识别 NA 值。数据集通常会将缺失值编码为空字符串、空格、“N/A”、“-”或哨兵数字(例如 -99 或 999)。这些编码方式会绕过上述所有检查而不被发现。导入时需要转换这些编码,以确保后续工作流程正常运行:
df <- read.csv(PATH, na.strings = c("", " ", "NA", "N/A", "-", "-99"))
导入数据后,务必检查每个数值列的范围。年龄为 -99 或票价为 9999 比正常的 NA 值危险得多,因为没有任何函数会对此发出警告。
缺失数据类型:完全随机缺失 (MCAR)、随机缺失 (MAR) 和随机非随机缺失 (MNAR)。
缺失值的原因决定了是否可以安全地进行插补。统计学家们认为存在三种机制。
- MCAR,完全随机缺失。 缺失的概率与任何因素无关,无论是否已被观察到,例如传感器随机失效。ping 或者说,对这些行进行插补不会引入偏差,只会降低精度。
- MAR,随机缺失。 概率取决于其他观测变量,但与缺失值本身无关。如果年龄较大的乘客不太可能被记录年龄,那么根据其他列的数据,年龄信息可能为随机缺失值(MAR)。使用这些列进行插补可以很好地处理这种情况。
- MNAR,即非随机缺失。 概率取决于未观测值本身,例如高收入者拒绝透露其收入。仅凭现有数据,任何插补方法都无法解决这个问题,而且缺失值本身也包含值得记录在标记列中的信息。
本教程中使用的均值插补法仅在完全随机缺失 (MCAR) 和简单随机缺失 (MAR) 的情况下才适用。即使如此,它也存在已知的代价:用相同的数值填充所有缺失值会降低该列的方差,并削弱其与其他所有列的相关性。对于更严谨的工作,请使用基于模型的方法,例如 mice 包,并始终保留一个标记列来指示哪些值是插补的。
变异()
mutate() 是 dplyr 动词,用于创建新变量或覆盖现有变量,因此是构建列的干净副本的自然工具。
我们将分两部分进行。我们将学习如何:
- 从数据框中排除缺失值
- 用平均值和中位数填补缺失值
动词 mutate() 非常容易使用。我们可以按照以下语法创建一个新变量:
mutate(df, name_variable_1 = condition, ...) arguments: -df: Data frame used to create a new variable -name_variable_1: Name and the formula to create the new variable -...: No limit constraint. Possibility to create more than one variable inside mutate()
排除缺失值(NA)
`na.omit()` 是 R 的基础函数,并非 dplyr 函数,但它同样可以干净利落地执行。它会删除所有包含至少一个 NA 值的行。这是最快的选择,但通常并非最佳选择,因为一个缺失值就会导致整个观测值被丢弃。
为了解决缺失观测值的问题,我们将使用泰坦尼克号数据集。在这个数据集中,我们可以访问悲剧发生期间船上乘客的信息。这个数据集有许多需要处理的 NA。
从互联网加载 CSV 文件,然后列出包含 NA 值的列:
PATH <- "https://raw.githubusercontent.com/guru99-edu/R-Programming/master/test.csv" df_titanic <- read.csv(PATH, sep = ",") # Return the column names containing missing observations list_na <- colnames(df_titanic)[ apply(df_titanic, 2, anyNA) ] list_na
输出:
## [1] "age" "fare"
在这里,
colnames(df_titanic)[apply(df_titanic, 2, anyNA)]
返回至少包含一个缺失值的列的名称。
年龄和票价列缺少值。
我们可以使用 na.omit() 删除它们。
library(dplyr) # Exclude the missing observations df_titanic_drop <-df_titanic %>% na.omit() dim(df_titanic_drop)
输出:
## [1] 1045 13
新的数据集包含 1045 行,而原始数据集包含 1309 行。
使用均值和中位数填补缺失数据。
你还可以使用均值或中位数来填补缺失值。一个好的做法是创建两个单独的变量分别存储均值和中位数。创建完成后,我们就可以用这些新创建的变量替换缺失值。
我们将使用 apply 方法来计算 NA 列的平均值。让我们看一个例子
步骤1) 在本教程的前面,我们将包含缺失值的列名称存储在名为 list_na 的列表中。我们将使用此列表
步骤2) 使用参数 na.rm = TRUE 计算均值。此参数是必需的,因为这些列存在缺失值,它告诉 R 忽略这些缺失值。
# Create mean average_missing <- apply(df_titanic[,colnames(df_titanic) %in% list_na], 2, mean, na.rm = TRUE) average_missing
Code 说明:
我们在apply方法中传递了4个参数。
- df:df_titanic[,colnames(df_titanic) %in% list_na]。此代码将从 list_na 对象中返回列名称(即“age”和“fare”)
- 2:计算列上的函数
- 平均值:计算平均值
- na.rm = TRUE:忽略缺失值
输出:
## age fare ## 29.88113 33.29548
我们成功创建了包含缺失观测值的列的平均值。这两个值将用于替换缺失的观测值。
步骤3) 替换 NA 值
dplyr 库中的动词 mutate 在创建新变量时很有用。我们不一定想更改原始列,因此我们可以创建一个没有 NA 的新变量。mutate 易于使用,我们只需选择一个变量名并定义如何创建此变量。以下是完整代码
# Create a new variable with the mean and median df_titanic_replace <- df_titanic %>% mutate(replace_mean_age = ifelse(is.na(age), average_missing[1], age), replace_mean_fare = ifelse(is.na(fare), average_missing[2], fare))
Code 说明:
我们创建两个变量,replace_mean_age 和 replace_mean_fare 如下:
- replace_mean_age = ifelse(is.na(年龄), average_missing[1], 年龄)
- replace_mean_fare = ifelse(is.na(fare), average_missing[2],fare)
如果 age 列有缺失值,则用 average_missing 的第一个元素(年龄平均值)替换,否则保留原始值。fare 的逻辑相同
sum(is.na(df_titanic_replace$age))
输出:
## [1] 263
替换后,新列中完全没有缺失值:
sum(is.na(df_titanic_replace$replace_mean_age))
输出:
## [1] 0
原始年龄列有 263 个缺失值,而新的 replace_mean_age 列已将每个缺失值都填充为平均年龄。
步骤4) 我们也可以用中位数替换缺失的观测值。
median_missing <- apply(df_titanic[,colnames(df_titanic) %in% list_na], 2, median, na.rm = TRUE) df_titanic_replace <- df_titanic %>% mutate(replace_median_age = ifelse(is.na(age), median_missing[1], age), replace_median_fare = ifelse(is.na(fare), median_missing[2], fare)) head(df_titanic_replace)
输出:
步骤5) 对于大型数据集,逐步处理的方法会变得繁琐。`sapply()` 函数将整个过程简化为一条语句,但代价是无法看到填充后的值。
sapply 不会创建 数据框,因此我们可以将 sapply() 函数包装在 data.frame() 中来创建一个数据框对象。
# Quick code to replace missing values with the mean df_titanic_impute_mean <- data.frame( sapply( df_titanic, function(x) ifelse(is.na(x), mean(x, na.rm = TRUE), x)))
使用 replace_na() 和 across() 进行现代插补
上面的 apply() 和 sapply() 方法仍然有效,但 tidyr 和 dplyr 现在以更安全、更易读的方式表达相同的操作。
replace_na() 用于固定值。 tidyr::replace_na() 接受一个命名列表,其中包含列及其替换值:
library(tidyr) df_titanic %>% replace_na(list(age = 29.88, fare = 33.30))
对每个数值列执行 across()。 这是 sapply() 单行函数的直接、类型安全的替代方案,而且与 sapply() 不同的是,它永远不会触及字符列或因子列:
library(dplyr) # Mean-impute every numeric column df_titanic %>% mutate(across(where(is.numeric), ~ ifelse(is.na(.x), mean(.x, na.rm = TRUE), .x))) # Median-impute instead df_titanic %>% mutate(across(where(is.numeric), ~ ifelse(is.na(.x), median(.x, na.rm = TRUE), .x)))
⚠️ 为什么使用 sapply() 快捷方式存在风险: 单行 sapply() 示例运行时间超过 每周 该列包含字符型和因子型列。对这些列调用 mean() 函数会返回 NA 值并发出警告,而 sapply() 函数会将整个结果强制转换为字符型。上面的 across(where(is.numeric), …) 版本完全避免了这个问题。
对备用列进行 coalesce() 操作。 当第二列可以提供缺失值时,coalesce() 返回其参数中第一个非缺失值:
df %>% mutate(age_final = coalesce(age, age_estimated, 0))
记录下你所做的更改。 在进行插补之前添加一个标志,以便后续模型能够从缺失值这一事实中学习:
df_titanic %>%
mutate(age_was_missing = is.na(age),
age = ifelse(is.na(age), mean(age, na.rm = TRUE), age))
R 语言中缺失值的处理:方法参考
本教程涵盖三种方法:
- 排除所有缺失的观测值
- 用平均值估算
- 使用中位数进行估算
下表总结了检测和清除情况:
| 图书馆 | 目的 | Code |
|---|---|---|
| 基地 | 列出缺失的观察结果 |
colnames(df)[apply(df, 2, anyNA)] |
| 基地 | 删除所有包含 NA 的行。 |
na.omit(df) |
用平均值或中位数进行插补可以通过两种方式进行
- 使用 apply
- 使用 sapply
| 付款方式 | 信息 | 优势 | 缺点 |
|---|---|---|---|
| 逐步应用 | 检查缺失的列,计算平均值/中位数,存储值,用 mutate() 替换 | 您可以看到估算的均值或中位数。 | 执行时间更长。处理大数据集时速度会比较慢 |
| 使用 sapply 的快捷方式 | 使用 sapply() 和 data.frame() 自动搜索缺失值并用平均值/中位数替换 | 代码短、速度快 | 估算值永远不会显示。 |



