dplyr фильтр: получить строки с минимумом переменной, но только первый, если несколько минимумов


Я хочу сделать сгруппированный фильтр с помощью dplyr, таким образом, что в каждой группе возвращается только та строка, которая имеет минимальное значение переменной x.

моя проблема: как и ожидалось, в случае нескольких минимумов все строки с минимальным значением возвращаются. Но в моем случае, Я хочу только первую строку если присутствуют несколько минимумов.

вот пример:

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

как и ожидалось, все минимумы будут вернулся:

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

С ddply, я бы подошел к задаче таким образом:

library(plyr)
ddply(df, .(A), function(z) {
    z[z$x == min(z$x), ][1, ]
})

... что работает:

  A x           y
1 A 1 -1.04584335
2 B 2  0.79600971
3 C 5 -0.08655151

Q: есть ли способ приблизиться к этому в dplyr? (по производительности)

6 55

6 ответов:

обновление

С dplyr >= 0.3 вы можете использовать

просто для полноты картины: вот окончательный dplyr решение, полученное из комментариев @hadley и @Arun:

library(dplyr)
df.g <- group_by(df, A)
filter(df.g, rank(x, ties.method="first")==1)

для чего это стоит, вот такой data.table решение, для тех, кто может быть заинтересован:

# 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]

Это может быть достигнуто с помощью row_number в сочетании с group_by. row_number обрабатывает связи, присваивая ранг не только по значению, но и по относительному порядку внутри вектора. Чтобы получить первую строку каждой группы с минимальным значением x:

df.g <- group_by(df, A)
filter(df.g, row_number(x) == 1)

для получения дополнительной информации см. dplyr виньетка на окне функции.

мне нравится sqldf за его простоту..

sqldf("select A,min(X),y from 'df.g' group by A")

выход:

A min(X)          y

1 A      1 -1.4836989

2 B      2  0.3755771

3 C      5  0.9284441

другой способ сделать это:

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

результат:

  a          b
1 1 -0.8356286
2 2 -2.2146999

также может быть легко адаптирован для получения строки в каждой группе с максимальным значением.