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"]