Um guia introdutório para importar, transformar, analisar e modelar dados
7ª Semana da Matemática Aplicada (SEMAP),
Universidade Estadual de Campinas (UNICAMP)
Agosto de 2025
Julia
, lançada oficialmente em 2012, tem se destacado como uma alternativa moderna para ciência de dados e computação científica, competindo com linguagens como Matlab
, Python
e R
;Julia
é gratuita;Julia
oferece desempenho próximo ao de C++, aliado à facilidade de aprendizado e sintaxe simples, comparáveis a Python
e R
;Julia
permite escrever código com símbolos matemáticos diretamente 🤓, facilitando a expressão de conceitos científicos;Julia
resolve o problema das duas linguagens 🆒;Google Colaboratory (Colab)
Atribuindo valores a diferentes variáveis e vetores
Acessando aos elementos dos vetores
Qual a diferença entre
e
Observação: O . (dot) faz com que a operação seja feita em todos os elementos do vetor.
Instalar pacotes:
Nota
Boas práticas em Julia
são:
Se quiser saber mais acerca de boas práticas, veja aqui.
Nesta seção veremos como importar nossos datasets, estando eles armazenados localmente ou online. Também veremos a diferença na leitura de diferentes formatos, como .csv, .txt e .xlsx.
Julia
conta com diversos pacotes para leitura de datasets, dentre eles:
DataFrames
: Manipulação e análise de dados em formato tabular, similar ao pandas (Python
) ou data.frame (R
);CSV
: Leitura e escrita de arquivos CSV de forma rápida e eficiente;XLSX
: Leitura e escrita de arquivos Excel (.xlsx).Com os pacotes necessários instalados, agora vejamos como as funções de leitura funcionam:
Podemos usar a função download()
para baixar um arquivo temporariamente e retorna o caminho local onde ele foi salvo.
Ex.:
Agora, vamos ler os dados que usaremos na aula de hoje.
using CSV
using DataFrames
file = "https://raw.githubusercontent.com/Arthur-Dionizio/minicurso-julia/main/datasets/dataset.csv";
dados = CSV.read(download(file), DataFrame);
first(dados, 5)
5×21 DataFrame
Row │ Column1 track_id artists album_name ⋯
│ Int64 String31 String? String? ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ 0 5SuOikwiRyPMVoIQDJUgSV Gen Hoshino Comedy ⋯
2 │ 1 4qPNDBW1i3p13qLCt0Ki3A Ben Woodward Ghost (Acousti
3 │ 2 1iJBSr7s7jYXzM8EGcbK5b Ingrid Michaelson;ZAYN To Begin Again
4 │ 3 6lfxq3CG4xtTiEg7opyCyx Kina Grannis Crazy Rich Asi
5 │ 4 5vjLSffimiIP26QG5WcN2K Chord Overstreet Hold On ⋯
18 columns omitted
6×21 DataFrame
Row │ Column1 track_id artists album_name ⋯
│ Int64 String31 String? String? ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ 0 5SuOikwiRyPMVoIQDJUgSV Gen Hoshino Comedy ⋯
2 │ 1 4qPNDBW1i3p13qLCt0Ki3A Ben Woodward Ghost (Acousti
3 │ 2 1iJBSr7s7jYXzM8EGcbK5b Ingrid Michaelson;ZAYN To Begin Again
4 │ 3 6lfxq3CG4xtTiEg7opyCyx Kina Grannis Crazy Rich Asi
5 │ 4 5vjLSffimiIP26QG5WcN2K Chord Overstreet Hold On ⋯
6 │ 5 01MVOl9KtVTNfFiBU9I7dc Tyrone Wells Days I Will Re
18 columns omitted
Conhecendo o dataset
Conhecendo o dataset
Conhecendo o dataset
A biblioteca Tidier.jl
possui vários pacotes que auxiliam na manipulação e análise de datasets. Para quem está vindo do R
, esses pacotes são bem similares e intuitivos. Aqui estão alguns dos pacotes:
TidierData
: Implementação 100% Julia
dos pacotes dplyr
e tidyr
do R
. Usado na tranformação e manipulação dos dados;TidierPlots
: Implementação 100% Julia
do pacote ggplot2
do R
;TidierStrings
: O objetivo deste pacote é replicar o stringr
do R
em Julia
de uma forma que funcione com o Tidier ou como uma função autônoma.Acessem o link https://tidierorg.github.io/Tidier.jl/v1.6.1/ para mais informações!
Para a nossa análise de hoje, vamos utilizar principalmente o pacote TidierData
.
Funções Macro
Para suportar a programação no estilo R
, o TidierData.jl
é implementado usando macros. Isso ocorre porque as macros são capazes de “capturar” o código antes de executá-lo, o que permite que o pacote suporte “expressões tidy” semelhantes ao R
que, de outra forma, não seriam consideradas código Julia
válido.
using TidierData
@chain dados begin
@filter(popularity > 50)
@arrange(desc("energy"))
@select(track_name, popularity, energy, acousticness)
@slice(1:5)
end
5×4 DataFrame
Row │ track_name popularity energy acousticness
│ String? Int64 Float64 Float64
─────┼────────────────────────────────────────────────────────────────
1 │ Meeresrauschen zum Schlafen 52 0.999 0.554
2 │ Voodoo People 56 0.998 0.00128
3 │ Bleed 60 0.998 8.12e-6
4 │ Wait A Minute 58 0.998 0.0496
5 │ Hooked 52 0.998 0.0207
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.
@filter()
: Filtra as linhas com base em uma restrição;@arrange()
: Ordena as linhas com base em uma coluna (desc()
para definir ordem crescente ou decrescente);@select()
: Seleciona as colunas de interesse;@slice()
: Seleciona as linhas para visualização.Obs.: A função
desc()
é uma função auxiliar.
Existem algumas funções auxiliares do pacote que é importante citarmos:
across()
: Aplica uma função a várias colunas de uma vez;n()
e row_number()
: Retornam o número total de linhas ou o número da linha;replace_missing()
: Substitui valores ausentes em uma coluna por um valor especificado.Outras funções auxiliares de outros pacotes que vale mencionar:
dropmissing()
: Remove as linhas que contêm valores faltantes (missing);unique()
: Retorna os valores distintos únicos de um vetor ou coluna, removendo duplicatas;nrow()
: Retorna o número de linhas de um DataFrame ou matriz;any()
: Testa se pelo menos um elemento de uma coleção (ou resultado de uma condição) é verdadeiro; retorna true
ou false
.Dica
Use a função any()
e a função ismissing
no formato row -> any(ismissing, row)
para verificar se há colunas sem informação. Nesse caso, a função filter()
da base do Julia
é mais eficiente, no formato filter(condição, dados)
.
Limpeza do dataset do Spotify: Tratando valores faltantes
1×21 DataFrame
Row │ Column1 track_id artists album_name track_name popul ⋯
│ Int64 String31 String? String? String? Int64 ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ 65900 1kR4gIb7nGxHPI3D2ifs59 missing missing missing ⋯
16 columns omitted
Vamos remover as linhas que possuem valores faltantes.
113999×21 DataFrame
Row │ Column1 track_id artists ⋯
│ Int64 String31 String ⋯
────────┼───────────────────────────────────────────────────────────────────────
1 │ 0 5SuOikwiRyPMVoIQDJUgSV Gen Hoshino ⋯
2 │ 1 4qPNDBW1i3p13qLCt0Ki3A Ben Woodward
3 │ 2 1iJBSr7s7jYXzM8EGcbK5b Ingrid Michaelson;ZAYN
4 │ 3 6lfxq3CG4xtTiEg7opyCyx Kina Grannis
5 │ 4 5vjLSffimiIP26QG5WcN2K Chord Overstreet ⋯
6 │ 5 01MVOl9KtVTNfFiBU9I7dc Tyrone Wells
7 │ 6 6Vc5wAMmXdKIAM7WUoEb7N A Great Big World;Christina Agui…
8 │ 7 1EzrEOXmMH3G43AXT1y7pA Jason Mraz
⋮ │ ⋮ ⋮ ⋮ ⋱
113993 │ 113993 4OkMK49i3NApR1KsAIsTf6 Chris Tomlin ⋯
113994 │ 113994 4WbOUe6T0sozC7z5ZJgiAA Lucas Cervetti
113995 │ 113995 2C3TZjDRiAzdyViavDJ217 Rainy Lullaby
113996 │ 113996 1hIz5L4IB9hN3WRYPOCGPw Rainy Lullaby
113997 │ 113997 6x8ZfSoqDjuNa5SVP5QjvX Cesária Evora ⋯
113998 │ 113998 2e6sXL2bYv4bSz6VTdnfLs Michael W. Smith
113999 │ 113999 2hETkH7cOfqmz3LqZDHZf5 Cesária Evora
18 columns and 113984 rows omitted
Verificaremos possíveis faixas duplicadas, e retirá-las caso haja alguma.
(89740, 113999)
89740×21 DataFrame
Row │ Column1 track_id artists a ⋯
│ Int64 String31 String S ⋯
───────┼────────────────────────────────────────────────────────────────────────
1 │ 0 5SuOikwiRyPMVoIQDJUgSV Gen Hoshino C ⋯
2 │ 1 4qPNDBW1i3p13qLCt0Ki3A Ben Woodward G
3 │ 2 1iJBSr7s7jYXzM8EGcbK5b Ingrid Michaelson;ZAYN T
4 │ 3 6lfxq3CG4xtTiEg7opyCyx Kina Grannis C
5 │ 4 5vjLSffimiIP26QG5WcN2K Chord Overstreet H ⋯
6 │ 5 01MVOl9KtVTNfFiBU9I7dc Tyrone Wells D
7 │ 6 6Vc5wAMmXdKIAM7WUoEb7N A Great Big World;Christina Agui… I
8 │ 7 1EzrEOXmMH3G43AXT1y7pA Jason Mraz W
⋮ │ ⋮ ⋮ ⋮ ⋱
89734 │ 113993 4OkMK49i3NApR1KsAIsTf6 Chris Tomlin S ⋯
89735 │ 113994 4WbOUe6T0sozC7z5ZJgiAA Lucas Cervetti F
89736 │ 113995 2C3TZjDRiAzdyViavDJ217 Rainy Lullaby #
89737 │ 113996 1hIz5L4IB9hN3WRYPOCGPw Rainy Lullaby #
89738 │ 113997 6x8ZfSoqDjuNa5SVP5QjvX Cesária Evora B ⋯
89739 │ 113998 2e6sXL2bYv4bSz6VTdnfLs Michael W. Smith C
89740 │ 113999 2hETkH7cOfqmz3LqZDHZf5 Cesária Evora M
18 columns and 89725 rows omitted
Julia
torna possível compreender e explorar conjuntos de dados, identificando padrões, tendências e possíveis outliers antes da modelagem.Statistics.jl
, StatsBase.jl
, DataFrames.jl
e TidierData.jl
serão essenciais para a análise 🆒.Algumas das principais medidas são:
mean(x)
representa média aritméticamedian(x)
representa a medianamode(x)
representa a moda (pacote StatsBase.jl
)var(x)
representa variânciastd(x)
representa o desvio padrãominimum(x)
e maximum(x)
são os valores mínimo e máximoquantile(x, p)
repesenta quantis (p
é o parâmetro da posição relativa da distribuição)length(x)
é o número de elementoscount(cond, x)
conta elementos que satisfazem uma condição cond
countmap(x)
cria uma tabela de frequências (pacote StatsBase.jl
)describe(df)
fornece estatísticas descritivas de um data framecor(x, y)
descreve uma matriz de correlação entre duas variáveis x
e y
cov(x, y)
covariância das variáveis x
e y
df = DataFrame(
idade = [20, 25, 30, 35, 40],
altura = [1.70, 1.75, 1.80, 1.65, 1.90],
nome = ["Ana", "Bruno", "Carlos", "Daniela", "Eduardo"]
);
describe(df)
3×7 DataFrame
Row │ variable mean min median max nmissing eltype
│ Symbol Union… Any Union… Any Int64 DataType
─────┼─────────────────────────────────────────────────────────────
1 │ idade 30.0 20 30.0 40 0 Int64
2 │ altura 1.76 1.65 1.75 1.9 0 Float64
3 │ nome Ana Eduardo 0 String
Dica
Para identificar os cinco artistas com maior número de faixas no dataset, utilize as funções groupby()
e combine()
para criar uma nova tabela contendo duas colunas: uma com o nome dos artistas e outra com a contagem de faixas. Em seguida, use sort!()
para ordenar de forma decrescente e first()
para exibir apenas os primeiros registros.
Dica
Para filtrar as faixas de um artista específico, como George Jones, utilize filter()
em conjunto com occursin()
na coluna :artists. Depois, aplique a função describe()
às variáveis numéricas para obter as estatísticas descritivas, como média, mediana, mínimo e máximo.
using Statistics, StatsPlots, Plots
df_george = filter(:artists => x -> occursin("George Jones", x), dados)
332×21 DataFrame
Row │ Column1 track_id artists album_name ⋯
│ Int64 String31 String String ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ 52000 4BcrWCZPXuoGU0SnAsJW1s George Jones George Jon ⋯
2 │ 52004 5O3EyD5kv3n3lGFIWNBSWO George Jones Walk Throu
3 │ 52005 69WdAXh4RBYrAj28jCVjby George Jones;Tammy Wynette Greatest H
4 │ 52007 6qMgx8ys4MNNxypaIvbG0i George Jones Super Hits
5 │ 52019 1pDO9I2o9IE2Tw79wxmo30 George Jones Wine Color ⋯
6 │ 52021 1q0wCfrE9eGFMzS2g8aVnx George Jones I Am What
7 │ 52022 54lkjZWc8TO7r1QSCiIwVG George Jones The Full D
8 │ 52023 62OQBigJvRuggCFv3E3LFK George Jones Country He
⋮ │ ⋮ ⋮ ⋮ ⋱
326 │ 52984 72YayXDj2CPF1QQOnejHBI George Jones By Request ⋯
327 │ 52986 39ihG6Oy6NVmtSY9m5khpO George Jones Country Ch
328 │ 52988 1bcQIecheLD42DwnFOIiXK George Jones Two Of The
329 │ 52990 7sYajCGYZCbsZbk997CPA8 George Jones Mr. Countr
330 │ 52994 5tjRbvA8nJiSoybzhunifE George Jones George Jon ⋯
331 │ 52996 2jsSAFUmu4BUqjcjKbLKg4 George Jones The Very B
332 │ 52999 4lIVGDbomi3JKpuFvBFISI George Jones Setlist: T
18 columns and 317 rows omitted
vars_numericas = [:popularity, :duration_ms, :danceability, :energy,
:loudness, :speechiness, :acousticness, :instrumentalness,
:liveness, :valence, :tempo]
11-element Vector{Symbol}:
:popularity
:duration_ms
:danceability
:energy
:loudness
:speechiness
:acousticness
:instrumentalness
:liveness
:valence
:tempo
11×7 DataFrame
Row │ variable mean min median max ⋯
│ Symbol Float64 Real Float64 Real ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ popularity 16.0723 9 13.0 56 ⋯
2 │ duration_ms 1.66069e5 109805 162556.0 267466
3 │ danceability 0.538476 0.25 0.5375 0.806
4 │ energy 0.356672 0.0821 0.32 0.824
5 │ loudness -12.4465 -19.538 -12.8915 -5.04 ⋯
6 │ speechiness 0.0350651 0.0238 0.0305 0.104
7 │ acousticness 0.68109 0.209 0.703 0.966
8 │ instrumentalness 0.00294164 0.0 2.555e-6 0.264
9 │ liveness 0.180608 0.0507 0.143 0.952 ⋯
10 │ valence 0.566295 0.136 0.5165 0.97
11 │ tempo 109.613 52.67 103.402 205.466
2 columns omitted
Plots.jl
, StatsPlots.jl
, Makie.jl
e Gadfly.jl
.bloxplot(x)
resume a distribuição dos dados destacando mediana, quartis e possíveis outliers.Ex.:
12-element Vector{Float64}:
5.1
5.5
5.3
5.0
5.2
5.8
5.4
6.0
4.9
5.6
5.7
6.2
bar(x)
representa frequências ou valores médios para categorias.Ex.:
4-element Vector{String}:
"Pop"
"Rock"
"Jazz"
"Funk"
4-element Vector{Int64}:
50
30
15
5
histogram(x)
exibe a distribuição de frequências de variáveis contínuas. Caso você não possua um conjunto de dados real, é possível gerar números pseudoaleatórios com o pacote Random.jl
e criar uma distribuição normal utilizando o pacote Distributions.jl
.Ex.:
using Random, Distributions, Plots
alturas = rand(Normal(170, 7), 500) # median = 7; std = 7; amostra = 500
500-element Vector{Float64}:
178.4124729014412
166.9622041256405
163.70631235789705
170.0918178591974
174.53378812132158
170.2658573658706
156.2776197940489
175.05097695903214
166.27331582162287
173.7600284209655
⋮
177.3501649766574
174.06053306563751
165.40645379880868
159.45209418222652
171.346179226558
164.21073531529498
168.35649558416983
171.53245339106155
166.8615383941321
Julia
, você pode usar o argumento normalize=:pdf
dentro da função histogram()
:Ex.:
500-element Vector{Float64}:
174.3495446431343
173.8737228390611
158.85386895001443
157.8200644433087
167.81736429191855
169.0475076177836
181.67023209191876
166.17975032297662
161.31062575023938
180.71605949916446
⋮
171.16239005045026
157.46952086972712
168.7844374451671
158.598709371962
175.8740350623445
165.23935771185265
181.00104688366673
173.75577117026157
175.4631207357802
scatter(x)
mostra a relação entre duas variáveis numéricas.Ex.:
TaskLocalRNG()
1:20
20-element Vector{Float64}:
2.6165758569299333
-0.24414501622834672
0.7907277953414082
3.1660147296701333
5.575175961247711
6.459637396103735
6.1564626712006145
5.288818757797606
9.138918282183788
9.765354390938374
13.438563019463931
12.585828669044913
12.93770373930144
14.63166620034562
10.675241994719254
14.219846687460377
14.823562972127426
19.407516651584604
19.286651786475026
20.296750733352162
Por que usar?
Ex.:
TaskLocalRNG()
50
50-element Vector{Float64}:
8.082879284649668
-11.220725081141733
-11.04636102329296
-4.169926351649334
2.8758798062385575
2.2981869805186763
-4.2176866439969265
-13.555906211011969
0.6945914109189361
-1.1732280453081336
⋮
-15.356122852942244
-3.9698822634906565
12.024837058441577
-7.206760308458197
-11.070844515339424
-12.662224189221503
-7.224043459095074
-10.88218513936287
7.037583257923017
50-element Vector{Float64}:
9.899865751336163
-7.146861102252421
7.265008863854744
-6.462641266272596
-2.2972589276981283
-0.30097803530417977
-1.4882234689621856
-6.617380554087792
2.0206349289818264
6.347389021939328
⋮
-16.72288075570318
-9.233624983854819
5.986673642104391
-7.9105733388448325
-18.27738387287059
-6.206306217834746
-2.4450820706966034
-0.24899164350975944
0.7544225504002386
50-element Vector{Float64}:
2.0753693241291695
1.9563524035797852
-1.5716250863105699
-0.2726604822086824
-0.8728662768443157
-3.792538515617369
1.7737773953237004
0.1375075696575552
-3.1554976141714035
0.7920762353948735
⋮
-0.6695150934069909
-0.9470585333039171
1.2413767167046914
1.9977620961810856
1.055699421484043
0.36710798292467095
1.8520824424740272
2.086382412637305
-0.7046051490409878
50-element Vector{Float64}:
4.868827180080793
-9.839860751068642
-9.312180296434576
-4.664087835532926
2.5104344355928014
2.329632918433605
-4.714208193995891
-11.798815194714487
3.06189167679625
-0.8229087369724278
⋮
-11.453503989021415
-1.4537363777514887
11.25660214830337
-6.423774602958674
-11.022821565567487
-10.213057377235257
-5.635380364547635
-10.38698889461114
5.101800154268422
50×4 DataFrame
Row │ A B C D
│ Float64 Float64 Float64 Float64
─────┼───────────────────────────────────────────────
1 │ 8.08288 9.89987 2.07537 4.86883
2 │ -11.2207 -7.14686 1.95635 -9.83986
3 │ -11.0464 7.26501 -1.57163 -9.31218
4 │ -4.16993 -6.46264 -0.27266 -4.66409
5 │ 2.87588 -2.29726 -0.872866 2.51043
6 │ 2.29819 -0.300978 -3.79254 2.32963
7 │ -4.21769 -1.48822 1.77378 -4.71421
8 │ -13.5559 -6.61738 0.137508 -11.7988
⋮ │ ⋮ ⋮ ⋮ ⋮
44 │ 12.0248 5.98667 1.24138 11.2566
45 │ -7.20676 -7.91057 1.99776 -6.42377
46 │ -11.0708 -18.2774 1.0557 -11.0228
47 │ -12.6622 -6.20631 0.367108 -10.2131
48 │ -7.22404 -2.44508 1.85208 -5.63538
49 │ -10.8822 -0.248992 2.08638 -10.387
50 │ 7.03758 0.754423 -0.704605 5.1018
35 rows omitted
4×4 Matrix{Float64}:
1.0 0.762891 -0.0288959 0.989709
0.762891 1.0 -0.0163423 0.756289
-0.0288959 -0.0163423 1.0 -0.116352
0.989709 0.756289 -0.116352 1.0
histogram()
aplicada à nova variável duration_min
. Em seguida, normalizamos os valores para que a área total do histograma seja igual a 1, permitindo sobrepor e comparar com a curva de densidade.89740-element Vector{Float64}:
3.8444333333333334
2.4935
3.5137666666666667
3.36555
3.314216666666667
3.570666666666667
3.8233333333333333
4.0491
3.1602166666666665
3.4265666666666665
⋮
4.17715
5.209433333333333
4.2671
5.0909
6.41665
6.416666666666667
4.5244333333333335
4.73155
4.030433333333334
histogram(dados[!, :duration_min],
bins = 50,
xlabel = "Duração (min)",
ylabel = "Número de músicas",
title = "Distribuição da duração das músicas",
normalize = true,
legend = false)
(c = :coolwarm)
para destacar relações positivas e negativas. Além disso, a escala foi definida no intervalo [-1, 1] e os nomes dos eixos foram rotacionados para facilitar a leitura.11-element Vector{Symbol}:
:popularity
:duration_ms
:danceability
:energy
:loudness
:speechiness
:acousticness
:instrumentalness
:liveness
:valence
:tempo
89740×11 DataFrame
Row │ popularity duration_ms danceability energy loudness speechiness ⋯
│ Int64 Int64 Float64 Float64 Float64 Float64 ⋯
───────┼────────────────────────────────────────────────────────────────────────
1 │ 73 230666 0.676 0.461 -6.746 0.143 ⋯
2 │ 55 149610 0.42 0.166 -17.235 0.0763
3 │ 57 210826 0.438 0.359 -9.734 0.0557
4 │ 71 201933 0.266 0.0596 -18.515 0.0363
5 │ 82 198853 0.618 0.443 -9.681 0.0526 ⋯
6 │ 58 214240 0.688 0.481 -8.807 0.105
7 │ 74 229400 0.407 0.147 -8.822 0.0355
8 │ 80 242946 0.703 0.444 -9.331 0.0417
⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋱
89734 │ 39 256026 0.505 0.687 -4.375 0.0287 ⋯
89735 │ 22 305454 0.331 0.171 -15.668 0.035
89736 │ 21 384999 0.172 0.235 -16.393 0.0422
89737 │ 22 385000 0.174 0.117 -18.318 0.0401
89738 │ 22 271466 0.629 0.329 -10.895 0.042 ⋯
89739 │ 41 283893 0.587 0.506 -10.889 0.0297
89740 │ 22 241826 0.526 0.487 -10.204 0.0725
5 columns and 89725 rows omitted
11×11 Matrix{Float64}:
1.0 -0.0231565 0.0642753 … -0.0115081 0.00728011
-0.0231565 1.0 -0.0641388 -0.147018 0.0260042
0.0642753 -0.0641388 1.0 0.492578 -0.0213292
0.0137251 0.0632583 0.143913 0.256317 0.258629
0.0716737 0.000360017 0.271838 0.289095 0.22866
-0.0470863 -0.0607507 0.109519 … 0.0329348 0.00403321
-0.0388283 -0.108478 -0.17704 -0.103781 -0.220252
-0.127477 0.117372 -0.19475 -0.330756 -0.0568849
-0.0138572 0.00810921 -0.132237 0.0123221 -0.00611303
-0.0115081 -0.147018 0.492578 1.0 0.0906169
0.00728011 0.0260042 -0.0213292 … 0.0906169 1.0
Objetivo: Prever a variável energy com base nas outras variáveis quantitativas. Para isso, utilizaremos um modelo de Regressão Linear Múltipla.
Primeiro, vamos fazer a divisão do dataset em dados de treino e dados de teste:
using MLJ
# Substitua o parâmetro ? pela porcentagem (em decimal) do dataset total que você deseja designar para
# treino. O Restante ficará para com dataset de teste.
train_idx, test_idx = partition(collect(eachindex(y)), 0.8, shuffle=true, rng=42)
X_train = X[train_idx, :]
X_test = X[test_idx, :]
y_train = y[train_idx]
y_test = y[test_idx]
X
e uma contendo apenas y
display()
. Por padrão, Julia
exibe somente o último objeto da célulaAgora, seguimos com a normalização das variáveis numéricas.
11×3 DataFrame
Row │ variable min max
│ Symbol Real Real
─────┼─────────────────────────────────────────
1 │ popularity 0 100
2 │ duration_ms 8586 5237295
3 │ danceability 0.0 0.985
4 │ energy 0.0 1.0
5 │ loudness -49.531 4.532
6 │ speechiness 0.0 0.965
7 │ acousticness 0.0 0.996
8 │ instrumentalness 0.0 1.0
9 │ liveness 0.0 1.0
10 │ valence 0.0 0.995
11 │ tempo 0.0 243.372
using MLJ
using StatsBase
# --- 3. Normalização das colunas contínuas ---
using MLJ
using StatsBase
X_train_scaled = deepcopy(X_train)
X_test_scaled = deepcopy(X_test)
for col in var_numericas # percorre cada coluna numérica
x_train = Float64.(X_train[!, col]) # garante que os valores sejam do tipo Float64
scaler = fit(UnitRangeTransform, x_train) # ajusta o transformador Min-Max aos dados de treino
X_train_scaled[!, col] = StatsBase.transform(scaler, x_train) # aplica a normalização nos dados de treino
X_test_scaled[!, col] = StatsBase.transform(scaler, Float64.(X_test[!, col])) # aplica a mesma transformação nos dados de teste usando os parâmetros do treino (mesma escala)
end
# colunas categóricas permanecem inalteradas
# Ver resumo estatístico das colunas numéricas
describe(X_train_scaled[:, var_numericas])
5×13 DataFrame
Row │ popularity duration_ms danceability energy loudness speechiness ⋯
│ Int64 Int64 Float64 Float64 Float64 Float64 ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ 73 230666 0.676 0.461 -6.746 0.143 ⋯
2 │ 55 149610 0.42 0.166 -17.235 0.0763
3 │ 57 210826 0.438 0.359 -9.734 0.0557
4 │ 71 201933 0.266 0.0596 -18.515 0.0363
5 │ 82 198853 0.618 0.443 -9.681 0.0526 ⋯
7 columns omitted
5×13 DataFrame
Row │ popularity duration_ms danceability energy loudness speechiness ⋯
│ Float64 Float64 Float64 Float64 Float64 Float64 ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ 0.02 0.0451771 0.474593 0.9 0.832843 0.0467358 ⋯
2 │ 0.19 0.0931106 0.535569 0.894 0.801102 0.0762694
3 │ 0.46 0.0265817 0.738821 0.471 0.752862 0.128497
4 │ 0.0 0.0322301 0.45122 0.669 0.791743 0.129534
5 │ 0.61 0.0214957 0.777439 0.75 0.779757 0.205181 ⋯
7 columns omitted
11×7 DataFrame
Row │ variable mean min median max nmis ⋯
│ Symbol Float64 Real Float64 Real Int6 ⋯
─────┼──────────────────────────────────────────────────────────────────────────
1 │ popularity 33.1988 0 33.0 100 ⋯
2 │ duration_ms 2.29144e5 8586 2.13296e5 5237295
3 │ danceability 0.562166 0.0 0.576 0.985
4 │ energy 0.634458 0.0 0.676 1.0
5 │ loudness -8.49899 -49.531 -7.185 4.532 ⋯
6 │ speechiness 0.0874423 0.0 0.0489 0.965
7 │ acousticness 0.328285 0.0 0.188 0.996
8 │ instrumentalness 0.173415 0.0 5.8e-5 1.0
9 │ liveness 0.216971 0.0 0.132 1.0 ⋯
10 │ valence 0.469474 0.0 0.457 0.995
11 │ tempo 122.058 0.0 122.013 243.372
2 columns omitted
Nesse momento, faremos o encoding das variáveis categóricas.
Aplicar one hot encoding em variáveis categóricas é uma técnica comum para converter essas variáveis em um formato que pode ser usado por algoritmos de aprendizado de máquina.
Isso envolve criar colunas binárias para cada categoria, permitindo que o modelo aprenda a partir dessas variáveis categóricas de forma mais eficaz.
Essa técnica é essencial em modelos como regressão linear, árvores de decisão e redes neurais, onde as variáveis categóricas precisam ser representadas numericamente.
using CategoricalArrays, MLJ
# --- converter colunas do tipo string para o tipo categórico (CategoricalValue) ---
for col in names(X_train_scaled)
if eltype(X_train_scaled[!, col]) <: AbstractString
X_train_scaled[!, col] = categorical(X_train_scaled[!, col])
X_test_scaled[!, col] = categorical(X_test_scaled[!, col])
end
end
# --- One-Hot Encoder (ajusta no treino, transforma treino e teste) ---
ohe = OneHotEncoder(drop_last=true) # drop_last evita multicolinearidade para GLM
"""One-Hot Encoding transforma cada categoria em uma coluna binária (0 ou 1).
Ex.: track_genre com valores Rock, Pop vira 2 colunas: [Rock, Pop].
"""
mach_ohe = MLJ.machine(ohe, X_train_scaled)
MLJ.fit!(mach_ohe)
X_train_cleaned = MLJ.transform(mach_ohe, X_train_scaled)
X_test_cleaned = MLJ.transform(mach_ohe, X_test_scaled)
"""Cria uma “máquina” com o encoder e o dataset de treino.
fit! → aprende quais categorias existem em cada coluna de treino.
transform → aplica a codificação nos dados de treino e teste usando os mesmos parâmetros aprendidos.
Resultado: X_train_cleaned e X_test_cleaned estão prontos para modelos de ML, com variáveis numéricas apenas.
"""
X_train_cleaned
Por último, transformaremos nosso dataset em matriz.
Essa técnica é essencial para modelos de Regressão Linear Múltipla
Float64.(Matrix(X_train_cleaned))
garante que todas as colunas sejam numéricas, requisito do GLM.jl
, o pacote que usaremos para implementar nossa Reg. Lin. Múltipla.
Nesse pacote, quando temos múltiplas features, podemos usar a forma matricial (X e y)
ou transformar X_train_cleaned
em DataFrame
com colunas nomeadas para usar fórmula.
Faremos uso da abordagem matricial, que funciona diretamente com DataFrame
ou Matrix
.
A função abaixo serve para retornar o tipo de dado de algum objeto. Em outras palavras:
Ela não mostra o conteúdo da variável.
Ela diz de que “classe” ou estrutura interna o objeto é, por exemplo: DataFrame
, Matrix{Float64}
, CategoricalArray{String,1,UInt32}
, etc.
71792×124 Matrix{Float64}:
0.02 0.0451771 0.474593 0.9 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.19 0.0931106 0.535569 0.894 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.46 0.0265817 0.738821 0.471 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.0 0.0322301 0.45122 0.669 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.61 0.0214957 0.777439 0.75 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.17 0.0374477 0.765244 0.954 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.35 0.0295779 0.583333 0.164 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.68 0.0308244 0.561992 0.89 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.1 0.0583974 0.783537 0.665 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.46 0.0852602 0.690041 0.939 0.0 0.0 0.0 0.0 0.0 0.0 0.0
⋮ ⋱ ⋮
0.2 0.0362946 0.292683 0.95 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.42 0.0565444 0.404472 0.573 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.19 0.0214544 0.73374 0.22 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.44 0.0494858 0.286585 0.273 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.41 0.0305391 0.698171 0.612 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.18 0.0251995 0.642276 0.233 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.01 0.0479097 0.471545 0.461 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.23 0.0399743 0.901423 0.482 … 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.38 0.0958937 0.734756 0.856 0.0 0.0 0.0 0.0 0.0 0.0 0.0
energy
:71792-element Vector{Float64}:
0.9
0.894
0.471
0.669
0.75
0.954
0.164
0.89
0.665
0.939
⋮
0.95
0.573
0.22
0.273
0.612
0.233
0.461
0.482
0.856
17948-element Vector{Float64}:
0.703
0.885
0.961
0.863
0.298
0.533
0.512
0.627
0.8
0.217
⋮
0.479
0.907
0.541
0.601
0.894
0.957
0.746
0.714
0.584
7ª SEMAP | Agosto de 2025 | www.ime.unicamp.br/julialang