Ich möchte einen gruppierten Filter so erstellen dplyr
, dass innerhalb jeder Gruppe nur die Zeile zurückgegeben wird, die den Mindestwert der Variablen hat x
.
Mein Problem ist: Wie erwartet werden bei mehreren Minima alle Zeilen mit dem Mindestwert zurückgegeben. In meinem Fall möchte ich die erste Zeile jedoch nur, wenn mehrere Minima vorhanden sind.
Hier ist ein Beispiel:
df <- data.frame(
A=c("A", "A", "A", "B", "B", "B", "C", "C", "C"),
x=c(1, 1, 2, 2, 3, 4, 5, 5, 5),
y=rnorm(9)
)
library(dplyr)
df.g <- group_by(df, A)
filter(df.g, x == min(x))
Wie erwartet werden alle Minima zurückgegeben:
Source: local data frame [6 x 3]
Groups: A
A x y
1 A 1 -1.04584335
2 A 1 0.97949399
3 B 2 0.79600971
4 C 5 -0.08655151
5 C 5 0.16649962
6 C 5 -0.05948012
Mit ddply hätte ich die Aufgabe folgendermaßen angegangen:
library(plyr)
ddply(df, .(A), function(z) {
z[z$x == min(z$x), ][1, ]
})
... was funktioniert:
A x y
1 A 1 -1.04584335
2 B 2 0.79600971
3 C 5 -0.08655151
F: Gibt es eine Möglichkeit, dies in dplyr anzugehen? (Aus Geschwindigkeitsgründen)
filter(df.g, rank(x) == 1)
?rank(x)==1
die gewünschten Ergebnisse?min_rank
dass hier hilft. Er braucht den ersten Min-Wert (sieheplyr
Lösung). 2) In jeder Programmiersprache, die Sie schreiben, ist die algorithmische Komplexität vonrank
(Bindungen = min, max, zuerst usw.) größer als nur das Rechnenmin
.rank(x, ties.method="first")==1
funktioniert nur , da min und min_rank nicht zwischen mehreren Minima unterscheiden.which.min
vorzeitige Optimierung betrachten. AFAIK ist eine natürliche Wahl, liest sich gut, ist leicht zu verstehen und schnell, da es auch O (n) ist.Antworten:
Aktualisieren
Mit dplyr> = 0.3 können Sie die
slice
Funktion in Kombination mit verwendenwhich.min
, was mein Lieblingsansatz für diese Aufgabe wäre:df %>% group_by(A) %>% slice(which.min(x)) #Source: local data frame [3 x 3] #Groups: A # # A x y #1 A 1 0.2979772 #2 B 2 -1.1265265 #3 C 5 -1.1952004
Ursprüngliche Antwort
Für die Beispieldaten können auch zwei
filter
nacheinander verwendet werden:group_by(df, A) %>% filter(x == min(x)) %>% filter(1:n() == 1)
quelle
do(head)
leichter zu lesen,df %>% group_by(A) %>% filter(x == min(x)) %>% do(head(.,1))
Error: expecting a single value
) - weißt du warum?dplyr_0.2, magrittr_1.0.0
top_n
aber aufgrund von Bindungen ist diese Methode wahrscheinlich der klare Gewinner - definitiv in Bezug auf die Leistung (im Vergleich zuarrange %>% slice
).Nur
dplyr
der Vollständigkeit halber: Hier ist die endgültige Lösung, abgeleitet aus den Kommentaren von @hadley und @Arun:library(dplyr) df.g <- group_by(df, A) filter(df.g, rank(x, ties.method="first")==1)
quelle
Für das, was es wert ist, ist hier eine
data.table
Lösung für diejenigen, die interessiert sein könnten:# approach with setting keys dt <- as.data.table(df) setkey(dt, A,x) dt[J(unique(A)), mult="first"] # without using keys dt <- as.data.table(df) dt[dt[, .I[which.min(x)], by=A]$V1]
quelle
Dies kann erreicht werden, indem in
row_number
Kombination mit verwendet wirdgroup_by
.row_number
Behandelt Bindungen, indem ein Rang nicht nur nach dem Wert, sondern auch nach der relativen Reihenfolge innerhalb des Vektors zugewiesen wird. So erhalten Sie die erste Zeile jeder Gruppe mit dem Mindestwertx
:df.g <- group_by(df, A) filter(df.g, row_number(x) == 1)
Weitere Informationen finden Sie in der dplyr- Vignette zu Fensterfunktionen .
quelle
Ein anderer Weg, es zu tun:
set.seed(1) x <- data.frame(a = rep(1:2, each = 10), b = rnorm(20)) x <- dplyr::arrange(x, a, b) dplyr::filter(x, !duplicated(a))
Ergebnis:
a b 1 1 -0.8356286 2 2 -2.2146999
Könnte auch leicht angepasst werden, um die Zeile in jeder Gruppe mit maximalem Wert zu erhalten.
quelle
Ich mag sqldf wegen seiner Einfachheit.
sqldf("select A,min(X),y from 'df.g' group by A")
Ausgabe:
A min(X) y 1 A 1 -1.4836989 2 B 2 0.3755771 3 C 5 0.9284441
quelle
Kam hierher und suchte nach einer Möglichkeit, dies mit mehr als einer zu tun. Dies wird die untersten zehn geben und die Bindungen bis zum letzten brechen, glaube ich
df.g %>% top_n(-10,row_number(x))
quelle