2012-10-11 7 views
8

Mögliche Duplizieren:
How to write an R function that evaluates an expression within a data-frameWarum funktioniert nicht faule Auswertung in dieser R-Funktion?

Ich möchte eine Funktion schreiben, die eine data.frame sortiert - stattdessen die umständliche Reihenfolge der Verwendung von(). Angesichts so etwas wie

> x=data.frame(a=c(5,6,7),b=c(3,5,1)) 
> x 
    a b 
1 5 3 
2 6 5 
3 7 1 

ich sagen möchte, so etwas wie:

sort.df(x,b) 

Also hier ist meine Funktion:

sort.df <- function(df, ...) { 
    with(df, df[order(...),]) 
} 

Ich war wirklich stolz. Bei der Lazy-Auswertung von R dachte ich, dass der ... -Parameter nur dann ausgewertet würde, wenn er benötigt würde - und zu diesem Zeitpunkt wäre er aufgrund von "mit" im Geltungsbereich.

Wenn ich die 'mit' Zeile direkt ausführen, funktioniert es. Aber die Funktion nicht.

> with(x,x[order(b),]) 
    a b 
3 7 1 
1 5 3 
2 6 5 
> sort.df(x,b) 
Error in order(...) : object 'b' not found 

Was ist falsch und wie es zu beheben? Ich sehe diese Art von "Magie" häufig in Paketen wie zum Beispiel plyr. Was ist der Trick?

+0

sort.df (x, x $ b) funktioniert, aber ich habe immer noch keine Ahnung, warum Art. df (x, b) funktioniert nicht – Ali

+0

Siehe auch 'plyr :: arrange', was genau das tut. – hadley

+1

Danke! Ich wusste nicht, arrangieren trotz der Verwendung von Plyr jeden Tag. Ein weiteres Beispiel, dass es schwierig ist, die richtigen Lösungen in der R-Welt zu finden - und so viel von guter R-Programmierung besteht darin, Best Practices mit ein paar guten Paketen zu lernen. –

Antwort

7

Es ist, weil, wenn Sie vorbei b Sie nicht tatsächlich ein Objekt übergeben. Setzen Sie eine browser in Ihre Funktion und Sie werden sehen, was ich meine. Ich habe das irgendwo von einem Internet-Roboter gestohlen:

x=data.frame(a=c(5,6,7),b=c(3,5,1)) 

sort.df <- function(df, ..., drop = TRUE){ 
    ord <- eval(substitute(order(...)), envir = df, enclos = parent.frame()) 
    return(df[ord, , drop = drop]) 
} 

sort.df(x, b) 

wird funktionieren.

Also, wenn Sie für eine nette Art und Weise gesuchtes dies in einem angelegten Sinne zu tun:

library(taRifx) 
sort(x, f=~b) 
+2

+ 1 für die nette Lösung und vor allem für das Herumspielen mit einem 'browser()' Aufruf innerhalb der Funktion. IMHO, das ist bei weitem der beste Weg, etwas über "..." und all die Ungereimtheiten zu erfahren, die es umgeben. –

+0

Jemand könnte mich korrigieren, aber 'enclos = parent.frame()' ist der Standard in 'eval', also einfach' eval (substitute (order (...)), envir = df) 'funktioniert auch :) – user1665355

9

Dies wird tun, was Sie wollen:

sort.df <- function(df, ...) { 
    dots <- as.list(substitute(list(...)))[-1] 
    ord <- with(df, do.call(order, dots)) 
    df[ord,] 
} 

## Try it out 
x <- data.frame(a=1:10, b=rep(1:2, length=10), c=rep(1:3, length=10)) 
sort.df(x, b, c) 

Und so wird:

sort.df2 <- function(df, ...) { 
    cl <- substitute(list(...)) 
    cl[[1]] <- as.symbol("order") 
    df[eval(cl, envir=df),] 
} 
sort.df2(x, b, c) 
+1

Oder 'sort.df <- Funktion (df, ...) df [Ordnung (Eval (Ersatz (...), df))] –

+0

@JoshuaUlrich - Nicht ganz das Gleiche. Ihre Sortierung endet erst mit dem ersten Element von '...', da 'substitute (...)' das nur erfasst. (Setzen Sie einen 'browser()' Aufruf in 'sort.df()', und vergleichen Sie dann 'substitute (...)' und 'substitute (list (...))', um zu sehen, was ich meine.) –

Verwandte Themen