在R中对含层级分组的表格里的factor变量y进行重排序
问题与解决方案:对因子变量y按组总和+组内值排序
原始数据
df <- tibble( x = as_factor(c("a", "a", "a", "b", "b", "b")), y = as_factor(1:6), val = c(10, 3, 8, 2, 6, 1) )
数据输出结果:
# A tibble: 6 x 3 x y val <fct> <fct> <dbl> 1 a 1 10 2 a 2 3 3 a 3 8 4 b 4 2 5 b 5 6 6 b 6 1
需求说明
需要对因子变量y进行重排序:
- 优先按
x分组后的val总和降序排列组(总和大的组整体排在前面) - 组内再按
val降序排列y
目标结果如下:
# A tibble: 6 x 4 # Groups: x [2] x y val sum <fct> <fct> <dbl> <dbl> 1 a 1 10 21 # x=="a"组的val总和更大,所以排在前面 2 a 3 8 21 # 组内按val降序排列y 3 a 2 3 21 4 b 5 6 9 5 b 4 2 9 6 b 6 1 9
尝试的方法及疑问
尝试用forcats::fct_reorder()结合group_by(x)实现:
df |> group_by(x) |> mutate(y = fct_reorder(y, val))
但未达到预期效果。需要解答:
fct_reorder()能否实现该需求?- 还有哪些更简洁的可行方法?
补充:已找到一个可行但较繁琐的解法:
df |> group_by(x) |> mutate(sum = sum(val)) |> arrange(desc(sum), desc(val)) |> ungroup() |> tibble::rowid_to_column() |> mutate(across(c(x, y), \(x) fct_reorder(x, rowid)))
解决方案
关于fct_reorder()的可行性
fct_reorder()无法直接结合group_by()实现这种双层排序需求。原因是fct_reorder()默认在全局范围内计算排序依据,即使分组后,它仅能实现组内y的排序,但组与组之间的顺序仍保留原始因子顺序,无法实现"先按组总和排序组,再组内排序"的双层逻辑。
简洁可行的方法
方法1:结合组总和与组内值作为排序依据
先计算每个x组的val总和,再将"组总和"和"组内val"组合作为fct_reorder()的排序变量,实现双层排序:
library(tidyverse) df |> group_by(x) |> mutate(sum_val = sum(val)) |> ungroup() |> mutate( y = fct_reorder(y, desc(sum_val, val)) ) |> arrange(y)
desc(sum_val, val)会先按sum_val降序,再按val降序,fct_reorder()根据该逻辑重排y的因子水平,最后arrange(y)即可得到目标顺序。
方法2:用fct_relevel()复用排序后的结果
先按需求排序数据集,再提取排序后的y水平,用fct_relevel()重置因子顺序:
df |> group_by(x) |> mutate(sum_val = sum(val)) |> ungroup() |> arrange(desc(sum_val), desc(val)) |> mutate(y = fct_relevel(y, y))
fct_relevel(y, y)会将当前排序后的y值序列作为新的因子水平顺序,后续arrange(y)将按该顺序排列。
方法3:直接修改因子水平属性
手动提取符合要求的y水平顺序,直接赋值给y的levels属性:
sorted_y <- df |> group_by(x) |> mutate(sum_val = sum(val)) |> ungroup() |> arrange(desc(sum_val), desc(val)) |> pull(y) df |> mutate(y = factor(y, levels = sorted_y)) |> arrange(y)
该方法先得到排序后的y序列,再将其设为新的因子水平,逻辑直接清晰。
内容的提问来源于stack exchange,提问作者Lukas
相关产品推荐
相关产品推荐

