4.8 Groupby e Combine

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.

4.8.1 Múltiplas Colunas de Origem

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


CC BY-NC-SA 4.0 Jose Storopoli, Rik Huijzer, Lazaro Alonso