使用每个组的最大值按组排序

时间:2017-07-27 19:57:02

标签: r

有没有办法按组使用每组减少的最大值进行排序,并通过减少值在组内进行排序?

输入

 x <- read.table(text = "Name Value 
                         A    20    
                         A    40   
                         A    35   
                         B    70    
                         B    80   
                         B    90    
                         C    10   
                         C    20  
                         C    30 ", header = T)

输出

Name Value 
B    90    
B    80   
B    70   
A    40    
A    35   
A    20    
C    30   
C    20  
C    10   

3 个答案:

答案 0 :(得分:7)

按名称分组,制作一个临时列,然后按其排列和值

df %>% group_by(Name) %>% 
  mutate(mx = max(Value)) %>% 
  arrange(desc(mx), desc(Value)) %>% 
  select(-mx)

答案 1 :(得分:5)

编辑:更正和基准

在准备基准测试期间,我注意到我的一些解决方案仅适用于OP提供的部分有序的样本数据,但却因任意改组的数据而失败。我已从我的答案中删除了这些内容,但它们仍然可以从历史中获得。

添加的基准测试部分比较了两种不同问题规模的基础R,data.tabledpylr解决方案。

为了完整起见,这里有两个data.table和一个基本R解决方案:

1。 data.table

library(data.table)
X <- data.table(x)
X[, tmp := max(Value), by = Name][order(-tmp, -Value)][, tmp := NULL][]
   Name Value
1:    B    90
2:    B    80
3:    B    70
4:    A    40
5:    A    35
6:    A    20
7:    C    30
8:    C    20
9:    C    10

@Frank提出了一个更简洁的data.table解决方案:

X <- data.table(x)
X[order(-Value), .SD, by = Name]

这是有效的,因为在排序之后保留了每个组中行的顺序,就像组的顺序一样。help("data.table"参数上的by)< / p>

2。基础R

这是Frank's suggestion的修改:

x[with(x, order(-ave(Value, Name, FUN = max), -Value)), ]
  Name Value
6    B    90
5    B    80
4    B    70
2    A    40
3    A    35
1    A    20
9    C    30
8    C    20
7    C    10

基准

为了针对不同的问题规模运行基准测试,我们需要编制样本数据:

library(magrittr)
library(data.table)

# prepare data
n_rows <- 1E1
set.seed(1234L)
x0 <- data.frame(
  Name = sample(LETTERS[seq_len(min(round(n_rows/3), length(LETTERS)))], n_rows, TRUE),
  Value = sample(n_rows))
# coerce to data.table
X0 <- data.table(x0)

某些解决方案会更改数据,因此每次运行都会以新的副本启动。复制操作也是定时的。

# run benchmarks
microbenchmark::microbenchmark(
  copy = x <- copy(x0),
  base_avg = {
    x <- copy(x0)
    x[with(x, order(-ave(Value, Name, FUN = max), -Value)),]
  },
  dt_tmp = {
    X <- copy(X0)
    X[, tmp := max(Value), by = Name][order(-tmp, -Value)][, tmp := NULL][]
  },
  dt_SD = {
    X <- copy(X0)
    X[order(-Value), .SD, by = Name]
  },
  dplyr_rt = {
    copy(x0) %>% 
      dplyr::group_by(Name) %>% 
      dplyr::mutate(mx = max(Value)) %>% 
      dplyr::arrange(desc(mx), desc(Value)) %>% 
      dplyr::select(-mx)
  },
  dplyr_loki = {
    copy(x0) %>% 
      dplyr::group_by(Name) %>%  # group by Name
      dplyr::mutate(NameMax = max(Value)) %>%  # create a temp variable holding the max of each Name
      dplyr::arrange(desc(NameMax), desc(Value)) %>%  # arrange with two columns
      dplyr::select(Name, Value) # select only the two input columns
  },
  times = 100L
)

以下是10行和1 M行的两种不同问题大小的结果。

只有10行(主要是测量开销),

Unit: microseconds
       expr      min        lq       mean    median        uq       max neval  cld
       copy   12.839   19.8235   23.69004   24.1660   27.9415    38.892   100 a   
   base_avg  149.145  193.5115  256.32913  227.8710  243.5400  2180.155   100 a   
     dt_tmp  951.883 1046.2780 1185.33735 1175.2215 1256.9685  1903.387   100  b  
      dt_SD  758.184  827.2810 1386.55800  970.0065 1035.5170 40170.486   100  b  
   dplyr_rt 7329.984 7700.2025 8107.00109 7925.8075 8273.7485 11631.389   100   c 
 dplyr_loki 8650.386 9059.3065 9820.92511 9269.0525 9745.3710 33805.600   100    d

基础R显然是最快的,其次是data.table两种方法,两种dplyr方法都是最后的。 dplyr之间的区别很有意思。似乎dplyr::select(-mx)更快,即开销比dplyr::select(Name, Value)更少,这是代码中的唯一差异。

有1 M行(n_rows <- 1E6),

Unit: milliseconds
       expr        min         lq       mean     median         uq        max neval  cld
       copy   1.378927   1.809747   2.404129   2.268131   2.374798   5.561016    11 a   
   base_avg 131.783388 143.402694 207.434114 255.460485 259.142658 276.782117    11   c 
     dt_tmp  70.030740  72.104982  84.694791  75.020852  76.197961 192.827694    11  b  
      dt_SD  53.406217  55.149698  58.090283  58.156189  60.084684  67.835483    11  b  
   dplyr_rt 752.707729 779.147098 821.327276 809.360011 878.231387 895.566707    11    d
 dplyr_loki 747.559410 765.500369 792.089826 793.458180 803.056861 895.127580    11    d

两个data.table解决方案比基本R方法快4倍,比dplyr解决方案快10倍以上,具有 dt_SD 的优势。 dplyr_rt dplyr_loki 之间的巨大差异消失了,现在似乎 dplyr_loki 更快了。

答案 2 :(得分:1)

这是一个dplyr解决方案。请参阅@ UweBlock的基准答案。

library(dplyr)
x %>% group_by(Name) %>%  # group by Name
      mutate(NameMax = max(Value)) %>%  # create a temp variable holding the max of each Name
      arrange(desc(NameMax), desc(Value)) %>%  # arrange with two columns
      select(Name, Value) # select only the two input columns

#     Name Value
#    <fctr> <int>
# 1      B    90
# 2      B    80
# 3      B    70
# 4      A    40
# 5      A    35
# 6      A    20
# 7      C    30
# 8      C    20
# 9      C    10