Por que Julia?

Por que escolher Julia?

  • A linguagem de programação 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;
  • É utilizada não apenas na academia, mas também fora dela;
  • 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 🆒;
  • Porque o projeto Do Zero ao Julia é 100% Unicamper (🕺);
  • Etc.

Do Zero ao Julia

Website do projeto”Do Zero ao Julia”: www.ime.unicamp.br/julialang

Primeiros passos

Primeiros passos

Passo 1: Abrir o Colab https://colab.research.google.com/

Passo 2: Criar novo notebook

Google Colaboratory (Colab)

Figura 1: Repositório da Aula

Primeiros passos

Passo 3: Configurar o Colab

Passo 4: Julia pronto pra ação
Figura 2: Julia no Colab

Primeiros passos

Atribuindo valores a diferentes variáveis e vetores

x = 20;
y = 8.3;
nome = "Julia";
idade = 19;
nomes  = ["Julia", "Lucas", "Pedro", "Carlos", "Maria", "Luisa", "Marcelo", "Camila"];
idades = [19, 20, 18, 22, 21, 19, 17, 23];
peso   = [65.5, 70.5, 65.2, 70.1, 65.1, 65.4, 65.3, 65.2];

Primeiros passos

Acessando aos elementos dos vetores

idades[2]
20
nomes[3]
"Pedro"

Primeiros passos

Qual a diferença entre

log(idades)

e

log.(idades)

Qual código está correto?

idades + 1    
idades .+ 1  

Observação: O . (dot) faz com que a operação seja feita em todos os elementos do vetor.

Primeiros passos

# Matrizes
A = [1.0 2.0 3.0; 4.0 5.0 6.0]   # 2x3 de Float64
B = [1 2 3; 4 5 6]  # 2x3 de Int
Z = zeros(3, 4) # Matriz de zeros
O = ones(Int, 2, 5) # Matriz de uns
R = rand(3, 3)   # Aleatória uniforme [0,1)
N = randn(3, 3)  # Aleatória normal padrão

Como acessar aos elementos da matriz?

A[1, 2]         # elemento da 1ª linha, 2ª coluna
A[2, :]         # linha 2 inteira
A[:, 3]         # coluna 3 inteira
A[1:2, 2:3]     # submatriz
A[1, 2] = 10.0  # alterar valor

Primeiros passos

Instalar pacotes:

using Pkg
Pkg.add("Statistics")
Pkg.add("Random")

Carregar pacotes:

using LinearAlgebra
using Statistics
using Random

Nota

Boas práticas em Julia são:

  1. Carregar os pacotes logo no começo do script
  2. Carregar cada pacote um a um (linha a linha)
  3. Carregar os pacotes em ordem alfabetica.

Se quiser saber mais acerca de boas práticas, veja aqui.

Importação de dados

Importação de dados

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).

Funções básicas

Com os pacotes necessários instalados, agora vejamos como as funções de leitura funcionam:

Funções básicas

Podemos usar a função download() para baixar um arquivo temporariamente e retorna o caminho local onde ele foi salvo.

Ex.:

Leitura da base de dados

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

Leitura da base de dados

import Pkg

Pkg.add("TidierData")
using DataFrames
using TidierData

@chain dados begin
    @slice(1:6)
end
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

Leitura da base de dados

Conhecendo o dataset

  1. track_name: Nome da faixa;
  2. artists: Os nomes dos artistas que interpretaram a faixa. Se houver mais de um artista, eles serão separados por ‘;’ ;
  3. album_name: O nome do álbum de onde a faixa pertence;
  4. popularity: A popularidade de uma faixa é um valor entre 0 e 100, sendo 100 o mais popular, sendo calculada a partir (de forma geral) do número de streams daquela faixa, e o quão recente foram essas streams;
  5. duration_ms: A duração da faixa em milissegundos;
  6. danceability: A dançabilidade descreve o quão adequada uma faixa é para dançar com base em uma combinação de elementos musicais, incluindo andamento, estabilidade do ritmo, força da batida e regularidade geral. Um valor de 0,0 é o menos dançante e 1,0 o mais dançante;

Leitura da base de dados

Conhecendo o dataset

  1. energy: Energia é uma medida de 0,0 a 1,0 e representa uma medida perceptual de intensidade e atividade. Normalmente, músicas energéticas parecem rápidas, altas e barulhentas. Por exemplo, o death metal tem alta energia, enquanto um prelúdio de Bach tem baixa pontuação na escala;
  2. loudness: O volume total de uma faixa em decibéis (dB);
  3. speechiness: Detecta a presença de palavras faladas em uma faixa. Quanto mais exclusivamente semelhante à fala for a gravação (por exemplo, talk show, audiolivro, poesia), mais próximo de 1,0 será o valor do atributo. Valores acima de 0,66 descrevem faixas que provavelmente são compostas inteiramente de palavras faladas. Valores entre 0,33 e 0,66 descrevem faixas que podem conter música e fala, seja em seções ou em camadas, incluindo casos como rap. Valores abaixo de 0,33 provavelmente representam música e outras faixas que não se assemelham à fala.
  4. acousticness: Uma medida de confiança de 0,0 a 1,0 para determinar se a faixa é acústica. 1,0 representa alta confiança de que a faixa é acústica.

Leitura da base de dados

Conhecendo o dataset

  1. valence: Uma medida de 0,0 a 1,0 que descreve a positividade musical transmitida por uma faixa. Faixas com alta valência soam mais positivas (por exemplo, felizes, alegres, eufóricas), enquanto faixas com baixa valência soam mais negativas (por exemplo, tristes, deprimidas, raivosas);
  2. tempo: O andamento estimado geral de uma faixa em batidas por minuto (BPM);
  3. track_genre: O gênero da faixa;
  4. instrumentalness: Prevê se uma faixa não contém vocais. Sons de “Ooh” e “aah” são tratados como instrumentais neste contexto. Faixas de rap ou palavra falada são claramente “vocais”. Quanto mais próximo o valor de instrumentalidade estiver de 1,0, maior a probabilidade de a faixa não conter conteúdo vocal.
  5. liveness: Detecta a presença de público na gravação. Valores mais altos de ao vivo representam uma probabilidade maior de que a faixa tenha sido tocada ao vivo. Um valor acima de 0,8 fornece alta probabilidade de que a faixa seja ao vivo.

Manipulação de banco de dados

Tidier.jl

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!

Pacote TidierData.jl

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.

Pacote TidierData.jl

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

Pacote TidierData.jl

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.

Pacote TidierData.jl

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.

Pacote TidierData.jl

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).

Pacote TidierData.jl

Limpeza do dataset do Spotify: Tratando valores faltantes

faltantes = filter(row -> any(ismissing, row), dados)
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

Pacote TidierData.jl

Vamos remover as linhas que possuem valores faltantes.

dados = @chain dados begin
    dropmissing()
end
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

Pacote TidierData.jl

Verificaremos possíveis faixas duplicadas, e retirá-las caso haja alguma.

nrow(unique(dados, :track_id)), nrow(dados)
(89740, 113999)
dados = unique(dados, :track_id)
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

Análise Exploratória de Dados (EDA)

EDA

  • Julia torna possível compreender e explorar conjuntos de dados, identificando padrões, tendências e possíveis outliers antes da modelagem.
  • Nesse tópico, iremos explorar algumas estatísticas resumo (média, mediana, desvio-padrão, etc.) e algumas distribuições (discretas e contínuas).
  • Os pacotes Statistics.jl, StatsBase.jl, DataFrames.jl e TidierData.jl serão essenciais para a análise 🆒.
using DataFrames
using StatsBase
using Statistics
using TidierData

EDA: Medidas de tendência central

Algumas das principais medidas são:

  • mean(x) representa média aritmética
  • median(x) representa a mediana
  • mode(x) representa a moda (pacote StatsBase.jl)

Ex.:

x = [1, 2, 2, 2, 5, 6, 7];
mean(x)
3.5714285714285716

EDA: Medidas de dispersão

  • var(x) representa variância
  • std(x) representa o desvio padrão
  • minimum(x) e maximum(x) são os valores mínimo e máximo
  • quantile(x, p) repesenta quantis (p é o parâmetro da posição relativa da distribuição)

Ex.:

x = [1, 2, 2, 2, 5, 6, 7];
std(x)
2.370453040886408

EDA: Contagem e frequência

  • length(x) é o número de elementos
  • count(cond, x) conta elementos que satisfazem uma condição cond
  • countmap(x) cria uma tabela de frequências (pacote StatsBase.jl)
x = ["maçã", "banana", "maçã", "laranja", "banana", "maçã"];
countmap(x)
Dict{String, Int64} with 3 entries:
  "laranja" => 1
  "banana"  => 2
  "maçã"    => 3

EDA: Informações resumidas

  • describe(df) fornece estatísticas descritivas de um data frame
  • cor(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

EDA

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.

EDA: Análise descritiva do dataset

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
                  
describe(select(df_george, vars_numericas))
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

EDA: Visualização de dados

  • O Julia oferece diversas bibliotecas para criar visualizações estatísticas de forma simples e flexível, permitindo a análise e interpretação de dados de maneira visual. Algumas bibliotecas populares incluem Plots.jl, StatsPlots.jl, Makie.jl e Gadfly.jl.

EDA: Boxplot

  • bloxplot(x) resume a distribuição dos dados destacando mediana, quartis e possíveis outliers.

Ex.:

x = [5.1, 5.5, 5.3, 5.0, 5.2, 5.8, 5.4, 6.0, 4.9, 5.6, 5.7, 6.2]
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

boxplot(x;
    xlabel = "Amostra",
    ylabel = "Valores",
    title = "Boxplot de Exemplo",
    legend = false
)

Barplot (gráfico de barras)

  • bar(x) representa frequências ou valores médios para categorias.

Ex.:

generos = ["Pop", "Rock", "Jazz", "Funk"]
4-element Vector{String}:
 "Pop"
 "Rock"
 "Jazz"
 "Funk"
freqs = [50, 30, 15, 5]
4-element Vector{Int64}:
 50
 30
 15
  5

bar(generos, freqs, xlabel="Gênero", ylabel="Frequência", 
    title="Número de músicas por gênero", legend=false)

EDA: Histograma

  • 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

histogram(
    alturas,
    bins = 20,
    xlabel = "Altura (cm)",
    ylabel = "Frequência",
    title = "Distribuição das Alturas",
    legend = false
)

EDA: Curva de densidade

  • Para gerar a curva de densidade em 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

EDA: Gráfico de dispersão

  • scatter(x) mostra a relação entre duas variáveis numéricas.

Ex.:

using Random, Plots

Random.seed!(123)
TaskLocalRNG()

x = 1:20
1:20
y = x .+ randn(20) .* 2
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

scatter(x, y,
    xlabel = "Variável X",
    ylabel = "Variável Y",
    title = "Exemplo de Gráfico de Dispersão",
    legend = false,
    color = :blue,
    markerstrokecolor = :black)

EDA: Matrizes de correlacão e mapas de calor

  • Matriz de Correlação: Tabela que mostra o grau de correlação (associação linear) entre pares de variáveis numéricas.
  • Mapa de Calor (Heatmap): Representação visual de uma matriz, onde os valores numéricos são exibidos como cores.

Por que usar?

  • Identificar relações fortes ou fracas entre variáveis.
  • Detectar multicolinearidade em análise estatística e modelagem.
  • Facilitar a interpretação visual de grandes conjuntos de dados

EDA: Matrizes de correlacão e mapas de calor

Ex.:

using Random, StatsPlots

Random.seed!(123)
TaskLocalRNG()

n = 50
50
A = randn(n) .* 10
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
B = A .* 0.5 .+ randn(n) .* 5
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
C = randn(n) .* 2
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
D = 0.8 .* A .- 0.3 .* C .+ randn(n)
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

df_rand = DataFrame(A=A, B=B, C=C, D=D)
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

corr_mat = cor(Matrix(df_rand))
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

heatmap(names(df_rand), names(df_rand), corr_mat;
    c = :coolwarm, clim=(-1,1), aspect_ratio=1,
    title="Matriz de Correlação (Dados Aleatórios)",
    xlabel="", ylabel="", xrotation=45, yrotation=45)

EDA: Gerando gráficos do dataset

  • Para o exercício 5, utilizamos a função 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.
using Statistics, StatsPlots, Plots

dados.duration_min = dados.duration_ms ./ 60000
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)

    
density!(dados[!, :duration_min], linewidth = 2, label = "Densidade")

EDA: Gerando gráficos do dataset

  • O gráfico de heatmap foi construído a partir da matriz, utilizando rótulos correspondentes aos nomes das variáveis e aplicando um esquema de cores (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

Modelagem

Objetivo: Prever a variável energy com base nas outras variáveis quantitativas. Para isso, utilizaremos um modelo de Regressão Linear Múltipla.

Pré-Processamento

Primeiro, vamos fazer a divisão do dataset em dados de treino e dados de teste:

var_categoricas = [:track_genre, :explicit]
X = select(dados, union(?, ?))
y = dados.variavel_resposta
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]

Pré-Processamento

  • Para verificar se deu certo, rode uma célula contendo apenas X e uma contendo apenas y
  • Para rodas ambos na mesma célula, você deve utilizar a função display(). Por padrão, Julia exibe somente o último objeto da célula

Agora, seguimos com a normalização das variáveis numéricas.

# verificando as colunas numéricas:
describe(dados[:, var_numericas], :min, :max)
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

Pré-Processamento

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

Pré-Processamento

# Visualizar as primeiras 5 linhas do df original para comparar
first(X, 5)
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

Pré-Processamento

# Visualizar as primeiras 5 linhas do df normalizado para comparar
first(X_train_scaled, 5)
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
# Ver resumo estatístico das colunas numéricas
describe(dados[:, var_numericas])
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

Pré-Processamento

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.

Pré-Processamento

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
typeof(X_train_cleaned)
DataFrame

Pré-Processamento

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.

Pré-Processamento

  • 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.

X_train_mat = Matrix(X_train_cleaned)
display(typeof(X_train_mat))

X_test_mat = Matrix(X_test_cleaned)
display(typeof(X_test_mat))
X_train_mat
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

Pré-Processamento

  • Vetorização da variável resposta (target variable), energy:
y_train_vec = y_train
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
y_test_vec  = y_test
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

Modelo de Regressão Linear Múltipla

Referências