Ich möchte einen gruppierten Filter mit dplyr
so erstellen, dass innerhalb jeder Gruppe nur die Zeile zurückgegeben wird, die den Mindestwert der Variablen x
hat.
Mein Problem ist: Wie erwartet werden bei mehreren Minima alle -Zeilen mit dem Mindestwert zurückgegeben. In meinem Fall jedoch ich möchte nur die erste Zeile, 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 so 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 Gründen der Geschwindigkeit)
Nur zur Vollständigkeit: Hier ist die endgültige dplyr
-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)
Mit dplyr> = 0.3 können Sie die slice
-Funktion in Kombination mit which.min
verwenden. Dies wäre meine Lieblingsmethode für diese Aufgabe:
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
Für die Beispieldaten können auch zwei filter
nacheinander verwendet werden:
group_by(df, A) %>%
filter(x == min(x)) %>%
filter(1:n() == 1)
Für was es wert ist, hier ist eine data.table
-Lösung für diejenigen, die daran interessiert sind:
# 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]
Dies kann durch Verwendung von row_number
in Kombination mit group_by
erreicht werden. 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 Mindestwert von x
:
df.g <- group_by(df, A)
filter(df.g, row_number(x) == 1)
Weitere Informationen finden Sie in der dplyr -Vignette zu Fensterfunktionen .
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
Kam hierher auf der Suche nach einem Weg, dies mit mehr als einem zu tun. Ich glaube, das gibt den unteren zehn, die die Krawatten zuletzt brechen
df.g %>%
top_n(-10,row_number(x))
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.