有没有办法按组使用每组减少的最大值进行排序,并通过减少值在组内进行排序?
输入
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
答案 0 :(得分:7)
按名称分组,制作一个临时列,然后按其排列和值
df %>% group_by(Name) %>%
mutate(mx = max(Value)) %>%
arrange(desc(mx), desc(Value)) %>%
select(-mx)
答案 1 :(得分:5)
在准备基准测试期间,我注意到我的一些解决方案仅适用于OP提供的部分有序的样本数据,但却因任意改组的数据而失败。我已从我的答案中删除了这些内容,但它们仍然可以从历史中获得。
添加的基准测试部分比较了两种不同问题规模的基础R,data.table
和dpylr
解决方案。
为了完整起见,这里有两个data.table
和一个基本R解决方案:
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>
这是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