import Pkg
# Suprime mensagens e warnings durante a instalação dos pacotes
redirect_stdout(devnull) do
redirect_stderr(devnull) do
Pkg.add("TidierData")
Pkg.add("DataFrames")
Pkg.add("RDatasets") # Vamos usar o pacote RDatasets para gerar um banco de dados para análise
end
endIntrodução ao pacote TidierData.jl
Um guia introdutório sobre o pacote TidierData.jl! Entenda como aplicar a gramática tidy para manipulação de dados de forma intuitiva, utilizando funções familiares para quem vem do tidyverse em R.
Introdução
O TidierData.jl é um pacote da linguagem julia que traz uma abordagem familiar aos usuários do R, inspirado diretamente nos pacotes dplyr e tidyr. Ele permite manipular e transformar conjuntos de dados com uma sintaxe concisa e legível no estilo tidy.
Neste post, vamos explorar como o TidierData.jl facilita tarefas comuns de manipulação de dados, como seleção, filtragem, agregações, junções e muito mais.
Por trás das cortinas, o TidierData.jl é construído sobre o robusto DataFrames.jl, combinando a performance de
juliacom a ergonomia dotidyverse.
Carregando o pacote
Funções
Carregando o dataset ‘mtcars’
using RDatasets
mtcars = dataset("datasets", "mtcars")32×12 DataFrame
Row │ Model MPG Cyl Disp HP DRat WT QS ⋯
│ String31 Float64 Int64 Float64 Int64 Float64 Float64 Fl ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ Mazda RX4 21.0 6 160.0 110 3.9 2.62 ⋯
2 │ Mazda RX4 Wag 21.0 6 160.0 110 3.9 2.875
3 │ Datsun 710 22.8 4 108.0 93 3.85 2.32
4 │ Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215
5 │ Hornet Sportabout 18.7 8 360.0 175 3.15 3.44 ⋯
6 │ Valiant 18.1 6 225.0 105 2.76 3.46
7 │ Duster 360 14.3 8 360.0 245 3.21 3.57
8 │ Merc 240D 24.4 4 146.7 62 3.69 3.19
⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋱
26 │ Fiat X1-9 27.3 4 79.0 66 4.08 1.935 ⋯
27 │ Porsche 914-2 26.0 4 120.3 91 4.43 2.14
28 │ Lotus Europa 30.4 4 95.1 113 3.77 1.513
29 │ Ford Pantera L 15.8 8 351.0 264 4.22 3.17
30 │ Ferrari Dino 19.7 6 145.0 175 3.62 2.77 ⋯
31 │ Maserati Bora 15.0 8 301.0 335 3.54 3.57
32 │ Volvo 142E 21.4 4 121.0 109 4.11 2.78
5 columns and 17 rows omitted
first(mtcars, 5)5×12 DataFrame
Row │ Model MPG Cyl Disp HP DRat WT QS ⋯
│ String31 Float64 Int64 Float64 Int64 Float64 Float64 Fl ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ Mazda RX4 21.0 6 160.0 110 3.9 2.62 ⋯
2 │ Mazda RX4 Wag 21.0 6 160.0 110 3.9 2.875
3 │ Datsun 710 22.8 4 108.0 93 3.85 2.32
4 │ Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215
5 │ Hornet Sportabout 18.7 8 360.0 175 3.15 3.44 ⋯
5 columns omitted
Funções Macro
O pacote oferece mais de 40 funções macro, as quais podemos usar para manipular dados. A seguir, exploraremos algumas das principais funções e observaremos sua sintaxe de forma clara e concisa.
using TidierData, DataFrames
# Exemplo com o dataset mtcars
@chain mtcars begin
@mutate(Eficiencia = MPG / WT)
@filter(Cyl == 6)
@arrange(desc("Eficiencia"))
@select(Model, MPG, WT, Eficiencia)
@slice(1:6)
end6×4 DataFrame
Row │ Model MPG WT Eficiencia
│ String31 Float64 Float64 Float64
─────┼──────────────────────────────────────────────
1 │ Mazda RX4 21.0 2.62 8.01527
2 │ Mazda RX4 Wag 21.0 2.875 7.30435
3 │ Ferrari Dino 19.7 2.77 7.11191
4 │ Hornet 4 Drive 21.4 3.215 6.6563
5 │ Merc 280 19.2 3.44 5.5814
6 │ Valiant 18.1 3.46 5.23121
Primeiramente, para quem já está familiarizado com a linguagem R, a função
@chain()é similar ao pipeline%>%ou|>, usado para encadear várias operações em sequência no mesmo conjunto de dados.Em seguida, criamos a nova coluna Eficiencia no nosso dataset, definida como a razão entre as colunas MPG e WT.
Além disso, aplicamos restrições à saída do dataset, filtrando apenas os carros que possuem exatamente 6 cilindros, usando
@filter().Selecionamos apenas as colunas Model, MPG, WT e Eficiencia, usando
@select().Ordenamos o dataset pelo valor da
Eficiencia, em ordem decrescente, usando@arrange()para ordenar edesc()para determinar que era decrescente.
Obs.:
desc()é uma função auxiliar.
- Por fim, optamos por visualizar apenas da linha 1 até a linha 6 do dataset, utilizando a função
@slice().
Agora, vejamos um exemplo de formatação dos dados e utilização das funções de join.
Vamos supor que possuímos dois dataframes: um com informações sobre clientes e outro com compras. Queremos combinar os dados para visualizar o nome dos clientes junto com seus respectivos valores de compra.
using TidierData, DataFrames
clientes = DataFrame(ID = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], Nome = ["Leonardo", "Arthur", "Carlos", "Esther", "Caio", "Guilherme", "Henrique", "Nicole", "Pedro", "Sarah"])10×2 DataFrame
Row │ ID Nome
│ Int64 String
─────┼──────────────────
1 │ 1 Leonardo
2 │ 2 Arthur
3 │ 3 Carlos
4 │ 4 Esther
5 │ 5 Caio
6 │ 6 Guilherme
7 │ 7 Henrique
8 │ 8 Nicole
9 │ 9 Pedro
10 │ 10 Sarah
compras = DataFrame(ID = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], Valor = [150, 200, 300, 100, 250, 220, 310, 420, 500, 50])10×2 DataFrame
Row │ ID Valor
│ Int64 Int64
─────┼──────────────
1 │ 1 150
2 │ 2 200
3 │ 3 300
4 │ 4 100
5 │ 5 250
6 │ 6 220
7 │ 7 310
8 │ 8 420
9 │ 9 500
10 │ 10 50
@left_join(compras, clientes)10×3 DataFrame
Row │ ID Valor Nome
│ Int64 Int64 String?
─────┼─────────────────────────
1 │ 1 150 Leonardo
2 │ 2 200 Arthur
3 │ 3 300 Carlos
4 │ 4 100 Esther
5 │ 5 250 Caio
6 │ 6 220 Guilherme
7 │ 7 310 Henrique
8 │ 8 420 Nicole
9 │ 9 500 Pedro
10 │ 10 50 Sarah
Aqui, usamos a função @left_join() para unir nossos dados através da coluna ID. Assim, obtemos um dataframe alinhado por ID, com as informações dos clientes e o valor de suas respectivas compras.
Observe que o uso da função
@left_join()é simples e intuitivo, pois ela une os dois dataframes automaticamente pela coluna em comum, sem necessidade de especificação explícita.
using TidierData, DataFrames
df = DataFrame(Nome = ["Thiago", "Vitor", "Gabriel", "Ana", "Gustavo", "Leticia"], Nota1 = [8, 7, 7.5, 6, 9, 10], Nota2 = [9, 10, 4, 5.5, 5, 9])6×3 DataFrame
Row │ Nome Nota1 Nota2
│ String Float64 Float64
─────┼───────────────────────────
1 │ Thiago 8.0 9.0
2 │ Vitor 7.0 10.0
3 │ Gabriel 7.5 4.0
4 │ Ana 6.0 5.5
5 │ Gustavo 9.0 5.0
6 │ Leticia 10.0 9.0
print(df)6×3 DataFrame
Row │ Nome Nota1 Nota2
│ String Float64 Float64
─────┼───────────────────────────
1 │ Thiago 8.0 9.0
2 │ Vitor 7.0 10.0
3 │ Gabriel 7.5 4.0
4 │ Ana 6.0 5.5
5 │ Gustavo 9.0 5.0
6 │ Leticia 10.0 9.0
@chain df begin
@pivot_longer(cols = [:Nota1, :Nota2], names_to = :Avaliação, values_to = :Nota)
end12×3 DataFrame
Row │ Nome Avaliação Nota
│ String String Float64
─────┼─────────────────────────────
1 │ Thiago Nota1 8.0
2 │ Vitor Nota1 7.0
3 │ Gabriel Nota1 7.5
4 │ Ana Nota1 6.0
5 │ Gustavo Nota1 9.0
6 │ Leticia Nota1 10.0
7 │ Thiago Nota2 9.0
8 │ Vitor Nota2 10.0
9 │ Gabriel Nota2 4.0
10 │ Ana Nota2 5.5
11 │ Gustavo Nota2 5.0
12 │ Leticia Nota2 9.0
Aqui, transformamos o dataframe do formato wide para long usando a função @pivot_longer(). A mesma lógica se aplica caso queiramos converter de long para wide, utilizando a função @pivot_wider().
Funções auxiliares
Além das macros principais como @mutate, @filter e @summarize, o TidierData.jl oferece funções auxiliares que tornam seu código mais expressivo, claro e eficiente. Vamos explorar algumas delas com exemplos práticos:
if_else()
Lógica condicional simples e vetorizada:
using TidierData, DataFrames
valores = DataFrame(Nome = ["A", "B", "C"], Salario = [3000, 5000, 4000])3×2 DataFrame
Row │ Nome Salario
│ String Int64
─────┼─────────────────
1 │ A 3000
2 │ B 5000
3 │ C 4000
@chain valores begin
@mutate(Status = if_else(`Salario` .> 4000, "Alto", "Médio ou Baixo"))
end3×3 DataFrame
Row │ Nome Salario Status
│ String Int64 String
─────┼─────────────────────────────────
1 │ A 3000 Médio ou Baixo
2 │ B 5000 Alto
3 │ C 4000 Médio ou Baixo
case_when()
Para múltiplas condições, substituindo vários if_else():
using TidierData, DataFrames
df2 = DataFrame(Nota = [10, 2, 6, 4, 7.1, 4.9, 9.7])7×1 DataFrame
Row │ Nota
│ Float64
─────┼─────────
1 │ 10.0
2 │ 2.0
3 │ 6.0
4 │ 4.0
5 │ 7.1
6 │ 4.9
7 │ 9.7
@chain df2 begin
@mutate(Conceito = case_when(
`Nota` .>= 9 => "A",
`Nota` .>= 7 => "B",
`Nota` .>= 5 => "C",
true => "D"
))
end7×2 DataFrame
Row │ Nota Conceito
│ Float64 String
─────┼───────────────────
1 │ 10.0 A
2 │ 2.0 D
3 │ 6.0 C
4 │ 4.0 D
5 │ 7.1 B
6 │ 4.9 D
7 │ 9.7 A
starts_with(), ends_with()
Selecionando colunas com nomes que seguem um padrão:
using TidierData, DataFrames
df3 = DataFrame(
Nome = ["José", "Carla", "Humberto"],
Nota1 = [7, 9, 8],
Nota2 = [8, 10, 10],
Idade = [20, 22, 24]
)3×4 DataFrame
Row │ Nome Nota1 Nota2 Idade
│ String Int64 Int64 Int64
─────┼───────────────────────────────
1 │ José 7 8 20
2 │ Carla 9 10 22
3 │ Humberto 8 10 24
comeca = @chain df3 begin
@select(`Nome`, starts_with("Nota"))
end3×3 DataFrame
Row │ Nome Nota1 Nota2
│ String Int64 Int64
─────┼────────────────────────
1 │ José 7 8
2 │ Carla 9 10
3 │ Humberto 8 10
termina = @chain df3 begin
@select(`Nome`, ends_with("ade"))
end3×2 DataFrame
Row │ Nome Idade
│ String Int64
─────┼─────────────────
1 │ José 20
2 │ Carla 22
3 │ Humberto 24
Obs.: As funções
endswith()da base dojuliaeends_with()do pacote TidierData.jl tem uma pequena diferença: a funçãoends_with()é usada para nomes de colunas, enquantoendswith()é usada para valores dentro das colunas.
Referências
Blog inspirado na documentação disponibilizada pelos autores do pacote, a qual pode ser acessada pelo link https://tidierorg.github.io/TidierData.jl/latest/.
Ferramentas de IA foram utilizadas para correção ortográfica e aprimoramento do texto.