dplyr过滤器:获取具有最小变量的行,但如果有多个最小值,则仅获取第一个


73

我想使用进行分组过滤dplyr,以这种方式在每个组中仅返回具有variable最小值的那一行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中解决此问题?(出于速度原因)


5
filter(df.g, rank(x) == 1)
哈德利2014年

2
@FelixS,能rank(x)==1得到想要的结果吗?
里卡多·萨波特塔2014年

4
@ hadley,1)我认为min_rank这里没有帮助。他需要第一个最小值(看plyr解决方案)。2)在您编写的任何编程语言中,算法的复杂度rank(ties = min,max,first等。)都将比仅计算复杂min
阿伦2014年

2
@Arun:是的,只能rank(x, ties.method="first")==1工作,因为min和min_rank不能区分多个最小值。
Felix S

4
@哈德利,我仍然不明白这怎么使你认为which.min是过早的优化。AFAIK是自然选择,读起来也很好,易于理解,而且速度也很快,因为它也恰好是O(n)。
阿伦(Arun)

Answers:


106

更新资料

使用dplyr> = 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)

3
我觉得do(head)更容易阅读,df %>% group_by(A) %>% filter(x == min(x)) %>% do(head(.,1))
baptiste 2014年

@baptiste确实看起来不错(但是,当我运行它时,我会收到一条错误消息Error: expecting a single value)-您知道为什么吗?
塔拉

不确定,也许我们使用的是其他版本;我有dplyr_0.2, magrittr_1.0.0
2014年

好的,问题是我仍在运行dplyr 0.1.3。Thx
塔拉特

1
我希望能够在top_n这里使用,但由于联系,该方法可能是明显的赢家-绝对在性能方面(与相比arrange %>% slice)。
康拉德·鲁道夫

37

出于完整性考虑:这是最终dplyr解决方案,源自@hadley和@Arun的评论:

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

16

对于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]

5

可以通过row_number结合使用来实现group_byrow_number通过不仅根据值而且还根据向量内的相对顺序来分配等级来处理联系。获取每个组的第一行,其最小值为x

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

有关更多信息,请参见窗口功能上的dplyr小插图


1

另一种方法是:

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

也可以很容易地调整为使每个组中的行具有最大值。


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

0

来到这里寻找一种方法来解决这个问题。我相信,这将使前十名最终平分秋色

df.g %>%
top_n(-10,row_number(x))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.