wake-up-neo.com

dplyr filter: Ruft Zeilen mit einem Minimum an Variablen ab, aber nur das erste, wenn mehrere Minima

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)

58
Felix S

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)
28
Felix S

Aktualisieren

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

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)
78

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]
14
Arun

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 .

4
junkka

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
0
nsr

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))
0
Kevin Mc

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. 

0
qed