4.8 Groupby 和 Combine
在 R 编程语言中,Wickham (2011) 推广了用于数据转换的 split-apply-combine 模式。 在该模式中,我们先将数据 split 成不同组,然后对每一组 apply 一个或多个函数,最后 combine 每组的结果。 DataFrames.jl 完全支持 split-apply-combine 模式。 本节使用之前的学生成绩数据作为示例。 假设想获得每个学生的平均成绩:
1. function all_grades()
2. df1 = grades_2020()
3. df1 = select(df1, :name, :grade_2020 => :grade)
4. df2 = grades_2021()
5. df2 = select(df2, :name, :grade_2021 => :grade)
6. rename_bob2(data_col) = replace.(data_col, "Bob 2" => "Bob")
7. df2 = transform(df2, :name => rename_bob2 => :name)
8. return vcat(df1, df2)
9. end
10. all_grades()
| name | grade |
|---|---|
| Sally | 1.0 |
| Bob | 5.0 |
| Alice | 8.5 |
| Hank | 4.0 |
| Bob | 9.5 |
| Sally | 9.5 |
| Hank | 6.0 |
按照该模式,先将数据集按照学生名称 split 为不同组,其次对每组数据 apply 均值函数,最后 combine 每组的结果。
在 split 步骤中使用的函数为 groupby,并将函数的第二个参数列 ID 指定为数据集分割的条件。
1. groupby(all_grades(), :name)
1. GroupedDataFrame with 4 groups based on key: name
2. Group 1 (2 rows): name = "Sally"
3. Row │ name grade
4. │ String Float64
5. ─────┼─────────────────
6. 1 │ Sally 1.0
7. 2 │ Sally 9.5
8. Group 2 (2 rows): name = "Bob"
9. Row │ name grade
10. │ String Float64
11. ─────┼─────────────────
12. 1 │ Bob 5.0
13. 2 │ Bob 9.5
14. Group 3 (1 row): name = "Alice"
15. Row │ name grade
16. │ String Float64
17. ─────┼─────────────────
18. 1 │ Alice 8.5
19. Group 4 (2 rows): name = "Hank"
20. Row │ name grade
21. │ String Float64
22. ─────┼─────────────────
23. 1 │ Hank 4.0
24. 2 │ Hank 6.0
mean 函数来自 Julia 标准库中的 Statistics 模块:
1. using Statistics
应用此函数时,需调用 combine 函数:
1. gdf = groupby(all_grades(), :name)
2. combine(gdf, :grade => mean)
| name | grade_mean |
|---|---|
| Sally | 5.25 |
| Bob | 7.25 |
| Alice | 8.5 |
| Hank | 5.0 |
想象一下,如果没有 groupby 和 combine 函数,则需按照下文这样做。 我们必须循环遍历数据以将其分割为多组,然后循环遍历每组以应用函数,以及 循环遍历每组以收集最终结果。 因此,split-apply-combine 模式是值得掌握的技术。
4.8.1 Multiple Source Columns
但如果我们想将一个函数应用到多列数据,该如何操作?
1. group = [:A, :A, :B, :B]
2. X = 1:4
3. Y = 5:8
4. df = DataFrame(; group, X, Y)
| group | X | Y |
|---|---|---|
| A | 1 | 5 |
| A | 2 | 6 |
| B | 3 | 7 |
| B | 4 | 8 |
操作与之前类似:
1. gdf = groupby(df, :group)
2. combine(gdf, [:X, :Y] .=> mean; renamecols=false)
| group | X | Y |
|---|---|---|
| A | 1.5 | 5.5 |
| B | 3.5 | 7.5 |
注意到,我们在右箭头 => 前使用了 . 点运算符,这表示 mean 函数将应用到多个列 [:X, :Y]。
要在combine中使用组合函数,一种简单的方法是创建一个函数来执行预期的组合变换。 例如,对于一组数据,在先应用 mean后调用 round 对值取整(即 Int )。
1. gdf = groupby(df, :group)
2. rounded_mean(data_col) = round(Int, mean(data_col))
3. combine(gdf, [:X, :Y] .=> rounded_mean; renamecols=false)
| group | X | Y |
|---|---|---|
| A | 2 | 6 |
| B | 4 | 8 |
CC BY-NC-SA 4.0 Jose Storopoli, Rik Huijzer, Lazaro Alonso, 刘贵欣 (中文翻译), 田俊 (中文审校)
