Visualisasi Data
Bulan ini Abbas masuk ke salah satu kemampuan paling impactful seorang data analyst: mengubah angka mentah menjadi cerita visual yang bisa dipahami siapapun. Tidak cukup hanya menemukan insight, Abbas juga harus bisa mengkomunikasikannya dengan chart yang tepat, bersih, dan punya pesan yang jelas.
Mengapa Visualisasi Data Penting
Visualisasi bukan tentang membuat chart yang cantik. Ini tentang membuat pola yang tak terlihat menjadi terlihat, dan mengubah angka menjadi keputusan.
Data mentah berbicara kepada komputer. Visualisasi berbicara kepada manusia. Tugas Abbas sebagai data analyst adalah menjadi penerjemah antara keduanya: mengambil ribuan baris data, menemukan cerita di dalamnya, dan menyajikannya dalam bentuk visual yang langsung dipahami.
- Pola tak terlihat dalam tabel langsung terlihat dalam chart. Ribuan baris angka tidak akan pernah mengungkap tren seefektif satu line chart yang tepat. Otak manusia dirancang untuk memproses pola visual jauh lebih cepat dari teks atau angka. Anscombe's Quartet adalah contoh klasik: empat dataset dengan statistik identik (mean, variance, dan korelasi sama persis) tapi pola scatter plot-nya sangat berbeda satu sama lain.
- Visualisasi adalah alat analisis, bukan hanya alat presentasi. Sebelum membangun model statistik, plot datanya terlebih dahulu. Satu scatter plot sering mengungkap outlier atau pola non-linear yang tidak akan terdeteksi oleh angka korelasi saja. Prinsip "plot first, model second" adalah kebiasaan yang wajib dimiliki analyst profesional.
- Chart yang salah bisa menyesatkan pembuat keputusan. Y-axis yang tidak mulai dari nol, pie chart dengan 12 irisan, atau 3D chart yang mendistorsi proporsi adalah bentuk misinformasi visual yang sering tidak disengaja. Abbas perlu tahu bukan hanya cara membuat chart, tapi cara membuat chart yang jujur terhadap data.
- Setiap jenis data punya chart yang paling cocok. Distribusi butuh histogram atau boxplot. Tren butuh line chart. Proporsi butuh bar chart, bukan pie chart. Relasi antar dua variabel numerik butuh scatter plot. Memilih chart yang salah berarti menyembunyikan informasi, bukan mengungkapnya.
- Python punya ekosistem visualisasi yang lengkap dan saling melengkapi. Matplotlib memberikan kontrol penuh atas setiap piksel. Seaborn menyederhanakan statistical plotting dengan kode minimal. Plotly menghadirkan interaktivitas yang bisa dijelajahi pembaca. Ketiganya perlu dikuasai karena punya use case yang berbeda.
- Prinsip desain bukan optional, tapi wajib. Warna yang tidak accessible bagi penderita buta warna, label yang terpotong karena layout buruk, legend yang ambigu, semuanya membuat chart tidak berguna meskipun data di baliknya sudah dianalisis dengan benar. Edward Tufte merumuskan ini sebagai "data-ink ratio": setiap tetes tinta harus membawa informasi yang bermakna.
- Storytelling adalah skill yang terpisah dari kemampuan teknis. Abbas bisa membuat 20 chart yang secara teknis benar, tapi jika tidak ada narasi yang menghubungkan semuanya, pembaca tidak akan tahu apa kesimpulannya. Chart yang bagus punya satu pesan yang jelas, judul yang aktif (bukan sekadar deskriptif), dan anotasi yang mengarahkan mata pembaca ke titik yang paling penting.
- Interaktivitas mengubah cara orang mengeksplorasi data. Dengan plotly, pembaca bisa hover untuk melihat detail, zoom ke area tertentu, atau menyembunyikan kategori dari legend. Ini membuat satu chart interaktif bisa menggantikan sepuluh chart statis, dan lebih sesuai dengan cara manusia modern mengonsumsi informasi berbasis web.
- Konteks geografis adalah dimensi penting untuk data Indonesia. Data per provinsi, per kabupaten, atau per kecamatan jauh lebih mudah dipahami ketika divisualisasikan di atas peta. Choropleth map adalah alat wajib bagi data analyst yang bekerja dengan data administrasi Indonesia, mulai dari data BPS hingga data kesehatan Kemenkes.
Minggu 1: Matplotlib Dasar
Fondasi semua visualisasi Python. Matplotlib adalah canvas tempat semua library lain dibangun. Abbas perlu memahaminya dari bawah agar bisa mengontrol setiap detail chart.
Anatomi, Chart Dasar, dan Kontrol Penuh atas Canvas
Figure, Axes, dan Subplot Anatomy: Memahami Canvas Matplotlib
Sebelum Abbas bisa membuat chart apapun, Abbas perlu memahami hirarki objek matplotlib. Figure adalah seluruh gambar (kanvas kosong). Axes adalah area plot di dalam Figure (tempat data digambar). Artist adalah setiap elemen visual seperti garis, teks, dan titik yang ada di dalam Axes. Satu Figure bisa punya banyak Axes.
plt.plot(x, y) singkat tapi implisit. Cara kedua, object-oriented style: fig, ax = plt.subplots(); ax.plot(x, y) lebih verbose tapi eksplisit dan lebih mudah dikontrol. Pahami perbedaan keduanya, lalu biasakan memakai OO-style.
fig = plt.figure(figsize=(10, 6), dpi=100). Tambahkan Axes secara eksplisit dengan ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]). Perhatikan parameter posisi: [left, bottom, width, height] dalam satuan fraksi 0 sampai 1 dari Figure. Ubah nilainya dan amati efeknya.
plt.subplots(). Gunakan fig, axes = plt.subplots(2, 3, figsize=(15, 10)) untuk membuat grid 2 baris 3 kolom. Iterasi dengan for ax in axes.flatten() dan isi setiap Axes dengan chart sederhana. Tambahkan plt.tight_layout() agar tidak saling tumpang tindih.
subplot2grid. Buat layout di mana satu chart besar mengambil seluruh kolom kiri, dan dua chart kecil bertumpuk di kolom kanan. Gunakan ax1 = plt.subplot2grid((2, 2), (0, 0), rowspan=2) untuk chart besar dan ax2 = plt.subplot2grid((2, 2), (0, 1)) untuk yang kecil.
ax.set_title() untuk setiap subplot dan fig.suptitle() sebagai judul utama Figure. Export dengan plt.savefig('latihan_hari1.png', dpi=150).
import matplotlib.pyplot as plt
import numpy as np
# Object-oriented style: cara yang disarankan untuk proyek nyata
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
fig.suptitle('Tiga Jenis Chart dalam Satu Figure', fontsize=16, fontweight='bold')
np.random.seed(42)
x_scatter = np.random.rand(50)
y_scatter = np.random.rand(50)
# Subplot 1: Scatter plot
axes[0].scatter(x_scatter, y_scatter, alpha=0.7, color='steelblue', s=60)
axes[0].set_title('Scatter Plot')
axes[0].set_xlabel('Variabel X')
axes[0].set_ylabel('Variabel Y')
# Subplot 2: Line chart
t = np.linspace(0, 2 * np.pi, 200)
axes[1].plot(t, np.sin(t), color='coral', linewidth=2.5)
axes[1].set_title('Line Chart')
axes[1].set_xlabel('Waktu (radian)')
axes[1].set_ylabel('Amplitudo')
# Subplot 3: Bar chart
kategori = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei']
nilai = [23, 45, 31, 52, 38]
axes[2].bar(kategori, nilai, color='mediumseagreen', edgecolor='white')
axes[2].set_title('Bar Chart')
axes[2].set_ylabel('Jumlah')
plt.tight_layout()
plt.savefig('latihan_hari1.png', dpi=150, bbox_inches='tight')
plt.show()
Line Chart yang Baik: Labels, Title, Legend, dan DPI
Line chart adalah chart paling sering dibuat dan paling sering dibuat dengan buruk. Hari ini Abbas akan mempelajari semua detail yang membuat line chart menjadi profesional: dari label axis yang informatif, legend yang tidak menutupi data, hingga resolusi yang cukup untuk presentasi cetak.
ax.set_xlabel('Tanggal', fontsize=12), ax.set_ylabel('Suhu (Celsius)', fontsize=12), dan ax.set_title('Tren Suhu Jakarta, Juni 2024', fontsize=14, fontweight='bold'). Pastikan setiap chart selalu punya ketiga elemen ini.
linestyle (solid, dashed, dotted, dashdot), marker (o, s, ^, D, *), markersize, color (nama warna, hex code, RGB tuple), dan linewidth. Buat dua garis, satu untuk suhu siang dan satu untuk suhu malam, dengan styling yang kontras tapi harmonis.
label= pada setiap ax.plot(), lalu panggil ax.legend(loc='best', framealpha=0.9, edgecolor='lightgray'). Eksperimen dengan posisi: 'upper left', 'lower right'. Untuk legend horizontal, tambahkan ncol=2. Jika legend tetap menutupi data, pindahkan ke luar chart dengan bbox_to_anchor=(1.01, 1).
ax.grid(True, alpha=0.3, linestyle='--', color='gray'). Hilangkan spine kanan dan atas (konvensi chart modern): ax.spines['right'].set_visible(False) dan ax.spines['top'].set_visible(False). Ini adalah teknik yang digunakan di The Economist dan Financial Times.
dpi=72 (web), dpi=150 (layar), dpi=300 (cetak). Lihat perbedaan ukuran file dan ketajaman teks. Selalu gunakan bbox_inches='tight' dan facecolor='white' agar background tidak transparan.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.random.seed(7)
hari = pd.date_range(start='2024-06-01', periods=30, freq='D')
suhu_siang = 32 + np.random.normal(0, 2, 30)
suhu_malam = 26 + np.random.normal(0, 1.5, 30)
fig, ax = plt.subplots(figsize=(12, 5))
ax.plot(hari, suhu_siang,
label='Suhu Siang', color='#e74c3c',
linewidth=2, marker='o', markersize=4)
ax.plot(hari, suhu_malam,
label='Suhu Malam', color='#3498db',
linewidth=2, linestyle='--', marker='s', markersize=4)
ax.set_title('Suhu Siang dan Malam Jakarta, Juni 2024',
fontsize=14, fontweight='bold', pad=15)
ax.set_xlabel('Tanggal', fontsize=11)
ax.set_ylabel('Suhu (Celsius)', fontsize=11)
ax.legend(loc='upper right', framealpha=0.9, edgecolor='lightgray')
ax.grid(True, alpha=0.3, linestyle='--')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
plt.tight_layout()
plt.savefig('suhu_jakarta.png', dpi=300, bbox_inches='tight', facecolor='white')
plt.show()
Menghilangkan spine kanan dan atas adalah konvensi yang digunakan mayoritas publikasi data journalism terkemuka. Ini membuat chart terlihat lebih modern dan mengurangi visual noise. Jadikan ini kebiasaan di setiap chart yang Abbas buat.
Bar Chart, Grouped Bar, dan Stacked Bar: Kapan Pakai Apa
Bar chart adalah chart yang paling sering disalahgunakan. Tiga variannya punya pesan yang sangat berbeda. Memilih yang salah berarti menyembunyikan atau mendistorsi informasi yang penting bagi pembaca.
ax.bar() untuk vertikal. Aturan penting: jika label kategori lebih dari 5 karakter atau jumlah kategori lebih dari 6, gunakan ax.barh() untuk horizontal agar label tidak tumpang tindih. Tambahkan ax.bar_label(container, fmt='%d', padding=3) untuk nilai di atas setiap bar.
x + width * offset. Gunakan grouped bar ketika Abbas ingin membandingkan nilai absolut antar kuartal dalam setiap produk secara berdampingan.
bottom=. Layer pertama mulai dari 0, layer kedua mulai dari nilai layer pertama, dan seterusnya. Stacked bar digunakan ketika total dan komposisi sama pentingnya untuk dipahami pembaca.
import matplotlib.pyplot as plt
import numpy as np
produk = ['Laptop', 'Mouse', 'Keyboard', 'Monitor']
q1 = np.array([120, 340, 210, 85])
q2 = np.array([145, 290, 250, 110])
q3 = np.array([160, 320, 230, 95])
x = np.arange(len(produk))
width = 0.25
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Grouped bar: bandingkan nilai absolut per kuartal
b1 = axes[0].bar(x - width, q1, width, label='Q1', color='#3498db')
b2 = axes[0].bar(x, q2, width, label='Q2', color='#e67e22')
b3 = axes[0].bar(x + width, q3, width, label='Q3', color='#2ecc71')
axes[0].set_xticks(x)
axes[0].set_xticklabels(produk)
axes[0].set_title('Grouped Bar: Perbandingan Antar Kuartal')
axes[0].legend()
axes[0].spines['right'].set_visible(False)
axes[0].spines['top'].set_visible(False)
# Stacked bar: lihat total dan komposisi
axes[1].bar(produk, q1, label='Q1', color='#3498db')
axes[1].bar(produk, q2, bottom=q1, label='Q2', color='#e67e22')
axes[1].bar(produk, q3, bottom=q1+q2, label='Q3', color='#2ecc71')
axes[1].set_title('Stacked Bar: Total dan Komposisi')
axes[1].legend()
axes[1].spines['right'].set_visible(False)
axes[1].spines['top'].set_visible(False)
plt.tight_layout()
plt.show()
Gunakan bar biasa untuk satu nilai per kategori. Gunakan grouped bar untuk membandingkan nilai absolut beberapa seri. Gunakan stacked bar ketika total dan komposisi sama penting. Gunakan 100% stacked bar ketika hanya proporsi yang relevan. Jangan gunakan pie chart jika lebih dari 3 kategori, bar chart selalu lebih akurat untuk mata manusia.
Scatter Plot dan Dimensi ke-3 dengan Size dan Color
Scatter plot adalah chart paling powerful untuk analisis eksplorasi (EDA). Dengan menambahkan size sebagai dimensi ke-3 dan color sebagai dimensi ke-4, satu scatter plot bisa menyampaikan empat variabel sekaligus tanpa kehilangan kejelasan.
alpha=0.6 untuk menangani overplotting ketika banyak titik bertumpuk di area yang sama.
s=array_ukuran di ax.scatter(). Misalnya, ukuran titik mewakili jumlah siswa per sekolah. Karena s adalah luas dalam pixel persegi, skala dengan pangkat dua: jika jumlah siswa 400, s=400/5=80. Tambahkan legend manual yang menjelaskan skala ukuran.
c=array_nilai bersama cmap='RdYlGn' untuk gradien warna merah-kuning-hijau. Tambahkan plt.colorbar(scatter, ax=ax, label='Nama Variabel') sebagai legend warna. Pilih colormap yang tepat: viridis atau plasma untuk data sekuensial, RdBu untuk data diverging yang punya nilai tengah netral.
koef = np.polyfit(x, y, 1). Plot hasilnya dengan ax.plot(x_line, np.poly1d(koef)(x_line), 'k--', alpha=0.6, label='Tren'). Hitung koefisien korelasi Pearson dengan np.corrcoef(x, y)[0,1] dan tampilkan nilainya di dalam chart menggunakan ax.text().
ax.annotate().
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
n = 120
jam_belajar = np.random.uniform(1, 10, n)
nilai = 50 + jam_belajar * 4 + np.random.normal(0, 8, n)
jml_siswa = np.random.uniform(100, 2000, n)
ipm_sekolah = np.random.uniform(60, 85, n)
fig, ax = plt.subplots(figsize=(10, 7))
scatter = ax.scatter(
jam_belajar, nilai,
s = jml_siswa / 10, # ukuran = jumlah siswa
c = ipm_sekolah, # warna = IPM sekolah
cmap = 'RdYlGn',
alpha = 0.75,
edgecolors = 'white', linewidth = 0.5
)
cbar = plt.colorbar(scatter, ax=ax)
cbar.set_label('IPM Sekolah', fontsize=10)
# Garis tren linear
koef = np.polyfit(jam_belajar, nilai, 1)
x_line = np.linspace(jam_belajar.min(), jam_belajar.max(), 100)
ax.plot(x_line, np.poly1d(koef)(x_line),
'k--', alpha=0.6, linewidth=1.5, label='Tren Linear')
r = np.corrcoef(jam_belajar, nilai)[0, 1]
ax.text(0.05, 0.95, f'r = {r:.2f}',
transform=ax.transAxes, fontsize=12, color='#333',
verticalalignment='top')
ax.set_xlabel('Jam Belajar per Hari', fontsize=12)
ax.set_ylabel('Nilai Ujian', fontsize=12)
ax.set_title('Hubungan Jam Belajar vs Nilai Ujian\n(ukuran titik = jumlah siswa, warna = IPM)',
fontsize=13)
ax.legend()
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
plt.show()
Latihan Integratif: 5 Chart dari Dataset Nilai Ujian Nasional
Hari ini Abbas mengintegrasikan semua yang sudah dipelajari minggu ini. Target: membuat 5 chart berbeda yang menjawab 5 pertanyaan berbeda dari satu dataset, semuanya dikemas dalam satu Figure rapi yang siap dipresentasikan.
df.info(), df.describe(), df.isnull().sum(), dan df['tahun'].value_counts(). Pahami strukturnya dulu.
ax.text() untuk memudahkan pembaca. Highlight titik penurunan terbesar dengan anotasi panah menggunakan ax.annotate().
sort_values(). Warnai bar Matematika merah karena paling rendah, sisanya abu-abu. Tambahkan label nilai di ujung setiap bar dengan ax.bar_label(). Ini adalah teknik "single-color highlight" yang mengarahkan perhatian pembaca.
x + np.random.uniform(-0.1, 0.1, n)) agar titik tidak bertumpuk. Warnai berdasarkan jenis sekolah. Tambahkan garis rata-rata horizontal per kelompok akreditasi.
gridspec: chart 1 lebar penuh di atas, chart 2 dan 3 berdampingan di tengah, chart 4 dan 5 berdampingan di bawah. Export sebagai laporan-un.png dengan dpi=300. Ini adalah format laporan visual siap pakai.
Setelah selesai membuat 5 chart, tambahkan anotasi teks pada setiap chart yang menyatakan satu insight utama dalam satu kalimat. Contoh yang benar: "Nilai matematika turun 8 poin sejak 2019." Contoh yang salah: "Grafik ini menunjukkan nilai matematika dari 2015 sampai 2023." Insight bukan deskripsi, insight adalah interpretasi.
Minggu 2: Seaborn untuk Statistik Visual
Seaborn dibangun di atas matplotlib dan dirancang khusus untuk statistical visualization. Dengan kode yang lebih singkat, hasilnya jauh lebih cantik dan informatif secara statistik.
Distribusi, Korelasi, dan Eksplorasi Data dengan Seaborn
Histogram dan KDE Plot: Memvisualisasikan Distribusi dengan Cantik
Pertanyaan paling mendasar dalam analisis data adalah: "Bagaimana distribusi dataku?" Histogram dan KDE (Kernel Density Estimate) adalah dua cara untuk menjawabnya. Seaborn membuat keduanya terlihat jauh lebih baik dari matplotlib standar dengan hampir nol konfigurasi tambahan.
import seaborn as sns; sns.set_theme(style='whitegrid'). Load dataset bawaan: df = sns.load_dataset('tips'). Seaborn punya beberapa dataset bawaan seperti tips, iris, titanic, penguins, dan flights. Eksplorasi dengan df.describe() dan df.dtypes sebelum memvisualisasikan.
sns.histplot(). Plot distribusi kolom total_bill. Eksperimen dengan parameter bins: terlalu sedikit (5) kehilangan detail, terlalu banyak (100) terlalu noisy. Gunakan bins='auto' atau bins=30 sebagai titik awal yang baik. Tambahkan kde=True untuk overlay kurva KDE di atas histogram.
sns.kdeplot(). Gunakan fill=True untuk mengisi area di bawah kurva. Overlay dua distribusi dalam satu Axes: distribusi tip pada hari weekday vs weekend. Gunakan label= untuk setiap kurva dan alpha=0.5 agar overlap terlihat.
hue. Gunakan sns.histplot(data=df, x='total_bill', hue='time', element='step') untuk melihat distribusi lunch vs dinner secara berdampingan dalam satu histogram. Parameter element='step' membuat histogram bertangga, lebih mudah dibaca ketika ada overlap.
facet, kanan bawah KDE stacked. Tulis observasi singkat di bawah setiap chart tentang apa yang bisa disimpulkan dari distribusinya.
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme(style='whitegrid', palette='muted')
df = sns.load_dataset('tips')
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
# Histogram + KDE overlay
sns.histplot(data=df, x='total_bill', bins=30, kde=True,
color='steelblue', ax=axes[0])
axes[0].set_title('Distribusi Total Tagihan (Histogram + KDE)')
axes[0].set_xlabel('Total Tagihan ($)')
# KDE per kelompok: lunch vs dinner
sns.kdeplot(data=df, x='total_bill', hue='time',
fill=True, alpha=0.4, ax=axes[1])
axes[1].set_title('Distribusi Tagihan: Lunch vs Dinner')
axes[1].set_xlabel('Total Tagihan ($)')
plt.tight_layout()
plt.show()
Gunakan histogram ketika Abbas ingin menunjukkan frekuensi aktual dan ukuran sampel penting untuk dipahami. Gunakan KDE ketika Abbas ingin membandingkan bentuk distribusi antar beberapa kelompok karena KDE lebih mudah dibaca ketika ada banyak overlay. Kombinasi keduanya adalah pilihan yang paling informatif.
Boxplot dan Violin Plot: Merangkum Distribusi dalam Satu Chart
Boxplot merangkum distribusi dalam 5 angka: minimum, Q1, median, Q3, dan maximum. Violin plot menggabungkan boxplot dengan KDE sehingga bentuk distribusi penuh terlihat. Keduanya adalah chart wajib untuk membandingkan distribusi antar kelompok kategorikal.
sns.boxplot(data=df, x='day', y='total_bill'). Identifikasi setiap elemen: garis tengah kotak = median, tepi bawah kotak = Q1 (persentil 25), tepi atas kotak = Q3 (persentil 75), whisker bawah = Q1 - 1.5*IQR, whisker atas = Q3 + 1.5*IQR, dan titik-titik di luar whisker = outlier. Latihan membaca tanpa melihat nilai numerik.
sns.violinplot(data=df, x='day', y='total_bill', inner='box'). Parameter inner='box' menampilkan mini boxplot di dalam violin. Bandingkan: boxplot hanya menunjukkan statistik ringkasan, violin plot menunjukkan apakah distribusi bimodal, skewed, atau flat.
hue untuk split per kategori. Tambahkan parameter hue='sex' pada violin plot. Seaborn otomatis membagi setiap violin menjadi dua sisi (split violin) dengan split=True. Ini cara paling kompak untuk membandingkan distribusi 2D (hari x jenis kelamin) dalam satu chart.
sns.stripplot(data=df, x='day', y='total_bill', size=3, alpha=0.4, color='black', jitter=True) di atas violin plot. Ini membuat chart "jujur": pembaca bisa melihat tidak hanya distribusi tapi juga jumlah dan penyebaran titik aktual. Gunakan teknik ini ketika n kecil (kurang dari 200 titik).
order=df.groupby('provinsi')['nilai'].median().sort_values(ascending=False).index. Ini adalah teknik yang membuat chart lebih mudah dibaca karena mata manusia otomatis mencari pola dari kiri ke kanan.
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme(style='whitegrid')
df = sns.load_dataset('tips')
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
# Violin plot dengan inner boxplot
sns.violinplot(data=df, x='day', y='total_bill',
inner='box', palette='muted', ax=axes[0])
axes[0].set_title('Distribusi Tagihan per Hari')
axes[0].set_xlabel('Hari')
axes[0].set_ylabel('Total Tagihan ($)')
# Split violin: breakdown per jenis kelamin
sns.violinplot(data=df, x='day', y='total_bill', hue='sex',
split=True, inner='quart', palette='Set2', ax=axes[1])
# Overlay strip plot untuk titik data aktual
sns.stripplot(data=df, x='day', y='total_bill',
size=2.5, alpha=0.35, color='black',
jitter=True, ax=axes[1])
axes[1].set_title('Split Violin: Tagihan per Hari dan Jenis Kelamin')
plt.tight_layout()
plt.show()
Heatmap untuk Korelasi Matrix: Lihat Semua Hubungan Sekaligus
Heatmap korelasi adalah chart yang wajib dibuat di awal setiap proyek analisis. Dengan satu chart, Abbas bisa melihat hubungan antara semua pasangan variabel numerik sekaligus, sebelum memutuskan mana yang perlu didalami lebih lanjut.
corr_matrix = df.select_dtypes('number').corr(). Buat heatmap dengan sns.heatmap(corr_matrix, cmap='coolwarm', center=0). Parameter center=0 memastikan warna netral (putih) ada di korelasi nol, merah untuk korelasi positif kuat, biru untuk negatif kuat.
annot=True. Gunakan sns.heatmap(..., annot=True, fmt='.2f', annot_kws={'size': 9}). Dengan nilai tercetak di setiap sel, pembaca tidak perlu menginterpretasikan warna saja. Ini membuat heatmap berguna sebagai tabel dan sebagai visualisasi sekaligus.
mask = np.triu(np.ones_like(corr_matrix, dtype=bool)) lalu tambahkan parameter mask=mask ke sns.heatmap(). Ini membuat chart lebih bersih dan mudah dibaca.
'coolwarm' atau 'RdBu_r' (diverging). Untuk data yang semua positif (misalnya confusion matrix): gunakan 'Blues' atau 'viridis' (sequential). Jangan gunakan 'jet' atau 'rainbow' karena tidak perceptually uniform dan bisa menyesatkan pembaca yang buta warna.
from sklearn.datasets import fetch_california_housing. Buat heatmap korelasi dengan mask segitiga atas, annotasi nilai, dan colormap yang tepat. Identifikasi pasangan variabel dengan korelasi lebih dari 0.7 dan tulis interpretasi: "Variabel X dan Y berkorelasi kuat positif, artinya ketika X naik, Y cenderung naik."
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_california_housing
import pandas as pd
data = fetch_california_housing()
df = pd.DataFrame(data.data, columns=data.feature_names)
df['MedHouseVal'] = data.target
corr = df.corr()
mask = np.triu(np.ones_like(corr, dtype=bool))
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
corr,
mask = mask,
cmap = 'coolwarm',
center = 0,
annot = True,
fmt = '.2f',
annot_kws= {'size': 8},
square = True,
linewidths = 0.5,
linecolor = 'white',
ax = ax
)
ax.set_title('Korelasi Matrix: California Housing Dataset',
fontsize=13, fontweight='bold', pad=15)
plt.tight_layout()
plt.show()
Pair Plot: Lihat Semua Relasi Antar Variabel Sekaligus
Pair plot (atau scatter matrix) adalah versi visual dari korelasi matrix, tapi lebih informatif. Alih-alih hanya angka korelasi, pair plot menampilkan distribusi setiap variabel pada diagonal dan scatter plot untuk setiap pasangan variabel di luar diagonal.
df = sns.load_dataset('iris') lalu sns.pairplot(df). Perhatikan strukturnya: diagonal berisi histogram setiap variabel, off-diagonal berisi scatter plot pasangan variabel. Dengan 4 variabel, ada 4x4=16 panel. Untuk dataset dengan lebih dari 8 variabel, pilih subset kolom yang relevan.
sns.pairplot(df, hue='species'). Setiap spesies akan memiliki warna berbeda di semua scatter plot sekaligus. Amati: pasangan variabel mana yang paling baik memisahkan ketiga spesies? Ini adalah pertanyaan kunci dalam pemilihan fitur untuk machine learning.
diag_kind. Bandingkan diag_kind='hist' (histogram pada diagonal) dengan diag_kind='kde' (KDE pada diagonal). KDE lebih informatif untuk melihat apakah distribusi setiap variabel per kelas terpisah atau overlap. Gunakan juga plot_kws={'alpha': 0.5} untuk mengurangi overplotting.
sns.pairplot(df, vars=['col_a', 'col_b', 'col_c'], hue='target'). Pilih kolom berdasarkan korelasi matrix yang sudah dibuat kemarin: fokus pada variabel yang punya korelasi tinggi dengan target atau antar sesama.
hue berdasarkan kategori ocean proximity. Tulis 3 observasi konkret: "Variabel X dan Y punya pola linear yang kuat untuk rumah dekat laut, tapi tidak untuk rumah inland."
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme(style='ticks')
df = sns.load_dataset('penguins').dropna()
g = sns.pairplot(
df,
hue = 'species',
diag_kind= 'kde',
plot_kws = {'alpha': 0.55, 'edgecolors': 'white', 'linewidth': 0.4},
diag_kws = {'fill': True, 'alpha': 0.4},
palette = 'Set2'
)
g.figure.suptitle('Pair Plot: Palmer Penguins Dataset',
fontsize=14, fontweight='bold', y=1.01)
plt.tight_layout()
plt.show()
Pair plot sangat berguna untuk eksplorasi data internal (Abbas memahami data), tapi jarang digunakan dalam laporan untuk non-technical audience karena terlalu padat. Gunakan pair plot untuk menemukan mana pasangan variabel yang menarik, lalu buat scatter plot individual yang lebih bersih untuk presentasi.
Latihan: Analisis Visual Dataset Housing Prices
Hari ini Abbas menggabungkan semua teknik seaborn minggu ini untuk menganalisis California Housing Dataset secara menyeluruh. Target: menghasilkan 6 chart yang masing-masing menjawab satu pertanyaan analitis.
sns.histplot(df['MedHouseVal'], bins=50, kde=True). Identifikasi apakah distribusi normal, skewed kanan, atau bimodal. Tambahkan garis vertikal untuk median dan mean menggunakan ax.axvline(). Interpretasi: distribusi skewed kanan berarti ada sedikit rumah sangat mahal yang menarik mean ke atas.
MedHouseVal dengan warna berbeda. Urutkan variabel berdasarkan korelasi dengan target menggunakan corr['MedHouseVal'].sort_values().
sns.scatterplot(x='MedInc', y='MedHouseVal', alpha=0.15, data=df). Tambahkan garis regresi dengan sns.regplot(). Scatter yang padat di area tertentu menunjukkan pola yang perlu didalami.
sns.boxplot(x=pd.cut(df['HouseAge'], bins=5), y='MedHouseVal') untuk membagi usia rumah menjadi 5 kelompok dan melihat distribusi harga per kelompok. Tambahkan judul yang menyatakan temuan, bukan sekadar deskripsi chart.
plt.subplot2grid. Chart 1 (distribusi harga) lebar penuh di baris pertama. Chart 2-5 mengisi baris kedua dan ketiga. Tambahkan judul utama dan caption singkat di bawah setiap chart. Export sebagai PDF menggunakan plt.savefig('housing_analysis.pdf', format='pdf').
Dalam California Housing Dataset, ada cap artifisial di nilai MedHouseVal = 5.0 (nilai maksimum dataset). Ini bisa dilihat dari histogram yang punya spike tidak wajar di ujung kanan. Buat chart yang menunjukkan anomali ini dengan jelas dan tulis interpretasi singkat mengapa anomali ini penting untuk dipahami sebelum membuat model prediksi.
Minggu 3: Plotly untuk Visualisasi Interaktif
Plotly mengubah chart statis menjadi pengalaman yang bisa dijelajahi. Pembaca bisa hover, zoom, filter, dan bahkan menyaksikan data berubah seiring waktu lewat animasi.
Interaktivitas, Peta Choropleth, dan Animasi Data
Plotly Express vs Plotly Graph Objects: Dua Cara, Satu Tujuan
Plotly punya dua API yang beda filosofinya. Plotly Express (px) adalah API tingkat tinggi: satu baris kode untuk chart lengkap, ideal untuk eksplorasi cepat. Plotly Graph Objects (go) adalah API tingkat rendah: kontrol penuh atas setiap elemen, ideal untuk chart yang sangat dikustomisasi. Abbas perlu menguasai keduanya.
pip install plotly. Di Jupyter Notebook, render otomatis. Di VS Code, gunakan fig.show(renderer='browser') agar terbuka di browser. Export ke HTML standalone: fig.write_html('chart.html'). Export ke PNG (butuh kaleido): pip install kaleido lalu fig.write_image('chart.png', scale=2).
import plotly.express as px; df = px.data.gapminder(); fig = px.scatter(df[df.year==2007], x='gdpPercap', y='lifeExp', size='pop', color='continent', hover_name='country'). Buat chart yang sama dengan import plotly.graph_objects as go. Perhatikan perbedaan jumlah baris kode.
fig.to_dict() untuk melihat struktur JSON di balik setiap Figure. Tiga komponen utama: data (list of traces), layout (pengaturan tampilan), dan frames (untuk animasi). Pahami ini karena semua kustomisasi plotly akhirnya bermuara ke memodifikasi tiga komponen ini.
fig.update_layout(). Setelah membuat chart dengan px, kustomisasi lebih lanjut menggunakan fig.update_layout(title='Judul Baru', title_font_size=18, plot_bgcolor='white', paper_bgcolor='white', font_family='Arial'). Ini adalah cara menggabungkan kemudahan px dengan fleksibilitas go.
import plotly.express as px
import plotly.graph_objects as go
# Cara 1: Plotly Express (singkat, cepat)
df = px.data.gapminder()
df_2007 = df[df['year'] == 2007]
fig_px = px.scatter(
df_2007,
x = 'gdpPercap',
y = 'lifeExp',
size = 'pop',
color = 'continent',
hover_name = 'country',
log_x = True,
size_max = 55,
title = 'GDP vs Harapan Hidup (2007)',
labels = {'gdpPercap': 'GDP per Kapita (USD, log)',
'lifeExp': 'Harapan Hidup (tahun)'}
)
fig_px.update_layout(
plot_bgcolor = 'white',
paper_bgcolor = 'white',
font_family = 'Arial'
)
fig_px.show()
# Export ke file HTML standalone
fig_px.write_html('gdp_vs_lifeexp.html', include_plotlyjs='cdn')
Gunakan Plotly Express untuk eksplorasi cepat dan chart standar (scatter, bar, line, histogram, choropleth). Mulai dengan px, lalu perbaiki dengan update_layout() dan update_traces(). Beralih ke Graph Objects hanya jika px tidak bisa menghasilkan layout yang Abbas butuhkan, misalnya untuk chart yang menggabungkan beberapa tipe trace dalam satu Figure.
Interactive Bar, Line, dan Scatter dengan Hover Info yang Kaya
Interaktivitas plotly bukan sekadar fitur estetika. Hover tooltip yang dirancang dengan baik bisa menggantikan puluhan label statis dan membuat pembaca aktif mengeksplorasi data alih-alih pasif membacanya.
px.bar(df, x='kota', y='penjualan', hover_data=['margin', 'target', 'growth_pct'], color='status'). Tambahkan hover_name='kota' untuk nama besar di atas tooltip. Perhatikan bahwa dengan plotly, satu bar chart sudah bisa menampilkan 5+ dimensi informasi hanya lewat tooltip.
hovertemplate. Template default plotly sering terlalu teknis. Ganti dengan: fig.update_traces(hovertemplate='<b>%{x}</b><br>Penjualan: Rp %{y:,.0f}<br>Pertumbuhan: %{customdata[0]:.1f}%'). Tambahkan customdata=df[['growth_pct']].values. Tooltip yang baik langsung menjawab pertanyaan pembaca tanpa perlu membaca axis.
px.line(df, x='bulan', y='nilai', color='provinsi', facet_col='mata_pelajaran', facet_col_wrap=2). Facet membagi chart menjadi panel per kategori. Klik item legend untuk highlight/hide provinsi tertentu di semua panel sekaligus karena plotly sinkronisasi legend antar facet.
px.scatter(df, x='X', y='Y', size='populasi', color='IPM', symbol='pulau', hover_name='provinsi'). Parameter symbol membedakan bentuk marker per kategori, berguna untuk membedakan kelompok tanpa hanya mengandalkan warna (lebih accessible untuk color-blind).
from plotly.subplots import make_subplots; fig = make_subplots(rows=1, cols=3). Chart 1: bar chart penjualan per kota. Chart 2: line chart tren bulanan. Chart 3: scatter plot margin vs penjualan. Export ke HTML: fig.write_html('dashboard.html').
import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(1)
kota = ['Jakarta', 'Surabaya', 'Bandung', 'Medan', 'Makassar']
df = pd.DataFrame({
'kota' : kota,
'penjualan' : np.random.randint(800, 3000, 5),
'target' : np.random.randint(1000, 2500, 5),
'growth_pct' : np.random.uniform(-5, 25, 5).round(1),
})
df['status'] = df.apply(lambda r: 'Tercapai' if r.penjualan >= r.target else 'Di Bawah Target', axis=1)
fig = px.bar(
df.sort_values('penjualan', ascending=False),
x = 'kota',
y = 'penjualan',
color = 'status',
hover_name = 'kota',
hover_data = {'target': True, 'growth_pct': ':.1f', 'status': False},
color_discrete_map = {'Tercapai': '#2ecc71', 'Di Bawah Target': '#e74c3c'},
title = 'Penjualan per Kota vs Target',
labels = {'penjualan': 'Penjualan (juta Rp)', 'kota': 'Kota'}
)
fig.update_layout(plot_bgcolor='white', paper_bgcolor='white')
fig.write_html('penjualan_kota.html')
fig.show()
Choropleth Map Indonesia: Data per Provinsi di Atas Peta
Choropleth map mewarnai wilayah geografis berdasarkan nilai data. Untuk data Indonesia per provinsi, ini adalah cara visualisasi yang paling intuitif: semua orang langsung tahu wilayah mana yang tinggi dan rendah tanpa perlu membaca tabel.
url = 'https://raw.githubusercontent.com/superpikar/indonesia-geojson/master/indonesia.geojson'. Load dengan import requests, json; geojson = json.loads(requests.get(url).text). Perhatikan struktur: setiap feature punya properties.state berisi nama provinsi. Nama ini harus cocok persis dengan kolom di DataFrame.
px.choropleth_mapbox(). Gunakan mapbox_style='carto-positron' untuk basemap yang bersih. Parameter kunci: geojson=geojson, locations='nama_provinsi', featureidkey='properties.state', color='nilai', color_continuous_scale='RdYlGn'. Set zoom=4 dan center={"lat": -2.5, "lon": 118} untuk posisi awal Indonesia.
hover_data={'nilai': True, 'rank': True, 'kategori': True}. Update hovertemplate agar tooltip menampilkan nama provinsi besar, nilai dengan format yang tepat, dan rank nasional. Tooltip yang baik membuat peta bisa berdiri sendiri sebagai alat eksplorasi.
import plotly.express as px
import pandas as pd
import requests, json
# Load GeoJSON Indonesia
url = 'https://raw.githubusercontent.com/superpikar/indonesia-geojson/master/indonesia.geojson'
geojson = json.loads(requests.get(url).text)
# Data IPM per provinsi (contoh - gunakan data BPS asli)
df = pd.DataFrame({
'provinsi': ['Aceh', 'Sumatera Utara', 'Riau', 'Jawa Barat',
'Jawa Tengah', 'DI Yogyakarta', 'Jawa Timur', 'Bali',
'Kalimantan Timur', 'Papua'],
'ipm' : [72.6, 73.8, 73.5, 73.0,
73.3, 82.5, 73.4, 79.9,
78.2, 61.4]
})
fig = px.choropleth_mapbox(
df,
geojson = geojson,
locations = 'provinsi',
featureidkey = 'properties.state',
color = 'ipm',
color_continuous_scale = 'RdYlGn',
range_color = (60, 85),
mapbox_style = 'carto-positron',
zoom = 3.8,
center = {'lat': -2.5, 'lon': 118},
opacity = 0.8,
hover_name = 'provinsi',
hover_data = {'ipm': ':.1f'},
title = 'Indeks Pembangunan Manusia per Provinsi, 2023'
)
fig.update_layout(margin={'r':0, 't':40, 'l':0, 'b':0})
fig.write_html('ipm_indonesia.html')
fig.show()
Animated Chart: Visualisasikan Data yang Berubah dari Waktu ke Waktu
Animasi mengungkap dimensi yang tidak bisa ditangkap oleh chart statis: perubahan seiring waktu. Hans Rosling mempopulerkan ini di TED Talk-nya dengan bubble chart yang bergerak. Plotly membuat teknik yang sama bisa direproduksi Abbas dalam hitungan baris kode.
animation_frame='year' dan animation_group='country' ke px.scatter(). Parameter animation_group memastikan setiap titik (negara) bergerak secara smooth dari satu frame ke frame berikutnya, bukan melompat tiba-tiba.
fig.layout.updatemenus[0].buttons[0].args[1]['frame']['duration'] = 800. Tambahkan slider tahun yang otomatis muncul. Pembaca bisa klik play untuk otomatis, atau drag slider untuk manual. Ini membuat animasi jauh lebih berguna dari GIF statis.
px.bar(df, x='nilai', y='provinsi', animation_frame='tahun', orientation='h', range_x=[0, 100]). Bar chart race adalah format yang viral di media sosial karena menunjukkan perubahan ranking secara dramatis. Pastikan sumbu x punya range tetap (range_x) agar perbandingan antar frame adil.
animation_frame='tahun' ke px.choropleth_mapbox(). Ini menggabungkan peta dan animasi dalam satu chart. Gunakan untuk menunjukkan bagaimana distribusi kemiskinan atau IPM per provinsi berubah dari 2010 ke 2023.
fig.write_html('animasi.html', include_plotlyjs='cdn'). File ini bisa dibuka di browser manapun tanpa perlu instalasi Python. Untuk embed di web, gunakan include_plotlyjs='cdn' agar ukuran file tetap kecil. Untuk dibagikan offline, gunakan include_plotlyjs=True.
import plotly.express as px
df = px.data.gapminder()
fig = px.scatter(
df,
x = 'gdpPercap',
y = 'lifeExp',
size = 'pop',
color = 'continent',
hover_name = 'country',
animation_frame= 'year',
animation_group= 'country',
log_x = True,
size_max = 55,
range_x = [100, 100000],
range_y = [25, 90],
title = 'GDP per Kapita vs Harapan Hidup, 1952-2007',
labels = {
'gdpPercap': 'GDP per Kapita (USD, log scale)',
'lifeExp' : 'Harapan Hidup (tahun)',
'pop' : 'Populasi'
}
)
# Percepat animasi menjadi 500ms per frame
fig.layout.updatemenus[0].buttons[0].args[1]['frame']['duration'] = 500
fig.layout.updatemenus[0].buttons[0].args[1]['transition']['duration'] = 400
fig.update_layout(plot_bgcolor='white', paper_bgcolor='white')
fig.write_html('gapminder_animasi.html', include_plotlyjs='cdn')
fig.show()
Latihan: Dashboard Mini Interaktif, Pendidikan per Provinsi Indonesia
Hari ini Abbas membangun dashboard mini yang menggabungkan semua teknik plotly minggu ini: bar chart interaktif, choropleth map, dan scatter plot, semuanya dalam satu file HTML yang siap dibagikan.
px.bar(..., orientation='h'). Urutkan dari tertinggi ke terendah. Warnai bar berdasarkan wilayah geografis (Jawa, Sumatera, dll). Tambahkan garis rata-rata nasional sebagai referensi dengan fig.add_vline(x=rata_rata, line_dash='dash', line_color='gray').
'RdYlGn'). Set range colorscale ke 85-100 agar perbedaan antar provinsi terlihat jelas (jangan gunakan range otomatis yang akan membuat perbedaan kecil terlihat besar).
px.scatter() dengan size, color, dan hover_name. Tambahkan garis tren menggunakan px.scatter(..., trendline='ols') yang otomatis menghitung dan memplot regresi linear. Perhatikan provinsi mana yang di atas atau di bawah tren nasional.
make_subplots(rows=2, cols=2, specs=[[{'type': 'bar'}, {'type': 'mapbox'}], [{'type': 'scatter', 'colspan': 2}, None]]) untuk layout: bar dan peta di atas, scatter lebar penuh di bawah. Tambahkan judul utama dashboard. Export ke dashboard_pendidikan.html dan pastikan semua interaktivitas berfungsi di browser.
Coba tambahkan dropdown filter menggunakan fig.update_layout(updatemenus=[dict(buttons=[...])]) untuk memilih indikator yang ditampilkan di choropleth: APM SD, APM SMP, APM SMA, atau Melek Huruf. Ini mengubah satu peta statis menjadi alat eksplorasi yang bisa menjawab beberapa pertanyaan sekaligus.
Minggu 4: Prinsip Desain Visualisasi
Teknis sudah dikuasai. Minggu ini Abbas naik level: belajar mengapa chart yang secara teknis benar masih bisa gagal berkomunikasi, dan bagaimana merancang visualisasi yang benar-benar mengubah cara orang melihat data.
Tufte, Warna, Storytelling, dan Proyek Visual Akhir
Chart Junk vs Data-Ink Ratio: Prinsip Tufte untuk Chart yang Jujur
Edward Tufte, profesor statistik di Yale, merumuskan prinsip yang mengubah cara dunia melihat visualisasi data. Inti ajarannya sederhana: setiap piksel dalam chart harus membawa informasi. Semua yang tidak membawa informasi adalah "chart junk" yang perlu dihilangkan.
"The interior decoration of graphics generates a lot of ink that does not tell the viewer anything new. The purpose of decoration varies: to make the graphic appear more scientific and precise, to enliven the display, to give the designer an opportunity to exercise artistic skills. Regardless of purpose, decoration can turn bores into disasters." Terjemahan bebas: dekorasi berlebihan bukan hanya tidak berguna, tapi aktif merusak komunikasi data.
Pemilihan Warna yang Accessible dan Bermakna
Warna adalah dimensi yang paling sering disalahgunakan dalam visualisasi. Pemilihan warna yang buruk bisa membuat chart tidak bisa dibaca oleh penderita buta warna (8% pria) atau membuat pesan yang salah karena asosiasi warna yang tidak sesuai konteks.
colors = ['#1b9e77', '#d95f02', '#7570b3']. ColorBrewer adalah standar de facto untuk kartografi dan visualisasi data ilmiah.
cmap='viridis' yang dirancang agar perceptually uniform dan aman untuk semua jenis buta warna.
Untuk categorical data: maksimal 7 warna, karena mata manusia kesulitan membedakan lebih dari itu. Untuk data ordinal: selalu gunakan palette sequential (satu hue, gradasi gelap-terang). Untuk data dengan nilai positif dan negatif: selalu gunakan diverging palette dengan warna netral di tengah. Dan selalu: hindari merah-hijau sebagai satu-satunya pembeda, karena itu adalah kombinasi yang paling sering tidak bisa dibedakan oleh penderita buta warna.
Storytelling dengan Data: Chart yang Punya Pesan, Bukan Sekadar Gambar
Seorang analyst yang baik menemukan insight. Seorang analyst yang hebat mengkomunikasikan insight tersebut sehingga orang lain mengambil keputusan. Perbedaannya ada di storytelling. Chart yang hanya menampilkan data adalah chart yang gagal separuh tugasnya.
ax.annotate('Penurunan akibat pandemi', xy=(titik_x, titik_y), xytext=(offset_x, offset_y), arrowprops=dict(arrowstyle='->', color='gray')). Anotasi adalah pengganti narasi lisan dalam presentasi tatap muka. Setiap chart yang akan dilihat tanpa penjelasan verbal harus punya anotasi yang menjelaskan momen paling penting.
Analyst biasa menjawab pertanyaan yang ditanya. Analyst yang berdampak menjawab pertanyaan yang belum ditanya, mengemas jawabannya dalam narasi yang jelas, dan membuat pembuat keputusan mudah bertindak berdasarkan temuannya. Storytelling adalah skill yang membedakan keduanya, dan seperti semua skill, bisa dipelajari dan dilatih.
Kapan Tabel Lebih Baik dari Chart
Salah satu kesalahan analyst junior yang paling umum adalah memaksa semua data menjadi chart. Ada situasi di mana tabel adalah cara yang lebih jelas dan lebih jujur untuk menyampaikan informasi. Abbas perlu tahu kapan harus berhenti menggambar chart.
df.style.background_gradient(cmap='Blues', subset=['nilai']) untuk memberi warna gradasi pada kolom numerik. Tambahkan .bar(subset=['persen'], color='steelblue') untuk mini bar chart di dalam sel tabel. Hasilnya adalah "heatmap table" yang mempertahankan kemudahan lookup tabel tapi menambahkan konteks visual.
import pandas as pd
import numpy as np
# Tabel dengan conditional formatting menggunakan pandas Styler
np.random.seed(3)
provinsi = ['DI Yogyakarta', 'DKI Jakarta', 'Bali', 'Jawa Timur',
'Jawa Tengah', 'Sumatera Barat', 'Kalimantan Timur', 'Papua']
df = pd.DataFrame({
'Provinsi' : provinsi,
'APM SD (%)' : np.random.uniform(85, 99, 8).round(1),
'APM SMP (%)': np.random.uniform(70, 95, 8).round(1),
'APM SMA (%)': np.random.uniform(55, 90, 8).round(1),
'IPM' : np.random.uniform(62, 83, 8).round(1),
})
styled = (
df.style
.background_gradient(cmap='Blues', subset=['APM SD (%)', 'APM SMP (%)', 'APM SMA (%)'])
.background_gradient(cmap='YlOrRd', subset=['IPM'])
.format({'APM SD (%)': '{:.1f}', 'APM SMP (%)': '{:.1f}',
'APM SMA (%)': '{:.1f}', 'IPM': '{:.1f}'})
.set_caption('Indikator Pendidikan per Provinsi (2023)')
)
styled.to_html('tabel_pendidikan.html')
Mini Project: Laporan Visual 10 Chart tentang Data Publik Indonesia
Ini adalah proyek paling penting Bulan 6. Abbas membuat laporan visual yang siap dipresentasikan kepada audience non-teknis: menggunakan semua prinsip yang sudah dipelajari dari Minggu 1 hingga hari ini. Bukan sekadar kumpulan chart, tapi sebuah cerita yang utuh.
Laporan visual Abbas dianggap berhasil jika memenuhi tiga kriteria: (1) Seseorang yang belum pernah melihat datanya bisa memahami semua chart tanpa penjelasan verbal. (2) Setiap chart punya satu pesan yang jelas dan tidak ambigu. (3) Setelah membaca seluruh laporan, pembaca tahu apa yang harus dilakukan atau dipikirkan selanjutnya. Jika salah satu dari tiga kriteria ini tidak terpenuhi, laporan masih belum selesai.
Referensi Buku
Empat buku yang membentuk fondasi pemikiran tentang visualisasi data. Abbas tidak perlu membaca semua dalam satu bulan, tapi setidaknya baca satu secara penuh dan tiga sisanya sebagai referensi.
Storytelling with Data
2015, Wiley
Buku paling praktis tentang bagaimana mengkomunikasikan data kepada audience bisnis. Knaflic bekerja di Google sebelum menulis ini, dan setiap prinsip di buku ini langsung bisa diterapkan. Fokus pada: memilih chart yang tepat, menghilangkan clutter, menggunakan warna sebagai emphasis, dan merancang narasi yang efektif. Wajib dibaca penuh oleh Abbas karena ini adalah "missing manual" yang tidak diajarkan di kursus teknis manapun.
The Visual Display of Quantitative Information
1983, Graphics Press
Buku klasik yang mendefinisikan standar untuk visualisasi data ilmiah dan analitis. Tufte memperkenalkan konsep data-ink ratio, chart junk, dan sparkline yang masih relevan hingga hari ini. Buku ini lebih tentang filosofi dan prinsip daripada teknis, tapi membacanya akan mengubah cara Abbas melihat setiap chart yang pernah ada. Tersedia di perpustakaan digital, atau beli edisi cetak karena desain fisik bukunya sendiri adalah demonstrasi prinsipnya.
Fundamentals of Data Visualization
2019, O'Reilly (Gratis online)
Buku terlengkap dan paling komprehensif tentang prinsip visualisasi yang tersedia gratis di clauswilke.com/dataviz. Wilke adalah profesor biologi komputasi di UT Austin. Buku ini mencakup hampir setiap jenis chart yang pernah ada, kapan menggunakannya, dan kapan menghindarinya. Sangat berguna sebagai referensi ketika Abbas menghadapi pertanyaan "chart apa yang paling cocok untuk data jenis ini?" Bisa dibaca per bab sesuai kebutuhan.
Data Visualisation: A Handbook for Data Driven Design
2016, SAGE Publications
Buku yang paling berorientasi pada proses desain. Kirk mengajarkan cara berpikir sistematis tentang visualisasi: mulai dari mendefinisikan tujuan, memilih data yang relevan, merancang visual, hingga mengevaluasi hasilnya. Sangat berguna untuk Abbas ketika mengerjakan proyek besar yang perlu alur kerja yang terstruktur. Juga mencakup banyak case study dari berbagai industri yang membuat prinsip-prinsipnya lebih konkret dan mudah dipahami.
Tools dan Ekosistem Visualisasi
Python bukan satu-satunya cara. Abbas perlu tahu tools apa saja yang ada di ekosistem, kapan menggunakan masing-masing, dan mana yang paling sesuai untuk konteks yang berbeda.
Fondasi semua visualisasi Python. Memberikan kontrol penuh atas setiap piksel. Semua library visualisasi Python lain dibangun di atas matplotlib atau terinspirasi olehnya. Lambat untuk eksplorasi cepat tapi tak tertandingi untuk chart yang sangat dikustomisasi.
pip install matplotlib
Statistical data visualization berbasis matplotlib. Satu baris kode menghasilkan chart yang butuh 20 baris di matplotlib mentah. Default style seaborn jauh lebih baik dari matplotlib. Ideal untuk EDA (Exploratory Data Analysis) karena sangat mudah membuat chart statistik seperti boxplot, violin, heatmap, dan pair plot.
pip install seaborn
Library visualisasi interaktif berbasis web (WebGL dan SVG). Output adalah chart HTML yang bisa dibuka di browser manapun tanpa Python. Mendukung 40+ jenis chart termasuk choropleth map, 3D scatter, animated chart, dan treemap. Plotly Express menyederhanakan API untuk kasus umum; Graph Objects memberikan kontrol penuh.
pip install plotly kaleido
Versi gratis Tableau Desktop dengan satu pembatasan: semua visualisasi harus dipublikasikan ke public.tableau.com. Drag-and-drop interface yang intuitif membuat chart profesional tanpa satu baris kode. Sangat berguna untuk presentasi ke stakeholder non-teknis. Pelajari ini untuk portfolio: banyak perusahaan Indonesia menggunakan Tableau untuk laporan BI.
Formerly Google Data Studio. Tool BI gratis dari Google yang terintegrasi sempurna dengan Google Sheets, BigQuery, Google Analytics, dan YouTube Analytics. Sangat populer di perusahaan Indonesia yang menggunakan ekosistem Google Workspace. Bisa embed laporan interaktif ke website atau dibagikan sebagai link. Cocok untuk laporan rutin yang datanya update otomatis dari Google Sheets.
Tool yang digunakan oleh journalist data di New York Times, Der Spiegel, dan banyak newsroom terkemuka. Antarmuka 4 langkah yang sangat sederhana: upload data, pilih chart, sesuaikan tampilan, publish. Menghasilkan chart SVG yang sangat bersih dan responsive. Gratis untuk penggunaan publik. Sangat bagus untuk belajar prinsip desain karena default Datawrapper sudah mengikuti best practice jurnalisme data.
Jangan mencoba menguasai semua tool sekaligus. Prioritaskan dalam urutan ini: (1) Kuasai matplotlib dan seaborn hingga bisa membuat chart apapun dari memori. (2) Kuasai plotly untuk interaktivitas dan peta. (3) Eksplorasi Tableau Public dan Looker Studio untuk memahami cara kerja BI tool. (4) Coba Datawrapper untuk satu proyek dan perhatikan bagaimana default pilihan desainnya mengajarkan prinsip yang lebih baik dari yang Abbas sadari. Setiap tool membuka cara berpikir baru tentang data.
Tips Praktis dan Jebakan Pemula
Delapan pelajaran yang biasanya hanya ditemukan setelah berkali-kali membuat chart yang salah. Abbas bisa mempersingkat kurva belajar itu dengan membaca dan mempraktikkan ini sekarang.
Urutan pemanggilan style sangat penting. Panggil plt.style.use('seaborn-v0_8-whitegrid') atau sns.set_theme(style='whitegrid', palette='muted') di bagian paling atas notebook, sebelum membuat Figure apapun. Kalau dipanggil di tengah, hanya chart setelahnya yang terpengaruh, dan ini menyebabkan inkonsistensi visual yang sulit dilacak. Buat satu sel inisialisasi khusus di setiap notebook yang berisi semua import dan konfigurasi style sekaligus.
Pie chart hanya bekerja ketika kategori berjumlah 3-5 dan perbedaan ukurannya signifikan. Untuk 6 kategori ke atas, mata manusia tidak bisa membandingkan sudut lingkaran dengan akurat. Solusinya selalu bar chart horizontal yang diurutkan dari terbesar ke terkecil. Abbas akan sering melihat pie chart dengan 10+ slice dalam laporan pemerintah atau media, dan itu hampir selalu pilihan yang salah secara komunikatif.
Setiap warna yang berbeda dalam chart menyampaikan pesan: "ini berbeda dari yang lain." Kalau Abbas mewarnai 12 bar dengan 12 warna berbeda hanya karena terlihat menarik, pembaca akan mencari pola yang tidak ada. Aturan praktis: gunakan satu warna untuk semua bar, lalu gunakan warna aksen yang berbeda hanya untuk bar yang ingin disorot. Teknik ini disebut "pre-attentive attribute" dan sangat efektif untuk mengarahkan perhatian pembaca ke insight utama.
Untuk bar chart dan area chart, sumbu Y yang tidak dimulai dari nol secara visual melebih-lebihkan perbedaan. Perbedaan 2% bisa terlihat seperti 300% kalau Y-axis dimulai dari 97. Matplotlib defaultnya tidak memotong axis, tapi kalau Abbas pernah mengimpor data dari Excel atau memakai library lain, cek selalu dengan ax.get_ylim(). Pengecualian: line chart yang menunjukkan tren kecil pada nilai besar (misalnya harga saham) boleh tidak dimulai dari nol, tapi harus diberi label yang sangat jelas.
8% pria mengalami buta warna merah-hijau. Artinya dari 25 orang yang melihat laporan Abbas, kemungkinan 2 orang tidak bisa membedakan kategori kalau Abbas memakai palette default. Gunakan palette yang sudah teruji: sns.color_palette('colorblind') untuk categorical, 'viridis' atau 'plasma' untuk sequential, dan 'RdBu' untuk diverging. Cara cek chart Abbas: screenshot, lalu pakai situs coblis.com untuk simulasi berbagai jenis buta warna.
Ukuran Figure bukan hanya estetika, tapi menentukan apakah teks bisa dibaca. Untuk notebook eksplorasi: figsize=(10, 6) sudah cukup. Untuk presentasi layar lebar: figsize=(16, 9) atau (20, 8). Untuk laporan PDF atau print: gunakan dpi=300 saat menyimpan. Untuk embed di web: dpi=150 sudah cukup tajam. Aturan cepat: kalau teks di chart terasa terlalu kecil, naikkan figsize dulu sebelum menaikkan fontsize, karena menaikkan fontsize saja akan membuat teks "memakan" area data.
Dual Y-axis (dua sumbu vertikal berbeda di kiri dan kanan) hampir selalu berbahaya karena skala yang berbeda memungkinkan manipulasi visual: hubungan antara dua garis bisa dibuat tampak berkorelasi atau tidak berkorelasi hanya dengan mengubah skala salah satu sumbu. Kalau Abbas perlu menampilkan dua metrik berbeda unit dalam satu chart, lebih baik buat dua subplot yang berdampingan dengan sumbu X yang disejajarkan. Ini lebih jujur dan lebih mudah dibaca.
Ketika menemukan titik data yang jauh dari kelompok utama di scatter plot, jangan hapus atau sembunyikan. Outlier sering kali adalah insight terpenting dalam dataset. Yang perlu dilakukan: tambahkan anotasi dengan ax.annotate() untuk menjelaskan outlier tersebut. Contoh: titik data yang jauh bisa menunjukkan provinsi dengan kondisi khusus, bulan dengan event luar biasa, atau kesalahan input yang perlu dikonfirmasi ke sumber data. Setiap outlier adalah pertanyaan yang belum terjawab.
Gunakan plt.savefig() dengan format yang sesuai tujuan: .png untuk presentasi dan web (raster, file lebih kecil), .svg untuk laporan yang perlu diperbesar tanpa blur (vector, bisa diedit di Inkscape/Illustrator), .pdf untuk dokumen akademik atau laporan formal. Selalu tambahkan bbox_inches='tight' agar label di pinggir tidak terpotong. Simpan sebelum memanggil plt.show(), karena setelah show() buffer figure dikosongkan dan savefig() akan menghasilkan file kosong.
Latihan Tambahan: Dataset Nyata Indonesia
Lima latihan menggunakan data publik Indonesia dari BPS, Kemendikbud, dan Kemenkes. Latihan ini lebih menantang dari yang ada di minggu 1-4 karena data asli tidak pernah serapi data tutorial.
Semua dataset bisa diunduh gratis dari: bps.go.id (statistik nasional), data.go.id (portal data terbuka pemerintah), dan databoks.katadata.co.id (data sudah dikurasi, format CSV lebih bersih). Untuk latihan ini, Abbas akan membuat data simulasi yang mencerminkan karakteristik data asli Indonesia, sehingga bisa langsung dikerjakan tanpa perlu mendaftar atau mengunduh file.
Dashboard Indeks Pembangunan Manusia (IPM) 34 Provinsi
IPM adalah indeks gabungan dari harapan hidup, pendidikan, dan pendapatan. Data ini punya karakteristik menarik: ada gap besar antara DKI Jakarta dan Papua, dan trennya bergerak lambat dari tahun ke tahun. Latihan ini melatih kemampuan membuat multi-chart dashboard sekaligus membaca pola regional.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
sns.set_theme(style='whitegrid', palette='muted')
# Data IPM simulasi 10 provinsi (representatif)
data_ipm = {
'provinsi': [
'DKI Jakarta', 'DI Yogyakarta', 'Kalimantan Timur',
'Bali', 'Riau', 'Sulawesi Utara', 'Jawa Tengah',
'NTT', 'Maluku', 'Papua'
],
'ipm_2023': [82.46, 80.22, 77.44, 77.21, 73.52,
73.81, 72.69, 65.28, 69.47, 61.39],
'ipm_2019': [80.76, 79.99, 75.83, 75.38, 72.71,
72.20, 71.73, 63.97, 68.19, 60.06],
'pengeluaran_juta': [19.2, 17.8, 15.6, 14.9, 13.8,
12.4, 11.9, 8.7, 9.2, 7.4]
}
df = pd.DataFrame(data_ipm).sort_values('ipm_2023', ascending=True)
# Tentukan warna: 2 terendah merah, 2 tertinggi hijau, sisanya abu-abu
colors = []
for i, val in enumerate(df['ipm_2023']):
if i < 2:
colors.append('#e74c3c')
elif i >= len(df) - 2:
colors.append('#27ae60')
else:
colors.append('#bdc3c7')
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
fig.suptitle('Indeks Pembangunan Manusia (IPM) Indonesia 2023',
fontsize=15, fontweight='bold', y=1.02)
# Panel kiri: horizontal bar
bars = axes[0].barh(df['provinsi'], df['ipm_2023'], color=colors, edgecolor='white')
axes[0].set_xlabel('Nilai IPM')
axes[0].set_title('Ranking IPM per Provinsi')
axes[0].set_xlim(55, 88)
for bar, val in zip(bars, df['ipm_2023']):
axes[0].text(val + 0.3, bar.get_y() + bar.get_height()/2,
f'{val:.1f}', va='center', fontsize=9)
# Panel kanan: scatter IPM vs pengeluaran
axes[1].scatter(df['pengeluaran_juta'], df['ipm_2023'],
s=120, color='steelblue', alpha=0.8, zorder=5)
for _, row in df.iterrows():
axes[1].annotate(row['provinsi'],
xy=(row['pengeluaran_juta'], row['ipm_2023']),
xytext=(4, 2), textcoords='offset points', fontsize=7.5)
# Garis tren
z = np.polyfit(df['pengeluaran_juta'], df['ipm_2023'], 1)
p = np.poly1d(z)
x_line = np.linspace(df['pengeluaran_juta'].min(), df['pengeluaran_juta'].max(), 100)
axes[1].plot(x_line, p(x_line), '--', color='coral', alpha=0.7, linewidth=1.5)
axes[1].set_xlabel('Pengeluaran Per Kapita (Juta Rp/tahun)')
axes[1].set_ylabel('Nilai IPM')
axes[1].set_title('Korelasi IPM dengan Pengeluaran Per Kapita')
plt.tight_layout()
plt.savefig('latihan_ipm.png', dpi=150, bbox_inches='tight')
plt.show()
Tren Pengguna Internet Indonesia 2015-2023 dengan Plotly Interaktif
Data penetrasi internet Indonesia punya narasi yang kuat: pertumbuhan masif di mobile, percepatan saat pandemi 2020, dan gap antara Jawa dan luar Jawa. Latihan ini melatih kemampuan menceritakan tren multi-dimensi dengan Plotly.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
tahun = [2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023]
penetrasi_nasional = [22.0, 25.4, 32.3, 39.9, 47.7, 53.7, 62.1, 66.5, 78.2]
penetrasi_jawa = [31.2, 36.1, 44.7, 53.2, 60.4, 67.9, 74.3, 79.1, 87.4]
penetrasi_luar = [14.1, 16.8, 22.4, 28.7, 36.3, 41.2, 51.7, 56.3, 70.1]
fig = make_subplots(
rows=1, cols=2,
subplot_titles=('Penetrasi Internet (%) per Wilayah', 'Pertambahan Pengguna per Tahun (%)')
)
# Panel kiri: tren penetrasi
for label, data, color in [
('Nasional', penetrasi_nasional, '#2980b9'),
('Jawa', penetrasi_jawa, '#27ae60'),
('Luar Jawa', penetrasi_luar, '#e67e22')
]:
fig.add_trace(go.Scatter(
x=tahun, y=data, name=label,
line=dict(color=color, width=2.5),
mode='lines+markers',
hovertemplate='%{y:.1f}%<br>Tahun %{x}<extra>' + label + '</extra>'
), row=1, col=1)
# Anotasi: momen pandemi
fig.add_vline(x=2020, line_dash='dot', line_color='gray',
annotation_text='Pandemi COVID-19', row=1, col=1)
# Panel kanan: pertumbuhan YoY
yoy = [0] + [round((penetrasi_nasional[i] - penetrasi_nasional[i-1]) /
penetrasi_nasional[i-1] * 100, 1) for i in range(1, len(tahun))]
bar_colors = ['#e74c3c' if v < 10 else '#27ae60' for v in yoy]
fig.add_trace(go.Bar(
x=tahun[1:], y=yoy[1:],
marker_color=bar_colors[1:],
name='Pertumbuhan YoY',
showlegend=False,
hovertemplate='+%{y:.1f}% dari tahun lalu<extra></extra>'
), row=1, col=2)
fig.update_layout(
title_text='Penetrasi Internet Indonesia 2015-2023',
title_font_size=16,
height=450,
hovermode='x unified',
template='plotly_white'
)
fig.write_html('latihan_internet.html')
fig.show()
Cari tahun dengan pertumbuhan YoY tertinggi. Apakah itu 2020 (pandemi) atau tahun lain? Kalau bukan 2020, pikirkan mengapa. Ini melatih kebiasaan "membaca chart, bukan hanya membuat chart." Data analyst yang baik selalu punya hipotesis sebelum melihat hasil visualisasi, lalu mencatat apakah hasil mendukung atau menolak hipotesis tersebut.
Heatmap Korelasi Indikator Kesehatan Antar Provinsi
Seaborn heatmap adalah salah satu alat paling efektif untuk melihat pola korelasi antar banyak variabel sekaligus. Latihan ini menggunakan data indikator kesehatan provinsi dan melatih kemampuan membaca pola dari matriks korelasi.
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
np.random.seed(2024)
n = 34 # 34 provinsi
# Simulasi data kesehatan dengan korelasi yang realistis
harapan_hidup = 65 + np.random.randn(n) * 4
angka_stunting = 28 - harapan_hidup * 0.3 + np.random.randn(n) * 3
akses_air_bersih = 70 + harapan_hidup * 0.5 + np.random.randn(n) * 5
fasilitas_nakes = 50 + harapan_hidup * 0.4 + np.random.randn(n) * 6
angka_kemiskinan = 25 - harapan_hidup * 0.4 + np.random.randn(n) * 4
df = pd.DataFrame({
'Harapan Hidup' : harapan_hidup,
'Stunting (%)' : angka_stunting,
'Air Bersih (%)' : akses_air_bersih,
'Nakes per 1000' : fasilitas_nakes,
'Kemiskinan (%)' : angka_kemiskinan
})
corr = df.corr()
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Heatmap dengan mask: tampilkan hanya segitiga bawah
mask = np.triu(np.ones_like(corr, dtype=bool), k=1)
sns.heatmap(
corr, ax=axes[0], mask=mask,
annot=True, fmt='.2f', cmap='RdBu_r',
vmin=-1, vmax=1, center=0,
square=True, linewidths=0.5,
cbar_kws={'shrink': 0.8}
)
axes[0].set_title('Korelasi Antar Indikator Kesehatan')
# Scatter: hubungan paling kuat dari heatmap
axes[1].scatter(df['Kemiskinan (%)'], df['Stunting (%)'],
alpha=0.7, s=80, color='steelblue')
z = np.polyfit(df['Kemiskinan (%)'], df['Stunting (%)'], 1)
p = np.poly1d(z)
x_fit = np.linspace(df['Kemiskinan (%)'].min(),
df['Kemiskinan (%)'].max(), 100)
axes[1].plot(x_fit, p(x_fit), 'r--', alpha=0.8)
axes[1].set_xlabel('Tingkat Kemiskinan (%)')
axes[1].set_ylabel('Angka Stunting (%)')
axes[1].set_title('Kemiskinan vs Stunting: Pasangan Korelasi Terkuat')
plt.tight_layout()
plt.savefig('latihan_kesehatan.png', dpi=150, bbox_inches='tight')
plt.show()
Heatmap menunjukkan bahwa kemiskinan berkorelasi dengan stunting. Ini tidak berarti kemiskinan secara langsung menyebabkan stunting. Keduanya mungkin sama-sama disebabkan faktor ketiga seperti akses layanan kesehatan atau tingkat pendidikan ibu. Abbas harus selalu menambahkan catatan "r = [nilai]" dan menulis frasa "berkorelasi dengan", bukan "menyebabkan", saat mendeskripsikan temuan dari heatmap korelasi.
Animasi Perubahan Ekspor Komoditas Indonesia 2010-2023
Plotly Express memiliki parameter animation_frame yang memungkinkan Abbas membuat chart animasi dengan satu baris tambahan. Latihan ini melatih kemampuan memvisualisasikan perubahan komposisi data dari waktu ke waktu.
import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
tahun_list = list(range(2010, 2024))
komoditas = ['Batu Bara', 'Minyak Sawit', 'Nikel',
'Karet', 'Kopi', 'Tekstil']
# Tren berbeda tiap komoditas: nikel naik, batu bara turun, sawit stabil
tren_base = {
'Batu Bara' : [28, 30, 27, 24, 22, 20, 19, 21, 22, 24, 26, 30, 28, 23],
'Minyak Sawit': [22, 23, 24, 23, 24, 24, 25, 26, 27, 26, 28, 30, 27, 26],
'Nikel' : [4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 17, 20, 22, 25],
'Karet' : [12, 11, 10, 9, 9, 8, 8, 7, 7, 7, 6, 6, 6, 6],
'Kopi' : [5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7],
'Tekstil' : [10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6]
}
rows = []
for i, tahun in enumerate(tahun_list):
for kom in komoditas:
rows.append({
'tahun' : tahun,
'komoditas': kom,
'nilai_miliar_usd': tren_base[kom][i] + np.random.randn() * 1.5
})
df = pd.DataFrame(rows)
fig = px.bar(
df, x='komoditas', y='nilai_miliar_usd',
color='komoditas',
animation_frame='tahun',
range_y=[0, 35],
color_discrete_sequence=px.colors.qualitative.Set2,
title='Nilai Ekspor Komoditas Utama Indonesia (Miliar USD)',
labels={'nilai_miliar_usd': 'Nilai (Miliar USD)', 'komoditas': 'Komoditas'}
)
fig.update_layout(
template='plotly_white',
showlegend=False,
height=480
)
fig.write_html('latihan_ekspor.html')
fig.show()
Setelah membuat animasi bar, buat juga versi line chart yang menampilkan semua tahun sekaligus tanpa animasi, dengan tiap komoditas sebagai satu garis berbeda warna. Bandingkan: kapan animasi lebih efektif dari static chart, dan kapan sebaliknya? Animasi bagus untuk presentasi lisan, static chart lebih baik untuk laporan tertulis karena pembaca bisa membandingkan semua titik sekaligus tanpa harus mengingat frame sebelumnya.
Proyek Mini: Satu Halaman Dashboard Pendidikan Indonesia
Latihan terakhir ini mengintegrasikan semua yang sudah dipelajari: matplotlib untuk chart statis, seaborn untuk distribusi statistik, dan plotly untuk satu chart interaktif. Hasilnya adalah satu halaman HTML yang bisa dibagikan.
<img> tag untuk PNG dan <iframe> untuk HTML Plotly. Tambahkan satu paragraf teks yang menjelaskan "pesan utama" dari keseluruhan dashboard dalam 3-4 kalimat.Dashboard Abbas dianggap selesai kalau seseorang yang belum pernah melihat data tersebut bisa membaca keseluruhan dashboard dalam 2 menit dan langsung memahami: (1) apa yang sedang diukur, (2) apa temuan utamanya, dan (3) apa yang menarik atau mengejutkan dari data tersebut. Kalau orang tersebut masih perlu bertanya, artinya ada chart atau label yang perlu diperbaiki.