Ana içeriğe geç
Kesit Python
İleri · Veri analizi ve bilimi

İleri görselleştirme: seaborn, plotly ve doğru grafik

seaborn ile istatistiksel grafikler (histplot, boxplot, violinplot, heatmap, pairplot, FacetGrid), plotly express ile etkileşim, altair, grafik seçimi rehberi, renk ve erişilebilirlik, yanıltıcı grafikler; alıştırmalar matplotlib ile.

Uygulamada aç, 32 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

Doğru soruya doğru grafik

matplotlib ile görselleştirme dersinde Figure/Axes yapısını, çizgi, çubuk ve histogramı kurdun. Gerçek işte bunun bir üst katı gelir: "Sepet tutarı şehirlere göre nasıl dağılıyor?" sorusu için kutu grafiği, "Hangi değişkenler birlikte hareket ediyor?" için korelasyon ısı haritası, "Her şehirde trend aynı mı?" için küçük çoklu grafikler (small multiples), yöneticinin fareyle üzerine gelip değer okuyacağı etkileşimli bir grafik. Bu işler için Python dünyasında iki yıldız vardır: seaborn (istatistiksel grafik, matplotlib üstüne kurulu) ve plotly (etkileşimli, tarayıcıda çalışan grafik).

Ama araçtan önemlisi karardır: hangi soru, hangi grafik, hangi renk ve hangi eksen. Kötü seçilmiş bir grafik, doğru veriyle yanlış karar aldırır. Bu ders hem araçları hem de bu karar rehberini verir.

Bu ortamdaseaborn ve plotly bu sayfada çalışır, ilk kullanımda iner: seaborn yaklaşık 295 KB, plotly yaklaşık 9,7 MB. Gri kutulardaki kodları bilgisayarında ya da kod editöründe dene; dersin sonundaki "seaborn ve plotly'yi çalıştır" bölümünde gerçek görevler var. İlk alıştırmalar aynı grafikleri matplotlib ile kurar, çünkü seaborn onun üstünde çalışır ve yapıyı görmek öğreticidir.

seaborn: tidy veri ile tek satırlık grafikler

seaborn'un sihri şudur: veriyi uzun (tidy) biçimde verirsin, sütun adlarını eksenlere ve renk (hue), panel (col, row) gibi görsel kanallara bağlarsın; gruplama, renk paleti ve gösterge (legend) kendiliğinden gelir.

import seaborn as sns
import matplotlib.pyplot as plt

sns.set_theme(style="whitegrid")              # tutarlı tema
# df: siparis_id, sehir, kategori, adet, tutar, segment

sns.histplot(data=df, x="tutar", bins=20, kde=True)               # dağılım
sns.boxplot(data=df, x="sehir", y="tutar")                        # gruplara göre dağılım
sns.violinplot(data=df, x="sehir", y="tutar", inner="quartile")   # dağılımın şekli
sns.scatterplot(data=df, x="adet", y="tutar", hue="segment")      # ilişki + renkle grup
sns.pairplot(df[["adet", "tutar", "indirim"]])                    # tüm ikili ilişkiler

kor = df[["adet", "tutar", "indirim"]].corr()
sns.heatmap(kor, annot=True, fmt=".2f", cmap="vlag", vmin=-1, vmax=1)

g = sns.FacetGrid(aylik, col="sehir", col_wrap=3, sharey=True)    # küçük çoklu grafikler
g.map_dataframe(sns.lineplot, x="ay", y="ciro")
plt.show()
Kendi bilgisayarında denepip install seaborn komutuyla kurulur (matplotlib ve pandas'ı da getirir). Jupyter'de grafik hücrenin altında görünür; betikte plt.show() pencere açar, plt.savefig("grafik.png", dpi=150) dosyaya yazar.

seaborn fonksiyonları iki türdür. Eksen düzeyi (axes-level: histplot, boxplot, scatterplot, heatmap) tek bir Axes'e çizer ve ax= parametresi alır; matplotlib'in alt grafiklerine yerleştirebilirsin. Figür düzeyi (figure-level: relplot, displot, catplot, pairplot, FacetGrid) kendi figürünü kurar ve col=/row= ile panellere böler.

Aynı grafikleri matplotlib ile kurmak

seaborn'un yaptığı işin altında matplotlib nesneleri vardır. Gruplara göre kutu ve keman grafiği (violin plot) için veriyi önce gruplara bölmek yeterlidir.

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame({
    "sehir": ["Ankara"] * 6 + ["İzmir"] * 6 + ["Bursa"] * 6,
    "tutar": [120, 340, 95, 410, 260, 1500, 80, 150, 210, 175, 140, 190, 300, 320, 280, 350, 310, 900],
})
sehirler = sorted(df["sehir"].unique())
gruplar = [df.loc[df["sehir"] == s, "tutar"].to_numpy() for s in sehirler]

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), sharey=True)
ax1.boxplot(gruplar)
ax1.set_xticks(range(1, len(sehirler) + 1), sehirler)
ax1.set_title("Kutu grafiği")
ax1.set_ylabel("Sepet tutarı (TL)")
ax2.violinplot(gruplar, showmedians=True)
ax2.set_xticks(range(1, len(sehirler) + 1), sehirler)
ax2.set_title("Keman grafiği")
fig.savefig("dagilim.png", dpi=100, bbox_inches="tight")
plt.close(fig)

print("eksen etiketleri:", [t.get_text() for t in ax1.get_xticklabels()])
print(df.groupby("sehir")["tutar"].describe()[["min", "25%", "50%", "75%", "max"]])

Kutu grafiği beş sayıyı (min, Q1, medyan, Q3, maks) ve aykırı değerleri gösterir: Ankara'daki 1500 TL'lik sipariş ayrı bir nokta olarak görünür. Keman grafiği ise dağılımın şeklini (iki tepeli mi, çarpık mı) da gösterir.

Korelasyon ısı haritası (heatmap) da bir imshow ve her hücreye bir text'tir. Dikkat: imshow'da x ekseni sütun, y ekseni satır indeksidir; hücre yazısı ax.text(j, i, ...) ile yazılır.

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame({
    "adet": [1, 3, 2, 5, 4, 6, 2, 8],
    "tutar": [120, 310, 260, 480, 450, 640, 190, 820],
    "indirim": [0, 5, 0, 10, 10, 15, 0, 20],
    "teslim_gun": [3, 2, 4, 1, 2, 1, 5, 1],
})
kor = df.corr().round(2)
fig, ax = plt.subplots(figsize=(5, 4))
im = ax.imshow(kor.to_numpy(), cmap="RdBu_r", vmin=-1, vmax=1)
ax.set_xticks(range(len(kor.columns)), kor.columns, rotation=45)
ax.set_yticks(range(len(kor.index)), kor.index)
for i in range(len(kor.index)):
    for j in range(len(kor.columns)):
        ax.text(j, i, f"{kor.iloc[i, j]:.2f}", ha="center", va="center")
fig.colorbar(im, ax=ax)
ax.set_title("Korelasyon matrisi")
plt.close(fig)
print(kor)
print("hücre yazısı:", len(ax.texts), "| renk skalası:", im.get_clim())
Diverging skala ve sabit sınırKorelasyon -1 ile 1 arasındadır ve 0 anlamlı bir orta noktadır. Bu yüzden iki uçlu (diverging) bir palet (RdBu, vlag) ve vmin=-1, vmax=1 kullan. Sınırı vermezsen renkler verideki en küçük ve en büyük değere göre gerilir; 0.3'lük zayıf bir korelasyon koyu kırmızı görünebilir.

Grafik için veri hazırlamak

Görselleştirme işinin yarısı pandas'tır. seaborn ve plotly uzun biçim ister; ayların takvim sırasında çizilmesi için sıralı kategori gerekir; şehirleri ciroya göre sıralamak okunurluğu artırır. (melt'i ileri pandas dersinde gördün.)

import pandas as pd

genis = pd.DataFrame({
    "sehir": ["Ankara", "İzmir", "Bursa"],
    "Oca": [1200, 800, 600], "Şub": [1500, 950, 720], "Mar": [1400, 1010, 690],
})
aylar = ["Oca", "Şub", "Mar"]
uzun = genis.melt(id_vars="sehir", var_name="ay", value_name="ciro")
uzun["ay"] = pd.Categorical(uzun["ay"], categories=aylar, ordered=True)
print(uzun.sort_values(["sehir", "ay"]).head(4))

# Çubuk grafik için: büyükten küçüğe sırala, payı hesapla
toplam = uzun.groupby("sehir")["ciro"].sum().sort_values(ascending=False)
pay = (toplam / toplam.sum() * 100).round(1)
print(pd.DataFrame({"ciro": toplam, "pay_%": pay}))

plotly express: etkileşimli grafik

plotly grafiği bir HTML/JavaScript nesnesidir: fareyle üzerine gelince değer görünür (hover), yakınlaştırılır, seriler göstergeden açılıp kapanır. Panolar (dashboard), Jupyter keşfi ve e-postayla gönderilecek tek dosyalık raporlar için idealdir. plotly.express (px) seaborn'a çok benzer bir arayüz sunar.

import plotly.express as px

fig = px.line(aylik, x="ay", y="ciro", color="sehir", markers=True,
              title="Anadolu Market aylık ciro")
fig.show()                                      # tarayıcıda / Jupyter'de etkileşimli

fig = px.scatter(df, x="adet", y="tutar", color="segment",
                 hover_data=["siparis_id", "sehir"], facet_col="kategori")
fig.write_html("siparisler.html")               # tek dosya, her tarayıcıda açılır

fig = px.bar(toplam_df, x="sehir", y="ciro", text_auto=".2s")
fig.update_layout(yaxis_title="Ciro (TL)")
Kendi bilgisayarında denepip install plotly. Jupyter'de fig.show() hücrede etkileşimli grafik açar; PNG olarak kaydetmek için ayrıca pip install kaleido gerekir. Web panosu kurmak istersen plotly'nin Dash kütüphanesine ya da Streamlit'e bak.

altair ise "grafiğin dilbilgisi" (grammar of graphics) yaklaşımını Python'a getirir: veriyi, işareti (mark) ve kodlamayı (encode) bildirirsin.

import altair as alt   # pip install altair

alt.Chart(df).mark_point().encode(
    x="adet", y="tutar", color="segment", tooltip=["siparis_id", "sehir"]
).interactive()

Grafik seçimi rehberi

Grafiği seçerken "hangi veriye sahibim?" diye değil "hangi soruya cevap arıyorum?" diye başla.

SoruGrafikseaborn / plotly
Zamanla nasıl değişti?Çizgilineplot / px.line
Hangi grup daha büyük?Sıralı çubuk (yatay, uzun etiketlerde)barplot / px.bar
Değerler nasıl dağılıyor?Histogram, yoğunlukhistplot, kdeplot / px.histogram
Gruplar arasında dağılım farkı?Kutu, kemanboxplot, violinplot / px.box
İki sayısal değişken ilişkili mi?Saçılım (dağılım) grafiğiscatterplot / px.scatter
Birçok değişkenin ilişkisi?Korelasyon ısı haritası, pairplotheatmap, pairplot / px.imshow
Parçanın bütüne oranı?Yığılmış çubuk; en fazla 3-4 dilimse pastahistplot(multiple="stack") / px.bar
Her grupta desen aynı mı?Küçük çoklu grafiklerFacetGrid, relplot(col=) / facet_col

Renk ve erişilebilirlik

Rengin üç işi vardır ve her biri farklı palet ister: kategorik (şehirler: birbirinden ayrışan renkler), sıralı (sequential: az → çok, tek renk tonu, örn. viridis), iki uçlu (diverging: negatif ↔ pozitif, ortası nötr). Erkeklerin yaklaşık %8'inde bir tür renk görme farklılığı vardır; en yaygını kırmızı-yeşil ayrımıdır. "Kırmızı kötü, yeşil iyi" grafiği bu izleyiciler için tek renktir.

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

aylar = ["Oca", "Şub", "Mar", "Nis"]
seriler = {"Ankara": [12, 15, 14, 18], "İzmir": [8, 9, 10, 11], "Bursa": [6, 7, 7, 9]}
renkler = ["#0072B2", "#E69F00", "#009E73"]          # Okabe-Ito: renk körü dostu
isaretler = ["o", "s", "^"]
cizgiler = ["-", "--", ":"]

fig, ax = plt.subplots(figsize=(7, 4))
for (sehir, y), r, m, c in zip(seriler.items(), renkler, isaretler, cizgiler):
    ax.plot(aylar, y, color=r, marker=m, linestyle=c, label=sehir)
    ax.annotate(sehir, (len(aylar) - 1, y[-1]), xytext=(5, 0), textcoords="offset points")
ax.set_title("Aylık ciro (bin TL)")
ax.legend()
plt.close(fig)
print([(l.get_label(), l.get_marker(), l.get_linestyle()) for l in ax.lines])

Yanıltıcı grafikler

Çoğu yanıltıcı grafik kötü niyetle değil, varsayılan ayarla üretilir. En sık beş tuzak:

TuzakEtkisiDoğrusu
Çubuk grafikte y ekseni 0'dan başlamıyor%2'lik fark 5 kat görünürÇubukta eksen 0'dan başlar; küçük farkı göstermek için çizgi ya da fark grafiği
Çift y ekseniÖlçek seçimiyle her iki seri "birlikte" gösterilebilirİki ayrı panel ya da endekslenmiş (başlangıç=100) seri
Seçilmiş zaman aralığıDüşüşün ortasından başlayan grafik "toparlanma" anlatırTam dönem ya da yıllık kıyas
3B ve çok dilimli pastaAçı ve hacim yanlış algılanırSıralı yatay çubuk
Farklı genişlikte histogram kutularıDağılımın şekli değişirEşit kutu, birkaç kutu sayısını dene
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

sube = ["Kadıköy", "Çankaya"]
ciro = [1000, 1020]                     # yalnızca %2 fark
fig, (a, b) = plt.subplots(1, 2, figsize=(8, 3))
a.bar(sube, ciro); a.set_ylim(990, 1025); a.set_title("Yanıltıcı")
b.bar(sube, ciro); b.set_ylim(0, 1100); b.set_title("Dürüst")
plt.close(fig)

def gorunen_oran(ax):
    alt = ax.get_ylim()[0]
    return round((ciro[1] - alt) / (ciro[0] - alt), 2)

print("gerçek oran:", round(ciro[1] / ciro[0], 2))
print("yanıltıcı grafikte görünen:", gorunen_oran(a))
print("dürüst grafikte görünen:", gorunen_oran(b))
Gerçek işteAnalist raporlarının büyük kısmı yönetici sunumuna ya da bir panoya gider. İyi ekiplerin ortak alışkanlıkları: her grafiğin başlığı bir bulgu cümlesidir ("İzmir'de sepet tutarı Mart'ta %12 arttı"), eksenlerde birim yazar, renkler kurum paletinden ve renk körü dostudur, aynı rapordaki grafikler aynı temayı (sns.set_theme ya da matplotlib rcParams) kullanır. Keşif aşamasında seaborn ve pairplot, sunumda sade matplotlib/seaborn PNG'leri, paylaşılan panoda plotly tercih edilir.
Derinleş

seaborn ve plotly'yi çalıştır

İki kütüphane de tarayıcıda çalışır. İlk kullanımda inerler: seaborn yaklaşık 295 KB, plotly yaklaşık 9,7 MB. plotly büyüktür; mobil veride uzun sürebilir. fig.show() grafiği çıktının içinde etkileşimli gösterir, ayrıca 4,5 MB'lık bir çizim kitaplığını taşır.

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

d = pd.DataFrame({"sehir": ["Van", "İzmir", "Van", "İzmir"], "tutar": [10, 40, 30, 20]})
sns.barplot(data=d, x="sehir", y="tutar", estimator="sum", errorbar=None)
plt.show()
SoruGrafik
Dağılım nasıl?histplot, boxplot
Grup karşılaştırmasıbarplot, countplot
Zaman içindelineplot
İki sayı ilişkisiscatterplot, regplot
Gruba göre küçük panolarFacetGrid, catplot
Etkileşim gerekiyorplotly
ÇalışmayanStreamlit ve Dash sunucu ister, burada çalışmaz. plotly'nin PNG kaydı (kaleido) da burada yoktur; HTML olarak kaydedersin.
Derinleş

Derinleş: grafikten panoya ve rapora

Tek grafik bir soruyu cevaplar; pano (dashboard) ise bir ekibin her sabah baktığı soruları tek ekranda toplar. Bu bölümde Python'la pano kurmanın iki popüler yolunu (Streamlit ve Dash), iyi pano tasarımının ilkelerini, veri hikâyesinin akışını ve e-postayla gönderilebilen tek dosyalık HTML rapor üretmeyi göreceksin. Rakamlar Kesit Power BI'ın kesit_bi.Satislar tablosundan (1620 satış satırı) geliyor; aynı panoyu orada Power BI ile kuruyorsun.

Önce rakamlar: KPI hesabı

import veri
import pandas as pd

s = veri.oku("kesit_bi", "Satislar")
s = s[s["Durum"] != "İptal"].copy()
s["YilAy"] = s["Tarih"].str[:7]

def kpi(df):
    ciro = df["Tutar"].sum()
    siparis = df["SiparisNo"].nunique()
    return {"ciro": round(ciro), "siparis": siparis, "sepet": round(ciro / siparis),
            "marj": round(1 - df["Maliyet"].sum() / ciro, 3)}

bu = kpi(s[s["YilAy"] == "2024-12"])
onceki = kpi(s[s["YilAy"] == "2024-11"])
for ad in bu:
    degisim = (bu[ad] - onceki[ad]) / onceki[ad]
    print(f"{ad:<8} {bu[ad]:>10,}  ({degisim:+.1%} geçen aya göre)")

Streamlit: betikten uygulamaya

Streamlit'te uygulama yukarıdan aşağı çalışan bir betiktir; kullanıcı bir filtreyi her değiştirdiğinde betik baştan çalışır. Bu yüzden veriyi okuyan fonksiyon @st.cache_data ile önbelleğe alınır. pip install streamlit ve streamlit run pano.py ile kendi bilgisayarında dene:

# pano.py
import pandas as pd
import plotly.express as px
import streamlit as st

st.set_page_config(page_title="Kesit Satış Panosu", layout="wide")

@st.cache_data
def yukle():
    s = pd.read_csv("Satislar.csv")
    return s[s["Durum"] != "İptal"].assign(YilAy=lambda d: d["Tarih"].str[:7])

s = yukle()
kanallar = st.sidebar.multiselect("Kanal", sorted(s["Kanal"].unique()), default=sorted(s["Kanal"].unique()))
ay = st.sidebar.selectbox("Ay", sorted(s["YilAy"].unique(), reverse=True))
f = s[s["Kanal"].isin(kanallar)]

k1, k2, k3 = st.columns(3)
bu, onceki = f[f["YilAy"] == ay], f[f["YilAy"] < ay]
k1.metric("Ciro", f"{bu['Tutar'].sum():,.0f} TL")
k2.metric("Sipariş", bu["SiparisNo"].nunique())
k3.metric("Ortalama sepet", f"{bu['Tutar'].sum() / max(bu['SiparisNo'].nunique(), 1):,.0f} TL")

aylik = f.groupby("YilAy", as_index=False)["Tutar"].sum()
st.plotly_chart(px.line(aylik, x="YilAy", y="Tutar", title="Aylık ciro"), use_container_width=True)
st.dataframe(bu.groupby("Kanal")["Tutar"].sum().sort_values(ascending=False))

Dash: bileşen ve geri çağırma

Dash (Plotly'nin çatısı) farklı bir model kullanır: sayfa düzeni bileşen ağacıdır, etkileşim ise girdi ve çıktıları açıkça bağlayan geri çağırma (callback) fonksiyonlarıyla kurulur. Yalnız etkilenen bileşen yeniden hesaplanır; daha çok kod ister ama büyük, çok sayfalı panolarda daha kontrollüdür (pip install dash).

from dash import Dash, Input, Output, dcc, html
import plotly.express as px

app = Dash(__name__)
app.layout = html.Div([
    dcc.Dropdown(sorted(s["Kanal"].unique()), "Mobil", id="kanal"),
    dcc.Graph(id="grafik"),
])

@app.callback(Output("grafik", "figure"), Input("kanal", "value"))
def guncelle(kanal):
    aylik = s[s["Kanal"] == kanal].groupby("YilAy", as_index=False)["Tutar"].sum()
    return px.bar(aylik, x="YilAy", y="Tutar")

app.run(debug=True)
StreamlitDashPower BI
ÖğrenmeEn kolayOrta (callback)Tıkla-sürükle + DAX
Ne zaman?Hızlı prototip, ML demosu, iç araçÖzelleştirilmiş, büyük web panosuKurumsal raporlama, yetki, paylaşım
YayınStreamlit Community Cloud, sunucuHerhangi bir Python web sunucusuPower BI Hizmeti

Pano tasarım ilkeleri

  • Üstte KPI kartları: 3–5 ana sayı, her birinin yanında karşılaştırma (geçen ay, hedef) ve yönü. Karşılaştırmasız sayı anlam taşımaz.
  • Soldan sağa, yukarıdan aşağı ayrıntıya: özet → eğilim (zaman çizgisi) → kırılım (kanal, kategori) → ayrıntı tablosu.
  • Filtreler tek yerde (kenar çubuğu ya da üst şerit) ve hepsini etkiler; etkin filtre ekranda görünsün.
  • Ayrıntıya inme (drill-down): bölge → şehir → müşteri gibi hiyerarşide tıklayarak inmek; her seviyede toplamlar tutarlı olmalı.
  • Az renk: gri temel, vurgu yalnız dikkat çekilecek yerde; kırmızı/yeşil yerine erişilebilir palet (bu dersteki renk bölümü).

Veri hikâyesi akışı panodan farklıdır; tek bir mesajı iletir: bağlam ("Aralık'ta ciro geçen yıla göre arttı"), gerilim ("ama iade oranı Mağaza kanalında iki katına çıktı"), çözüm ("Mağaza iade sürecinin incelenmesini öneriyoruz"). Her grafiğin başlığı bir bulgu cümlesi olsun: "Aylık ciro" değil, "Ciro Aralık'ta yılın zirvesine çıktı".

Tek dosyalık HTML rapor

Herkesin tarayıcısı vardır; sunucu kurmadan paylaşılabilecek en pratik çıktı tek bir HTML dosyasıdır. Grafikleri base64 ile gömersen dosya dışa bağımlılık olmadan açılır. plotly ile etkileşimli rapor için fig.to_html(full_html=False, include_plotlyjs="cdn") parçalarını birleştirirsin (ya da çevrimdışı için include_plotlyjs=True).

import base64, html, io
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

aylik = s[s["YilAy"].str.startswith("2024")].groupby("YilAy")["Tutar"].sum()
fig, ax = plt.subplots(figsize=(6, 2.5))
ax.plot(aylik.index.str[5:], aylik.values, color="#2a6f97")
ax.set_title("2024'te ciro yıl sonuna doğru yükseldi")
tampon = io.BytesIO()
fig.savefig(tampon, format="png", dpi=100, bbox_inches="tight")
plt.close(fig)
png = base64.b64encode(tampon.getvalue()).decode()

rapor = f"""<!doctype html><html lang="tr"><meta charset="utf-8">
<title>{html.escape("Satış Raporu — Aralık 2024")}</title>
<h1>Aralık 2024</h1><p>Ciro: {bu['ciro']:,} TL</p>
<img alt="Aylık ciro" src="data:image/png;base64,{png}"></html>"""
print(len(rapor), "karakter; görsel gömülü:", "data:image/png;base64," in rapor)
Gerçek işteAnalistler genellikle iki çıktı üretir: kurumsal pano (Power BI, Tableau, Looker) ve haftalık/aylık yorumlu rapor (HTML, PDF, e-posta). Streamlit ise veri bilimi ekiplerinin model sonuçlarını paydaşa göstermesinin en hızlı yoludur. Hangi aracı seçersen seç, mülakatlarda değerlendirilen şey aynıdır: doğru KPI, karşılaştırma, temiz hiyerarşi ve tek cümlelik bulgu.
Uzmanlaş

Grafiğin dilbilgisi

seaborn, plotly express ve altair aynı fikre dayanır: veri + görsel kanal eşlemesi (x, y, renk, boyut, şekil, panel) + işaret (nokta, çizgi, çubuk). Bu dili öğrenince araç değiştirmek kolaylaşır; R'daki ggplot2, Power BI ve Tableau da aynı mantıkla çalışır. Kanalların algı gücü sıralıdır: ortak eksende konum en doğru okunur, sonra uzunluk, açı, alan ve en son renk tonu. Önemli karşılaştırmayı konuma, ikincil bilgiyi renge ver.

Spagetti yerine küçük çoklu grafikler

On şehrin trendini tek grafikte on çizgiyle göstermek okunmaz. Ortak y ekseniyle (sharey=True) on küçük panel, hem her şehrin desenini hem de şehirler arası düzeyi gösterir. Gerekirse her panelde diğer şehirleri soluk gri arka plan olarak çiz.

Çıktı kalitesi

Sunum için dpi=200 PNG ya da ölçeklenebilir SVG/PDF kaydet; bbox_inches="tight" etiketlerin kesilmesini önler. Rapor üretimini fonksiyonlaştır: veri hazırlama, çizim ve kaydetme ayrı fonksiyonlarda olsun ki aynı grafiği her ay tek komutla yeniden üretebilesin ve testleyebilesin.

Alıştırmalara geç (32)