Para a linguagem de programação R, Wickham (2011) popularizou a chamada estratégia _split-apply-combine (ou dividir-aplicar-combinar) para transformações de dados. Em essência, essa estratégia divide o dataset em grupos distintos, aplica uma ou mais funções para cada grupo e, depois, combina o resultado. DataFrames.jl
suporta totalmente o método dividir-aplicar-combinar. Usaremos o exemplo das notas do aluno como antes. Suponha que queremos saber a nota média de cada aluno:
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 |
A estratégia é dividir o dataset em alunos distintos, aplicar a função média para cada aluno e combinar o resultado.
A divisão é chamada groupby
e passamos como segundo argumento o ID da coluna em que queremos dividir o dataset:
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
Nós aplicamos a função mean
da biblioteca padrão de Julia no módulo Statistics
:
using Statistics
Para aplicá-la, utilizamos a função 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 |
Imagine ter que fazer isso sem as funções groupby
e combine
. Precisaríamos iterar sobre nossos dados para dividi-los em grupos, em seguida, iterar sobre os registros em cada divisão para aplicar a função e, finalmente, iterar sobre cada grupo para obter o resultado final. Vê-se assim que é muito bom conhecer a técnica dividir-aplicar-combinar.
Mas, e se quisermos aplicar uma função à várias colunas de nosso dataset?
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 |
Isso é feito de maneira semelhante:
gdf = groupby(df, :group)
combine(gdf, [:X, :Y] .=> mean; renamecols=false)
group | X | Y |
---|---|---|
A | 1.5 | 5.5 |
B | 3.5 | 7.5 |
Perceba que usamos o operador dot .
antes da seta à direita =>
para indicar que o mean
tem que ser aplicado a múltiplas colunas de origem [:X, :Y]
.
Para usar funções compostas, uma maneira simples é criar uma função que faça as transformações compostas pretendidas. Por exemplo, para uma série de valores, vamos primeiro pegar o mean
seguido de round
para um número inteiro (também conhecido como um inteiro 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 |