在 R 编程语言中,Wickham (2011) 推广了用于数据转换的 split-apply-combine 模式。 在该模式中,我们先将数据 split 成不同组,然后对每一组 apply 一个或多个函数,最后 combine 每组的结果。 DataFrames.jl
完全支持 split-apply-combine 模式。 本节使用之前的学生成绩数据作为示例。 假设想获得每个学生的平均成绩:
function all_grades()
df1 = grades_2020()
df1 = select(df1, :name, :grade_2020 => :grade)
df2 = grades_2021()
df2 = select(df2, :name, :grade_2021 => :grade)
rename_bob2(data_col) = replace.(data_col, "Bob 2" => "Bob")
df2 = transform(df2, :name => rename_bob2 => :name)
return vcat(df1, df2)
end
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 指定为数据集分割的条件。
groupby(all_grades(), :name)
GroupedDataFrame with 4 groups based on key: name
Group 1 (2 rows): name = "Sally"
Row │ name grade
│ String Float64
─────┼─────────────────
1 │ Sally 1.0
2 │ Sally 9.5
Group 2 (2 rows): name = "Bob"
Row │ name grade
│ String Float64
─────┼─────────────────
1 │ Bob 5.0
2 │ Bob 9.5
Group 3 (1 row): name = "Alice"
Row │ name grade
│ String Float64
─────┼─────────────────
1 │ Alice 8.5
Group 4 (2 rows): name = "Hank"
Row │ name grade
│ String Float64
─────┼─────────────────
1 │ Hank 4.0
2 │ Hank 6.0
mean
函数来自 Julia 标准库中的 Statistics
模块:
using Statistics
应用此函数时,需调用 combine
函数:
gdf = groupby(all_grades(), :name)
combine(gdf, :grade => mean)
name | grade_mean |
---|---|
Sally | 5.25 |
Bob | 7.25 |
Alice | 8.5 |
Hank | 5.0 |
想象一下,如果没有 groupby
和 combine
函数,则需按照下文这样做。 我们必须循环遍历数据以将其分割为多组,然后循环遍历每组以应用函数,以及 循环遍历每组以收集最终结果。 因此,split-apply-combine 模式是值得掌握的技术。
但如果我们想将一个函数应用到多列数据,该如何操作?
group = [:A, :A, :B, :B]
X = 1:4
Y = 5:8
df = DataFrame(; group, X, Y)
group | X | Y |
---|---|---|
A | 1 | 5 |
A | 2 | 6 |
B | 3 | 7 |
B | 4 | 8 |
操作与之前类似:
gdf = groupby(df, :group)
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
)。
gdf = groupby(df, :group)
rounded_mean(data_col) = round(Int, mean(data_col))
combine(gdf, [:X, :Y] .=> rounded_mean; renamecols=false)
group | X | Y |
---|---|---|
A | 2 | 6 |
B | 4 | 8 |