Analisis yang berhenti pada angka-angka berserakan belum menyelesaikan pekerjaannya. Bagian terakhir dari alur kerja analis adalah komunikasi: mengubah temuan menjadi laporan yang bisa dibaca orang lain — guru wali kelas, kepala sekolah, atau rekan satu tim — tanpa harus membuka ulang seluruh perhitungan. Laporan yang baik memuat statistik inti yang mudah dipindai, menyebut anomali secara eksplisit, dan menutup dengan kesimpulan yang merujuk angka. Pada bagian ini kita menyusun laporan_lengkap.py, satu skrip mandiri yang menyatukan seluruh temuan Bagian 1 sampai 3 dan mencetaknya sebagai laporan rapi.
Merancang Laporan #
Empat keputusan desain menentukan bentuk laporan. Pertama, skrip menyarikan seluruh perhitungan ke dalam dirinya sendiri: dataset tertanam sebagai daftar baris, sehingga siapa pun yang menjalankan ulang skrip tahun depan akan mendapat angka yang persis sama — sifat reproduktif yang menjadi standar praktik analisis data. Kedua, tabel statistik tiap mata pelajaran dicetak satu baris per mapel, bukan satu halaman per mapel, agar laporan tetap ringkas. Ketiga, temuan anomali ditempatkan di bagian tersendiri — laporan yang menyembunyikan outlier akan menyesatkan pembacanya. Keempat, kesimpulan ditulis sebagai kalimat pendek yang setiap angkanya dihitung ulang dari data, bukan disalin dari bagian sebelumnya, sehingga skrip tetap benar bila data diganti.
Skrip laporan_lengkap.py #
Berikut isi lengkap skripnya. Simpan sebagai laporan_lengkap.py di folder yang sama dengan halaman ini, lalu jalankan dengan python3 laporan_lengkap.py:
"""laporan_lengkap.py -- laporan statistik nilai ujian satu kelas.
Dataset: nilai ujian 30 siswa untuk tiga mata pelajaran.
Seluruh perhitungan memakai data yang melekat pada skrip ini,
sehingga laporan dapat direproduksi kapan saja tanpa berkas lain.
"""
import statistics
from scipy.stats import skew
DATA = [
("Adi", 70, 78, 88), ("Bunga", 88, 70, 60), ("Cakra", 76, 91, 93),
("Dewi", 80, 84, 73), ("Eka", 65, 67, 70), ("Fajar", 84, 82, 78),
("Gilang", 92, 74, 64), ("Hana", 68, 89, 90), ("Intan", 78, 76, 75),
("Joko", 86, 80, 83), ("Kirana", 73, 86, 58), ("Lukman", 62, 64, 71),
("Maya", 82, 93, 68), ("Nadia", 45, 72, 76), ("Okta", 90, 88, 92),
("Putri", 66, 75, 65), ("Qori", 77, 81, 74), ("Rafi", 81, 68, 79),
("Sari", 60, 95, 62), ("Tirta", 79, 77, 72), ("Utami", 74, 69, 85),
("Vina", 87, 85, 67), ("Wahyu", 67, 73, 77), ("Yuni", 76, 90, 70),
("Zaki", 83, 80, 81), ("Bagas", 69, 87, 59), ("Citra", 75, 71, 86),
("Danu", 64, 92, 74), ("Eni", 72, 83, 63), ("Filda", 85, 80, 82),
]
MAPEL = [("matematika", 1), ("bahasa", 2), ("sains", 3)]
def nilai_kolom(kolom):
return [baris[kolom] for baris in DATA]
def deteksi_outlier(kolom):
"""Kembalikan daftar (nama, nilai, z) dengan |z| > 2 (std sampel)."""
nilai = nilai_kolom(kolom)
mean = statistics.mean(nilai)
s = statistics.stdev(nilai)
hasil = []
for baris, x in zip(DATA, nilai):
z = (x - mean) / s
if abs(z) > 2:
hasil.append((baris[0], x, z))
return hasil
print("LAPORAN NILAI UJIAN - 30 SISWA, 3 MATA PELAJARAN")
print("=" * 48)
print(f"{'mapel':<11}{'mean':>7}{'median':>8}{'modus':>10}{'std':>7}{'min':>4}{'max':>4}{'skew':>7}")
for nama, kolom in MAPEL:
nilai = nilai_kolom(kolom)
print(f"{nama:<11}{statistics.mean(nilai):>7.2f}{statistics.median(nilai):>8.1f}"
f"{str(statistics.multimode(nilai)):>10}{statistics.stdev(nilai):>7.2f}"
f"{min(nilai):>4}{max(nilai):>4}{skew(nilai):>7.2f}")
print()
print("Deteksi outlier (|z| > 2, std sampel):")
for nama, kolom in MAPEL:
out = deteksi_outlier(kolom)
if out:
for siswa, x, z in out:
print(f" {nama:<10s}: {siswa}, nilai {x}, z = {z:.2f}")
else:
print(f" {nama:<10s}: tidak ada")
mat = nilai_kolom(1)
bah = nilai_kolom(2)
sai = nilai_kolom(3)
mean_penuh = statistics.mean(mat)
mean_tanpa = statistics.mean([x for x in mat if x != 45])
print()
print("Catatan mean vs median:")
print(f" matematika: mean {mean_penuh:.2f}, median {statistics.median(mat):.1f};")
print(f" tanpa nilai 45, mean naik ke {mean_tanpa:.2f}, "
f"median tetap {statistics.median([x for x in mat if x != 45]):.1f}")
print()
print("Kesimpulan")
print(f"1. bahasa: capaian tertinggi dan sebaran paling rapat "
f"(mean {statistics.mean(bah):.2f}, std {statistics.stdev(bah):.2f}).")
print(f"2. matematika: satu outlier rendah (nilai 45) menarik mean "
f"dari {mean_tanpa:.2f} menjadi {mean_penuh:.2f}.")
print(f"3. sains: sedikit miring kanan (skew {skew(sai):.2f}); mean "
f"{statistics.mean(sai):.2f} di atas median {statistics.median(sai):.1f}.")
print("4. seluruh angka di atas dihitung ulang dari data yang melekat pada skrip ini.")Struktur skrip mengikuti empat keputusan desain di atas. Daftar DATA dan pemetaan MAPEL menjadi sumber tunggal kebenaran; dua function kecil — nilai_kolom untuk mengambil satu kolom dan deteksi_outlier untuk aturan |z| > 2 — dipakai ulang oleh beberapa bagian laporan. Tabel utama dicetak dengan f-string yang formatnya menjajarkan kolom (:<11 rata kiri untuk nama mapel, :>7.2f rata kanan untuk angka), teknik yang sama dengan cuplikan-cuplikan sebelumnya. Perhatikan juga bahwa laporan mencetak nilai dengan pembulatan dua desimal, konsisten dengan seluruh bagian sebelumnya.
Output yang Dihasilkan #
Jalankan skrip dari foldernya, dan laporan lengkap tercetak dalam 21 baris:
Output yang Dihasilkan:
LAPORAN NILAI UJIAN - 30 SISWA, 3 MATA PELAJARAN
================================================
mapel mean median modus std min max skew
matematika 75.13 76.0 [76] 10.37 45 92 -0.68
bahasa 80.00 80.0 [80] 8.55 64 95 -0.04
sains 74.50 74.0 [70, 74] 10.00 58 93 0.15
Deteksi outlier (|z| > 2, std sampel):
matematika: Nadia, nilai 45, z = -2.91
bahasa : tidak ada
sains : tidak ada
Catatan mean vs median:
matematika: mean 75.13, median 76.0;
tanpa nilai 45, mean naik ke 76.17, median tetap 76.0
Kesimpulan
1. bahasa: capaian tertinggi dan sebaran paling rapat (mean 80.00, std 8.55).
2. matematika: satu outlier rendah (nilai 45) menarik mean dari 76.17 menjadi 75.13.
3. sains: sedikit miring kanan (skew 0.15); mean 74.50 di atas median 74.0.
4. seluruh angka di atas dihitung ulang dari data yang melekat pada skrip ini.Membaca Laporan #
Tabel utama memadatkan seluruh temuan Bagian 2 dan 3 ke dalam tiga baris. Baris matematika meringkas inti masalahnya: mean 75.13 di bawah median 76.0, min 45 yang jauh dari max 92, skew −0.68 — semua penanda miring kiri karena outlier pada kolom yang sama. Baris bahasa memperlihatkan sebaran ideal: mean, median, dan modus sama-sama 80, std terkecil (8.55), skew −0.04. Baris sains menampilkan modus ganda [70, 74] dan skew 0.15 yang sesuai mean di atas median. Bagian deteksi outlier mengulang keputusan formal Bagian 3 — Nadia, nilai 45, z = −2.91 — dan bagian catatan mengabadikan eksperimen mean-median: satu nilai menggeser mean 1.04 poin tanpa menggerakkan median.
Setiap angka pada laporan ini telah dihitung dan diverifikasi terpisah pada bagian-bagian sebelumnya, dan skrip menghitungnya kembali dari data yang melekat — dua jalur menuju angka yang sama adalah bentuk paling sederhana dari verifikasi. Bila Anda ingin menyimpan laporan sebagai berkas teks, cukup arahkan keluarannya: python3 laporan_lengkap.py > laporan.txt menghasilkan berkas yang isinya identik dengan tampilan di atas.
Proyek pertama tuntas: dataset dimuat, dibedah, digambarkan bentuknya, dan dilaporkan dengan skrip yang dapat direproduksi. Alur yang sama akan kita jalankan sekali lagi pada data yang karakternya sepenuhnya berbeda — deret waktu harga komoditas yang memuat sel kosong dan anomali — pada Proyek: Analisis Harga Komoditas.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
laporan_lengkap.py
Unduh berkas"""laporan_lengkap.py -- laporan statistik nilai ujian satu kelas.
Dataset: nilai ujian 30 siswa untuk tiga mata pelajaran.
Seluruh perhitungan memakai data yang melekat pada skrip ini,
sehingga laporan dapat direproduksi kapan saja tanpa berkas lain.
"""
import statistics
from scipy.stats import skew
DATA = [
("Adi", 70, 78, 88), ("Bunga", 88, 70, 60), ("Cakra", 76, 91, 93),
("Dewi", 80, 84, 73), ("Eka", 65, 67, 70), ("Fajar", 84, 82, 78),
("Gilang", 92, 74, 64), ("Hana", 68, 89, 90), ("Intan", 78, 76, 75),
("Joko", 86, 80, 83), ("Kirana", 73, 86, 58), ("Lukman", 62, 64, 71),
("Maya", 82, 93, 68), ("Nadia", 45, 72, 76), ("Okta", 90, 88, 92),
("Putri", 66, 75, 65), ("Qori", 77, 81, 74), ("Rafi", 81, 68, 79),
("Sari", 60, 95, 62), ("Tirta", 79, 77, 72), ("Utami", 74, 69, 85),
("Vina", 87, 85, 67), ("Wahyu", 67, 73, 77), ("Yuni", 76, 90, 70),
("Zaki", 83, 80, 81), ("Bagas", 69, 87, 59), ("Citra", 75, 71, 86),
("Danu", 64, 92, 74), ("Eni", 72, 83, 63), ("Filda", 85, 80, 82),
]
MAPEL = [("matematika", 1), ("bahasa", 2), ("sains", 3)]
def nilai_kolom(kolom):
return [baris[kolom] for baris in DATA]
def deteksi_outlier(kolom):
"""Kembalikan daftar (nama, nilai, z) dengan |z| > 2 (std sampel)."""
nilai = nilai_kolom(kolom)
mean = statistics.mean(nilai)
s = statistics.stdev(nilai)
hasil = []
for baris, x in zip(DATA, nilai):
z = (x - mean) / s
if abs(z) > 2:
hasil.append((baris[0], x, z))
return hasil
print("LAPORAN NILAI UJIAN - 30 SISWA, 3 MATA PELAJARAN")
print("=" * 48)
print(f"{'mapel':<11}{'mean':>7}{'median':>8}{'modus':>10}{'std':>7}{'min':>4}{'max':>4}{'skew':>7}")
for nama, kolom in MAPEL:
nilai = nilai_kolom(kolom)
print(f"{nama:<11}{statistics.mean(nilai):>7.2f}{statistics.median(nilai):>8.1f}"
f"{str(statistics.multimode(nilai)):>10}{statistics.stdev(nilai):>7.2f}"
f"{min(nilai):>4}{max(nilai):>4}{skew(nilai):>7.2f}")
print()
print("Deteksi outlier (|z| > 2, std sampel):")
for nama, kolom in MAPEL:
out = deteksi_outlier(kolom)
if out:
for siswa, x, z in out:
print(f" {nama:<10s}: {siswa}, nilai {x}, z = {z:.2f}")
else:
print(f" {nama:<10s}: tidak ada")
mat = nilai_kolom(1)
bah = nilai_kolom(2)
sai = nilai_kolom(3)
mean_penuh = statistics.mean(mat)
mean_tanpa = statistics.mean([x for x in mat if x != 45])
print()
print("Catatan mean vs median:")
print(f" matematika: mean {mean_penuh:.2f}, median {statistics.median(mat):.1f};")
print(f" tanpa nilai 45, mean naik ke {mean_tanpa:.2f}, "
f"median tetap {statistics.median([x for x in mat if x != 45]):.1f}")
print()
print("Kesimpulan")
print(f"1. bahasa: capaian tertinggi dan sebaran paling rapat "
f"(mean {statistics.mean(bah):.2f}, std {statistics.stdev(bah):.2f}).")
print(f"2. matematika: satu outlier rendah (nilai 45) menarik mean "
f"dari {mean_tanpa:.2f} menjadi {mean_penuh:.2f}.")
print(f"3. sains: sedikit miring kanan (skew {skew(sai):.2f}); mean "
f"{statistics.mean(sai):.2f} di atas median {statistics.median(sai):.1f}.")
print("4. seluruh angka di atas dihitung ulang dari data yang melekat pada skrip ini.")