Chapter Visualisasi dengan matplotlib menggambar lima grafik inti dengan mengatur figur dan sumbu secara langsung. Chapter ini naik satu tingkat: seaborn, pustaka yang dibangun di atas matplotlib dan mengkhususkan diri pada grafik statistik. Seluruh data contoh section ini dipakai ulang — nilai 30 siswa, dua kelas yang dibandingkan, jam belajar terhadap nilai, dan DataFrame sepuluh siswa dari chapter Analisis Data dengan pandas — sehingga fokus bergeser dari cara menggambar ke apa yang disederhanakan. Tiga grafik lama digambar ulang, dua jenis grafik baru ditambahkan, dan sebuah tabel panduan menutup pertanyaan praktis terakhir: kapan memilih matplotlib, kapan seaborn. Seluruh contoh diuji pada Python 3.12.15 dengan seaborn 0.13.2, matplotlib 3.9.2, dan pandas 2.2.3; pemasangannya satu baris, python3 -m pip install seaborn.

Lapisan Statistik di Atas matplotlib #

seaborn menjawab keluhan lama pengguna matplotlib: grafik statistik yang umum — histogram, box plot, scatter dengan garis regresi, diagram batang dari data mentah — menuntut kode perakitan sendiri. seaborn memetakan panggilan satu baris langsung ke grafik jadi: data mentah masuk, agregasi dan penggambaran keluar sekaligus. Tiga hal yang dibawanya: default yang dipilih untuk data statistik, antarmuka yang memakai DataFrame pandas sebagai input utama, dan tema tampilan yang bisa dipasang sekali untuk seluruh laporan — dokumentasinya di seaborn.pydata.org .

Hubungan seaborn dengan matplotlib perlu dipahami sejak awal: setiap fungsi seaborn mengembalikan objek Axes matplotlib. Grafik yang tampak khas seaborn sebenarnya tersusun dari artist matplotlib biasa, sehingga seluruh kebiasaan chapter sebelumnya tetap berlaku — backend Agg untuk server tanpa layar, fig.savefig() untuk menyimpan, plt.close() untuk membereskan memori. Blok-blok chapter ini mengikuti pola itu, dengan berkas uji sementara ditulis ke /tmp agar folder chapter tetap bersih.

Kebutuhanmatplotlibseaborn
Histogram satu variabelplt.hist(nilai, bins=5)sns.histplot(nilai, bins=5)
Diagram batang dari tabel frekuensi jadiplt.bar(kategori, frekuensi)(tidak ada — bukan tugasnya)
Diagram batang dari data mentahhitung dulu, lalu plt.barsns.countplot(data=df, x="kelas")
Scatter plotplt.scatter(x, y)sns.scatterplot(x=x, y=y)
Scatter + garis regresipolyfit lalu gambar manualsns.regplot(x=x, y=y)
Box plot dua kelompokplt.boxplot([a, b])sns.boxplot(data=[a, b])
Matriks korelasi sebagai gambarimshow + colorbar manualsns.heatmap(matriks)

Soal tampilan: sekali memanggil sns.set_theme(), seluruh figur berikutnya — termasuk figur matplotlib polos — memakai tema seaborn: grid aktif, latar sumbu kelabu muda, palet warna serasi. Efeknya global karena seaborn menulis ke rcParams matplotlib.

Histogram 30 Siswa, Digambar Ulang #

Grafik pertama adalah histogram nilai 30 siswa — data yang sama dengan chapter Histogram dan chapter Visualisasi dengan matplotlib. Parameter bins=5 dipakai agar kelasnya identik dengan tabel frekuensi manual; setelah grafik terbentuk, tinggi batang dibaca kembali dari objek ax sebagai bukti bahwa yang dikembalikan seaborn memang Axes matplotlib:

python
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import seaborn as sns

nilai = [60, 62, 63, 65, 65, 67, 68, 68, 69, 70,
         70, 71, 72, 72, 73, 74, 74, 74, 75, 75,
         76, 77, 77, 78, 79, 80, 81, 82, 83, 84]

fig, ax = plt.subplots()
sns.histplot(nilai, bins=5, ax=ax)
ax.set_xlabel("Nilai ulangan")
ax.set_ylabel("Jumlah siswa")
ax.set_title("Sebaran Nilai 30 Siswa")
fig.savefig("/tmp/histogram_seaborn.png")
plt.close()

tinggi = [p.get_height() for p in ax.patches]
tepi = [p.get_x() for p in ax.patches]
tepi.append(tepi[-1] + ax.patches[-1].get_width())
print("tinggi batang:", [int(t) for t in tinggi])
print("tepi kelas   :", [f"{t:.1f}" for t in tepi])

Output yang Dihasilkan:

text
tinggi batang: [3, 6, 9, 7, 5]
tepi kelas   : ['60.0', '64.8', '69.6', '74.4', '79.2', '84.0']

Angka-angka ini identik dengan keluaran plt.hist pada chapter sebelumnya: frekuensi [3, 6, 9, 7, 5] dan enam batas kelas 60.0 sampai 84.0 — seaborn memakai mesin binning yang sama. Gambar yang tersimpan memperlihatkan lima batang dengan puncak 9 siswa di kelas tengah, menurun ke 6 lalu 3 di kiri dan ke 7 lalu 5 di kanan: sebaran menumpuk di tengah. Label dipasang lewat ax.set_xlabel dan ax.set_title — metode ax yang sama dari chapter matplotlib, karena objeknya memang jenis yang sama. Satu parameter baru yang layak dicoba: sns.histplot(nilai, bins=5, kde=True) menambahkan kurva kernel density estimate, taksiran halus bentuk sebaran, di atas batang.

Box Plot Dua Kelas, Angka yang Sama #

Grafik kedua membandingkan sebaran dua kelas. Chapter Box Plot menghitung komponennya dengan metode inclusive, dan seaborn menghitung kuartil dengan interpolasi linear yang sama seperti np.percentile — dua jalur yang bertemu di angka identik, sehingga kotak yang tergambar bisa dibaca ulang dari data:

python
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

kelas_a = [68, 75, 75, 81, 84, 88, 90, 92, 97]
kelas_b = [74, 78, 80, 81, 82, 83, 85, 87, 90]

fig, ax = plt.subplots()
sns.boxplot(data=[kelas_a, kelas_b], ax=ax)
ax.set_xticks([0, 1], ["Kelas A", "Kelas B"])
ax.set_ylabel("Nilai")
ax.set_title("Perbandingan Sebaran Dua Kelas")
fig.savefig("/tmp/boxplot_seaborn.png")
plt.close()

print("kuartil A (Q1, median, Q3):", np.percentile(kelas_a, [25, 50, 75]))
print("kuartil B (Q1, median, Q3):", np.percentile(kelas_b, [25, 50, 75]))
print("whisker A:", min(kelas_a), "sampai", max(kelas_a))
print("whisker B:", min(kelas_b), "sampai", max(kelas_b))

Output yang Dihasilkan:

text
kuartil A (Q1, median, Q3): [75. 84. 90.]
kuartil B (Q1, median, Q3): [80. 82. 85.]
whisker A: 68 sampai 97
whisker B: 74 sampai 90

Gambar yang tersimpan menampilkan dua kotak bersebelahan pada satu sumbu, dan seluruh angkanya persis chapter Box Plot: kotak Kelas A membentang dari Q1 = 75 ke Q3 = 90 (IQR 15) dengan median 84 dan whisker 68 sampai 97, sedangkan kotak Kelas B hanya selebar 5 poin (80 ke 85), median 82, whisker 74 sampai 90 — tanpa outlier di luar whisker. Argumen data=[kelas_a, kelas_b] adalah bentuk wide-form: tiap list menjadi satu kotak, dan posisinya di sumbu-x memang 0 dan 1 sehingga label kelas dipasang lewat ax.set_xticks.

Scatter Plot dengan Garis Regresi: regplot #

Grafik ketiga adalah pasangan jam belajar dan nilai enam siswa — data chapter Korelasi dan chapter Simple Linear Regression. Di chapter matplotlib kita menggambar titiknya saja; garis regresi harus dihitung sendiri dengan np.polyfit lalu digambar sebagai plt.plot. sns.regplot melakukan keduanya dalam satu panggilan: titik, garis least squares, dan pita keyakinan di sekeliling garis:

python
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import statistics

jam = [2, 3, 4, 5, 6, 7]
nilai = [65, 70, 75, 82, 86, 91]

fig, ax = plt.subplots()
sns.regplot(x=jam, y=nilai, ax=ax)
ax.set_xlabel("Jam belajar per hari")
ax.set_ylabel("Nilai ulangan")
ax.set_title("Jam Belajar terhadap Nilai")
fig.savefig("/tmp/regresi_seaborn.png")
plt.close()

b1, b0 = np.polyfit(jam, nilai, 1)
print(f"slope b1     : {b1:.4f}")
print(f"intercept b0 : {b0:.4f}")
print(f"korelasi r   : {statistics.correlation(jam, nilai):.4f}")

Output yang Dihasilkan:

text
slope b1     : 5.2857
intercept b0 : 54.3810
korelasi r   : 0.9981

Garis yang digambar regplot adalah garis least squares yang sama dengan hitungan manual chapter Simple Linear Regression:

text
ŷ = b0 + b1 · x = 54.3810 + 5.2857 · x

Slope 5.2857 berarti tiap jam belajar tambahan berasosiasi dengan 5.29 poin nilai, dan prediksi pada 5 jam — 54.3810 + 5.2857 × 5 = 80.81 — angka yang sama dengan evaluasi model pada chapter itu. Korelasi 0.9981 menjelaskan mengapa keenam titik duduk rapat di sekitar garis. Pita keabuan di sekeliling garis adalah confidence band 95 persen yang dibangun lewat bootstrap; ci=None membuangnya bila garis polos diinginkan. Inilah contoh paling terang perbedaan dua pustaka: hasilnya bisa dicapai keduanya, tetapi satu panggilan regplot menggantikan beberapa baris perakitan matplotlib.

countplot: Menghitung sambil Menggambar #

Dua grafik berikut adalah jenis yang tidak punya padanan satu baris di matplotlib. Yang pertama, countplot, menghitung frekuensi kategori lalu menggambarkannya — pekerjaan value_counts() dari chapter pandas, kini dalam bentuk gambar:

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

df = pd.DataFrame({
    "kelas": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"],
    "matematika": [68, 75, 75, 81, 84, 88, 90, 92, 97, 70],
    "bahasa": [70, 78, 74, 79, 85, 82, 88, 90, 94, 72],
})

fig, ax = plt.subplots()
sns.countplot(data=df, x="kelas", ax=ax)
ax.set_ylabel("Jumlah siswa")
ax.set_title("Sepuluh Siswa Menurut Kelas")
fig.savefig("/tmp/countplot_kelas.png")
plt.close()

print("tinggi batang:", [int(p.get_height()) for p in ax.patches])
print(df["kelas"].value_counts())

Output yang Dihasilkan:

text
tinggi batang: [5, 5]
kelas
A    5
B    5
Name: count, dtype: int64

Tinggi kedua batang terbaca dari ax.patches dan berimpit dengan keluaran value_counts() pada chapter pandas: lima dan lima. Kekuatan sekaligus batasannya ada di situ: countplot menghitung baris data mentah, sehingga tabel yang sudah teragregasi — penjualan kopi 120/85/60/45/30 cangkir dari chapter Diagram Batang — tetap paling tepat digambar dengan plt.bar(kategori, frekuensi). Memilih alat berarti membaca dulu bentuk datanya: baris mentah atau ringkasan jadi.

heatmap: Matriks Korelasi sebagai Gambar #

Jenis kedua, heatmap, menggambar matriks sebagai grid sel berwarna; kombinasi paling lazim adalah matriks korelasi — hitung dengan df.corr(), serahkan ke sns.heatmap. Kita pakai kolom numerik DataFrame sepuluh siswa, pasangan matematika-bahasa yang korelasinya sudah dihitung pada chapter Statistik dengan NumPy:

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

df = pd.DataFrame({
    "kelas": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"],
    "matematika": [68, 75, 75, 81, 84, 88, 90, 92, 97, 70],
    "bahasa": [70, 78, 74, 79, 85, 82, 88, 90, 94, 72],
})

matriks = df[["matematika", "bahasa"]].corr()
print(matriks.round(4))

fig, ax = plt.subplots()
sns.heatmap(matriks, annot=True, fmt=".4f", cmap="vlag",
            vmin=-1, vmax=1, ax=ax)
ax.set_title("Korelasi Nilai Sepuluh Siswa")
fig.savefig("/tmp/heatmap_korelasi.png")
plt.close()

Output yang Dihasilkan:

text
            matematika  bahasa
matematika      1.0000  0.9714
bahasa          0.9714  1.0000

Cara membaca heatmap: baris dan kolom adalah variabel, dan tiap sel menampilkan korelasi pasangannya. Diagonal selalu 1.0000 karena variabel berkorelasi sempurna dengan dirinya sendiri, dan matriks simetris — sel (matematika, bahasa) dan (bahasa, matematika) berisi angka yang sama. Satu-satunya pasangan informatif, r = 0.9714, muncul di dua sel luar diagonal: korelasi positif sangat kuat yang dibahas tuntas pada chapter Korelasi. Warna memakai colormap diverging "vlag" dengan skala dibentang penuh vmin=-1, vmax=1: ujung skala mewakili korelasi −1 dan +1, titik tengah mewakili 0, sehingga tanda korelasi terbedakan warnanya — bukan sekadar gelap-terang — dan angka 0.9714 tampak jenuh di sisi positif. annot=True menuliskan nilai di dalam sel dengan format fmt=".4f", dan colorbar di samping gambar memetakan warna ke angka. Pada matriks 2×2 ini heatmap memang sederhana, tetapi panggilan yang sama menskalakan ke belasan kolom numerik — tempat grid warna menjadi alat baca pola korelasi antar banyak variabel sekaligus.

DataFrame dan hue: Grafik per Kelompok #

Kekuatan terbesar seaborn muncul saat data berbentuk long-form: satu baris per observasi, kolom untuk kategori. DataFrame sepuluh siswa berbentuk wide-form — satu kolom per mapel — dan df.melt() meratakannya menjadi pasangan kelas-mapel-nilai (20 baris). Parameter hue="kelas" lalu memecah tiap kategori sumbu-x menurut warna, tanpa loop dan tanpa memotong kolom manual:

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

df = pd.DataFrame({
    "kelas": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"],
    "matematika": [68, 75, 75, 81, 84, 88, 90, 92, 97, 70],
    "bahasa": [70, 78, 74, 79, 85, 82, 88, 90, 94, 72],
})

panjang = df.melt(id_vars="kelas", value_vars=["matematika", "bahasa"],
                  var_name="mapel", value_name="nilai")
print(panjang.groupby(["mapel", "kelas"])["nilai"].mean())

fig, ax = plt.subplots()
sns.boxplot(data=panjang, x="mapel", y="nilai", hue="kelas", ax=ax)
ax.set_xlabel("Mata pelajaran")
ax.set_ylabel("Nilai")
ax.set_title("Sebaran Nilai Menurut Mapel dan Kelas")
fig.savefig("/tmp/boxplot_hue.png")
plt.close()

fig, ax = plt.subplots()
sns.barplot(data=panjang, x="mapel", y="nilai", hue="kelas",
            errorbar="sd", ax=ax)
ax.set_xlabel("Mata pelajaran")
ax.set_ylabel("Rata-rata nilai")
ax.set_title("Rata-rata Nilai Menurut Mapel dan Kelas")
fig.savefig("/tmp/barplot_hue.png")
plt.close()

Output yang Dihasilkan:

text
mapel       kelas
bahasa      A        77.2
            B        85.2
matematika  A        76.6
            B        87.4
Name: nilai, dtype: float64

Gambar box plot menampilkan empat kotak: matematika-A (Q1 75, median 75, Q3 81), matematika-B (88, 90, 92), bahasa-A (74, 78, 79), dan bahasa-B (82, 88, 90) — kotak kelas B di kedua mapel duduk lebih tinggi, tetapi kotak matematika-B hanya selebar IQR 4 sementara whiskernya menjulur dari 70 sampai 97: rata-rata unggul, sebaran jauh lebih lebar. Angka yang tercetak — 76.6, 77.2, 87.4, 85.2 — identik dengan keluaran groupby pada chapter pandas; barplot menggambarnya sebagai tinggi batang, dengan whisker satu standard deviation di tiap sisi (6.19 dan 10.29 poin untuk matematika A dan B, 5.63 dan 8.56 untuk bahasa A dan B — dihitung dengan pembagi n − 1).

matplotlib atau seaborn? Panduan Memilih #

seaborn bukan pengganti matplotlib — keduanya menggambar objek yang sama, dan figur seaborn dapat disunting lanjut dengan metode ax dari chapter matplotlib. Perbedaannya ada pada titik mulai: berapa banyak perakitan yang disediakan pustaka sebelum Anda menulis baris pertama. Tabel berikut merangkum pilihan yang wajar untuk tugas yang paling sering muncul di analisis statistik; panduan memilih bentuk grafik itu sendiri dibahas pada Section Visualisasi Data dan dirangkum di NIST/SEMATECH e-Handbook .

TugasPilihan wajarAlasan singkat
Histogram, box plot, scatter dari list kecilkeduanyahasil angka identik; pilih sesuai selera kode
Diagram batang dari tabel frekuensi jadiplt.barcountplot menuntut data mentah per baris
Diagram batang dari data mentah per kategorisns.countplotmenghitung dan menggambar sekaligus
Scatter + garis regresi + pita keyakinansns.regplotpadanan matplotlib butuh polyfit manual
Grafik per kelompok dari DataFrame (hue)sns.boxplot, sns.barplottanpa loop dan tanpa memotong kolom
Matriks korelasi sebagai gambarsns.heatmapimshow + colorbar manual jadi satu baris
Kontrol presisi tiap elemen, layout khususmatplotlib object-orientedakses penuh ke artist
Tema seragam seluruh laporansns.set_themesatu baris untuk seluruh rcParams

Aturan praktisnya sederhana: bila grafik memetakan statistik dari tabel berlabel — agregasi, pengelompokan, pemecahan warna — seaborn menghemat baris; bila grafik menuntut susunan khusus, mulai dari matplotlib dengan pengetahuan fig/ax yang sudah Anda miliki. Keduanya kerap dipakai bergantian dalam satu skrip: seaborn untuk draf eksplorasi, matplotlib untuk penyuntingan akhir figur terpilih.

Skrip Terpadu: visualisasi_seaborn.py #

Empat grafik inti chapter ini dirangkai menjadi satu skrip yang dapat diunduh dan dijalankan ulang — pola yang sama dengan skrip pendukung chapter-chapter sebelumnya di section ini. Jalankan dengan python3 visualisasi_seaborn.py:

"""visualisasi_seaborn.py — empat grafik statistik dari data section ini."""

import matplotlib
matplotlib.use("Agg")  # backend tanpa layar: gambar disimpan, bukan ditampilkan
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd

nilai = [60, 62, 63, 65, 65, 67, 68, 68, 69, 70,
         70, 71, 72, 72, 73, 74, 74, 74, 75, 75,
         76, 77, 77, 78, 79, 80, 81, 82, 83, 84]
jam = [2, 3, 4, 5, 6, 7]
nilai6 = [65, 70, 75, 82, 86, 91]
df = pd.DataFrame({
    "kelas": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"],
    "matematika": [68, 75, 75, 81, 84, 88, 90, 92, 97, 70],
    "bahasa": [70, 78, 74, 79, 85, 82, 88, 90, 94, 72],
})

sns.set_theme()  # tema diterapkan sekali di awal, seluruh grafik mengikuti

fig, ax = plt.subplots()  # grafik 1: histogram sebaran nilai 30 siswa
sns.histplot(nilai, bins=5, ax=ax)
ax.set_xlabel("Nilai ulangan")
ax.set_ylabel("Jumlah siswa")
ax.set_title("Sebaran Nilai 30 Siswa")
fig.savefig("histogram_seaborn.png")
plt.close()
print("histogram_seaborn.png tersimpan")

fig, ax = plt.subplots()  # grafik 2: scatter dengan garis regresi
sns.regplot(x=jam, y=nilai6, ax=ax)
ax.set_xlabel("Jam belajar per hari")
ax.set_ylabel("Nilai ulangan")
ax.set_title("Jam Belajar terhadap Nilai")
fig.savefig("regresi_seaborn.png")
plt.close()
print("regresi_seaborn.png tersimpan")

matriks = df[["matematika", "bahasa"]].corr()
fig, ax = plt.subplots()  # grafik 3: heatmap matriks korelasi
sns.heatmap(matriks, annot=True, fmt=".4f", cmap="vlag",
            vmin=-1, vmax=1, ax=ax)
ax.set_title("Korelasi Nilai Sepuluh Siswa")
fig.savefig("heatmap_korelasi.png")
plt.close()
print("heatmap_korelasi.png tersimpan")

panjang = df.melt(id_vars="kelas", value_vars=["matematika", "bahasa"],
                  var_name="mapel", value_name="nilai")
fig, ax = plt.subplots()  # grafik 4: boxplot dengan hue
sns.boxplot(data=panjang, x="mapel", y="nilai", hue="kelas", ax=ax)
ax.set_xlabel("Mata pelajaran")
ax.set_ylabel("Nilai")
ax.set_title("Sebaran Nilai Menurut Mapel dan Kelas")
fig.savefig("boxplot_hue.png")
plt.close()
print("boxplot_hue.png tersimpan")

b1, b0 = np.polyfit(jam, nilai6, 1)
print(f"slope regresi     : {b1:.4f}")
print(f"korelasi mapel    : {matriks.loc['matematika', 'bahasa']:.4f}")
print(f"mean matematika A : {df.loc[df['kelas'] == 'A', 'matematika'].mean():.1f}")
print(f"mean matematika B : {df.loc[df['kelas'] == 'B', 'matematika'].mean():.1f}")

Output yang Dihasilkan:

text
histogram_seaborn.png tersimpan
regresi_seaborn.png tersimpan
heatmap_korelasi.png tersimpan
boxplot_hue.png tersimpan
slope regresi     : 5.2857
korelasi mapel    : 0.9714
mean matematika A : 76.6
mean matematika B : 87.4

Skrip ini menempatkan sns.set_theme() di awal agar keempat grafik konsisten — sekaligus menunjukkan bahwa tema tidak mengubah angka: slope 5.2857, korelasi 0.9714, dan mean 76.6/87.4 semua identik dengan contoh-contoh di atas. Berbeda dari blok ilustrasi yang menulis berkas uji ke /tmp, skrip unduhan ini menyimpan empat berkas png di folder tempat ia dijalankan; ganti nama berkas pada fig.savefig bila ingin lokasi lain. Galeri jenis grafik beserta parameternya terdokumentasi di seaborn.pydata.org , dan karena setiap grafiknya berujung pada Axes matplotlib, rujukan matplotlib.org tetap relevan untuk penyuntingan lanjutan.

Section Statistika dengan Python kini lengkap: dari statistics bawaan, NumPy, pandas, matplotlib, seaborn, hingga SciPy dan regresi. Grafik yang baik memperlihatkan data sebagaimana adanya, tetapi keputusan apa yang boleh disimpulkan dari sebuah gambar — dan apa yang wajib disertakan saat melaporkannya — bukan lagi urusan pustaka. Section berikutnya, Interpretasi dan Etika, membahas justru sisi itu.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

visualisasi_seaborn.py

Unduh berkas
python
"""visualisasi_seaborn.py — empat grafik statistik dari data section ini."""

import matplotlib
matplotlib.use("Agg")  # backend tanpa layar: gambar disimpan, bukan ditampilkan
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd

nilai = [60, 62, 63, 65, 65, 67, 68, 68, 69, 70,
         70, 71, 72, 72, 73, 74, 74, 74, 75, 75,
         76, 77, 77, 78, 79, 80, 81, 82, 83, 84]
jam = [2, 3, 4, 5, 6, 7]
nilai6 = [65, 70, 75, 82, 86, 91]
df = pd.DataFrame({
    "kelas": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"],
    "matematika": [68, 75, 75, 81, 84, 88, 90, 92, 97, 70],
    "bahasa": [70, 78, 74, 79, 85, 82, 88, 90, 94, 72],
})

sns.set_theme()  # tema diterapkan sekali di awal, seluruh grafik mengikuti

fig, ax = plt.subplots()  # grafik 1: histogram sebaran nilai 30 siswa
sns.histplot(nilai, bins=5, ax=ax)
ax.set_xlabel("Nilai ulangan")
ax.set_ylabel("Jumlah siswa")
ax.set_title("Sebaran Nilai 30 Siswa")
fig.savefig("histogram_seaborn.png")
plt.close()
print("histogram_seaborn.png tersimpan")

fig, ax = plt.subplots()  # grafik 2: scatter dengan garis regresi
sns.regplot(x=jam, y=nilai6, ax=ax)
ax.set_xlabel("Jam belajar per hari")
ax.set_ylabel("Nilai ulangan")
ax.set_title("Jam Belajar terhadap Nilai")
fig.savefig("regresi_seaborn.png")
plt.close()
print("regresi_seaborn.png tersimpan")

matriks = df[["matematika", "bahasa"]].corr()
fig, ax = plt.subplots()  # grafik 3: heatmap matriks korelasi
sns.heatmap(matriks, annot=True, fmt=".4f", cmap="vlag",
            vmin=-1, vmax=1, ax=ax)
ax.set_title("Korelasi Nilai Sepuluh Siswa")
fig.savefig("heatmap_korelasi.png")
plt.close()
print("heatmap_korelasi.png tersimpan")

panjang = df.melt(id_vars="kelas", value_vars=["matematika", "bahasa"],
                  var_name="mapel", value_name="nilai")
fig, ax = plt.subplots()  # grafik 4: boxplot dengan hue
sns.boxplot(data=panjang, x="mapel", y="nilai", hue="kelas", ax=ax)
ax.set_xlabel("Mata pelajaran")
ax.set_ylabel("Nilai")
ax.set_title("Sebaran Nilai Menurut Mapel dan Kelas")
fig.savefig("boxplot_hue.png")
plt.close()
print("boxplot_hue.png tersimpan")

b1, b0 = np.polyfit(jam, nilai6, 1)
print(f"slope regresi     : {b1:.4f}")
print(f"korelasi mapel    : {matriks.loc['matematika', 'bahasa']:.4f}")
print(f"mean matematika A : {df.loc[df['kelas'] == 'A', 'matematika'].mean():.1f}")
print(f"mean matematika B : {df.loc[df['kelas'] == 'B', 'matematika'].mean():.1f}")