Gráficos que respondem à pergunta
Escolher barras, linhas, histogramas e dispersão, com eixos, unidades e comparação correta.
Um gráfico deve tornar uma comparação mais fácil de ler. Antes de desenhar, identifica a variável do eixo horizontal, a medida do eixo vertical e a operação que produziu os pontos.
Escolher o gráfico
| Pergunta | Gráfico | O que observar |
|---|---|---|
| Que cidade vendeu mais? | Barras | Altura por categoria |
| Como evoluiu a faturação diária? | Linha | Ordem temporal e intervalos |
| Como se distribuem as quantidades? | Histograma | Frequência dentro de intervalos |
| Quantidade e total estão relacionados? | Dispersão | Um ponto por observação |
| Como varia a quantidade por cidade? | Caixa de bigodes | Mediana e dispersão, conforme a convenção |
Num gráfico de barras, cada barra representa uma categoria. Num histograma, cada barra representa um intervalo de valores. Mudar os intervalos pode mudar a impressão da distribuição, por isso indica como os escolheste.
Figura e eixos
Matplotlib separa a figura, Figure, dos eixos, Axes. Usa fig, ax = plt.subplots() e chama métodos em ax para deixar claro em que gráfico estás a trabalhar.
import pandas as pd
import matplotlib.pyplot as plt
vendas = pd.DataFrame({"cidade": ["Porto", "Porto", "Braga", "Braga"], "quantidade": [2, 3, 5, 4]})
totais = vendas.groupby("cidade")["quantidade"].sum().sort_index()
print(totais.to_dict())
fig, ax = plt.subplots(figsize=(5, 3))
barras = ax.bar(totais.index, totais.values)
ax.bar_label(barras)
ax.set_ylabel("Unidades vendidas")
ax.set_xlabel("Cidade")
ax.set_title("Total de unidades por cidade")
ax.set_ylim(0, 10)
fig.tight_layout()
plt.show()Dados de entrada
A tabela imprime Braga 9 e Porto 5. Braga vendeu quatro unidades a mais. Não são nove vendas: estamos a somar quantidades, não a contar linhas.
O gráfico usa o zero como origem para que as alturas sejam proporcionais às quantidades. Um eixo que começa em 4 exageraria a diferença visual. Os rótulos nas barras permitem conferir o valor sem estimar a altura.
Linhas e intervalos
Ordena as datas antes de desenhar uma linha. Uma linha liga pontos e sugere uma evolução entre eles; não transformes categorias sem ordem numa curva.
fig, ax = plt.subplots()
ax.plot(datas, totais, marker="o")
ax.set_ylabel("Faturação diária, euros")
fig.autofmt_xdate()
Se faltam dias, decide se a linha deve interromper-se ou se existe uma regra que permite estimar. Um dia sem observação não é automaticamente um dia com zero vendas.
Histograma e dispersão
ax.hist(quantidades, bins=[0, 2, 4, 6, 8])
ax.set_xlabel("Unidades por venda")
ax.set_ylabel("Número de vendas")
Os intervalos contam observações. Confere as convenções dos limites: em histogramas NumPy/Matplotlib, os intervalos são normalmente fechados à esquerda e abertos à direita, com o último a incluir também o extremo direito.
ax.scatter(quantidades, totais) põe um ponto por venda. Uma associação entre duas variáveis não prova uma causa. Se total é quantidade vezes preço, parte da associação vem da própria definição.
Vários gráficos e exportação
fig, axes = plt.subplots(1, 2) cria dois eixos. Indica os rótulos e conserva escalas comparáveis quando pedes ao leitor que compare alturas. Uma legenda identifica séries, não substitui unidades nos eixos.
fig.savefig("vendas.png", dpi=150, bbox_inches="tight") guarda a figura localmente. Usa SVG para gráficos vetoriais quando esse formato é adequado ao destino. Guarda ou mostra a figura depois de definir rótulos e layout; plt.close(fig) liberta a figura quando já não é necessária.
Gráficos de pandas
Uma Series ou DataFrame tem métodos .plot que usam normalmente Matplotlib. Para a Series totais, totais.plot.bar() desenha uma barra por cidade. df.plot(x="data", y="total") escolhe as colunas dos dois eixos. O objeto devolvido é um Axes, que podes usar para rótulos e unidades.
| Método | Resultado | Condição de leitura |
|---|---|---|
.plot.bar() | Barras verticais por categoria | Confere se são valores brutos ou agregados |
.plot.bar(stacked=True) | Barras empilhadas | Cada segmento acrescenta ao total da categoria |
.plot.barh() | Barras horizontais | Útil com nomes compridos |
.plot.area() | Áreas, normalmente empilhadas | Indica se cada série é componente de um total |
.plot.box() | Caixa de bigodes por coluna | Compara quartis e dispersão |
.plot.hexbin(x="x", y="y", gridsize=25) | Contagem de pontos por hexágono | A cor precisa de uma escala de contagens |
.plot.pie() | Frações de um total | Usa valores não negativos e um total positivo |
.plot.kde() | Densidade estimada | Exige SciPy e depende do alisamento |
Num gráfico empilhado, o topo da última série representa a soma das componentes. Para comparar uma componente entre categorias, a posição sem origem comum pode dificultar a leitura; barras lado a lado podem ser mais adequadas.
Na caixa de bigodes habitual de Matplotlib, a caixa vai do primeiro ao terceiro quartil e a linha interior marca a mediana. Os bigodes alcançam observações dentro de 1,5 vezes a amplitude interquartil para além da caixa. Pontos exteriores são observações fora desses limites, não necessariamente erros. Confere a convenção do exercício.
Uma estimativa KDE desenha uma densidade suave a partir das observações. A altura não é uma contagem e a probabilidade corresponde a uma área sob a curva. A escolha da largura de banda muda o alisamento. Com poucas observações, não interpretes cada pico como um grupo real.
Acumular e desenhar uma janela móvel
cumsum() substitui cada posição pela soma até essa posição. Se os valores forem 2, 3 e 4, o resultado acumulado é 2, 5 e 9. Nos exemplos com valores aleatórios dos notebooks, isto transforma incrementos numa trajetória acumulada. Não são vendas medidas.
Uma semente torna a sequência aleatória reproduzível no mesmo gerador. rng = np.random.default_rng(123456) cria um gerador explícito; os seus resultados não precisam de coincidir com os de np.random.seed, que usa outra interface. Uma distribuição uniforme de random e uma normal de standard_normal também representam hipóteses diferentes.
media_movel = valores.rolling(20).mean()
desvio_movel = valores.rolling(20).std()
ax.plot(valores.index, valores)
ax.plot(media_movel.index, media_movel)
ax.fill_between(
valores.index,
media_movel - 2 * desvio_movel,
media_movel + 2 * desvio_movel,
alpha=0.2,
)
Com a janela inteira exigida por defeito, os primeiros 19 resultados da média de 20 observações ficam em falta. A banda entre média e dois desvios de cada lado descreve dispersão local; não é automaticamente um intervalo de confiança para a média nem uma previsão garantida.
Exercícios
Escolher uma distribuição
Queres mostrar como se distribuem quantidades por venda em intervalos numéricos. Que gráfico responde diretamente ao pedido?
Primeira pista
Mais uma pista
O eixo horizontal deve representar intervalos da quantidade.
Ver solução
Escolhe um histograma e indica os limites dos intervalos. O eixo vertical conta vendas, não unidades vendidas.
- Histograma. Conta observações em intervalos de valores da quantidade.
- Linha por cidade. Sugere uma ordem entre categorias e não mostra a distribuição em intervalos.
- Barras da faturação por cidade. Responde a uma comparação de totais por categoria, outra pergunta.
Erros frequentes
Confundir uma barra por categoria com uma barra por intervalo numérico.
Justificar a escala
Braga vendeu 9 unidades e Porto 5. Explica por que começar o eixo de barras em 4 muda a comparação visual.
Primeira pista
Mais uma pista
Com origem 4, as alturas visíveis são 9 - 4 e 5 - 4.
Ver solução
As alturas seriam 5 e 1, sugerindo uma razão de cinco para um. Os valores reais têm razão 9/5 = 1,8. Usa zero para conservar a proporção das alturas.
Confere a tua resposta:
- Indiquei que as alturas passam a representar 5 e 1 unidades acima da origem escolhida.
- Expliquei que isso exagera a razão visual entre os valores.
- Conservei rótulos, unidades e zero na origem para a comparação de alturas.
Erros frequentes
Concluir que o rótulo numérico por si só elimina a distorção visual.