Teknik sudah terkunci pada Bagian 7, prasyaratnya sudah lolos daftar periksa, dan kini keempat pertanyaan dijawab satu per satu dengan angka. Pada bagian ini setiap blok kode berdiri sendiri — fungsi muat_bersih dari Bagian 5 selalu menyertai — sehingga Anda bisa menjalankan bagian mana pun terlepas dari bagian lain. Seluruh angka yang keluar di sini menjadi bahan baku laporan akhir pada Bagian 11, jadi cara membacanya kita kerjakan bersamaan dengan menghitungnya, terutama p-value yang paling sering disalahartikan pemula. Fungsi-fungsi statistiknya berasal dari modul scipy.stats — dokumentasi resminya ada di SciPy Stats .

P1: Korelasi Jam Belajar dengan Nilai #

Pertanyaan pertama menyuruh kita mengukur hubungan dua kolom numerik. Satu pemanggilan pearsonr menjawabnya:

python
import pandas as pd
from scipy import stats


def muat_bersih(path):
    """Muat data mentah dan terapkan seluruh langkah pembersihan."""
    df = pd.read_csv(path).drop_duplicates().reset_index(drop=True)
    df["kelas"] = df["kelas"].str.upper()
    df["kehadiran_persen"] = pd.to_numeric(df["kehadiran_persen"].replace("-", pd.NA))
    df = df.dropna(subset=["kehadiran_persen"]).reset_index(drop=True)
    df["kehadiran_persen"] = df["kehadiran_persen"].astype(int)
    return df


bersih = muat_bersih("../data/nilai_siswa.csv")

hasil = stats.pearsonr(bersih["jam_belajar_mingguan"], bersih["nilai_ujian"])
print("Korelasi Pearson jam belajar vs nilai ujian:")
print(f"  r       : {hasil.statistic:.4f}")
print(f"  p-value : {hasil.pvalue:.2e}")
print(f"  n       : {len(bersih)}")

Output yang Dihasilkan:

text
Korelasi Pearson jam belajar vs nilai ujian:
  r       : 0.9395
  p-value : 1.50e-14
  n       : 30

Mari kita baca kedua angkanya:

  1. r = 0,9395 — hubungan positif dan sangat kuat. Semakin lama siswa belajar, semakin tinggi nilainya, dan titik-titiknya rapat di sekitar satu tren. Angka sebesar ini jarang dijumpai pada data dunia nyata; data latihan memang dirancang lebih bersih daripada kehidupan.
  2. p = 1,50e-14 — notasi ilmiah untuk 0,000000000000015. Maknanya: bila jam belajar dan nilai sebenarnya tidak berhubungan sama sekali, peluang mendapat r sebesar 0,94 dari 30 siswa hampir nol. Nilai ini jauh di bawah ambang lazim 0,001, sehingga hubungan ini bukan main-main kebetulan.
  3. Yang tidak dikatakan angka ini: r tidak menyebut sebab-akibat, dan tidak menyebut berapa poin nilai naik per jam — pekerjaan P4. Untuk diskusi lebih dalam mengenai membaca koefisien ini, kembalilah ke chapter Korelasi dan Kovarians .

P2: Korelasi Kehadiran dengan Nilai — dan Satu Temuan Tak Terduga #

Pertanyaan kedua terlihat serupa dengan yang pertama, tetapi hasilnya menyimpan putaran cerita. Kita hitung korelasi kehadiran dengan nilai, sekaligus korelasi silang kehadiran dengan jam belajar:

python
import pandas as pd
from scipy import stats


def muat_bersih(path):
    """Muat data mentah dan terapkan seluruh langkah pembersihan."""
    df = pd.read_csv(path).drop_duplicates().reset_index(drop=True)
    df["kelas"] = df["kelas"].str.upper()
    df["kehadiran_persen"] = pd.to_numeric(df["kehadiran_persen"].replace("-", pd.NA))
    df = df.dropna(subset=["kehadiran_persen"]).reset_index(drop=True)
    df["kehadiran_persen"] = df["kehadiran_persen"].astype(int)
    return df


bersih = muat_bersih("../data/nilai_siswa.csv")

r_hadir = stats.pearsonr(bersih["kehadiran_persen"], bersih["nilai_ujian"])
r_silang = stats.pearsonr(bersih["jam_belajar_mingguan"], bersih["kehadiran_persen"])
print("Korelasi Pearson kehadiran vs nilai ujian:")
print(f"  r(kehadiran, nilai) : {r_hadir.statistic:.4f} (p = {r_hadir.pvalue:.2e})")
print(f"  r(jam, kehadiran)   : {r_silang.statistic:.4f} (p = {r_silang.pvalue:.2e})")

Output yang Dihasilkan:

text
Korelasi Pearson kehadiran vs nilai ujian:
  r(kehadiran, nilai) : 0.8664 (p = 6.10e-10)
  r(jam, kehadiran)   : 0.8676 (p = 5.44e-10)

Pembacaannya menuntut kehati-hatian tambahan:

  1. Kehadiran memang berkorelasi kuat dengan nilai — r = 0,8664, jauh dari nol dan signifikan. Jawaban P2 secara teknis: ya, berhubungan.
  2. Tetapi kehadiran juga berkorelasi kuat dengan jam belajar — r = 0,8676, hampir persis sekuat korelasinya dengan nilai. Siswa yang rajin belajar umumnya juga rajin hadir; kedua kebiasaan itu berjalan beriringan dalam data ini.
  3. Akibatnya, sumbangan kehadiran sulit dipisahkan dari jam belajar. Kita tidak bisa mengatakan “kehadiran menaikkan nilai terlepas dari jam belajar”, karena hampir tidak ada siswa yang hadir rajin tetapi belajar sedikit — bahan pembandingnya nyaris tidak ada di data. Ini keterbatasan desain data, bukan kesalahan hitungan, dan akan ditulis apa adanya pada temuan laporan.

P3: Uji-t Kelas A versus Kelas B #

Pertanyaan ketiga membandingkan dua kelompok bebas. Uji-t dua sampel bebas menguji hipotesis nol bahwa rata-rata populasi kedua kelas sama — kerangka hipotesisnya pernah kita bahas pada chapter Pengujian Hipotesis :

python
import pandas as pd
from scipy import stats


def muat_bersih(path):
    """Muat data mentah dan terapkan seluruh langkah pembersihan."""
    df = pd.read_csv(path).drop_duplicates().reset_index(drop=True)
    df["kelas"] = df["kelas"].str.upper()
    df["kehadiran_persen"] = pd.to_numeric(df["kehadiran_persen"].replace("-", pd.NA))
    df = df.dropna(subset=["kehadiran_persen"]).reset_index(drop=True)
    df["kehadiran_persen"] = df["kehadiran_persen"].astype(int)
    return df


bersih = muat_bersih("../data/nilai_siswa.csv")

nilai_a = bersih.loc[bersih["kelas"] == "A", "nilai_ujian"]
nilai_b = bersih.loc[bersih["kelas"] == "B", "nilai_ujian"]
hasil = stats.ttest_ind(nilai_a, nilai_b)

print(f"Rata-rata kelas A : {nilai_a.mean():.2f} (n = {len(nilai_a)}, "
      f"std = {nilai_a.std():.2f})")
print(f"Rata-rata kelas B : {nilai_b.mean():.2f} (n = {len(nilai_b)}, "
      f"std = {nilai_b.std():.2f})")
print(f"Selisih rata-rata : {nilai_a.mean() - nilai_b.mean():.2f} poin")
print(f"t = {hasil.statistic:.4f}")
print(f"p-value = {hasil.pvalue:.4f}")
print(f"p lebih besar dari 0,05? {hasil.pvalue > 0.05}")

Output yang Dihasilkan:

text
Rata-rata kelas A : 70.60 (n = 15, std = 13.02)
Rata-rata kelas B : 66.87 (n = 15, std = 12.85)
Selisih rata-rata : 3.73 poin
t = 0.7904
p-value = 0.4359
p lebih besar dari 0,05? True

Inilah angka yang paling harus dibaca pelan-pelan:

  1. Selisih 3,73 poin terlihat nyata di atas kertas, tetapi uji-t menilainya wajar muncul dari kebetulan. p-value 0,4359 berarti: bila kedua kelas sesungguhnya setara, selisih sebesar itu atau lebih masih muncul sekitar 44 persen kali pengambilan sampel. Angka seperti itu tidak memberi alasan menolak hipotesis nol.
  2. Kesimpulan yang benar adalah “belum ada bukti cukup bahwa kedua kelas berbeda” — bukan “terbukti kedua kelas setara”. Gagal menemukan bukti bukanlah bukti ketiadaannya; dengan 15 siswa per kelas, hanya selisih yang besar yang bisa terdeteksi.
  3. p-value bukan peluang hipotesis nol benar. Ia menjawab pertanyaan tentang data semacam ini seberapa sering muncul, bukan pertanyaan tentang kebenaran kelas. Salah baca ini adalah sumber utama klaim berlebihan dalam laporan pemula.

P4: Regresi Linear Nilai atas Jam Belajar #

Pertanyaan terakhir meminta besaran perkiraan. linregress mencocokkan garis nilai = intercept + slope × jam pada data, lalu kita gunakan garisnya untuk memperkirakan nilai pada beberapa titik jam belajar:

python
import pandas as pd
from scipy import stats


def muat_bersih(path):
    """Muat data mentah dan terapkan seluruh langkah pembersihan."""
    df = pd.read_csv(path).drop_duplicates().reset_index(drop=True)
    df["kelas"] = df["kelas"].str.upper()
    df["kehadiran_persen"] = pd.to_numeric(df["kehadiran_persen"].replace("-", pd.NA))
    df = df.dropna(subset=["kehadiran_persen"]).reset_index(drop=True)
    df["kehadiran_persen"] = df["kehadiran_persen"].astype(int)
    return df


bersih = muat_bersih("../data/nilai_siswa.csv")

hasil = stats.linregress(bersih["jam_belajar_mingguan"], bersih["nilai_ujian"])
print(f"slope     : {hasil.slope:.4f}")
print(f"intercept : {hasil.intercept:.4f}")
print(f"r         : {hasil.rvalue:.4f}")
print(f"R-kuadrat : {hasil.rvalue ** 2:.4f}")
print(f"p-value   : {hasil.pvalue:.2e}")
print()
for jam in (2, 6, 10, 16):
    perkiraan = hasil.intercept + hasil.slope * jam
    print(f"perkiraan nilai untuk {jam:2d} jam belajar : {perkiraan:.1f}")

Output yang Dihasilkan:

text
slope     : 2.8794
intercept : 42.0505
r         : 0.9395
R-kuadrat : 0.8826
p-value   : 1.50e-14

perkiraan nilai untuk  2 jam belajar : 47.8
perkiraan nilai untuk  6 jam belajar : 59.3
perkiraan nilai untuk 10 jam belajar : 70.8
perkiraan nilai untuk 16 jam belajar : 88.1

Cara membaca koefisien-koefisien tersebut:

  1. Slope 2,88 dibaca sebagai asosiasi rata-rata: tiap tambahan satu jam belajar per minggu sejalan dengan nilai sekitar 2,9 poin lebih tinggi. Perkiraan 6 jam (59,3) dan 10 jam (70,8) berselisih hampir tepat dua kali slope — konsistensi garis lurus.
  2. Intercept 42,05 adalah perkiraan nilai saat nol jam belajar — tetapi tidak ada satu pun siswa yang belajar nol jam (data mulai dari 2 jam), sehingga angka ini berada di luar jangkauan data dan tidak boleh dibawa-bawa sebagai fakta.
  3. R-kuadrat 0,8826 berarti model menjelaskan 88,3 persen variasi nilai — sisa 11,7 persen dijelaskan hal-hal di luar jam belajar: kehadiran, bakat, persiapan, dan keberuntungan menjawab soal.
  4. Garis hanya berlaku pada rentang 2 sampai 16 jam. Memakainya untuk memperkirakan nilai siswa yang belajar 25 jam adalah ekstrapolasi — bentuk melengkung atau mendatarnya hubungan di luar rentang tidak bisa diketahui dari data ini.

Jawaban Keempat Pertanyaan #

Hasil sepanjang bagian ini dirangkum menjadi empat baris jawaban yang siap masuk laporan:

KodePertanyaanJawaban singkatAngka kunci
P1Jam belajar berhubungan dengan nilai?Ya, positif dan sangat kuatr = 0,94; p < 0,001
P2Kehadiran berhubungan dengan nilai?Ya, tetapi sejalan pula dengan jam belajarr = 0,87; r silang = 0,87
P3Rata-rata kedua kelas berbeda bermakna?Belum ada bukti cukupselisih 3,7 poin; p = 0,44
P4Berapa perkiraan tiap tambahan jam?Sekitar 2,9 poin per jamslope = 2,88; R-kuadrat = 0,88

Empat jawaban ini masih berupa angka di atas kertas. Supaya terbaca oleh mata pembaca laporan — dan agar klaim linearitas pada Bagian 7 benar-benar diperiksa — kita gambar bentuknya pada Bagian 9: Memvisualisasikan Temuan .