Tiga bagian terakhir menghasilkan dataset bersih, angka kunci, dan empat grafik — kini semuanya dirangkai menjadi satu skrip yang dapat dijalankan ulang kapan saja, di mana saja. Bagian ini menutup proyek dengan dua pekerjaan: menulis analisis_lengkap.py yang mencetak laporan lima temuan bernomor beserta angka kuncinya, lalu — bagian yang tidak kalah penting — membaca temuan itu dengan kerangka kritis dari Section Interpretasi dan Etika. Sebuah laporan analisis yang hanya berisi angka belum selesai; angka baru bermakna setelah dibaca dengan kesadaran atas apa yang data ini bisa dan tidak bisa katakan.
Skrip analisis_lengkap.py #
Skrip berikut menjalankan alur lengkap proyek dalam satu berkas: memuat dataset bersih, menghitung statistik ringkasan, mengagregasi per departemen, mengukur korelasi, mendeteksi outlier lewat z-score, lalu mencetak lima temuan. Dataset bersih 29 baris disematkan langsung sebagai teks CSV di dalam skrip — keputusan yang membuat skrip mandiri — dan seluruh angka keluarannya adalah angka yang telah diverifikasi pada Bagian 2 dan 3. Simpan skrip ini di folder kerja Anda, lalu jalankan dengan python3 analisis_lengkap.py:
"""analisis_lengkap.py -- analisis menyeluruh dataset karyawan PT Maju Bersama.
Dataset bersih (29 baris) dari Bagian 1 disematkan langsung sebagai teks CSV
agar skrip dapat dijalankan tanpa bergantung pada lokasi berkas data.
"""
import io
import numpy as np
import pandas as pd
csv_bersih = """id,nama,departemen,umur,masa_kerja,gaji,skor_kinerja
P001,Budi Santoso,Produksi,34,6,11.5,79
P002,Agus Wijaya,Produksi,28,3,9.4,74
P003,Joko Prasetyo,Produksi,41,12,14.6,81
P004,Tono Hartono,Produksi,25,1,8.0,70
P005,Eko Saputra,Produksi,38,9,13.5,83
P006,Hendra Gunawan,Produksi,31,5,10.8,77
P007,Udin Samsudin,Produksi,46,16,16.2,80
P008,Wahyu Nugroho,Produksi,52,20,18.8,85
P009,Yanto Kurniawan,Produksi,24,2,8.7,72
P010,Siti Rahmawati,Keuangan,35,8,12.0,76
P011,Dewi Lestari,Keuangan,29,4,9.8,73
P012,Rina Marlina,Keuangan,33.5,6,11.0,75
P013,Fajar Ramadhan,Keuangan,42,14,15.0,78
P014,Indra Maulana,Keuangan,33,7,11.2,74
P015,Prakasa Wibowo,Keuangan,56,22,45.0,88
P016,Maya Anggraini,Pemasaran,30,5,12.3,86
P017,Rian Hidayat,Pemasaran,27,3,11.0,84
P018,Gita Permata,Pemasaran,36,10,14.8,89
P019,Nanda Pratama,Pemasaran,32,7,13.2,87
P020,Citra Kirana,Pemasaran,26,2,10.2,82
P021,Bayu Setiawan,Pemasaran,39,13,16.4,90
P022,Laila Sari,Pemasaran,30,6,12.4,85
P023,Oka Mahendra,Pemasaran,44,18,18.8,91
P024,Tuti Handayani,SDM,37,9,11.7,71
P025,Yuni Astuti,SDM,28,3,8.5,68
P026,Putri Ayu,SDM,33,6,10.2,70
P027,Dedi Kurnia,SDM,40,11,12.4,70
P028,Wawan Susanto,SDM,45,15,14.7,73
P029,Sari Wulandari,SDM,26,1,7.5,67
"""
df = pd.read_csv(io.StringIO(csv_bersih))
mean_gaji = df["gaji"].mean()
std_gaji = df["gaji"].std(ddof=1)
med_gaji = df["gaji"].median()
print("=== Statistik gaji (juta Rp), n =", len(df), "===")
print(f"mean {mean_gaji:.2f} | median {med_gaji:.2f} | std {std_gaji:.2f} | "
f"min {df['gaji'].min():.2f} | max {df['gaji'].max():.2f}")
print()
print("=== Per departemen: n, rata-rata gaji, rata-rata skor ===")
gab = df.groupby("departemen").agg(
n=("id", "count"), gaji_mean=("gaji", "mean"), skor_mean=("skor_kinerja", "mean")
)
for dep, n, gm, sm in zip(gab.index, gab["n"], gab["gaji_mean"], gab["skor_mean"]):
print(f"{dep:9s}: n={n:2d} gaji {gm:5.2f} skor {sm:5.2f}")
print()
r_masa = np.corrcoef(df["gaji"], df["masa_kerja"])[0, 1]
r_skor = np.corrcoef(df["gaji"], df["skor_kinerja"])[0, 1]
print("=== Korelasi gaji ===")
print(f"r(gaji, masa_kerja) = {r_masa:.3f}")
print(f"r(gaji, skor_kinerja) = {r_skor:.3f}")
print()
z = (df["gaji"] - mean_gaji) / std_gaji
out = df[z.abs() > 2]
print("=== Outlier gaji (|z| > 2) ===")
for _, baris in out.iterrows():
print(f"{baris['id']} {baris['nama']}: gaji {baris['gaji']:.1f} juta, z = {z[baris.name]:.2f}")
print()
skor_dept = df.groupby("departemen")["skor_kinerja"].mean()
med_keu = df.loc[df["departemen"] == "Keuangan", "gaji"].median()
gaji_dept = df.groupby("departemen")["gaji"].mean()
print("=== Ringkasan temuan ===")
print(f"1. Sebaran gaji condong kanan: mean {mean_gaji:.2f} juta melampaui median "
f"{med_gaji:.2f} juta karena satu gaji direktur {df['gaji'].max():.1f} juta.")
print(f"2. Gaji seiring masa kerja (r = {r_masa:.3f}) lebih kuat daripada "
f"seiring skor kinerja (r = {r_skor:.3f}).")
print(f"3. Outlier P015 menarik rata-rata gaji Keuangan ke {gaji_dept['Keuangan']:.2f} juta, "
f"jauh di atas mediannya {med_keu:.2f} juta.")
print(f"4. Skor kinerja berbeda antar departemen: Pemasaran {skor_dept['Pemasaran']:.2f} "
f"tertinggi, SDM {skor_dept['SDM']:.2f} terendah (selisih "
f"{skor_dept['Pemasaran'] - skor_dept['SDM']:.2f} poin).")
print("5. Temuan bersifat deskriptif atas 29 baris: korelasi bukan kausalitas, "
"dan generalisasi ke seluruh perusahaan perlu kehati-hatian.")Output yang Dihasilkan:
=== Statistik gaji (juta Rp), n = 29 ===
mean 13.43 | median 12.00 | std 6.75 | min 7.50 | max 45.00
=== Per departemen: n, rata-rata gaji, rata-rata skor ===
Keuangan : n= 6 gaji 17.33 skor 77.33
Pemasaran: n= 8 gaji 13.64 skor 86.75
Produksi : n= 9 gaji 12.39 skor 77.89
SDM : n= 6 gaji 10.83 skor 69.83
=== Korelasi gaji ===
r(gaji, masa_kerja) = 0.775
r(gaji, skor_kinerja) = 0.527
=== Outlier gaji (|z| > 2) ===
P015 Prakasa Wibowo: gaji 45.0 juta, z = 4.68
=== Ringkasan temuan ===
1. Sebaran gaji condong kanan: mean 13.43 juta melampaui median 12.00 juta karena satu gaji direktur 45.0 juta.
2. Gaji seiring masa kerja (r = 0.775) lebih kuat daripada seiring skor kinerja (r = 0.527).
3. Outlier P015 menarik rata-rata gaji Keuangan ke 17.33 juta, jauh di atas mediannya 11.60 juta.
4. Skor kinerja berbeda antar departemen: Pemasaran 86.75 tertinggi, SDM 69.83 terendah (selisih 16.92 poin).
5. Temuan bersifat deskriptif atas 29 baris: korelasi bukan kausalitas, dan generalisasi ke seluruh perusahaan perlu kehati-hatian.Struktur skrip mengikuti urutan bagian-bagian proyek:
- Statistik ringkasan: mean, median, standard deviation, minimum, dan maksimum gaji — angka yang sama dengan
describe()Bagian 2 (mean 13.43, median 12.00, std 6.75). - Agregasi per departemen:
aggdengan tiga keluaran bernama (banyaknya karyawan, rata-rata gaji, rata-rata skor); perulanganzipmencetak tiap baris dengan format rata kanan ({dep:9s}) agar kolomnya sejajar. - Korelasi: dua pasangan yang relevan untuk keputusan gaji — r(gaji, masa kerja) 0.775 dan r(gaji, skor) 0.527.
- Outlier: z-score seperti Bagian 2, mencetak satu-satunya baris dengan |z| di atas 2.
- Ringkasan temuan: lima kalimat bernomor yang masing-masing memuat angka kunci — bentuk terpadat dari laporan kita, dan titik mulai pembacaan kritis di bawah.
Menjalankan skrip yang sama berulang kali selalu menghasilkan keluaran yang identik: tidak ada angka acak, tidak ada waktu sistem, dan seluruh data dibekukan di dalam skrip. Sifat deterministik inilah yang membuat angka pada laporan ini dapat diperiksa ulang oleh siapa pun — prinsip reproduktif yang juga menjadi dasar penerbitan statistik resmi lembaga seperti BPS .
Membaca Lima Temuan dengan Kritis #
Lima temuan pada akhir skrip adalah pernyataan deskriptif; membacanya sebagai lebih dari itu memerlukan tiga pemeriksaan yang telah dilatih Section Interpretasi dan Etika.
Korelasi bukan kausalitas. Temuan kedua menyatakan gaji seiring masa kerja dengan r = 0.775 — hubungan linear yang sangat kuat, tetapi kata “seiring” sengaja dipilih, bukan “menyebabkan”. Tiga penjelasan sama-sama konsisten dengan angka ini: kebijakan senioritas menaikkan gaji seiring lamanya bekerja; karyawan berprestasi bertahan lebih lama sehingga terakumulasi di masa kerja panjang; atau jabatan tinggi — yang menyertai gaji besar — memang butuh waktu bertahun-tahun untuk dicapai. Data 29 baris ini tidak memuat kolom jabatan, sehingga ketiga cerita tidak dapat dipisahkan; chapter Correlation vs Causation menyebut variabel pembayang semacam ini sebagai confounder. Temuan kedua juga menunjukkan r(gaji, skor) = 0.527 — lebih lemah dari r(gaji, masa kerja) — yang bila dibaca tergesa bisa menjadi kesimpulan “kinerja kurang diberi imbalan”, padahal skor dan masa kerja sendiri berkorelasi 0.498 dan saling bertumpang tindih.
Outlier adalah keputusan pelaporan. Temuan ketiga memuat angka yang tampak ganjil: rata-rata gaji Keuangan 17.33 juta sementara mediannya 11.60 juta. Selisih itu bukan anomali data, melainkan akibat satu gaji direktur pada departemen beranggotakan enam orang. P015 tidak dibuang karena nilainya sah — tetapi setiap laporan yang mengutip rata-rata Keuangan tanpa median akan menyesatkan pembacanya. Praktik yang kita pakai — melaporkan mean dan median berdampingan — adalah bentuk paling sederhana dari transparansi yang dituntut kerangka Membaca Hasil Statistik: pembaca berhak tahu angka yang tidak dilaporkan, termasuk bahwa satu nilai mengangkat seluruh rata-rata.
Perbedaan antar kelompok perlu konteks. Temuan keempat — skor Pemasaran 86.75 tertinggi dan SDM 69.83 terendah, berselisih 16.92 poin — nyata pada data ini, tetapi tabel agregasi tidak menjawab mengapa: bisa jadi instrumen penilaian tiap departemen berbeda, komposisi tugas berbeda, atau memang budaya kinerjanya berbeda. Dengan 6-9 orang per kelompok, rata-rata departemen juga mudah bergeser bila dua-tiga karyawan berubah. Kerangka delapan pertanyaan skeptis dari chapter Membaca Hasil Statistik terpakai langsung di sini: siapa yang mengumpulkan data (sistem HR internal), bagaimana variabel didefinisikan (skor kinerja adalah instrumen internal yang tidak dijelaskan cara pengukurannya), dan berapa n per kelompok (6-9).
Keterbatasan ukuran data. Temuan kelima bukan kalimat basa-basi penutup. Dengan n = 29, seluruh korelasi dan rata-rata pada laporan ini sensitif terhadap beberapa baris: tambahkan atau buang tiga karyawan dan angka-angkanya akan bergeser nyata. Data ini juga potret satu titik waktu (cross-sectional), sehingga tidak bisa bicara tentang perubahan dari tahun ke tahun — jenis pertanyaan yang membutuhkan data deret waktu dari Section Analisis Deret Waktu. Dan karena yang dianalisis adalah seluruh karyawan perusahaan, angka ini mendeskripsikan populasi internal PT Maju Bersama; perbandingan ke perusahaan lain adalah generalisasi yang melampaui data.
Konteks Bisnis dan Langkah Lanjutan #
Bagi manajemen, temuan ini diterjemahkan menjadi tindakan yang sesuai kadarnya. Selisih skor antar departemen menjadi alasan meninjau instrumen penilaian dan beban kerja — bukan vonis bahwa SDM berkinerja rendah; data ini bahkan tidak mengukur penyebabnya. Hubungan gaji yang jauh lebih mengikuti masa kerja (r = 0.775) daripada skor kinerja (r = 0.527) menjadi bahan diskusi kebijakan: apakah struktur remunerasi memang ingin bergerak ke arah berbasis kinerja. Keberadaan satu gaji direktur yang mengubah wajah statistik Keuangan menjadi pengingat bahwa laporan eksekutif sebaiknya menyertakan median. Dan dua nilai isian dari Bagian 1 — umur P012 (33.5) dan skor P027 (70) — perlu disebut di bagian metodologi laporan agar pembaca tahu mana angka terukur dan mana angka perkiraan.
Langkah lanjutan yang jujur bagi analisis semacam ini: menambah jumlah data (karyawan lintas cabang atau beberapa periode penilaian), menambah kolom yang menguji dugaan confounder seperti jabatan, dan mendokumentasikan setiap keputusan pembersihan. Dengan rujukan metodologi yang lebih dalam, NIST/SEMATECH e-Handbook membahas praktik ringkasan data dan pemeriksaan pencilan, sedangkan Khan Academy Statistics & Probability menyediakan latihan membaca hubungan antar variabel.
Proyek ini menutup Course Statistika dengan lingkaran penuh: dari memuat berkas yang belum sempurna, membersihkannya dengan alasan yang dicatat, menghitung angka yang terverifikasi, melukis bentuknya, hingga membaca hasil dengan kesadaran akan batasnya. Keterampilan yang sama akan bekerja pada data Anda sendiri — dan bila ingin memperdalam rujukan resmi, Section Sumber Belajar menyiapkan peta lengkapnya.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
analisis_lengkap.py
Unduh berkas"""analisis_lengkap.py -- analisis menyeluruh dataset karyawan PT Maju Bersama.
Dataset bersih (29 baris) dari Bagian 1 disematkan langsung sebagai teks CSV
agar skrip dapat dijalankan tanpa bergantung pada lokasi berkas data.
"""
import io
import numpy as np
import pandas as pd
csv_bersih = """id,nama,departemen,umur,masa_kerja,gaji,skor_kinerja
P001,Budi Santoso,Produksi,34,6,11.5,79
P002,Agus Wijaya,Produksi,28,3,9.4,74
P003,Joko Prasetyo,Produksi,41,12,14.6,81
P004,Tono Hartono,Produksi,25,1,8.0,70
P005,Eko Saputra,Produksi,38,9,13.5,83
P006,Hendra Gunawan,Produksi,31,5,10.8,77
P007,Udin Samsudin,Produksi,46,16,16.2,80
P008,Wahyu Nugroho,Produksi,52,20,18.8,85
P009,Yanto Kurniawan,Produksi,24,2,8.7,72
P010,Siti Rahmawati,Keuangan,35,8,12.0,76
P011,Dewi Lestari,Keuangan,29,4,9.8,73
P012,Rina Marlina,Keuangan,33.5,6,11.0,75
P013,Fajar Ramadhan,Keuangan,42,14,15.0,78
P014,Indra Maulana,Keuangan,33,7,11.2,74
P015,Prakasa Wibowo,Keuangan,56,22,45.0,88
P016,Maya Anggraini,Pemasaran,30,5,12.3,86
P017,Rian Hidayat,Pemasaran,27,3,11.0,84
P018,Gita Permata,Pemasaran,36,10,14.8,89
P019,Nanda Pratama,Pemasaran,32,7,13.2,87
P020,Citra Kirana,Pemasaran,26,2,10.2,82
P021,Bayu Setiawan,Pemasaran,39,13,16.4,90
P022,Laila Sari,Pemasaran,30,6,12.4,85
P023,Oka Mahendra,Pemasaran,44,18,18.8,91
P024,Tuti Handayani,SDM,37,9,11.7,71
P025,Yuni Astuti,SDM,28,3,8.5,68
P026,Putri Ayu,SDM,33,6,10.2,70
P027,Dedi Kurnia,SDM,40,11,12.4,70
P028,Wawan Susanto,SDM,45,15,14.7,73
P029,Sari Wulandari,SDM,26,1,7.5,67
"""
df = pd.read_csv(io.StringIO(csv_bersih))
mean_gaji = df["gaji"].mean()
std_gaji = df["gaji"].std(ddof=1)
med_gaji = df["gaji"].median()
print("=== Statistik gaji (juta Rp), n =", len(df), "===")
print(f"mean {mean_gaji:.2f} | median {med_gaji:.2f} | std {std_gaji:.2f} | "
f"min {df['gaji'].min():.2f} | max {df['gaji'].max():.2f}")
print()
print("=== Per departemen: n, rata-rata gaji, rata-rata skor ===")
gab = df.groupby("departemen").agg(
n=("id", "count"), gaji_mean=("gaji", "mean"), skor_mean=("skor_kinerja", "mean")
)
for dep, n, gm, sm in zip(gab.index, gab["n"], gab["gaji_mean"], gab["skor_mean"]):
print(f"{dep:9s}: n={n:2d} gaji {gm:5.2f} skor {sm:5.2f}")
print()
r_masa = np.corrcoef(df["gaji"], df["masa_kerja"])[0, 1]
r_skor = np.corrcoef(df["gaji"], df["skor_kinerja"])[0, 1]
print("=== Korelasi gaji ===")
print(f"r(gaji, masa_kerja) = {r_masa:.3f}")
print(f"r(gaji, skor_kinerja) = {r_skor:.3f}")
print()
z = (df["gaji"] - mean_gaji) / std_gaji
out = df[z.abs() > 2]
print("=== Outlier gaji (|z| > 2) ===")
for _, baris in out.iterrows():
print(f"{baris['id']} {baris['nama']}: gaji {baris['gaji']:.1f} juta, z = {z[baris.name]:.2f}")
print()
skor_dept = df.groupby("departemen")["skor_kinerja"].mean()
med_keu = df.loc[df["departemen"] == "Keuangan", "gaji"].median()
gaji_dept = df.groupby("departemen")["gaji"].mean()
print("=== Ringkasan temuan ===")
print(f"1. Sebaran gaji condong kanan: mean {mean_gaji:.2f} juta melampaui median "
f"{med_gaji:.2f} juta karena satu gaji direktur {df['gaji'].max():.1f} juta.")
print(f"2. Gaji seiring masa kerja (r = {r_masa:.3f}) lebih kuat daripada "
f"seiring skor kinerja (r = {r_skor:.3f}).")
print(f"3. Outlier P015 menarik rata-rata gaji Keuangan ke {gaji_dept['Keuangan']:.2f} juta, "
f"jauh di atas mediannya {med_keu:.2f} juta.")
print(f"4. Skor kinerja berbeda antar departemen: Pemasaran {skor_dept['Pemasaran']:.2f} "
f"tertinggi, SDM {skor_dept['SDM']:.2f} terendah (selisih "
f"{skor_dept['Pemasaran'] - skor_dept['SDM']:.2f} poin).")
print("5. Temuan bersifat deskriptif atas 29 baris: korelasi bukan kausalitas, "
"dan generalisasi ke seluruh perusahaan perlu kehati-hatian.")