dplyr中mutate(across(where()))替换NA为0失效问题咨询
解决tibble数据中NA值替换为0的问题
问题背景
我有如下结构的tibble数据,想要把所有NA值替换为0:
# A tibble: 10 x 6 area_fips NAME variable estimate moe percent <chr> <chr> <chr> <dbl> <dbl> <dbl> 1 01001 Autauga County, Alabama Less than 9th grade 909 235 NA 2 01001 Autauga County, Alabama 9th to 12th grade, no diploma 3364 520 NA 3 01001 Autauga County, Alabama High school graduate (includes equivalency) 11880 665 NA 4 01001 Autauga County, Alabama Some college, no degree 7663 648 NA 5 01001 Autauga County, Alabama Associate's degree 3323 505 NA 6 01001 Autauga County, Alabama Bachelor's degree 6320 607 NA 7 01001 Autauga County, Alabama Graduate or professional degree 4401 558 NA 8 01001 Autauga County, Alabama Less than 9th grade NA NA NA 9 01001 Autauga County, Alabama 9th to 12th grade, no diploma NA NA NA 10 01001 Autauga County, Alabama High school graduate (includes equivalency) NA NA NA
尝试用新版dplyr语法实现,但代码没产生任何效果:
data %>% mutate(across(.cols = everything(), ~ replace(., is.na(.), 0))) data %>% mutate(across(where(is.numeric), ~ replace(., is.na(.), 0))) data %>% mutate(across(where(is.na), ~ replace(., is.na(.), 0)))
已知replace(is.na(.), 0)是可行写法,但困惑上述代码失效的原因,数据的dput结果如下:
structure(list(area_fips = c("01001", "01001", "01001", "01001", "01001", "01001", "01001", "01001", "01001", "01001"), NAME = c("Autauga County, Alabama", "Autauga County, Alabama", "Autauga County, Alabama", "Autauga County, Alabama", "Autauga County, Alabama", "Autauga County, Alabama", "Autauga County, Alabama", "Autauga County, Alabama", "Autauga County, Alabama", "Autauga County, Alabama" ), variable = c("Less than 9th grade", "9th to 12th grade, no diploma", "High school graduate (includes equivalency)", "Some college, no degree", "Associate's degree", "Bachelor's degree", "Graduate or professional degree", "Less than 9th grade", "9th to 12th grade, no diploma", "High school graduate (includes equivalency)" ), estimate = c(909, 3364, 11880, 7663, 3323, 6320, 4401, NA, NA, NA), moe = c(235, 520, 665, 648, 505, 607, 558, NA, NA, NA ), percent = c(NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_)), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"))
问题分析与解决
代码失效的核心原因
- 未对结果赋值:dplyr是函数式编程框架,所有操作都返回新的数据框,不会直接修改原变量。你运行的代码只是生成了替换NA后的新数据框,但没有把它重新赋值给
data,所以原数据看起来毫无变化。 - 第三个写法逻辑错误:
across(where(is.na), ...)的用法完全不对,where()要求传入的函数能对整列返回单个逻辑值(判断列是否符合筛选条件),但is.na()是逐元素返回逻辑值,因此这个写法无法选中任何列,自然不会有替换效果。
正确的实现方式
方式1:修正赋值逻辑(用原有写法)
把处理后的结果重新赋值给原变量,或者新变量:
# 重新赋值给原变量 data <- data %>% mutate(across(where(is.numeric), ~ replace(., is.na(.), 0))) # 或者用magrittr的复合赋值管道(需要先加载magrittr包) library(magrittr) data %<>% mutate(across(where(is.numeric), ~ replace(., is.na(.), 0)))
方式2:用tidyr的replace_na()简化写法
replace_na()是tidyr包专门用于替换NA值的函数,写法更简洁直观:
library(tidyr) data <- data %>% mutate(across(where(is.numeric), replace_na, 0))
验证效果
运行上述代码后,数据中estimate、moe、percent列的NA值都会被替换为0,字符列(area_fips、NAME、variable)因为没有NA,不会被修改。
内容的提问来源于stack exchange,提问作者Ben G
相关产品推荐
相关产品推荐

