2014-09-10 234 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