考慮這個例子僅數據爲數據的子集定義的新的變量:創建使用`dplyr`
set.seed(1234567)
mydf <- data.frame(var1 = runif(10), var2 = c(runif(5), rep(NA, 5)))
而這個實施例向量化功能,不幸的是,觸發一個錯誤時的參數之一是NA
myfn <- function(x, y){
sum(x:y)
}
myfn <- Vectorize(myfn)
現在,在dplyr
鏈的中間,我需要使用myfn
創建一個新變量。此新變量(var3
)僅在var1
和var2
不是NA
時定義。
因此,類似情況最常見的解決方案是使用ifelse
。像這樣的東西。
mydf %>%
mutate(var3 = ifelse(
test = is.na(var2),
yes = NA,
no = myfn(var1, var2)))
但是,這並不在我的情況下工作,因爲ifelse
全矢量var1
和var2
反正實際上傳遞給myfn
而不僅僅是部分向量時test
是FALSE
。並且它全部中斷,因爲myfn
每當收到NA
時都會中斷。
那麼,什麼是聰明的dplyr
解決方案呢? (我能想到如此多的解決方案,而無需使用dplyr
,但我在dplyr
- 友好的解決方案只是有興趣)
它發生,我認爲filter
可以幫助和一個非常可讀和dplyr
而Y碼確實工作
mydf %>%
filter(!is.na(var2)) %>%
mutate(var3 = myfn(var1, var2))
var1 var2 var3
1 0.56226084 0.62588794 0.56226084
2 0.72649850 0.24145251 0.72649850
3 0.91524985 0.03768974 0.91524985
4 0.02969437 0.51659297 0.02969437
5 0.76750970 0.81845788 0.76750970
但後來我不得不把它保存在一個臨時對象,然後創建在所有NA
原始數據var3
,並把所有回一起在相同的數據(因爲據我所知unfilter
一些有suggested不存在,...,但)。
所以只是爲了說明我想要的輸出,該代碼產生它(不使用dplyr
在所有):
mydf$var3 <- NA
index <- !is.na(mydf$var2)
mydf$var3[index] <- myfn(mydf$var1[index], mydf$var2[index])
mydf
> mydf
var1 var2 var3
1 0.56226084 0.62588794 0.56226084
2 0.72649850 0.24145251 0.72649850
3 0.91524985 0.03768974 0.91524985
4 0.02969437 0.51659297 0.02969437
5 0.76750970 0.81845788 0.76750970
6 0.48005398 NA NA
7 0.08837960 NA NA
8 0.86294587 NA NA
9 0.49660306 NA NA
10 0.85350403 NA NA
編輯:
我接受了@ krlmlr的解決方案,因爲它是我一直在尋找:清晰,易讀,簡潔的代碼,可輕鬆集成到dplyr
鏈中。對於我的例子,這個解決方案看起來像這樣。
mydf %>%
rowwise %>%
mutate(var3 = if(is.na(var2)) NA else myfn(var1, var2))
但是,正如@krlmlr在他的回答中指出的那樣,逐行操作在性能方面存在成本。對於小數據集或單次操作可能並不重要,但對於較大的數據集或重複數百萬次的操作,這可能相當可觀。舉例來說,下面是使用microbenchmark
和三種解決方案(base,dyplr和data.table)進行的比較,該解決方案應用於較大的數據集(不是大規模或任何其他數據集,只有1000行,而不是原始示例中的10行)。
library(data.table)
library(dplyr)
set.seed(1234567)
mydf <- data.frame(var1 = runif(1000), var2 = c(runif(500), rep(NA, 500)))
myfn <- function(x, y){
sum(x:y)
}
myfn <- Vectorize(myfn)
using_base <- function(){
mydf$var3 <- NA
index <- !is.na(mydf$var2)
mydf$var3[index] <- myfn(mydf$var1[index], mydf$var2[index])
}
using_dplyr <- function(){
mydf <- mydf %>%
rowwise %>%
mutate(var3 = if(is.na(var2)) NA else myfn(var1, var2))
}
using_datatable <- function(){
setDT(mydf)[!is.na(var2), var3 := myfn(var1, var2)]
}
library(microbenchmark)
mbm <- microbenchmark(
using_base(), using_dplyr(), using_datatable(),
times = 1000)
library(ggplot2)
autoplot(mbm)
正如你可以看到,使用rowwise
的dplyr
解決方案比其base
和data.table
競爭對手慢得多。
你的函數只複製從'var1'非'NA'值到'var3',是意? – mtoto
它是一個示例函數。這不是我的實際功能。這只是一個例子,在這裏提供一個簡短的可重現代碼來說明問題 – elikesprogramming
如何修復你的函數,使它在收到NA時不會中斷? – krlmlr