Visualização: Boas Práticas

Aula 06 — Módulo 1: Análise Exploratória

Heitor Ramos
heitor@dcc.ufmg.br

Departamento de Ciência da Computação — UFMG

Módulo 1 · Análise Exploratória

Um bom gráfico torna a comparação correta mais fácil e a interpretação errada mais difícil.

Hoje

  1. Definir a mensagem antes de desenhar.
  2. Escolher escalas, ordem e contexto honestos.
  3. Usar cor e destaque com intenção.
  4. Reduzir ruído sem apagar evidência.
  5. Comparar representações da mesma base.

A base principal da aula

TMDB 5000 Movie Dataset

  • 4.803 filmes;
  • orçamento, receita, popularidade, votos, duração e data;
  • gêneros e produtoras em campos estruturados;
  • dados oriundos da API do The Movie Database.

Fonte: Kaggle — TMDB 5000.

Para condicionamento espacial e temporal, retomaremos também a amostra da Olist, com pedidos brasileiros de 2017 ligados ao estado do cliente.

O objetivo da base

Investigar como escolhas visuais alteram conclusões sobre:

  • composição por gênero;
  • notas e popularidade;
  • orçamento, receita e lucro;
  • mudanças ao longo do tempo.

Uma linha representa um filme.

Principais colunas

coluna significado cuidado
budget orçamento informado zero pode significar ausência
revenue receita informada cobertura desigual
vote_average média das notas depende de vote_count
genres gêneros em JSON um filme pode ter vários
release_date lançamento base incompleta no tempo

Descrição rápida

movies.shape
movies[["budget", "revenue", "vote_average"]].describe()

Resultado: 4.803 filmes. Para relações financeiras, usamos 3.157 registros com orçamento e receita acima de US$ 100 mil.

Comece pela pergunta, não pelo gráfico

Perguntas distintas pedem representações distintas:

  • ranking: quem está acima?
  • distribuição: como os valores se espalham?
  • relação: duas medidas variam juntas?
  • composição: como um total se divide?
  • mudança: como evolui no tempo?

O título deve entregar o ponto

“Receita por gênero” apenas nomeia variáveis.

“A dispersão dentro dos gêneros supera a diferença entre suas medianas” comunica uma leitura verificável.

Título bom é uma afirmação sustentada pelo gráfico.

Barras ordenadas reduzem trabalho mental

Gêneros de filmes ordenados por contagem.
Código do gráfico
movies["main_genre"].value_counts().head(8).sort_values().plot.barh()

Barras precisam de zero

O comprimento da barra codifica a magnitude desde uma origem.

Se o eixo começa perto do menor valor, a parte visível deixa de representar corretamente a razão entre categorias.

Linhas e pontos não têm a mesma obrigação quando a pergunta é sobre variação em torno de um nível.

Comparação entre barras com eixo correto e eixo truncado.

Os valores são os mesmos nos dois painéis: 1.207 dramas e 1.042 comédias.

Código do gráfico
pair = movies["main_genre"].value_counts().loc[["Drama", "Comedy"]]
ax.set_ylim(0, pair.max())          # comparação honesta
ax.set_ylim(pair.min() * .94, ...)  # diferença exagerada

Escalas duplas confundem comparações

Os mesmos dados de filmes e notas parecem se opor com o segundo eixo usual e acompanhar-se quando esse eixo é invertido.

Dois eixos verticais permitem que qualquer par de curvas pareça acompanhar-se. Aqui, os dados são idênticos nos dois painéis; apenas o segundo eixo foi invertido.

Prefira:

  • índices com uma base comum;
  • painéis alinhados;
  • variação percentual claramente definida;
  • uma relação direta em dispersão.
Código do gráfico
ax2 = ax.twinx()
ax.plot(annual["year"], annual["filmes"])
ax2.plot(annual["year"], annual["nota"], color="orange")
ax2.invert_yaxis()  # escolha possível, mas enganosa

Escala linear esconde caudas longas

Orçamento e receita de filmes em escalas linear e logarítmica.
Código do gráfico
sns.scatterplot(data=valid, x="budget", y="revenue")
plt.xscale("log"); plt.yscale("log")

Log transforma razões em distâncias

Em escala logarítmica, multiplicações iguais ocupam distâncias iguais:

\[\log(ab)=\log(a)+\log(b).\]

Passar de 1 para 10 ocupa o mesmo espaço que passar de 10 para 100.

Log exige contexto

  • informe a transformação;
  • use marcas em valores interpretáveis;
  • não aplique log a zero sem decidir o significado;
  • explique se a relação relevante é multiplicativa.

Transformar pode revelar estrutura, mas muda a pergunta visual.

Tempo possui ordem

Número de filmes por ano na base TMDB.
Código do gráfico
movies.groupby("year").size().loc[1980:2016].plot.line()

Linhas sugerem continuidade

Use linha quando:

  • o eixo possui ordem;
  • intervalos adjacentes são comparáveis;
  • conectar pontos tem significado.

Não conecte categorias nominais: a linha inventaria um caminho entre elas.

Datas precisam refletir o processo

Data de lançamento, data de registro e data de atualização não são intercambiáveis.

O eixo temporal deve representar o acontecimento que a pergunta menciona.

Observar uma razão muda a pergunta

Contagens anuais de dramas e comédias ao lado da razão entre as duas contagens.

Contagem mede volume; razão mede a composição relativa entre dois gêneros.

Código do gráfico
contagens = movies.groupby(["year", "main_genre"]).size().unstack()
razao = contagens["Drama"] / contagens["Comedy"]

Justaposição: mesmo domínio, lado a lado

Quatro séries anuais de filmes apresentadas lado a lado com os mesmos eixos.

A escala comum permite comparar tendência e magnitude sem misturar as séries.

Código do gráfico
sns.relplot(data=annual, x="year", y="filmes", col="main_genre",
            kind="line", sharex=True, sharey=True)

Condicionando por espaço e tempo

Quatro mapas do Brasil mostrando pedidos da Olist por estado e trimestre de 2017.

O contorno dos estados mantém a forma do Brasil legível mesmo nas regiões com preenchimento claro.

Um mapa anual esconderia quando a concentração ocorreu; um gráfico temporal sem mapa esconderia onde.

Código do gráfico
pedidos = olist.groupby(["trimestre", "customer_state"]).size()
# quatro mapas, mesmo domínio de cores e contornos visíveis
PatchCollection(estados, edgecolor="#34454f", linewidth=.8)

Distribuições contam mais que médias

Boxplots de notas de filmes por gênero.
Código do gráfico
sns.boxplot(data=movies, x="vote_average", y="main_genre")

Mostre a incerteza quando ela importa

Uma média baseada em 20 votos não tem a mesma estabilidade que outra baseada em 20 mil.

Use intervalos, pontos brutos, tamanho da amostra ou transparência sobre a cobertura.

Comparar partes: ângulos ou comprimentos?

A mesma composição de gêneros em pizza e barras.
Código do gráfico
ax.pie(share, labels=share.index)
share.sort_values().plot.barh()

Cor qualitativa separa grupos

Use matizes diferentes para categorias sem ordem.

  • limite o número de cores;
  • preserve contraste;
  • não atribua gradiente a categorias nominais;
  • mantenha o mesmo significado entre painéis.

Cor sequencial mostra magnitude

Dispersão de orçamento e receita colorida pela quantidade de votos.
Código do gráfico
plt.scatter(valid.budget, valid.revenue,
            c=np.log10(valid.vote_count), cmap="viridis")

Cor divergente precisa de um centro

Use uma escala divergente quando existe um ponto de referência significativo:

  • zero;
  • média;
  • meta;
  • ausência de mudança.

Sem centro, duas cores opostas inventam uma ruptura.

Destaque orienta a atenção

Ranking de filmes por lucro com um único destaque em laranja.
Código do gráfico
colors = ["#b7c4ca"] * 9 + ["#d97727"]
plt.barh(best["original_title"], best["profit"], color=colors)

Cinza é parte da paleta

Quando tudo tem cor forte, nada se destaca.

Use cinza para contexto e uma cor de acento para a observação ligada ao título.

Destaque é hierarquia, não ornamentação.

Ruído compete com os dados

Comparação entre gráfico carregado e versão com hierarquia visual.
Código do gráfico
ax.barh(categorias, valores, color=[acento if foco else cinza])

Remova o que não ajuda a comparação

  • bordas pesadas;
  • fundos saturados;
  • grades em excesso;
  • efeitos 3D;
  • legendas distantes;
  • casas decimais sem significado;
  • rótulos repetidos.

Mas simplificar não é apagar contexto

Gráfico de barras com título informativo, unidade, tamanho da base e fonte.
Código do exemplo
ax.set_title("Drama e comédia dominam a amostra do TMDB 5000", loc="left")
ax.set_xlabel("número de filmes na base")
ax.text(0, -.18, "Unidade: filme · n = 4.803 · Fonte: TMDB 5000 / Kaggle",
        transform=ax.transAxes)

Simplificar remove ruído; contextualizar preserva significado: título, unidade, população, período, fonte, filtros e tamanho da amostra entram quando são necessários para interpretar o gráfico.

Anotação aproxima evidência e interpretação

Série temporal com o ponto máximo anotado diretamente no gráfico.
Código do exemplo
ax.annotate("Pico da base", xy=(ano_pico, n_pico),
            xytext=(-120, -45), textcoords="offset points",
            arrowprops={"arrowstyle": "->"})

Anote exceções relevantes, mudanças de regime e valores ligados ao argumento — não cada ponto.

Legendas aumentam a distância visual

Comparação entre séries identificadas por legenda e por rótulos diretos.
Código do exemplo
ax.plot(ano, filmes, label=genero)
ax.text(ano.iloc[-1] + .7, filmes.iloc[-1], genero,
        color=cor, va="center")

O rótulo direto reduz o movimento dos olhos entre a série e sua identificação. Se a legenda for necessária, preserve a ordem do gráfico e aproxime-a dos dados.

Razão de aspecto altera inclinações

A mesma série temporal em painéis estreito e largo, produzindo inclinações aparentes diferentes.
Código do exemplo
fig, axes = plt.subplots(1, 2, figsize=(12, 4.8),
                         gridspec_kw={"width_ratios": [1, 2.4]})
for ax in axes:
    ax.plot(annual["year"], annual["filmes"])

Use proporções estáveis entre comparações e evite escolher o formato apenas porque ele dramatiza a mudança.

Sobreposição pede transparência ou agregação

Dispersão opaca comparada a uma agregação hexbin dos mesmos filmes.
Código do exemplo
ax.scatter(valid["budget"], valid["revenue"], alpha=.15, s=12)
ax.hexbin(valid["budget"], valid["revenue"],
          gridsize=32, mincnt=1, bins="log",
          xscale="log", yscale="log")

Transparência preserva os pontos; hexbin agrega localmente e revela densidade. Não confunda saturação visual com concentração real.

Muitos dados não significam muitos elementos

Notas individuais de filmes comparadas à mediana anual e ao intervalo central.
Código do exemplo
yearly = movies.groupby("year")["vote_average"].agg(
    mediana="median",
    q25=lambda x: x.quantile(.25),
    q75=lambda x: x.quantile(.75),
)
ax.fill_between(yearly.index, yearly.q25, yearly.q75)
ax.plot(yearly.index, yearly.mediana)

A agregação reduz marcas sem esconder toda a dispersão: a linha mostra a mediana e a faixa mostra os 50% centrais. Ainda assim, examine grupos pequenos antes de resumir.

Acessibilidade faz parte da precisão

Comparação entre identificação apenas por cor e identificação redundante por cor, tipo de linha e rótulo.
Código do exemplo
ax.plot(ano, drama, color=azul, linestyle="-", label="Drama")
ax.plot(ano, comedia, color=laranja, linestyle="--", label="Comédia")

Não dependa apenas da cor: combine contraste, traço, forma ou rótulo. Fonte legível, descrição alternativa e linguagem direta também fazem parte da precisão.

Ética: o gráfico também argumenta

Escolhas de filtro, escala, ordem e destaque influenciam a conclusão.

Não existe visualização neutra, mas existe visualização transparente e auditável.

Um fluxo de trabalho reproduzível

flowchart LR
  P[Pergunta] --> D[Dados]
  D --> E[Esboço]
  E --> V[Verificação]
  V --> C[Contexto]
  C --> R[Revisão]

flowchart LR
  P[Pergunta] --> D[Dados]
  D --> E[Esboço]
  E --> V[Verificação]
  V --> C[Contexto]
  C --> R[Revisão]

Revise o gráfico na escala em que será apresentado.

Checklist antes de publicar

  1. A mensagem corresponde aos dados?
  2. Escalas e unidades são honestas?
  3. O gráfico permite a comparação desejada?
  4. Há contexto suficiente para interpretar?
  5. Cor e destaque têm função?
  6. O resultado funciona sem explicação oral?

Crítica guiada

Ao encontrar um gráfico ruim, não pare em “está feio”. Pergunte:

  • qual tarefa ficou difícil?
  • qual canal visual falhou?
  • que contexto está ausente?
  • que representação resolveria o problema?

Síntese

  • Defina a pergunta e a comparação.
  • Preserve escalas e referências honestas.
  • Use ordem, posição e destaque para reduzir esforço.
  • Acrescente contexto sem poluir.
  • Mostre limitações, filtros e incerteza.

O bloco de visualização fecha aqui

Nas aulas 03–06, passamos de tabelas e tipos de dados para EDA e visualização. O Módulo 1 continua na Aula 07 com tendências centrais e dispersão.

Agora sabemos estruturar, investigar e comunicar padrões; na próxima aula, vamos aprender a resumir centro e dispersão sem perder de vista a distribuição.

Material de apoio