我想使用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
问:有没有办法在dplyr中解决这个问题?(出于速度原因)
答案 0 :(得分:81)
使用dplyr&gt; = 0.3,您可以将slice
函数与which.min
结合使用,这将是我最喜欢的此任务方法:
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
对于样本数据,也可以在彼此之后使用两个filter
:
group_by(df, A) %>%
filter(x == min(x)) %>%
filter(1:n() == 1)
答案 1 :(得分:32)
为了完整性:这是最终的dplyr
解决方案,源自@hadley和@Arun的评论:
library(dplyr)
df.g <- group_by(df, A)
filter(df.g, rank(x, ties.method="first")==1)
答案 2 :(得分:14)
对于那些可能感兴趣的人来说,这是一个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]
答案 3 :(得分:4)
这可以通过将row_number
与group_by
结合使用来实现。 row_number
通过不仅通过值而且通过向量内的相对顺序分配等级来处理关系。要使每个组的第一行的最小值为x
:
df.g <- group_by(df, A)
filter(df.g, row_number(x) == 1)
有关详细信息,请参阅dplyr vignette on window functions。
答案 4 :(得分:2)
dplyr
提供 slice_min
函数,通过参数 with_ties = FALSE
library(dplyr)
df %>%
group_by(A) %>%
slice_min(x, with_ties = FALSE)
输出:
# A tibble: 3 x 3
# Groups: A [3]
A x y
<fct> <dbl> <dbl>
1 A 1 0.273
2 B 2 -0.462
3 C 5 1.08
答案 5 :(得分:0)
我喜欢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
答案 6 :(得分:0)
另一种方法:
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
也可轻松调整以使每组中的行具有最大值。
答案 7 :(得分:0)
来这里,寻找一种可以解决多个问题的方法。我相信,这将使前十名,最后的关系破裂
df.g %>%
top_n(-10,row_number(x))