2014-09-10 239 views
12

我试图实现类似于this question但具有必须由NA替换的多个值以及大数据集中的值。R:使用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个不同的值,我将如何处理这个问题?有没有办法指定多个值,而必须执行由|运算符分隔的一系列笨拙条件?
+0

你可以使用'%'%。 – Roland 2014-09-10 14:49:15

回答

12

你也可以做到这一点使用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 

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

+0

+1您可以发布基准吗?谢谢。 – akrun 2014-09-11 07:57:32

+0

根据我的数据,我从17秒变为1.8秒,与@akrun方法相比降低了10倍!谢谢! – Peutch 2014-09-11 09:26:05

+0

@akrun,我在我的答案中添加了基准。 – A5C1D2H2I1M1N2O1R2T1 2014-09-11 12:22:24

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 
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 
+0

谢谢。根据我的数据,这个解决方案比'sapply'方法快6到7倍。 – Peutch 2014-09-10 15:33:54

+0

@荷兰盾 - 我想我已经用'replace'把这个速度提高了一小部分 - 你能测试一下你的实际数据吗? – thelatemail 2014-09-11 05:01:57

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 

荡你Thela!

+0

+1我将它转换为矩阵,例如显示“数字”列进行比较 – akrun 2014-09-11 12:29:13

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