2015-06-11 46 views
6

我有如下所示的數據幀:dplyr:組中的最大值,不包括每行中的值?

> df <- data_frame(g = c('A', 'A', 'B', 'B', 'B', 'C'), x = c(7, 3, 5, 9, 2, 4)) 
> df 
Source: local data frame [6 x 2] 

    g x 
1 A 7 
2 A 3 
3 B 5 
4 B 9 
5 B 2 
6 C 4 

我知道如何與最大x值加列各組g

> df %>% group_by(g) %>% mutate(x_max = max(x)) 
Source: local data frame [6 x 3] 
Groups: g 

    g x x_max 
1 A 7  7 
2 A 3  7 
3 B 5  9 
4 B 9  9 
5 B 2  9 
6 C 4  4 

但我想是得到的是每個組的最大值x的值g,不包括x的值,每行

對於給定的例子中,所需的輸出應該是這樣的:

Source: local data frame [6 x 3] 
Groups: g 

    g x x_max x_max_exclude 
1 A 7  7    3 
2 A 3  7    7 
3 B 5  9    9 
4 B 9  9    5 
5 B 2  9    9 
6 C 4  4   NA 

我想我可能能夠使用row_number()刪除特定的元素,並採取了什麼仍然是最大的,但命中警告消息和得到不正確-Inf輸出:

> df %>% group_by(g) %>% mutate(x_max = max(x), r = row_number(), x_max_exclude = max(x[-r])) 
Source: local data frame [6 x 5] 
Groups: g 

    g x x_max r x_max_exclude 
1 A 7  7 1   -Inf 
2 A 3  7 2   -Inf 
3 B 5  9 1   -Inf 
4 B 9  9 2   -Inf 
5 B 2  9 3   -Inf 
6 C 4  4 1   -Inf 
Warning messages: 
1: In max(c(4, 9, 2)[-1:3]) : 
    no non-missing arguments to max; returning -Inf 
2: In max(c(4, 9, 2)[-1:3]) : 
    no non-missing arguments to max; returning -Inf 
3: In max(c(4, 9, 2)[-1:3]) : 
    no non-missing arguments to max; returning -Inf 

什麼是最{可讀性,簡潔,高效}辦法讓dplyr這個輸出?任何洞察到爲什麼我的嘗試使用row_number()不起作用也將不勝感激。謝謝您的幫助。

+0

這是代碼:總結(GROUP_BY(DF,G),max.x = MAX(X ))? –

+0

謝謝,@陳盛林,但這不符合上述示例中的所需輸出。這給了我每個組的最大'x'值(返回一個3行的data_frame)。但我想要的是具有與輸入表相同行數的data_frame,其中行'r'處的值給出'g'組中的最大'x'值,不包括行'r'。有關具體示例,請參閱上面的「所需輸出」。 – Eric

回答

4

你可以嘗試:

df %>% 
    group_by(g) %>% 
    arrange(desc(x)) %>% 
    mutate(max = ifelse(x == max(x), x[2], max(x))) 

其中給出:

#Source: local data frame [6 x 3] 
#Groups: g 
# 
# g x max 
#1 A 7 3 
#2 A 3 7 
#3 B 9 5 
#4 B 5 9 
#5 B 2 9 
#6 C 4 NA 

基準

我試過的解決方案至今在benchma RK:

df <- data.frame(g = sample(LETTERS, 10e5, replace = TRUE), 
       x = sample(1:10, 10e5, replace = TRUE)) 

library(microbenchmark) 

mbm <- microbenchmark(
    steven = df %>% 
    group_by(g) %>% 
    arrange(desc(x)) %>% 
    mutate(max = ifelse(x == max(x), x[2], max(x))), 
    eric = df %>% 
    group_by(g) %>% 
    mutate(x_max = max(x), 
      x_max2 = sort(x, decreasing = TRUE)[2], 
      x_max_exclude = ifelse(x == x_max, x_max2, x_max)) %>% 
    select(-x_max2), 
    arun = setDT(df)[order(x), x_max_exclude := c(rep(x[.N], .N-1L), x[.N-1L]), by=g], 
    times = 50 
) 

@ Arun的data.table溶液是最快:

# Unit: milliseconds 
# expr  min  lq  mean median  uq  max neval cld 
# steven 158.58083 163.82669 197.28946 210.54179 212.1517 260.1448 50 b 
# eric 223.37877 228.98313 262.01623 274.74702 277.1431 284.5170 50 c 
# arun 44.48639 46.17961 54.65824 47.74142 48.9884 102.3830 50 a 

enter image description here

3

有趣的問題。下面是使用data.table一個辦法:

require(data.table) 
setDT(df)[order(x), x_max_exclude := c(rep(x[.N], .N-1L), x[.N-1L]), by=g] 

的想法是爲了通過x並在這些指標中,我們按g。由於我們有有序的索引,因此對於第一個.N-1行,最大值是.N處的值。對於第.N行,這是第.N-1行的值。

.N是一個特殊的變量,它保存每個組中的觀察值的數量。

我會留給你和/或dplyr專家來翻譯這個(或用另一種方法回答)。

+0

感謝data.table版本@Arun。我認爲這與迄今爲止我最好的dplyr解決方案(我剛剛發佈)在精神上相似,儘管我不知道我的data.table足以說明它們是否相同。 – Eric

+2

埃裏克,類似的想法,但不是執行。你爲每個組調用'sort()',然後有'ifelse()'... – Arun

2

這是我迄今爲止最好的。不知道是否有更好的方法。

df %>% 
    group_by(g) %>% 
    mutate(x_max = max(x), 
     x_max2 = sort(x, decreasing = TRUE)[2], 
     x_max_exclude = ifelse(x == x_max, x_max2, x_max)) %>% 
    select(-x_max2) 
+0

你可以簡化爲:'group_by(df,g)%>%mutate(max = ifelse(x == max(x),sort(x,decrease = TRUE)[2],max(x)))' –

0

另一種方式與功能:我們編寫一個名爲max_exclude函數,它是你所描述的操作

df %>% group_by(g) %>% mutate(x_max_exclude = max_exclude(x)) 
Source: local data frame [6 x 3] 
Groups: g 

    g x x_max_exclude 
1 A 7    3 
2 A 3    7 
3 B 5    9 
4 B 9    5 
5 B 2    9 
6 C 4   NA 

max_exclude <- function(v) { 
    res <- c() 
    for(i in seq_along(v)) { 
    res[i] <- suppressWarnings(max(v[-i])) 
    } 
    res <- ifelse(!is.finite(res), NA, res) 
    as.numeric(res) 
} 

它與base R太:

df$x_max_exclude <- with(df, ave(x, g, FUN=max_exclude)) 
Source: local data frame [6 x 3] 

    g x x_max_exclude 
1 A 7    3 
2 A 3    7 
3 B 5    9 
4 B 9    5 
5 B 2    9 
6 C 4   NA 

基準

這裏有一個教訓孩子,for循環當心!

big.df <- data.frame(g=rep(LETTERS[1:4], each=1e3), x=sample(10, 4e3, replace=T)) 


microbenchmark(
    plafort_dplyr = big.df %>% group_by(g) %>% mutate(x_max_exclude = max_exclude(x)), 
    plafort_ave = big.df$x_max_exclude <- with(big.df, ave(x, g, FUN=max_exclude)), 
    StevenB = (big.df %>% 
    group_by(g) %>% 
    mutate(max = ifelse(row_number(desc(x)) == 1, x[row_number(desc(x)) == 2], max(x))) 
    ), 
    Eric = df %>% 
    group_by(g) %>% 
    mutate(x_max = max(x), 
      x_max2 = sort(x, decreasing = TRUE)[2], 
      x_max_exclude = ifelse(x == x_max, x_max2, x_max)) %>% 
    select(-x_max2), 
    Arun = setDT(df)[order(x), x_max_exclude := c(rep(x[.N], .N-1L), x[.N-1L]), by=g] 
) 

Unit: milliseconds 
      expr  min  lq  mean median  uq  max neval 
plafort_dplyr 75.219042 85.207442 89.247409 88.203225 90.627663 179.553166 100 
    plafort_ave 75.907798 84.604180 87.136122 86.961251 89.431884 104.884294 100 
     StevenB 4.436973 4.699226 5.207548 4.931484 5.364242 11.893306 100 
      Eric 7.233057 8.034092 8.921904 8.414720 9.060488 15.946281 100 
      Arun 1.789097 2.037235 2.410915 2.226988 2.423638 9.326272 100 
+0

這似乎相當昂貴。不確定這可能會擴大更大的數據集。 –

+1

@StevenBeaupré它可能是。這只是另一個想法。 –

+1

@StevenBeaupré我測試了速度。令人尷尬的緩慢。 –