Grafik batang dan garis mengabaikan satu dimensi yang justru sering menjadi kunci keputusan: lokasi. Penjualan per kecamatan, sebaran kedai pesaing, hingga wilayah yang belum terjangkau armada pengiriman — semuanya paling mudah dibaca ketika digambar sebagai peta. Pada chapter ini kita memvisualisasikan data geospasial yang bentuknya sudah kita kenal di Section Pengumpulan Data : poligon kecamatan dalam berkas GeoJSON, dibaca dengan geopandas, lalu diwarnai menurut nilainya menjadi choropleth — peta wilayah yang arsirannya mewakili angka. Sebagai penutup, kita menumpangkan titik lokasi kedai di atas peta itu.
Choropleth: Peta yang Diwarnai Angka #
Choropleth adalah peta berbatas wilayah (kecamatan, provinsi, negara) yang setiap wilayahnya diwarnai menurut nilai sebuah variabel — makin pekat warnanya, makin tinggi nilainya. Ia menjawab pertanyaan “di mana” yang tidak bisa dijawab grafik batang: bukan sekadar berapa penjualan tiap kecamatan, melainkan wilayah mana yang menonjol dan apakah wilayah-wilayah itu berdempetan membentuk kawasan. Syaratnya satu: variabel yang diwarnai harus bisa dibandingkan adil antar wilayah (nilai absolut seperti total penjualan sudah cukup untuk memantau performa tiap wilayah; untuk membandingkan “kuantitas belanja” antar wilayah berpenduduk berbeda, gunakan nilai per penduduk).
Dataset kita memuat empat kecamatan Bandung — poligon sederhana pada koordinat WGS84 — beserta rekap penjualan Toko Kopi Kita dalam penjualan_kecamatan.csv:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"nama": "Sukajadi"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[107.6, -6.91],
[107.62, -6.91],
[107.62, -6.9],
[107.6, -6.9],
[107.6, -6.91]
]
]
}
},
{
"type": "Feature",
"properties": {
"nama": "Coblong"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[107.62, -6.91],
[107.65, -6.91],
[107.65, -6.9],
[107.62, -6.9],
[107.62, -6.91]
]
]
}
},
{
"type": "Feature",
"properties": {
"nama": "Andir"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[107.6, -6.9],
[107.62, -6.9],
[107.62, -6.89],
[107.6, -6.89],
[107.6, -6.9]
]
]
}
},
{
"type": "Feature",
"properties": {
"nama": "Regol"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[107.62, -6.9],
[107.65, -6.9],
[107.65, -6.89],
[107.62, -6.89],
[107.62, -6.9]
]
]
}
}
]
}nama,penjualan_ribu
Sukajadi,8600
Coblong,13200
Andir,5400
Regol,9800Membaca Peta dan Menempelkan Angka #
Langkah pertama tetap membaca dan memeriksa: pastikan geometri terbaca sebagai poligon dan CRS-nya memang geografis (EPSG:4326) — dua hal yang menentukan apakah peta akan tergambar benar. Setelah itu angka penjualan ditempelkan ke wilayahnya lewat merge pada kolom nama, persis pola DataFrame.merge
yang kita kenal:
import geopandas as gpd
gdf = gpd.read_file("kecamatan.geojson")
print(gdf)
print()
print("Jenis geometri:", gdf.geom_type.tolist())
print("CRS:", gdf.crs)Output yang Dihasilkan:
nama geometry
0 Sukajadi POLYGON ((107.6 -6.91, 107.62 -6.91, 107.62 -6...
1 Coblong POLYGON ((107.62 -6.91, 107.65 -6.91, 107.65 -...
2 Andir POLYGON ((107.6 -6.9, 107.62 -6.9, 107.62 -6.8...
3 Regol POLYGON ((107.62 -6.9, 107.65 -6.9, 107.65 -6....
Jenis geometri: ['Polygon', 'Polygon', 'Polygon', 'Polygon']
CRS: EPSG:4326import geopandas as gpd
import pandas as pd
gdf = gpd.read_file("kecamatan.geojson")
penjualan = pd.read_csv("penjualan_kecamatan.csv")
peta = gdf.merge(penjualan, on="nama", how="left")
tabel = peta.drop(columns="geometry")
print(tabel.to_string(index=False))
print()
print("Total penjualan empat kecamatan:", peta["penjualan_ribu"].sum(), "ribu rupiah")Output yang Dihasilkan:
nama penjualan_ribu
Sukajadi 8600
Coblong 13200
Andir 5400
Regol 9800
Total penjualan empat kecamatan: 37000 ribu rupiahDua pemeriksaan awal lolos: empat poligon utuh dan CRS geografis siap tampil. Tabel gabungan menegaskan ceritanya — Coblong memimpin dengan 13.200 ribu rupiah, hampir dua setengah lipat Andir yang terkecil (5.400). Angka-angka itu kini siap diwarnai ke dalam peta.
Menggambar Choropleth #
Satu panggilan .plot() menyelesaikan semuanya: column memilih variabel yang diwarnai, cmap memilih palet, legend menampilkan batang warna, dan edgecolor memberi garis batas antar wilayah:
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
gdf = gpd.read_file("kecamatan.geojson")
penjualan = pd.read_csv("penjualan_kecamatan.csv")
peta = gdf.merge(penjualan, on="nama", how="left")
ax = peta.plot(column="penjualan_ribu", cmap="Greens", legend=True,
edgecolor="white", figsize=(7, 5))
ax.set_title("Penjualan Toko Kopi Kita per Kecamatan (ribu rupiah)")
ax.set_xlabel("Bujur (derajat)")
ax.set_ylabel("Lintang (derajat)")
plt.show()Peta yang tampil memuat empat kotak wilayah tersusun mozaik dua kali dua. Coblong — kotak kanan atas — paling pekat warnanya karena penjualannya tertinggi (13.200), disusul Regol di kanan bawah (9.800) dan Sukajadi di kiri atas (8.600) dengan gradasi yang nyaris berurutan; Andir di kiri bawah paling pucat (5.400). Batang warna di sisi kanan memetakan gelap-terang ke rentang rupiah, dan garis putih memisahkan kecamatan satu sama lain. Sekali pandang, pola yang tidak kelihatan di tabel menjadi jelas: dua wilayah dengan penjualan terbaik sama-sama berada di sisi timur (bujur lebih besar) peta.
Menumpangkan Titik Lokasi #
Choropleth menjawab “wilayah mana yang menonjol”; lapisan titik menjawab “di tepat mana sesuatu berada”. Kedai Toko Kopi Kita yang dua gerainya berdiri di wilayah berbeda kita bangun sebagai GeoDataFrame titik memakai points_from_xy — urutannya [bujur, lintang], bukan sebaliknya — lalu digambar di atas peta yang sama lewat parameter ax:
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
gdf = gpd.read_file("kecamatan.geojson")
penjualan = pd.read_csv("penjualan_kecamatan.csv")
peta = gdf.merge(penjualan, on="nama", how="left")
kedai = gpd.GeoDataFrame({
"kedai": ["Toko Kopi Kita - Cendana", "Toko Kopi Kita - Dipatiukur"],
"bujur": [107.61, 107.635],
"lintang": [-6.905, -6.895],
}, geometry=gpd.points_from_xy([107.61, 107.635], [-6.905, -6.895]), crs="EPSG:4326")
ax = peta.plot(column="penjualan_ribu", cmap="Greens", edgecolor="white",
figsize=(7, 5), legend=True, legend_kwds={"label": "penjualan (ribu rupiah)"})
kedai.plot(ax=ax, color="red", marker="x", markersize=90)
for _, baris in kedai.iterrows():
ax.annotate(baris["kedai"], xy=(baris["bujur"], baris["lintang"]),
xytext=(3, 5), textcoords="offset points", fontsize=8)
ax.set_title("Peta penjualan dan lokasi kedai")
plt.show()Peta gabungan ini menampilkan mozaik choropleth yang sama dengan dua tanda silang merah di atasnya: gerai Cendana di tengah wilayah Sukajai-Andir (kiri), dan gerai Dipatiukur di antara Coblong dan Regol (kanan). Label nama kedai mengambang sedikit di kanan-atas tiap tanda. Kombinasi ini langsung memunculkan pertanyaan bisnis yang relevan: penjualan tinggi di sisi timur bertepatan dengan lokasi gerai Dipatiukur — apakah timur memang pasar yang lebih kuat, atau gerai itulah yang menarik pasarnya? Peta tidak menjawabnya; ia menunjukkan di mana mencari jawabannya.
Rangkuman #
- Choropleth mewarnai wilayah menurut nilai variabel; ia menjawab pertanyaan “di mana”, bukan hanya “berapa” — dan paling tepat dipakai ketika nilainya sebanding antar wilayah.
- Alurnya tiga langkah:
gpd.read_file()membaca GeoJSON,merge()menempelkan angka pada wilayahnya, lalu.plot(column=..., cmap=..., legend=True, edgecolor=...)menggambar peta. - Palet satu arah (
Greens,Blues) untuk nilai bergradasi tunggal; simpan palet dua arah untuk data yang berkisar di kedua sisi nol. - Lapisan titik lewat
gpd.points_from_xy(bujur, lintang)dankedai.plot(ax=ax)menambahkan konteks lokasi persis di atas choropleth — denganaxyang sama, keduanya berbagi satu kanvas dan satu CRS. - EPSG:4326 cukup untuk menampilkan peta; hitung luas/jarak hanya setelah proyeksi ke CRS berbentuk meter.
Peta choropleth memakai satu kekuatan besar — warna yang makin pekat makin tinggi — dan kekuatan itu bisa disalahgunakan: skala warna yang dipilih sewenang-wenang, palet yang menyilaukan, atau rentang yang dipangkas bisa membuat peta (dan grafik apa pun) berbicara lebih banyak atau lebih sedikit daripada angkanya. Chapter penutup Section ini mengaudit persis masalah itu: Menghindari Visualisasi Menyesatkan .