Fundamentos do Plotly

“A diferença entre um gráfico que informa e um que transforma está na gramática dos gráficos. Quando você domina essa linguagem visual, cada dataset se torna uma oportunidade de descoberta.”

Você já internalizou os fundamentos da gramática dos gráficos. Agora é o momento de traduzir essa compreensão conceitual em fluência técnica com Plotly - a biblioteca Python que combina a gramática dos gráficos com interatividade poderosa.

Por que Plotly?

Enquanto outras bibliotecas criam gráficos estáticos, o Plotly oferece interatividade nativa. Você pode criar visualizações que respondem ao usuário - zoom, hover, seleção - tudo funcionando automaticamente. É a diferença entre mostrar uma fotografia e oferecer uma experiência visual completa.

Neste módulo, você desenvolverá:

  • Fluência na sintaxe intuitiva que espelha seu pensamento analítico
  • Metodologia sistemática para construir visualizações interativas
  • Intuição visual para escolher tipos de gráficos apropriados
  • Sensibilidade estética para criar paletas e layouts profissionais
  • Capacidade de interatividade para criar experiências visuais envolventes
# Configuração inicial e imports
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np

# Configurar tamanho padrão 16:9 para todos os gráficos
DEFAULT_WIDTH = 800
DEFAULT_HEIGHT = 500

# Dataset: pinguins de Palmer
penguins = pd.read_csv('https://raw.githubusercontent.com/allisonhorst/palmerpenguins/master/inst/extdata/penguins.csv')
penguins = penguins.dropna()

Conhecendo os Pinguins de Palmer

Nosso dataset contém dados reais coletados na Estação Palmer, Antártica (2007-2009) por Allison Horst. É um dataset perfeito para aprender visualização porque:

  • 3 espécies distintas com características morfológicas diferentes
  • Variáveis contínuas e categóricas que permitem explorar toda a gramática visual
  • Contexto científico real - cada observação representa um pinguim real estudado

Ilustração das três espécies de pinguins de Palmer

As medidas do bico são fundamentais para distinguir as espécies:

Ilustração das medidas do bico: comprimento e profundidade

# Primeira visão dos dados
penguins.head()
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 male 2007
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 female 2007
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 female 2007
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 female 2007
5 Adelie Torgersen 39.3 20.6 190.0 3650.0 male 2007

Variáveis principais:

  • species: Adelie, Chinstrap, Gentoo (categórica)
  • bill_length_mm, bill_depth_mm: Dimensões do bico (contínuas)
  • flipper_length_mm, body_mass_g: Características físicas (contínuas)
  • island: Biscoe, Dream, Torgersen (categórica)
  • sex: Female, Male (categórica)

Do Pensamento à Visualização: A Gramática em Ação

🧠 Como experts pensam: “Quero entender se existe relação entre as dimensões do bico dos pinguins. Preciso mapear comprimento no eixo X, profundidade no Y, e usar pontos para mostrar cada indivíduo.”

O Plotly traduz esse pensamento diretamente em código através de uma sintaxe intuitiva:

graph LR
    A["DADOS<br/>O que visualizar"] --> B["MARCA<br/>Como representar"]
    B --> C["ENCODING<br/>Onde mapear"]
    C --> D["REFINAMENTO<br/>Como melhorar"]
    D --> E["💎 Insight Visual"]
    
    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style C fill:#e8f5e8
    style D fill:#fff3e0
    style E fill:#f1c40f

Nível 1: A Base

graph LR
    A["DADOS"] --> B["MARCA"]
    B --> C["ENCODING<br/>(mínimo)"]
    
    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style C fill:#e8f5e8

Importante🤔 Pergunta Analítica

Existe correlação entre comprimento e profundidade do bico?

# Passo 1: Traduzindo pensamento em código
# "Quero pontos (scatter) que mostrem comprimento vs profundidade"
fig = px.scatter(
    penguins,
    x="bill_length_mm",    # Comprimento no X
    y="bill_depth_mm",     # Profundidade no Y
    title="Relação entre Dimensões do Bico",
    labels={
        "bill_length_mm": "Comprimento do Bico (mm)",
        "bill_depth_mm": "Profundidade do Bico (mm)"
    },
    width=DEFAULT_WIDTH,
    height=DEFAULT_HEIGHT
)
fig.update_traces(marker=dict(size=8, opacity=0.6))
fig.show()
Dica💡 Insight

Há uma correlação negativa interessante: quanto maior o bill_length, menor o bill_depth. Mas será que isso faz sentido? Pode haver algo oculto nos dados.

Nota📝 Conceito

O Plotly automaticamente reconhece variáveis numéricas e as trata apropriadamente nos eixos, incluindo formatação e escalas otimizadas.

Nível 2: Revelando Padrões

graph LR
    A["DADOS"] --> B["MARCA"]
    B --> C["ENCODING<br/>(adicionando contexto)"]
    
    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style C fill:#e8f5e8

Importante🤔 Pergunta Analítica

Será que esse padrão é consistente entre todas as espécies?

# Passo 2: Adicionando a dimensão "espécie" através da cor
fig = px.scatter(
    penguins,
    x="bill_length_mm",
    y="bill_depth_mm", 
    color="species",  # ← Revelamos padrões ocultos!
    title="Revelando Padrões por Espécie",
    labels={
        "bill_length_mm": "Comprimento do Bico (mm)",
        "bill_depth_mm": "Profundidade do Bico (mm)",
        "species": "Espécie"
    },
    width=DEFAULT_WIDTH,
    height=DEFAULT_HEIGHT
)
fig.update_traces(marker=dict(size=10, opacity=0.8))
fig.show()
Dica💡 Insight

O que parecia uma correlação negativa geral são três correlações positivas separadas por espécie! Exemplo clássico do Paradoxo de Simpson.

Nível 3: Qualidade Avançada

graph LR
    A["DADOS"] --> B["MARCA<br/>(refinada)"]
    B --> C["ENCODING<br/>(refinada e mais contexto)"]
    C --> D["REFINAMENTO<br/>(título e aspecto)"]
    
    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style C fill:#e8f5e8
    style D fill:#fff3e0

Importante🤔 Pergunta Analítica

Como transformar este insight em uma comunicação clara e impactante?

# Passo 3: Versão avançada com interatividade
fig = px.scatter(
    penguins,
    x="bill_length_mm",
    y="bill_depth_mm",
    color="species",
    hover_data=["species", "island", "bill_length_mm", "bill_depth_mm"],
    title="Paradoxo de Simpson na Morfologia dos Pinguins<br><sup>Correlação aparentemente negativa revela três padrões positivos distintos por espécie</sup>",
    labels={
        "bill_length_mm": "Comprimento do Bico (mm)",
        "bill_depth_mm": "Profundidade do Bico (mm)",
        "species": "Espécie"
    },
    color_discrete_sequence=px.colors.qualitative.D3,
    width=DEFAULT_WIDTH,
    height=DEFAULT_HEIGHT
)

# Refinamentos avançados
fig.update_traces(
    marker=dict(size=12, opacity=0.8, line=dict(width=0.5, color="white"))
)

fig.update_layout(
    title=dict(x=0, xanchor="left"),
    xaxis=dict(showline=True, zeroline=False),
    yaxis=dict(showline=True, zeroline=False)
)

fig.show()

A Linguagem dos Tipos de Dados

O Plotly automaticamente reconhece tipos de dados, mas entender essas distinções nos ajuda a fazer escolhas visuais melhores.

Os Quatro Tipos Fundamentais

Exemplos dos tipos de dados:

  • Quantitativo (numérico): 32.1 a 59.6 mm (comprimento do bico)
  • Nominal (categórico): Adelie, Chinstrap, Gentoo (espécies)
  • Temporal (data/tempo): 2007 a 2009 (anos de coleta)
  • Ordinal (categoria ordenada): pequeno < médio < grande (categorias ordenadas)

Regra prática: Pergunte-se sempre: “Esta variável representa quantidade, categoria, ordem ou tempo?”

# Como a escolha do tipo afeta a visualização
from plotly.subplots import make_subplots

# Criar subplots lado a lado
fig = make_subplots(
    rows=1, cols=2,
    subplot_titles=("Cor Quantitativa (gradiente)", "Cor Nominal (categorias)")
)

# Gráfico 1: Cor quantitativa (massa corporal)
fig_quant = px.scatter(
    penguins,
    x="bill_length_mm",
    y="bill_depth_mm",
    color="body_mass_g",  # Massa como gradiente contínuo
    color_continuous_scale="Blues"
)

# Gráfico 2: Cor categórica (espécie)
fig_cat = px.scatter(
    penguins,
    x="bill_length_mm",
    y="bill_depth_mm",
    color="species"  # Espécie como categorias distintas
)

# Adicionar traces aos subplots
for trace in fig_quant.data:
    fig.add_trace(trace, row=1, col=1)

for trace in fig_cat.data:
    fig.add_trace(trace, row=1, col=2)

# Layout
fig.update_layout(
    height=400,
    width=DEFAULT_WIDTH,
    title_text="Comparação de Esquemas de Cores",
    showlegend=True
)

fig.show()

Insight importante: Mesma variável visual (cor), significados diferentes! Quantitativo cria gradientes para magnitude, categórico cria cores distintas para grupos.

Tipos de Gráficos

Cada tipo de gráfico responde melhor a tipos específicos de perguntas analíticas.

Scatter Plots: Explorando Relações

Use px.scatter() quando quiser explorar relações entre duas variáveis contínuas.

# Criar subplots para comparação
fig = make_subplots(rows=1, cols=2, subplot_titles=("Versão Básica", "Versão Avançada"))

# Gráfico básico
basic_fig = px.scatter(
    penguins,
    x="bill_length_mm",
    y="bill_depth_mm",
    labels={"bill_length_mm": "Comprimento do Bico (mm)",
            "bill_depth_mm": "Profundidade do Bico (mm)"}
)
basic_fig.update_traces(marker=dict(size=6, opacity=1))  # tamanho pequeno, sem transparência

# Gráfico avançado
advanced_fig = px.scatter(
    penguins,
    x="bill_length_mm",
    y="bill_depth_mm",
    color="species",
    labels={"bill_length_mm": "Comprimento do Bico (mm)",
            "bill_depth_mm": "Profundidade do Bico (mm)",
            "species": "Espécie"}
)
advanced_fig.update_traces(marker=dict(size=10, opacity=0.7, line=dict(width=0.5, color="white")))

# Adicionar traces aos subplots
for trace in basic_fig.data:
    fig.add_trace(trace, row=1, col=1)

for trace in advanced_fig.data:
    fig.add_trace(trace, row=1, col=2)

# Layout
fig.update_layout(height=400, width=DEFAULT_WIDTH, title_text="Comparação de Scatter Plots", showlegend=True)
fig.show()

Dica: Use transparência e bordas para revelar sobreposições nos dados.

Gráficos de Barras: Comparações

Use px.bar() para comparar valores entre categorias.

# Barras para comparações categóricas
mean_bill = penguins.groupby("species")["bill_length_mm"].mean().reset_index()

fig = px.bar(
    mean_bill,
    x="species",
    y="bill_length_mm",
    color_discrete_sequence=["steelblue"],
    labels={
        "species": "Espécie",
        "bill_length_mm": "Comprimento Médio do Bico (mm)"
    },
    title="Comparação: Comprimento Médio do Bico por Espécie"
)

fig.update_layout(
    width=DEFAULT_WIDTH,
    height=DEFAULT_HEIGHT
)
fig.show()

Barras facilitam a comparação visual de magnitudes entre categorias.

# Barras horizontais
penguins_summary = penguins.groupby(['species', 'island']).size().reset_index(name='count')

fig = px.bar(
    penguins_summary,
    y="species",
    x="count",
    color="island",
    orientation="h",
    labels={
        "species": "Espécie",
        "count": "Quantidade",
        "island": "Ilha"
    },
    title="Distribuição de Pinguins por Espécie e Ilha",
    color_discrete_sequence=px.colors.qualitative.D3
)

fig.update_layout(
    width=DEFAULT_WIDTH,
    height=DEFAULT_HEIGHT,
    barmode="stack"
)
fig.show()

Gráficos de Linha

Use px.line() para séries temporais e evolução ao longo do tempo.

# Simular dados temporais para demonstração
temporal_data = pd.DataFrame({
    'year': np.repeat(range(2007, 2020), 3),
    'species': ['Adelie', 'Chinstrap', 'Gentoo'] * 13,
    'count': np.random.poisson(50, 39)
})

fig = px.line(
    temporal_data,
    x='year',
    y='count',
    color='species',
    markers=True,  # pontos visíveis nos anos
    labels={
        'year': 'Ano',
        'count': 'Quantidade',
        'species': 'Espécie'
    },
    title="Evolução Populacional Simulada"
)

fig.update_layout(
    width=DEFAULT_WIDTH,
    height=DEFAULT_HEIGHT
)
fig.show()

Gráficos de Área

Use px.area() para mostrar volume e acumulação ao longo do tempo.

# Área empilhada mostrando evolução temporal simulada
temporal_counts = pd.DataFrame({
    'year': list(range(2007, 2010)) * 3,
    'species': np.repeat(['Adelie','Chinstrap','Gentoo'],3),
    'count': [45, 48, 52, 12, 15, 18, 38, 42, 45]
})

fig = px.area(
    temporal_counts,
    x='year',
    y='count',
    color='species',
    labels={
        'year': 'Ano',
        'count': 'Quantidade',
        'species': 'Espécie'
    },
    title="Evolução Populacional por Espécie (simulado)"
)

fig.update_layout(
    width=DEFAULT_WIDTH,
    height=DEFAULT_HEIGHT
)
fig.update_xaxes(range=[2006, 2010])  # adiciona espaço extra no início/fim
fig.show()

Box Plots

Use px.box() para visualizar distribuições e identificar outliers.

# Box plots revelam distribuições
fig = px.box(
    penguins,
    x='species',
    y='bill_depth_mm',
    points=False,
    labels={'species': 'Espécie', 'bill_depth_mm': 'Profundidade do Bico (mm)'},
    title="Distribuição da Profundidade do Bico por Espécie"
)

fig.update_layout(
    width=DEFAULT_WIDTH,
    height=DEFAULT_HEIGHT
)
fig.show()

🚧 Seções em Migração

Nota📝 Nota sobre o Conteúdo

As seções seguintes estão sendo migradas de Altair para Plotly. Por enquanto, focaremos nos tipos de gráficos principais mostrados acima: scatter plots, bar charts, line plots, area charts e box plots.

Para mais exemplos avançados, consulte a documentação do Plotly: https://plotly.com/python/

Exemplo Final: Dashboard Simples

Vamos criar um dashboard simples combinando os tipos de gráficos que aprendemos:

# Dashboard exemplo combinando múltiplos tipos de gráfico
from plotly.subplots import make_subplots

# Criar subplots
fig = make_subplots(
    rows=2, cols=2,
    subplot_titles=("Relação Bill", "Distribuição por Ilha", "Massa por Espécie", "Evolução Temporal"),
    specs=[[{"type": "scatter"}, {"type": "bar"}],
           [{"type": "box"}, {"type": "bar"}]]
)

# 1. Scatter plot
for species in penguins['species'].unique():
    species_data = penguins[penguins['species'] == species]
    fig.add_trace(
        go.Scatter(x=species_data['bill_length_mm'], y=species_data['bill_depth_mm'],
                  mode='markers', name=species, legendgroup=species),
        row=1, col=1
    )

# 2. Bar chart - distribuição por ilha
island_counts = penguins.groupby(['island', 'species']).size().reset_index(name='count')
for species in penguins['species'].unique():
    species_data = island_counts[island_counts['species'] == species]
    fig.add_trace(
        go.Bar(x=species_data['island'], y=species_data['count'], 
               name=species, legendgroup=species, showlegend=False),
        row=1, col=2
    )

# 3. Box plot - massa por espécie  
for species in penguins['species'].unique():
    species_data = penguins[penguins['species'] == species]
    fig.add_trace(
        go.Box(y=species_data['body_mass_g'], name=species, 
               legendgroup=species, showlegend=False),
        row=2, col=1
    )

# 4. Bar chart - evolução por ano
yearly_counts = penguins.groupby(['year', 'species']).size().reset_index(name='count')
for species in penguins['species'].unique():
    species_data = yearly_counts[yearly_counts['species'] == species]
    fig.add_trace(
        go.Bar(x=species_data['year'], y=species_data['count'], 
               name=species, legendgroup=species, showlegend=False),
        row=2, col=2
    )

# Layout
fig.update_layout(height=600, title_text="Dashboard Palmer Penguins")
fig.show()

Exportação e Integração

Salvando Gráficos

# Para salvar um gráfico Plotly:
fig.write_html("pinguins_palmer.html")  # Interativo
fig.write_image("pinguins_palmer.png", width=800, height=600, scale=2)  # Alta qualidade
fig.write_image("pinguins_palmer.svg")  # Vetorial

Conclusão

Parabéns! Se você chegou até aqui, dominou os fundamentos da visualização de dados com Plotly e a gramática dos gráficos. Você partiu dos conceitos básicos, aprendeu a sintaxe intuitiva, explorou diferentes tipos de gráficos e encodings, e agora sabe como criar visualizações interativas profissionais que comunicam insights de forma clara e impactante.

A gramática dos gráficos que você internalizou é uma habilidade transferível - os princípios que aprendeu se aplicam a qualquer ferramenta de visualização. Mais importante ainda, você desenvolveu o pensamento analítico visual: a capacidade de traduzir perguntas em escolhas visuais apropriadas.

Com estes fundamentos sólidos, você está preparado para transformar qualquer dataset em narrativas visuais convincentes e interativas, seja para exploração pessoal, apresentações executivas ou publicações científicas.

Recursos Adicionais