Em Section 4.3.1, vimos que filter funciona pegando uma ou mais colunas de origem e filtrando-as por meio da aplicação de uma função de “filtragem.” Para recapitular, aqui está um exemplo de filtro usando a sintaxe source => f::Function: filter(:name => name -> name == "Alice", df).
Em Section 4.4, vimos que select pode pegar uma ou mais colunas de origem e colocá-las em uma ou mais colunas de destino source => target. Também para recapitular aqui está um exemplo: select(df, :name => :people_names).
Nesta seção, discutimos como transformar variáveis, ou seja, como modificar dados. Em DataFrames.jl, a sintaxe é source => transformation => target.
Como antes, usamos o dataset grades_2020:
function grades_2020()
name = ["Sally", "Bob", "Alice", "Hank"]
grade_2020 = [1, 5, 8.5, 4]
DataFrame(; name, grade_2020)
end
grades_2020()
| name | grade_2020 |
|---|---|
| Sally | 1.0 |
| Bob | 5.0 |
| Alice | 8.5 |
| Hank | 4.0 |
Suponha que queremos aumentar todas as notas em grades_2020 por 1. Primeiro, definimos uma função que leva como argumento um vetor de dados e retorna todos os seus elementos acrescidos de 1. Depois usamos a função transform do DataFrames.jl que, como todas as funções nativas DataFrames.jl’, tem como primeiro argumento um DataFrame, seguido pela sintaxe de transformação:
plus_one(grades) = grades .+ 1
transform(grades_2020(), :grade_2020 => plus_one)
| name | grade_2020 | grade_2020_plus_one |
|---|---|---|
| Sally | 1.0 | 2.0 |
| Bob | 5.0 | 6.0 |
| Alice | 8.5 | 9.5 |
| Hank | 4.0 | 5.0 |
Aqui, a função plus_one recebe toda a coluna :grade_2020. Essa é a razão pela qual adicionamos o caractere de “ponto” do broadcasting . antes do operador +. Para uma recapitulação sobre broadcasting por gentileza veja Section 3.2.1.
Como dissemos acima, a minilinguagem do módulo DataFrames.jl é sempre source => transformation => target. Então, se quisermos manter a nomenclatura da coluna alvo (target) na coluna de output podemos fazer:
transform(grades_2020(), :grade_2020 => plus_one => :grade_2020)
| name | grade_2020 |
|---|---|
| Sally | 2.0 |
| Bob | 6.0 |
| Alice | 9.5 |
| Hank | 5.0 |
Também podemos usar o argumento de palavra-chave renamecols=false:
transform(grades_2020(), :grade_2020 => plus_one; renamecols=false)
| name | grade_2020 |
|---|---|
| Sally | 2.0 |
| Bob | 6.0 |
| Alice | 9.5 |
| Hank | 5.0 |
A mesma transformação também pode ser escrita com select:
select(grades_2020(), :, :grade_2020 => plus_one => :grade_2020)
| name | grade_2020 |
|---|---|
| Sally | 2.0 |
| Bob | 6.0 |
| Alice | 9.5 |
| Hank | 5.0 |
onde o : significa “selecione todas as colunas” como descrito em Section 4.4. Como alternativa, você também pode utilizar o broadcasting de Julia e modificar a coluna grade_2020 acessando-a com df.grade_2020:
df = grades_2020()
df.grade_2020 = plus_one.(df.grade_2020)
df
| name | grade_2020 |
|---|---|
| Sally | 2.0 |
| Bob | 6.0 |
| Alice | 9.5 |
| Hank | 5.0 |
Mas, embora o último exemplo seja mais fácil, pois se baseia em operações nativas de Julia, é altamente recomendável usar as funções fornecidas por DataFrames.jl na maioria dos casos porque são mais robustas e fáceis de trabalhar.
Para mostrar como transformar duas colunas ao mesmo tempo, usamos os dados sobre os quais realizamos o left join em Section 4.6:
leftjoined = leftjoin(grades_2020(), grades_2021(); on=:name)
| name | grade_2020 | grade_2021 |
|---|---|---|
| Sally | 1.0 | 9.5 |
| Hank | 4.0 | 6.0 |
| Bob | 5.0 | missing |
| Alice | 8.5 | missing |
Com isso, podemos adicionar uma coluna dizendo se alguém foi aprovado pelo critério de que todas as suas notas estivessem acima 5.5:
pass(A, B) = [5.5 < a || 5.5 < b for (a, b) in zip(A, B)]
transform(leftjoined, [:grade_2020, :grade_2021] => pass; renamecols=false)
| name | grade_2020 | grade_2021 | grade_2020_grade_2021 |
|---|---|---|---|
| Sally | 1.0 | 9.5 | true |
| Hank | 4.0 | 6.0 | true |
| Bob | 5.0 | missing | missing |
| Alice | 8.5 | missing | true |
Podemos limpar o resultado e colocar a lógica em uma função para obter uma lista de todos os alunos aprovados:
function only_pass()
leftjoined = leftjoin(grades_2020(), grades_2021(); on=:name)
pass(A, B) = [5.5 < a || 5.5 < b for (a, b) in zip(A, B)]
leftjoined = transform(leftjoined, [:grade_2020, :grade_2021] => pass => :pass)
passed = subset(leftjoined, :pass; skipmissing=true)
return passed.name
end
only_pass()
["Sally", "Hank", "Alice"]