dieses Beispiel Daten vor:neuen Variable nur für eine Teilmenge der Daten definierte erstellen mit `dplyr`
set.seed(1234567)
mydf <- data.frame(var1 = runif(10), var2 = c(runif(5), rep(NA, 5)))
Und diese vektorisiert Beispiel Funktion, die leider einen Fehler auslöst, wenn eines des Arguments ist NA
myfn <- function(x, y){
sum(x:y)
}
myfn <- Vectorize(myfn)
nun in der Mitte einer dplyr
Kette brauche ich eine neue Variable erstellen myfn
verwenden. Diese neue Variable (var3
) wird nur definiert, wenn var1
und var2
nicht NA
sind.
Die gebräuchlichste Lösung für ähnliche Situationen ist also ifelse
. Etwas wie das.
mydf %>%
mutate(var3 = ifelse(
test = is.na(var2),
yes = NA,
no = myfn(var1, var2)))
Aber nicht in meinem Fall arbeiten, weil ifelse
sowieso passiert eigentlich den ganzen Vektor var1
und var2
zu myfn
und nicht nur den Untervektor, wenn test
FALSE
ist. Und es bricht alles, weil myfn
bricht, wann immer eine NA
erhält.
Also, was ist die clevere dplyr
Lösung dafür? (Kann ich mir vorstellen viele Lösungen für diese ohne dplyr
, aber ich bin nur Interesse an einer dplyr
-friendly Lösung)
Es fiel mir ein, dass filter
helfen könnte, und arbeitet in der Tat mit einem sehr gut lesbar und dplyr
y Code
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
Aber dann hätte ich diese in einem temporären Objekt speichern, dann var3
in den Originaldaten erstellen, die alle mit NA
und alle wieder zusammen in den gleichen Daten setzen ('verursachen, soweit ich das unfilter
wissen, dass einige haben suggested existiert nicht, ..., noch).
Also nur die Ausgabe zu veranschaulichen, was ich will, dieser Code erzeugt es (kein dplyr
überhaupt verwendet wird):
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
EDIT:
I @ krlmlr Lösung akzeptiert, weil es, was ich ist war auf der Suche nach: klaren, leicht lesbaren und prägnanten Code, der sich mühelos in eine dplyr
-Kette integrieren lässt. Für mein Beispiel sieht diese Lösung so aus.
mydf %>%
rowwise %>%
mutate(var3 = if(is.na(var2)) NA else myfn(var1, var2))
Doch wie @krlmlr wies in seiner Antwort aus, Zeile für Zeile zu betreiben eine Kosten in Bezug auf die Leistung hat. Für kleine Datensätze oder einmalige Operationen ist es möglicherweise nicht von Bedeutung, aber für größere Datenmengen oder die millionenfache Wiederholung der Operation könnte es beträchtlich sein. Zur Veranschaulichung, hier ist ein Vergleich mit microbenchmark
und drei Lösungen (Basis, dyplr und data.table) über einen etwas größeren Datensatz (nicht massiv oder irgendetwas, nur 1000 Zeilen statt 10 in meinem ursprünglichen Beispiel) angewendet.
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)
Und wie Sie sehen können, die dplyr
Lösung rowwise
mit wesentlich langsamer als seine base
und data.table
Rivalen.
Ihre Funktion ist nur das Kopieren von nicht 'NA' Werte von' var1' in 'var3', ist, dass beabsichtigt? – mtoto
ist eine Beispielfunktion. Das ist nicht meine eigentliche Funktion. Es ist nur ein Beispiel, um hier einen kurzen reproduzierbaren Code zur Verfügung zu stellen, der das Problem veranschaulicht. – elikesprogramming
Wie wäre es, Ihre Funktion so zu reparieren, dass sie nicht bricht, wenn sie NA empfängt? – krlmlr