R:用NA替换多列数据帧中的多个值

时间:2014-09-10 14:38:56

标签: r replace dataframe multiple-columns

我正在努力实现与this question类似的功能,但必须用NA替换多个值,并且在大型数据集中。

df <- data.frame(name = rep(letters[1:3], each = 3), foo=rep(1:9),var1 = rep(1:9), var2 = rep(3:5, each = 3))

生成此数据帧:

df
  name foo var1 var2
1    a   1    1    3
2    a   2    2    3
3    a   3    3    3
4    b   4    4    4
5    b   5    5    4
6    b   6    6    4
7    c   7    7    5
8    c   8    8    5
9    c   9    9    5

我想用NA替换所有出现的,例如3和4,但仅在以“var”开头的列中。

我知道我可以使用[]运算符的组合来实现我想要的结果:

df[,grep("^var[:alnum:]?",colnames(df))][ 
        df[,grep("^var[:alnum:]?",colnames(df))] == 3 |
        df[,grep("^var[:alnum:]?",colnames(df))] == 4
   ] <- NA

df
  name foo var1 var2
1    a   1    1    NA
2    a   2    2    NA
3    a   3    NA   NA
4    b   4    NA   NA
5    b   5    5    NA
6    b   6    6    NA
7    c   7    7    5
8    c   8    8    5
9    c   9    9    5

现在我的问题如下:

  1. 考虑到我的实际情况,有没有办法以有效的方式做到这一点 数据集有大约100.000行,500个变量中有400个开始 用“var”。当我使用时,我的计算机似乎(主观上)很慢 双括号技术。
  2. 如果,我将如何解决问题 而不是2个值(3和4)被NA替换,我有一个很长的 比方说,100个不同的值?有没有办法指定多个值,必须由|运算符分隔一系列笨拙的条件?

7 个答案:

答案 0 :(得分:13)

您也可以使用replace

执行此操作
sel <- grepl("var",names(df))
df[sel] <- lapply(df[sel], function(x) replace(x,x %in% 3:4, NA) )
df

#  name foo var1 var2
#1    a   1    1   NA
#2    a   2    2   NA
#3    a   3   NA   NA
#4    b   4   NA   NA
#5    b   5    5   NA
#6    b   6    6   NA
#7    c   7    7    5
#8    c   8    8    5
#9    c   9    9    5

使用一百万行数据样本进行的一些快速基准测试表明,这比其他答案更快。

答案 1 :(得分:7)

你也可以这样做:

col_idx <- grep("^var", names(df))
values <- c(3, 4)
m1 <- as.matrix(df[,col_idx])
m1[m1 %in% values] <- NA
df[col_idx]  <- m1
df
#   name foo var1 var2
#1    a   1    1   NA
#2    a   2    2   NA
#3    a   3   NA   NA
#4    b   4   NA   NA
#5    b   5    5   NA
#6    b   6    6   NA
#7    c   7    7    5
#8    c   8    8    5
#9    c   9    9    5

答案 2 :(得分:4)

我还没有计划这个选项,但是我写了一个名为makemeNA的函数,它是my GitHub-only "SOfun" package的一部分。

使用该功能,方法将是这样的:

library(SOfun)

Cols <- grep("^var", names(df))
df[Cols] <- makemeNA(df[Cols], NAStrings = as.character(c(3, 4)))
df
#   name foo var1 var2
# 1    a   1    1   NA
# 2    a   2    2   NA
# 3    a   3   NA   NA
# 4    b   4   NA   NA
# 5    b   5    5   NA
# 6    b   6    6   NA
# 7    c   7    7    5
# 8    c   8    8    5
# 9    c   9    9    5

该函数使用na.strings中的type.convert参数转换为NA


使用以下命令安装软件包:

library(devtools)
install_github("SOfun", "mrdwab")

(或者您最喜欢的从GitHub安装软件包的方法)。


这是一些基准测试。我决定让事情变得有趣,并用NA替换数字和非数字值,看看比较的方式。

以下是示例数据:

n <- 1000000
set.seed(1)
df <- data.frame(
  name1 = sample(letters[1:3], n, TRUE), 
  name2 = sample(letters[1:3], n, TRUE),
  name3 = sample(letters[1:3], n, TRUE),
  var1 = sample(9, n, TRUE), 
  var2 = sample(5, n, TRUE),
  var3 = sample(9, n, TRUE))

以下是要测试的功能:

fun1 <- function() {
  Cols <- names(df)
  df[Cols] <- makemeNA(df[Cols], NAStrings = as.character(c(3, 4, "a")))
  df
}

fun2 <- function() {
  values <- c(3, 4, "a")
  col_idx <- names(df)
  m1 <- as.matrix(df)
  m1[m1 %in% values] <- NA
  df[col_idx]  <- m1
  df
}

fun3 <- function() {
  values <- c(3, 4, "a")
  col_idx <- names(df)
  val_idx <- sapply(df[col_idx], "%in%", table = values)
  is.na(df[col_idx]) <- val_idx
  df
}

fun4 <- function() {
  sel <- names(df)
  df[sel] <- lapply(df[sel], function(x) 
    replace(x, x %in% c(3, 4, "a"), NA))
  df
}

我打破fun2fun3。我并不为fun2而疯狂,因为它将所有内容转换为相同的类型。我还希望fun3更慢。

system.time(fun2())
#    user  system elapsed 
#    4.45    0.33    4.81 

system.time(fun3())
#    user  system elapsed 
#   34.31    0.38   34.74 

所以现在它归结于我和Thela ......

library(microbenchmark)
microbenchmark(fun1(), fun4(), times = 50)
# Unit: seconds
#    expr      min       lq   median       uq      max neval
#  fun1() 2.934278 2.982292 3.070784 3.091579 3.617902    50
#  fun4() 2.839901 2.964274 2.981248 3.128327 3.930542    50

Dang you Thela!

答案 3 :(得分:3)

这是一种方法:

# the values that should be replaced by NA
values <- c(3, 4)

# index of columns
col_idx <- grep("^var", names(df))
# [1] 3 4

# index of values (within these columns)
val_idx <- sapply(df[col_idx], "%in%", table = values)
#        var1  var2
#  [1,] FALSE  TRUE
#  [2,] FALSE  TRUE
#  [3,]  TRUE  TRUE
#  [4,]  TRUE  TRUE
#  [5,] FALSE  TRUE
#  [6,] FALSE  TRUE
#  [7,] FALSE FALSE
#  [8,] FALSE FALSE
#  [9,] FALSE FALSE

# replace with NA
is.na(df[col_idx]) <- val_idx

df
#   name foo var1 var2
# 1    a   1    1   NA
# 2    a   2    2   NA
# 3    a   3   NA   NA
# 4    b   4   NA   NA
# 5    b   5    5   NA
# 6    b   6    6   NA
# 7    c   7    7    5
# 8    c   8    8    5
# 9    c   9    9    5

答案 4 :(得分:1)

我认为dplyr非常适合此任务。
使用@thelatemail建议的replace(),您可以执行以下操作:

library("dplyr")
df <- df %>% 
  mutate_at(vars(starts_with("var")),
            funs(replace(., . %in% c(3, 4), NA)))

df
#   name foo var1 var2
# 1    a   1    1   NA
# 2    a   2    2   NA
# 3    a   3   NA   NA
# 4    b   4   NA   NA
# 5    b   5    5   NA
# 6    b   6    6   NA
# 7    c   7    7    5
# 8    c   8    8    5
# 9    c   9    9    5

答案 5 :(得分:1)

dplyr 1.0.0(2020 年初)以来,我相信 dplyr 的方法是:

library(dplyr)
df %>% mutate(across(starts_with('var'), ~replace(., . %in% c(3,4), NA)))

  name foo var1 var2
1    a   1    1   NA
2    a   2    2   NA
3    a   3   NA   NA
4    b   4   NA   NA
5    b   5    5   NA
6    b   6    6   NA
7    c   7    7    5
8    c   8    8    5
9    c   9    9    5

使用 naniar 包的另一种方法,它使用谓词函数(此处使用 str_detect())巧妙地将缺失值归入所选列:

library(dplyr)
library(stringr)
library(naniar)

df%>%replace_with_na_if(str_detect(names(.), '^var'), ~.%in%c(3,4))

很高兴看到 naniar 包更新为与当前的 tidyselect synthax 一起使用 across() 及其选择帮助程序,例如: df%>%mutate(across(starts_with('var'), replace_with_na_all(condition=~.%in% c(3, 4))))

答案 6 :(得分:0)

这是一个dplyr解决方案:

# Define replace function
repl.f <- function(x) ifelse(x%in%c(3,4), NA,x)

library(dplyr)
cbind(select(df, -starts_with("var")),
  mutate_each(select(df, starts_with("var")), funs(repl.f)))

  name foo var1 var2
1    a   1    1   NA
2    a   2    2   NA
3    a   3   NA   NA
4    b   4   NA   NA
5    b   5    5   NA
6    b   6    6   NA
7    c   7    7    5
8    c   8    8    5
9    c   9    9    5