2012-04-09 8 views
21

Ich habe eine dataframe und ich möchte eine Funktion anwenden, die die Werte von drei Spalten und berechnet die minimale Differenz zwischen den drei Werten.Verwenden Sie mehrere Spalten als Variablen mit Sapply

#dataset 
df <- data.frame(a= sample(1:100, 10),b = sample(1:100, 10),c= sample(1:100, 10)) 

#function 
minimum_distance <- function(a,b,c) 
{ 
    dist1 <- abs(a-b) 
    dist2 <- abs(a-c) 
    dist3 <- abs(b-c) 
    return(min(dist1,dist2,dist3)) 
} 

Ich suche so etwas wie:

df$distance <- sapply(df, function(x) minimum_distance(x$a,x$b,x$c)) 
## errormessage 
Error in x$a : $ operator is invalid for atomic vectors 

Während ich ddply verwenden:

df2 <- ddply(df,.(a),function(r) {data.frame(min_distance=minimum_distance(r$a,r$b, r$c))}, .drop=FALSE) 

Dies nicht alle Spalten nicht halten. Irgendwelche Vorschläge?

Edit: Ich landete mit:

df$distance <- mapply(minimum_distance, df$a, df$b, df$c) 

Antwort

38

Versuchen mapply():

qq <- mapply(minimum_distance, df$a, df$b, df$c) 
+0

einfach und elegant. danke – zach

+0

Welche ine ist die schnellste? oder effizienter? – Bharath

6

try this:

do.call("mapply", c(list(minimum_distance), df)) 

aber Sie können vektorisiert Version schreiben:

pminimum_distance <- function(a,b,c) 
{ 
dist1 <- abs(a-b) 
dist2 <- abs(a-c) 
dist3 <- abs(b-c) 
return(pmin(dist1,dist2,dist3)) 
} 
pminimum_distance(df$a, df$b, df$c) 

# or 
do.call("pminimum_distance", df) 
+0

das ist schlau, aber ein bisschen weniger einfach danke mapply. – zach

4

Ich weiß, das beantwortet wurde, aber ich würde eigentlich einen anderen Ansatz nehmen, die eine beliebige Anzahl von Spalten in Anspruch nimmt und mehr verallgemeinerbar einen äußeren Ansatz:

vdiff <- function(x){ 
    y <- outer(x, x, "-") 
    min(abs(y[lower.tri(y)])) 
} 

apply(df, 1, vdiff) 

Ich denke, das ein wenig sauberer und flexibel ist.

EDIT: Pro zachs Kommentare Ich schlage diese mehr formalisierte Funktion, die auf Datenrahmen mit nicht-numerischen Spalten auch funktioniert, indem Sie sie entfernen und nur auf die numerischen Spalten handeln.

cdif <- function(dataframe){ 
    df <- dataframe[, sapply(dataframe, is.numeric)] 
    vdiff <- function(x){ 
     y <- outer(x, x, "-") 
     min(abs(y[lower.tri(y)])) 
    } 
    return(apply(df, 1, vdiff)) 
} 

#TEST it out 
set.seed(10) 
(df <- data.frame(a = sample(1:100, 10), b = sample(1:100, 10), 
    c = sample(1:100, 10), d = LETTERS[1:10])) 

cdif(df) 
+0

nette Idee. Mein realer Datenrahmen ist jedoch keine Matrix - könnte dies für die Verwendung in einem Datenrahmen mit Textspalten modifiziert werden? etwas wie äußere (x, x, "-", drop_string = T)? – zach

+0

Die Funktion "äußere" bedeutet nicht unbedingt, dass Sie an einer Matrix arbeiten. Es benötigt nur zwei Vektoren und eine Funktion und erstellt eine Matrix aller möglichen Kombinationen für diese beiden Vektoren. Hier gebe ich einfach zweimal den gleichen Vektor (die Zeile) nach außen und den Funktionssubtraktions-Operator an. Ich habe ein bisschen zu meiner Lösung hinzugefügt, um eine in sich geschlossene Funktion zu machen, die auf Datenrahmen wirkt und alles ausschließt, was nicht numerisch ist. "Äußeres" kann sehr kraftvoll sein Ich wünschte nur, ich könnte mich daran erinnern, es mehr zu benutzen. Soweit der Drop_string = T? Kein Glück, aber 'sapply' mit einer' is.numeric'-Abfrage funktioniert gut. –

+0

sehr nett. Ich stimme zu, dass das Äußere sehr mächtig ist und dass dies für eine größere Matrix der richtige Weg wäre, anstatt jede Spalte oder jeden Wert anzugeben. – zach

0

Es ist besser, eine Funktion zu schreiben und dann mapply auf den Vektoren verwenden:

f1 <- function(a,b,c){ 
d =abs(a-b) 
e =abs(b-c) 
f= abs(c-a) 
return(pmin(d,e,f)) 
} 

qq <- mapply(f1, df$a, df$b, df$c) 
Verwandte Themen