Bulan 8: Survey dan Research Methods
Abbas belajar merancang penelitian yang valid, memilih metode sampling yang tepat, menghindari bias, dan membangun survey yang menghasilkan data berkualitas tinggi untuk dianalisis lebih lanjut.
Apa yang Abbas Kuasai di Bulan Ini
Kompetensi yang Dicapai
- Memahami jenis-jenis desain penelitian, termasuk perbedaan penelitian kuantitatif dan kualitatif, serta penelitian observasional dan eksperimental.
- Memilih metode sampling yang sesuai dengan konteks penelitian, antara random sampling, stratified sampling, systematic sampling, dan cluster sampling.
- Mengidentifikasi dan memitigasi bias dalam proses pengumpulan data, termasuk selection bias, response bias, dan confirmation bias.
- Merancang pertanyaan survey yang valid, meliputi jenis pertanyaan (Likert scale, multiple choice, open-ended) dan urutan yang tidak mengarahkan responden.
- Menggunakan Google Forms dan tools lainnya untuk membangun, menyebarkan, dan mengumpulkan data survey secara sistematis.
- Menganalisis data survey dengan Python dan Pandas, termasuk membersihkan data respons, menghitung distribusi jawaban, dan membuat visualisasi hasil survey.
- Memahami konsep validitas dan reliabilitas dalam konteks instrumen pengukuran, termasuk cara menguji Cronbach's Alpha untuk konsistensi internal.
- Menyusun laporan hasil survey yang ringkas, akurat, dan dapat dikomunikasikan kepada audiens non-teknis, sebagai persiapan proyek P4: Mini Survey.
Minggu 1: Desain Penelitian
Minggu pertama membangun fondasi metodologi riset. Abbas memahami bagaimana peneliti memilih pendekatan yang tepat sebelum mengumpulkan satu pun data, dan mengapa keputusan awal ini menentukan seluruh kualitas penelitian.
# Simulasi: membuat dataset penelitian observasional vs eksperimental
import pandas as pd
import numpy as np
np.random.seed(42)
# Observasional: kita hanya mencatat apa yang sudah ada
observasional = pd.DataFrame({
'siswa_id': range(1, 31),
'metode_belajar': np.random.choice(['aktif', 'pasif'], 30),
'nilai_ujian': np.random.randint(60, 100, 30)
})
# Eksperimental: kita ASSIGN kelompok secara random (RCT)
eksperimental = pd.DataFrame({
'siswa_id': range(1, 31),
'kelompok': ['treatment']*15 + ['control']*15,
'nilai_sebelum': np.random.randint(60, 80, 30),
'nilai_sesudah': np.concatenate([
np.random.randint(75, 100, 15), # treatment: lebih tinggi
np.random.randint(60, 85, 15) # control: tidak banyak berubah
])
})
print("=== Dataset Observasional ===")
print(observasional.groupby('metode_belajar')['nilai_ujian'].mean())
print("\n=== Dataset Eksperimental ===")
eksperimental['peningkatan'] = eksperimental['nilai_sesudah'] - eksperimental['nilai_sebelum']
print(eksperimental.groupby('kelompok')['peningkatan'].mean())
import pandas as pd
import numpy as np
np.random.seed(7)
# Populasi: 500 siswa dari 5 kelas
populasi = pd.DataFrame({
'id': range(1, 501),
'kelas': np.repeat(['X-A', 'X-B', 'X-C', 'XI-A', 'XI-B'], 100),
'nilai': np.concatenate([
np.random.normal(75, 10, 100), # X-A
np.random.normal(78, 8, 100), # X-B
np.random.normal(72, 12, 100), # X-C
np.random.normal(80, 9, 100), # XI-A
np.random.normal(82, 7, 100), # XI-B
]).clip(0, 100)
})
n = 50 # ukuran sampel target
# 1. Simple Random Sampling
srs = populasi.sample(n=n, random_state=1)
print(f"SRS mean nilai: {srs['nilai'].mean():.2f}")
# 2. Stratified Sampling (proporsional: 10 per kelas)
stratified = populasi.groupby('kelas', group_keys=False).apply(
lambda x: x.sample(n=10, random_state=1)
)
print(f"Stratified mean nilai: {stratified['nilai'].mean():.2f}")
# 3. Systematic Sampling (setiap ke-10)
interval = len(populasi) // n
start = np.random.randint(0, interval)
systematic = populasi.iloc[start::interval].head(n)
print(f"Systematic mean nilai: {systematic['nilai'].mean():.2f}")
# Bandingkan dengan populasi asli
print(f"\nPopulasi mean nilai: {populasi['nilai'].mean():.2f}")
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(99)
# Populasi: 1000 siswa dengan berbagai nilai
populasi = pd.DataFrame({
'id': range(1, 1001),
'nilai_mat': np.random.normal(70, 15, 1000).clip(0, 100),
'jam_belajar': np.random.normal(3, 1.5, 1000).clip(0, 10)
})
# Korelasi sesungguhnya
corr_pop = populasi['nilai_mat'].corr(populasi['jam_belajar'])
print(f"Korelasi populasi: {corr_pop:.3f}")
# Sampel TANPA bias: random sampling
sampel_fair = populasi.sample(n=100, random_state=1)
corr_fair = sampel_fair['nilai_mat'].corr(sampel_fair['jam_belajar'])
print(f"Korelasi sampel fair: {corr_fair:.3f}")
# Sampel DENGAN selection bias: hanya ambil siswa nilai > 75
# (misalnya: survei hanya disebarkan di kelas unggulan)
sampel_bias = populasi[populasi['nilai_mat'] > 75].sample(n=100, random_state=1)
corr_bias = sampel_bias['nilai_mat'].corr(sampel_bias['jam_belajar'])
print(f"Korelasi sampel bias (nilai > 75): {corr_bias:.3f}")
print("\n--- Kesimpulan yang bisa berbeda ---")
print(f"Dari sampel fair: jam belajar {'berkorelasi' if corr_fair > 0.1 else 'tidak berkorelasi'} kuat dengan nilai")
print(f"Dari sampel bias: jam belajar {'berkorelasi' if corr_bias > 0.1 else 'tidak berkorelasi'} kuat dengan nilai")
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(21)
# Skenario: Apakah tidur cukup (>=7 jam) meningkatkan nilai?
# H0: mean nilai tidur_cukup == mean nilai tidur_kurang
# H1: mean nilai tidur_cukup > mean nilai tidur_kurang
tidur_cukup = np.random.normal(78, 10, 60) # 60 siswa tidur >= 7 jam
tidur_kurang = np.random.normal(72, 12, 40) # 40 siswa tidur < 7 jam
# One-sided t-test (H1: cukup > kurang)
t_stat, p_value = stats.ttest_ind(tidur_cukup, tidur_kurang, alternative='greater')
alpha = 0.05
print("=== Pengujian Hipotesis ===")
print(f"H0: Tidur cukup tidak meningkatkan nilai (mean sama)")
print(f"H1: Tidur cukup meningkatkan nilai (mean lebih tinggi)")
print(f"\nRata-rata tidur cukup: {tidur_cukup.mean():.2f}")
print(f"Rata-rata tidur kurang: {tidur_kurang.mean():.2f}")
print(f"t-statistic: {t_stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"\nKesimpulan (alpha={alpha}):")
if p_value < alpha:
print("Tolak H0: Ada bukti bahwa tidur cukup meningkatkan nilai.")
else:
print("Gagal tolak H0: Tidak cukup bukti untuk mendukung H1.")
# Ringkasan: checklist desain penelitian yang baik
checklist = {
'Pertanyaan Penelitian': [
'Pertanyaan sudah spesifik dan bisa diuji?',
'H0 dan H1 sudah dirumuskan dengan jelas?',
'Variabel independen dan dependen sudah diidentifikasi?'
],
'Sampling': [
'Populasi target sudah didefinisikan?',
'Metode sampling sesuai dengan sumber daya?',
'Ukuran sampel cukup untuk analisis statistik?'
],
'Potensi Bias': [
'Cara rekrut responden bisa menyebabkan selection bias?',
'Pertanyaan survey netral, tidak mengarahkan?',
'Ada mekanisme untuk meminimalkan response bias?'
],
'Etika': [
'Informed consent sudah disiapkan?',
'Data anonimitas terjaga?',
'Responden tahu hak untuk mengundurkan diri?'
]
}
for kategori, items in checklist.items():
print(f"\n[{kategori}]")
for item in items:
print(f" [ ] {item}")
print("\n--- Gunakan checklist ini sebelum mulai survei apapun ---")
Minggu 2: Desain Survey yang Baik
Minggu ini Abbas belajar seni dan sains merancang survey. Survey yang buruk menghasilkan data yang tidak bisa dipercaya, sekuat apapun analisis statistiknya. Abbas akan berlatih menulis pertanyaan yang tepat, memilih skala pengukuran yang sesuai, dan menggunakan Google Forms untuk mengumpulkan data secara nyata.
# Analisis panjang survey vs completion rate (simulasi berdasarkan riset)
import pandas as pd
import matplotlib.pyplot as plt
data = {
'jumlah_pertanyaan': [5, 10, 15, 20, 25, 30, 40, 50],
'completion_rate': [0.92, 0.85, 0.76, 0.65, 0.55, 0.45, 0.30, 0.20],
'waktu_menit': [2, 4, 6, 8, 10, 12, 16, 20]
}
df = pd.DataFrame(data)
print("Hubungan Panjang Survey dan Completion Rate:")
print(df.to_string(index=False))
# Titik optimal
optimal = df[df['completion_rate'] >= 0.75]
print(f"\nUntuk completion rate >= 75%:")
print(f" Maksimal pertanyaan: {optimal['jumlah_pertanyaan'].max()}")
print(f" Maksimal waktu: {optimal['waktu_menit'].max()} menit")
print(f"\nRekomendasi: Survey Abbas sebaiknya 10-15 pertanyaan (4-6 menit)")
import pandas as pd
import numpy as np
from collections import Counter
np.random.seed(15)
n = 100
# Simulasi respons berbagai jenis pertanyaan
respons_survey = pd.DataFrame({
# Likert (1-5): "Seberapa sering Abbas belajar di luar jam sekolah?"
'frekuensi_belajar': np.random.choice([1,2,3,4,5], n,
p=[0.05, 0.15, 0.35, 0.30, 0.15]),
# Multiple choice: "Metode belajar yang paling sering digunakan"
'metode_belajar': np.random.choice(
['Membaca buku', 'Video YouTube', 'Les privat', 'Diskusi teman'],
n, p=[0.25, 0.40, 0.20, 0.15]
),
# Open-ended: disimulasikan kata kunci
'kesulitan_belajar': np.random.choice(
['matematika sulit', 'kurang fokus', 'tidak ada waktu',
'bahan terlalu banyak', 'bosan', 'tidak mengerti'],
n
)
})
# Analisis Likert: distribusi frekuensi + mean
print("=== Likert: Frekuensi Belajar (1=tidak pernah, 5=setiap hari) ===")
print(respons_survey['frekuensi_belajar'].value_counts().sort_index())
print(f"Mean: {respons_survey['frekuensi_belajar'].mean():.2f}")
# Analisis Multiple Choice
print("\n=== Multiple Choice: Metode Belajar ===")
print(respons_survey['metode_belajar'].value_counts())
# Analisis Open-ended: word frequency sederhana
print("\n=== Open-ended: Kata Kunci Kesulitan Belajar ===")
all_words = ' '.join(respons_survey['kesulitan_belajar']).split()
word_freq = Counter(all_words).most_common(10)
for word, count in word_freq:
print(f" {word}: {count}x")
# Checklist kualitas pertanyaan survey
def evaluasi_pertanyaan(pertanyaan: str, pilihan: list = None) -> dict:
"""
Checklist sederhana untuk mengevaluasi kualitas satu pertanyaan survey.
Dalam praktiknya, Abbas mengisi ini secara manual untuk setiap pertanyaan.
"""
hasil = {
'pertanyaan': pertanyaan,
'checklist': {}
}
checklist_items = [
('Satu fokus', 'Pertanyaan hanya menanyakan SATU hal, bukan dua sekaligus'),
('Netral', 'Tidak ada kata yang menggiring ke jawaban tertentu'),
('Jelas', 'Semua istilah dalam pertanyaan sudah didefinisikan/dipahami'),
('Tidak mengasumsikan', 'Tidak ada asumsi yang embedded dalam pertanyaan'),
('Pilihan lengkap', 'Semua kemungkinan jawaban tercakup (termasuk N/A jika perlu)'),
('Pilihan eksklusif', 'Pilihan jawaban tidak tumpang tindih'),
]
for kode, deskripsi in checklist_items:
hasil['checklist'][kode] = {'deskripsi': deskripsi, 'status': '[ ] Belum diperiksa'}
return hasil
# Contoh penggunaan
pertanyaan_test = "Apakah Abbas setuju bahwa belajar di pagi hari lebih efektif?"
hasil = evaluasi_pertanyaan(pertanyaan_test)
print(f"Pertanyaan: {hasil['pertanyaan']}")
print("\nChecklist Evaluasi:")
for kode, detail in hasil['checklist'].items():
print(f" {detail['status']} [{kode}] {detail['deskripsi']}")
print("\n--- Isi checklist ini untuk setiap pertanyaan di survey Abbas ---")
import pandas as pd
import numpy as np
# Simulasi: data seperti yang diekspor Google Forms ke CSV
# Kolom pertama selalu "Timestamp", lalu tiap pertanyaan jadi satu kolom
np.random.seed(33)
n = 50
data_forms = pd.DataFrame({
'Timestamp': pd.date_range('2024-01-15 08:00', periods=n, freq='15min'),
'Berapa lama Abbas belajar per hari? (jam)': np.random.choice(
['Kurang dari 1', '1-2 jam', '2-3 jam', 'Lebih dari 3 jam'], n
),
'Metode belajar utama Abbas (pilih semua yang berlaku)': np.random.choice(
['Baca buku;Video;', 'Diskusi teman;', 'Les privat;Baca buku;', 'Video;'], n
),
'Seberapa puas Abbas dengan hasil belajar? (1=tidak puas, 5=sangat puas)': np.random.randint(1, 6, n),
'Apa hambatan terbesar dalam belajar?': np.random.choice(
['Sulit fokus', 'Materi susah', 'Tidak ada waktu', 'Kurang motivasi'], n
)
})
print("=== Preview Data Google Forms ===")
print(data_forms.head(3).to_string())
print("\n=== Info Dataset ===")
print(f"Jumlah respons: {len(data_forms)}")
print(f"Kolom: {list(data_forms.columns)}")
# Perhatikan: kolom checkbox berisi string dengan titik koma sebagai separator
print("\n=== Contoh Parsing Kolom Checkbox ===")
metode_col = 'Metode belajar utama Abbas (pilih semua yang berlaku)'
all_choices = data_forms[metode_col].str.split(';').explode()
all_choices = all_choices[all_choices.str.strip() != '']
print(all_choices.value_counts())
import math
def hitung_sampel_slovin(N: int, e: float = 0.05) -> int:
"""
Hitung ukuran sampel minimal dengan rumus Slovin.
N = ukuran populasi
e = margin of error (default 5% = 0.05)
"""
n = N / (1 + N * e**2)
return math.ceil(n)
def hitung_sampel_proporsi(p: float = 0.5, e: float = 0.05, z: float = 1.96) -> int:
"""
Hitung ukuran sampel untuk estimasi proporsi (populasi besar/tidak diketahui).
p = proporsi perkiraan (0.5 = paling konservatif)
e = margin of error
z = z-score (1.96 untuk confidence level 95%)
"""
n = (z**2 * p * (1 - p)) / e**2
return math.ceil(n)
# Contoh: sekolah Abbas punya 800 siswa
populasi = 800
print("=== Ukuran Sampel Minimal (Rumus Slovin) ===")
for margin in [0.05, 0.08, 0.10]:
n = hitung_sampel_slovin(populasi, margin)
print(f" Margin error {margin*100:.0f}%: minimal {n} responden")
print("\n=== Ukuran Sampel Estimasi Proporsi (populasi besar) ===")
for margin in [0.05, 0.08, 0.10]:
n = hitung_sampel_proporsi(e=margin)
print(f" Margin error {margin*100:.0f}%: minimal {n} responden")
print("\n--- Untuk survey sekolah biasanya cukup pakai Slovin dengan e=10% ---")
Minggu 3: Analisis Data Survey
Survey sudah disebarkan dan data sudah terkumpul. Minggu ini Abbas belajar mengolah data respons mentah menjadi insight yang bisa dikomunikasikan. Mulai dari pembersihan data, analisis distribusi jawaban, hingga pengujian konsistensi instrumen dengan Cronbach's Alpha.
import pandas as pd
import numpy as np
# Simulasi data mentah Google Forms yang belum bersih
np.random.seed(55)
n = 80
raw_data = pd.DataFrame({
'Timestamp': pd.to_datetime(
pd.date_range('2024-02-01 07:00', periods=n, freq='18min').tolist()
).astype(str),
'Nama (opsional)': [''] * n,
'Jenis Kelamin': np.random.choice(
['Laki-laki', 'Perempuan', 'L', 'P', 'laki-laki', 'perempuan',
'LAKI-LAKI', None], n
),
'Kelas': np.random.choice(['X', 'XI', 'XII', 'kelas 10', 'kelas 11', '10', '11'], n),
'Kepuasan Belajar (1-5)': np.random.choice([1, 2, 3, 4, 5, None], n,
p=[0.05, 0.10, 0.30, 0.35, 0.15, 0.05]),
})
print('=== Data Mentah ===')
print(raw_data.head(8).to_string())
print(f'\nShape: {raw_data.shape}')
print(f'\nMissing values:\n{raw_data.isnull().sum()}')
# Bersihkan Timestamp
raw_data['Timestamp'] = pd.to_datetime(raw_data['Timestamp'])
raw_data['hari_pengisian'] = raw_data['Timestamp'].dt.day_name()
raw_data['jam_pengisian'] = raw_data['Timestamp'].dt.hour
# Standardisasi Jenis Kelamin
mapping_gender = {
'L': 'Laki-laki', 'laki-laki': 'Laki-laki', 'LAKI-LAKI': 'Laki-laki',
'P': 'Perempuan', 'perempuan': 'Perempuan'
}
raw_data['Jenis Kelamin'] = raw_data['Jenis Kelamin'].replace(mapping_gender)
print('\n=== Setelah Standardisasi Gender ===')
print(raw_data['Jenis Kelamin'].value_counts(dropna=False))
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(77)
n = 100
df = pd.DataFrame({
'kelas': np.random.choice(['X', 'XI', 'XII'], n, p=[0.35, 0.40, 0.25]),
'kepuasan_belajar': np.random.choice([1,2,3,4,5], n, p=[0.05,0.15,0.30,0.35,0.15]),
'jam_belajar_hari': np.random.choice(['<1', '1-2', '2-3', '>3'], n, p=[0.10,0.40,0.35,0.15]),
'metode_utama': np.random.choice(
['Baca buku', 'Video', 'Les privat', 'Diskusi'], n, p=[0.20,0.45,0.15,0.20]
)
})
# Distribusi frekuensi Likert
print('=== Kepuasan Belajar (1-5) ===')
freq = df['kepuasan_belajar'].value_counts().sort_index()
pct = df['kepuasan_belajar'].value_counts(normalize=True).sort_index() * 100
ringkasan_likert = pd.DataFrame({'Jumlah': freq, 'Persen (%)': pct.round(1)})
print(ringkasan_likert)
print(f"Mean: {df['kepuasan_belajar'].mean():.2f} | Median: {df['kepuasan_belajar'].median()}")
# Cross-tabulation: kepuasan vs kelas
print('\n=== Cross-tab: Rata-rata Kepuasan per Kelas ===')
print(df.groupby('kelas')['kepuasan_belajar'].agg(['mean', 'count']).round(2))
# Crosstab distribusi metode belajar per kelas
print('\n=== Cross-tab: Metode Belajar per Kelas ===')
ct = pd.crosstab(df['kelas'], df['metode_utama'], normalize='index') * 100
print(ct.round(1))
import pandas as pd
import re
from collections import Counter
# Stopwords Bahasa Indonesia (daftar minimal)
STOPWORDS_ID = {
'yang', 'dan', 'di', 'ke', 'dari', 'untuk', 'dengan', 'ini', 'itu',
'adalah', 'ada', 'tidak', 'saya', 'aku', 'kamu', 'mereka', 'kita',
'sudah', 'akan', 'bisa', 'juga', 'lebih', 'atau', 'tapi', 'karena',
'jadi', 'saat', 'kalau', 'sangat', 'masih', 'dalam', 'sering', 'pernah'
}
respons_teks = [
'matematika terlalu sulit dan soalnya membingungkan',
'kurang waktu karena banyak kegiatan ekstrakurikuler',
'tidak ada buku yang cocok untuk belajar mandiri',
'matematika susah apalagi bagian aljabar',
'banyak tugas menumpuk dari berbagai mata pelajaran',
'waktu belajar sedikit karena pulang sekolah sudah malam',
'guru menjelaskan terlalu cepat sehingga sulit diikuti',
'tidak ada teman yang bisa diajak diskusi materi sulit',
'buku pelajaran tidak tersedia di perpustakaan',
'terlalu banyak tugas sehingga tidak sempat memahami materi',
]
def preprocess(teks):
teks = teks.lower()
teks = re.sub(r'[^\w\s]', '', teks)
kata = teks.split()
return [k for k in kata if k not in STOPWORDS_ID and len(k) > 2]
# Word frequency
semua_kata = []
for r in respons_teks:
semua_kata.extend(preprocess(r))
word_freq = Counter(semua_kata).most_common(15)
print('=== 15 Kata Terbanyak (setelah preprocessing) ===')
for kata, count in word_freq:
bar = '#' * count
print(f' {kata:<20} {bar} ({count})')
# Kategorisasi manual
def kategorikan(respons):
r = respons.lower()
if any(w in r for w in ['matematika', 'aljabar', 'soal', 'sulit', 'susah']):
return 'Kesulitan Materi'
elif any(w in r for w in ['waktu', 'malam', 'tugas', 'menumpuk', 'banyak']):
return 'Manajemen Waktu'
elif any(w in r for w in ['buku', 'perpustakaan', 'tersedia']):
return 'Akses Sumber Belajar'
elif any(w in r for w in ['guru', 'menjelaskan', 'cepat']):
return 'Metode Pengajaran'
elif any(w in r for w in ['teman', 'diskusi']):
return 'Lingkungan Sosial'
return 'Lainnya'
df_teks = pd.DataFrame({'respons': respons_teks})
df_teks['tema'] = df_teks['respons'].apply(kategorikan)
print('\n=== Distribusi Tema ===')
print(df_teks['tema'].value_counts())
import pandas as pd
import numpy as np
np.random.seed(88)
n = 100
# Simulasi: 5 pertanyaan Likert yang mengukur "Motivasi Belajar"
# Q1-Q4 benar-benar terkait motivasi, Q5 adalah pertanyaan asing
base = np.random.normal(3, 0.8, n)
df_alpha = pd.DataFrame({
'Q1_target_belajar': (base + np.random.normal(0, 0.3, n)).clip(1, 5).round(),
'Q2_semangat_sekolah': (base + np.random.normal(0, 0.4, n)).clip(1, 5).round(),
'Q3_rajin_pr': (base + np.random.normal(0, 0.35, n)).clip(1, 5).round(),
'Q4_suka_belajar': (base + np.random.normal(0, 0.3, n)).clip(1, 5).round(),
'Q5_jarak_ke_sekolah': np.random.randint(1, 6, n).astype(float),
})
def cronbach_alpha(df):
# Hitung Cronbach's Alpha dari DataFrame pertanyaan Likert
k = df.shape[1]
var_items = df.var(axis=0, ddof=1).sum()
var_total = df.sum(axis=1).var(ddof=1)
return (k / (k - 1)) * (1 - var_items / var_total)
# Alpha keseluruhan
alpha_all = cronbach_alpha(df_alpha)
print(f"Cronbach's Alpha (semua 5 pertanyaan): {alpha_all:.4f}")
# Alpha jika satu pertanyaan dihapus
print('\n=== Alpha If Item Deleted ===')
for col in df_alpha.columns:
df_tanpa = df_alpha.drop(columns=[col])
alpha_tanpa = cronbach_alpha(df_tanpa)
delta = alpha_tanpa - alpha_all
arah = 'naik' if delta > 0 else 'turun'
print(f' Tanpa {col}: {alpha_tanpa:.4f} ({arah} {abs(delta):.4f})')
print('\n--- Q5 yang tidak terkait akan meningkatkan alpha jika dihapus ---')
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
np.random.seed(42)
n = 100
df = pd.DataFrame({
'kelas': np.random.choice(['X', 'XI', 'XII'], n, p=[0.33, 0.40, 0.27]),
'jk': np.random.choice(['Laki-laki', 'Perempuan'], n, p=[0.48, 0.52]),
'kepuasan': np.random.choice([1,2,3,4,5], n, p=[0.05,0.12,0.28,0.38,0.17]),
'metode': np.random.choice(['Baca', 'Video', 'Les', 'Diskusi'], n),
'jam_belajar': np.random.choice(['<1j', '1-2j', '2-3j', '>3j'], n,
p=[0.10, 0.38, 0.35, 0.17])
})
fig = plt.figure(figsize=(14, 10))
fig.suptitle('Ringkasan Hasil Survey: Kebiasaan Belajar Siswa', fontsize=14, fontweight='bold')
gs = gridspec.GridSpec(2, 3, figure=fig, hspace=0.45, wspace=0.35)
# Panel 1: Distribusi kelas
ax1 = fig.add_subplot(gs[0, 0])
df['kelas'].value_counts().sort_index().plot(kind='bar', ax=ax1, color='#4C72B0', edgecolor='white')
ax1.set_title('Distribusi Kelas Responden', fontsize=10)
ax1.set_ylabel('Jumlah'); ax1.set_xlabel('')
ax1.tick_params(axis='x', rotation=0)
# Panel 2: Distribusi Likert kepuasan
ax2 = fig.add_subplot(gs[0, 1])
counts = df['kepuasan'].value_counts().sort_index()
bars = ax2.bar(counts.index, counts.values, color='#55A868', edgecolor='white')
ax2.set_title('Kepuasan Belajar (1-5)', fontsize=10)
ax2.set_xlabel('Skor'); ax2.set_ylabel('Jumlah')
for bar, val in zip(bars, counts.values):
ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
str(val), ha='center', va='bottom', fontsize=9)
# Panel 3: Metode belajar
ax3 = fig.add_subplot(gs[0, 2])
df['metode'].value_counts().plot(kind='barh', ax=ax3, color='#C44E52', edgecolor='white')
ax3.set_title('Metode Belajar Utama', fontsize=10)
ax3.set_xlabel('Jumlah')
# Panel 4: Kepuasan per kelas
ax4 = fig.add_subplot(gs[1, :2])
crosstab = pd.crosstab(df['kelas'], df['kepuasan'], normalize='index') * 100
crosstab.plot(kind='bar', ax=ax4, colormap='RdYlGn', edgecolor='white', width=0.7)
ax4.set_title('Distribusi Kepuasan Belajar per Kelas (%)', fontsize=10)
ax4.set_xlabel('Kelas'); ax4.set_ylabel('Persentase (%)')
ax4.tick_params(axis='x', rotation=0)
ax4.legend(title='Skor', bbox_to_anchor=(1.01, 1), loc='upper left', fontsize=8)
# Panel 5: Jam belajar
ax5 = fig.add_subplot(gs[1, 2])
urutan = ['<1j', '1-2j', '2-3j', '>3j']
jam_ct = df['jam_belajar'].value_counts().reindex(urutan)
ax5.pie(jam_ct.values, labels=urutan, autopct='%1.0f%%',
colors=['#FDD878', '#F4A261', '#E76F51', '#C44E52'], startangle=90)
ax5.set_title('Jam Belajar per Hari', fontsize=10)
plt.savefig('/tmp/survey_ringkasan.png', dpi=150, bbox_inches='tight')
print('Dashboard disimpan ke /tmp/survey_ringkasan.png')
Minggu 4: Pelaporan Hasil Survey
Minggu terakhir Bulan 8 adalah tentang mengomunikasikan temuan. Data yang sudah dianalisis tidak bernilai jika tidak bisa dipahami oleh orang lain. Abbas akan belajar menyusun laporan yang jelas, membuat presentasi yang persuasif, dan menyelesaikan Proyek P4: Mini Survey sebagai puncak dari bulan ini.
# Template struktur laporan (panduan penulisan)
laporan_template = {
'judul': 'Analisis Kebiasaan Belajar Siswa SMA: Studi Survey 2024',
'pendahuluan': {
'latar_belakang': '[Mengapa topik ini penting? Apa yang belum diketahui?]',
'tujuan_penelitian': '[3-4 tujuan spesifik dalam bentuk poin]',
'pertanyaan_penelitian': '[2-3 pertanyaan yang dijawab oleh survey ini]'
},
'metode': {
'populasi': '[Definisi populasi target]',
'sampel': '[Jumlah responden, metode sampling, kriteria inklusi/eksklusi]',
'instrumen': '[Jumlah pertanyaan, jenis skala, cara validasi]',
'periode': '[Tanggal mulai dan selesai pengumpulan data]',
'analisis': '[Metode analisis yang digunakan]'
},
'hasil': {
'profil_responden': '[Demografis: kelas, jenis kelamin, distribusi]',
'temuan_utama': [
'Temuan 1: [fakta dari data tanpa interpretasi]',
'Temuan 2: [fakta dari data tanpa interpretasi]',
'Temuan 3: [fakta dari data tanpa interpretasi]'
],
'reliabilitas_instrumen': '[Nilai Cronbach Alpha dan interpretasinya]'
},
'diskusi': {
'interpretasi': '[Mengapa temuan bisa seperti ini?]',
'keterbatasan': [
'Keterbatasan 1: [aspek metodologi yang membatasi generalisasi]',
'Keterbatasan 2: [potensi bias yang tidak bisa dihilangkan]',
'Keterbatasan 3: [hal lain yang perlu diakui jujur]'
],
'implikasi': '[Apa yang bisa dilakukan berdasarkan temuan ini?]'
},
'kesimpulan': '[Rangkuman 3-5 kalimat tentang apa yang ditemukan]'
}
for bagian, isi in laporan_template.items():
print(f'\n[{bagian.upper()}]')
if isinstance(isi, dict):
for sub, val in isi.items():
preview = str(val)[:60] + '...' if len(str(val)) > 60 else str(val)
print(f' {sub}: {preview}')
else:
print(f' {isi}')
import pandas as pd
import numpy as np
np.random.seed(22)
n = 87
df = pd.DataFrame({
'kelas': np.random.choice(['X', 'XI', 'XII'], n, p=[0.34, 0.41, 0.25]),
'jk': np.random.choice(['Laki-laki', 'Perempuan'], n, p=[0.46, 0.54]),
'kepuasan': np.random.choice([1,2,3,4,5], n, p=[0.05,0.12,0.28,0.38,0.17]),
'metode': np.random.choice(
['Baca buku', 'Video', 'Les privat', 'Diskusi'], n,
p=[0.20, 0.46, 0.14, 0.20]
)
})
# Otomatisasi teks profil responden
total = len(df)
per_kelas = df['kelas'].value_counts().sort_index()
per_jk = df['jk'].value_counts()
print('=== PROFIL RESPONDEN ===\n')
print(f'Total responden yang memenuhi syarat: {total} orang.')
for kls, jumlah in per_kelas.items():
pct = jumlah / total * 100
print(f' Kelas {kls}: {jumlah} responden ({pct:.1f}%)')
print()
for jk, jumlah in per_jk.items():
pct = jumlah / total * 100
print(f' {jk}: {jumlah} responden ({pct:.1f}%)')
print('\n=== TEMUAN UTAMA ===\n')
top_metode = df['metode'].value_counts().idxmax()
top_pct = df['metode'].value_counts(normalize=True).max() * 100
mean_kepuasan = df['kepuasan'].mean()
print(f"Metode belajar yang paling banyak dipilih adalah '{top_metode}'")
print(f'dengan {top_pct:.1f}% responden memilihnya sebagai metode utama.')
print()
kategori = 'cukup puas' if mean_kepuasan >= 3 else 'kurang puas'
print(f'Rata-rata kepuasan belajar: {mean_kepuasan:.2f} dari skala 1-5 ({kategori}).')
# Panduan menulis diskusi: template pertanyaan pemandu
pertanyaan_diskusi = {
'46% responden memilih video sebagai metode belajar utama': [
'Mengapa video lebih populer dari metode lain?',
'Apa implikasi bagi guru yang masih menggunakan metode ceramah?',
'Apakah ada penjelasan alternatif selain yang paling obvious?'
],
'Mean kepuasan belajar 3.42 dari 5': [
'Apa yang membuat siswa merasa belum sangat puas?',
'Apakah ada perbedaan kepuasan antar kelas atau jenis kelamin?',
'Faktor apa dari open-ended yang mungkin menjelaskan angka ini?'
],
'Tema Manajemen Waktu paling dominan di open-ended': [
'Mengapa waktu menjadi hambatan utama, bukan materi yang sulit?',
'Apakah ini terkait dengan beban ekstrakurikuler atau jadwal sekolah?',
'Rekomendasi apa yang bisa diberikan kepada siswa dan sekolah?'
]
}
print('=== TEMPLATE PANDUAN DISKUSI ===\n')
for temuan, pertanyaan in pertanyaan_diskusi.items():
print(f'TEMUAN: {temuan}')
print('Pertanyaan untuk dikembangkan menjadi paragraf diskusi:')
for i, q in enumerate(pertanyaan, 1):
print(f' {i}. {q}')
print()
print('--- Setiap pertanyaan bisa menjadi 2-3 kalimat dalam paragraf diskusi ---')
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(11)
n = 87
df = pd.DataFrame({
'kepuasan': np.random.choice([1,2,3,4,5], n, p=[0.05,0.12,0.28,0.38,0.17]),
'kelas': np.random.choice(['X', 'XI', 'XII'], n, p=[0.34, 0.41, 0.25])
})
# Hero chart: kepuasan per kelas dengan anotasi
fig, ax = plt.subplots(figsize=(9, 5))
kelas_means = df.groupby('kelas')['kepuasan'].mean().sort_index()
kelas_sem = df.groupby('kelas')['kepuasan'].sem()
colors = ['#4C72B0', '#55A868', '#C44E52']
bars = ax.bar(kelas_means.index, kelas_means.values, color=colors,
edgecolor='white', linewidth=1.5, width=0.55,
yerr=kelas_sem.values, capsize=5,
error_kw={'ecolor': '#333', 'linewidth': 1})
ax.set_ylim(0, 5.5)
ax.set_ylabel('Rata-rata Kepuasan Belajar (1-5)', fontsize=11)
ax.set_xlabel('Kelas', fontsize=11)
ax.set_title(
'Kepuasan Belajar Meningkat Seiring Jenjang Kelas\n(n=87, error bar = SEM)',
fontsize=12, fontweight='bold', pad=12
)
grand_mean = df['kepuasan'].mean()
ax.axhline(y=grand_mean, color='gray', linestyle='--', linewidth=1, alpha=0.7)
ax.text(2.6, grand_mean + 0.08,
f'Rata-rata keseluruhan: {grand_mean:.2f}',
fontsize=9, color='gray')
for bar, mean_val in zip(bars, kelas_means.values):
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.18,
f'{mean_val:.2f}', ha='center', fontsize=11, fontweight='bold')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.tight_layout()
plt.savefig('/tmp/hero_chart.png', dpi=150, bbox_inches='tight')
print('Hero chart disimpan ke /tmp/hero_chart.png')
# Checklist akhir Proyek P4: Mini Survey
deliverables = {
'Survey Instrument': {
'item': 'File Google Forms (link aktif atau PDF)',
'kriteria': 'Semua pertanyaan terbaca jelas, ada disclaimer anonimitas'
},
'Dataset Mentah': {
'item': 'File CSV hasil ekspor (anonim, tidak ada nama lengkap)',
'kriteria': 'Minimal 30 responden, semua kolom header jelas'
},
'Notebook Analisis': {
'item': 'File .ipynb dengan kode berjalan dari awal hingga akhir',
'kriteria': 'Setiap sel punya output, ada komentar penjelasan'
},
'Laporan Tertulis': {
'item': 'Dokumen dengan 5 bagian standar (Pendahuluan sampai Kesimpulan)',
'kriteria': 'Minimal 800 kata, setiap klaim didukung angka'
},
'Presentasi': {
'item': 'File slide (PPT, Google Slides, atau PDF) 8-10 slide',
'kriteria': 'Setiap slide punya satu pesan utama, visualisasi terbaca jelas'
},
'Retrospektif': {
'item': 'Dokumen refleksi satu halaman',
'kriteria': 'Menjawab: apa yang baik, apa yang akan diubah, pelajaran terbesar'
}
}
print('=== CHECKLIST DELIVERABLE P4: MINI SURVEY ===\n')
for nama, detail in deliverables.items():
print(f'[ ] {nama}')
print(f" File : {detail['item']}")
print(f" Kriteria: {detail['kriteria']}")
print()
print('=' * 50)
print('Semua item di atas harus selesai sebelum Abbas')
print('melanjutkan ke Bulan 9: Machine Learning.')
print('=' * 50)
print('\n=== KOMPETENSI YANG DIPEROLEH DI BULAN 8 ===')
capaian = [
'Merancang penelitian: memilih jenis, metode sampling, dan hipotesis',
'Mendeteksi dan memitigasi berbagai jenis bias dalam penelitian',
'Membuat survey yang valid dengan pertanyaan Likert dan open-ended',
'Menggunakan Google Forms untuk pengumpulan data secara profesional',
'Membersihkan data survey mentah dengan Pandas',
'Menganalisis distribusi jawaban dan melakukan cross-tabulation',
'Menganalisis teks open-ended dengan word frequency dan kategorisasi',
"Menghitung dan menginterpretasikan Cronbach's Alpha",
'Menulis laporan penelitian lengkap dengan struktur standar',
'Membuat presentasi berbasis data dengan data storytelling'
]
for i, item in enumerate(capaian, 1):
print(f' {i:2d}. {item}')
Buku Pendukung Bulan 08
Empat referensi ini memperdalam pemahaman Abbas tentang metodologi riset dan analisis survey, dari level praktis hingga konseptual.
Tools yang Digunakan
Enam tools utama yang mendukung seluruh proses riset dan analisis survey di Bulan 8.
Tips Praktis dan Jebakan Pemula
Berikut adalah pelajaran yang sering dipelajari dengan cara yang susah oleh para peneliti pemula. Abbas bisa belajar lebih awal dan menghindari kesalahan yang sama.
Pertanyaan yang buruk menggiring jawaban responden. Contoh pertanyaan buruk: "Seberapa parah masalah sampah di sekolah kamu?" Kata "parah" sudah menanamkan asumsi bahwa ada masalah. Versi yang lebih baik: "Bagaimana kondisi kebersihan di sekolah kamu?" dengan pilihan jawaban yang netral: sangat bersih, cukup bersih, cukup kotor, sangat kotor. Pertanyaan netral menghasilkan data yang jauh lebih bisa dipercaya.
Hindari frasa seperti "apakah kamu setuju bahwa...", "bukankah lebih baik jika...", atau "mengingat bahwa X sudah terbukti buruk, apa pendapat kamu?". Semua frasa ini sudah menanamkan jawaban yang diinginkan. Dalam analisis data, bias ini tidak bisa dikoreksi setelah data terkumpul karena sudah melekat pada cara pertanyaan diajukan.
Abbas bisa menggunakan pola pertanyaan berikut sebagai kerangka dasar:
Untuk mengukur frekuensi: "Seberapa sering Abbas melakukan X dalam 1 bulan terakhir?" dengan skala: Tidak pernah / 1-2 kali / 3-5 kali / Lebih dari 5 kali.
Untuk mengukur tingkat kepuasan: Skala Likert 1 hingga 5, di mana 1 = Sangat Tidak Puas dan 5 = Sangat Puas. Selalu cantumkan label di kedua ujung skala agar responden tidak salah tafsir.
Untuk pertanyaan terbuka: Taruh di bagian akhir survey. Responden yang sudah menjawab pertanyaan tertutup lebih siap menulis jawaban panjang di akhir daripada di awal.
Survey dengan 10 responden tidak bisa dijadikan dasar kesimpulan yang kuat. Sebagai panduan praktis untuk Abbas: minimal 30 responden untuk analisis dasar, 100 responden untuk analisis yang bisa dibagi ke subkelompok (misalnya membandingkan laki-laki vs perempuan), dan 400 responden untuk margin of error di bawah 5% dengan confidence level 95%. Kalau sampel terlalu kecil, sebut keterbatasannya secara eksplisit di laporan.
Mulai dengan pertanyaan yang mudah dan tidak mengancam (demografi umum, pertanyaan faktual). Lanjut ke pertanyaan inti topik di bagian tengah, saat responden sudah masuk ke "mode menjawab". Taruh pertanyaan sensitif (pendapatan, opini kontroversial) di akhir bagian tengah, bukan di awal. Akhiri dengan pertanyaan terbuka atau pesan penutup. Urutan ini meningkatkan completion rate dan kualitas jawaban.
Pertanyaan seperti "Apakah kamu puas dengan fasilitas dan pelayanan sekolah?" adalah satu pertanyaan yang menanyakan dua hal sekaligus. Kalau jawabannya "tidak puas", Abbas tidak tahu apakah responden tidak puas dengan fasilitas, pelayanan, atau keduanya. Solusinya: pecah menjadi dua pertanyaan terpisah. Ini aturan paling sering dilanggar dalam survey pemula.
Sebelum menyebar survey ke seluruh responden target, coba ke 3-5 orang terlebih dahulu dan minta mereka bicara keras tentang apa yang mereka pikirkan saat membaca setiap pertanyaan. Abbas akan terkejut betapa banyak pertanyaan yang ternyata ambigu atau membingungkan. Pilot test menghemat waktu dan menjaga kualitas data jauh lebih efektif daripada merevisi setelah data sudah terkumpul.
Struktur laporan yang diterima secara luas: (1) Pendahuluan, latar belakang dan tujuan penelitian. (2) Metodologi, siapa yang diteliti, berapa sampelnya, bagaimana cara pengumpulan data, dan alat apa yang dipakai. (3) Hasil, sajikan data secara objektif dengan tabel dan chart, tanpa interpretasi dulu. (4) Diskusi, interpretasi hasil, bandingkan dengan teori atau penelitian sebelumnya, akui keterbatasan. (5) Kesimpulan, jawab pertanyaan penelitian awal secara singkat. Jangan campurkan Hasil dan Diskusi karena akan membuat pembaca bingung mana fakta dan mana interpretasi Abbas.
Latihan Tambahan
Latihan berikut dirancang lebih menantang dari latihan harian. Abbas bisa mengerjakannya setelah selesai minggu terkait, atau sebagai persiapan sebelum mengerjakan Proyek P4.
Berikut adalah survey dengan berbagai masalah desain. Tugas Abbas: identifikasi minimal 7 kesalahan, lalu tulis versi yang sudah diperbaiki untuk setiap pertanyaan.
# Survey Asli (bermasalah) - salin ke file teks, analisis manual
survey_bermasalah = [
"1. Apakah kamu setuju bahwa media sosial merusak mental remaja? [Ya/Tidak]",
"2. Seberapa sering kamu menggunakan media sosial dan internet setiap hari?",
"3. Berapa lama kamu scroll TikTok, Instagram, YouTube, dan Twitter hari ini?",
"4. Apakah orang tua kamu tidak peduli dengan penggunaan HP kamu? [Sangat Setuju - Tidak Setuju]",
"5. Nilai pelajaran apa yang turun karena terlalu banyak main HP?",
"6. Usia kamu berapa?", # pertanyaan ini ditaruh di nomor 6, bukan di awal
"7. Apakah kamu pernah merasa bahwa media sosial, games, dan YouTube membuat kamu tidak fokus belajar dan tidur larut?",
]
# Panduan analisis: untuk setiap pertanyaan, cek:
# - Apakah ada leading words? (merusak, tidak peduli, terlalu banyak)
# - Apakah menanyakan dua hal sekaligus?
# - Apakah urutan logis?
# - Apakah skala jawaban konsisten dan netral?
# - Apakah ada asumsi yang tidak perlu?
print("Identifikasi kesalahan pada setiap nomor, lalu tulis perbaikannya.")
Abbas akan melakukan siklus analisis survey penuh: load data, bersihkan, hitung statistik deskriptif, crosstab, deteksi outlier, lalu simpulkan temuan.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Simulasi data survey 120 responden
np.random.seed(7)
n = 120
df = pd.DataFrame({
'id': range(1, n+1),
'kelas': np.random.choice(['X', 'XI', 'XII'], n, p=[0.35, 0.35, 0.30]),
'jenis_kelamin': np.random.choice(['Laki-laki', 'Perempuan'], n, p=[0.48, 0.52]),
'jam_medsos_harian': np.random.exponential(scale=3, size=n).clip(0, 12).round(1),
'kepuasan_sekolah': np.random.choice([1,2,3,4,5], n, p=[0.05,0.10,0.25,0.40,0.20]),
'motivasi_belajar': np.random.choice([1,2,3,4,5], n, p=[0.08,0.15,0.30,0.32,0.15]),
'ikut_ekskul': np.random.choice(['Ya', 'Tidak'], n, p=[0.60, 0.40]),
})
# --- LANGKAH 1: Explorasi awal ---
print("=== Info Dataset ===")
print(df.info())
print("
=== Missing Values ===")
print(df.isnull().sum())
# --- LANGKAH 2: Statistik deskriptif variabel numerik ---
print("
=== Statistik Deskriptif ===")
print(df[['jam_medsos_harian', 'kepuasan_sekolah', 'motivasi_belajar']].describe().round(2))
# --- LANGKAH 3: Distribusi frekuensi variabel kategorik ---
print("
=== Distribusi Kelas ===")
print(df['kelas'].value_counts(normalize=True).mul(100).round(1).astype(str) + '%')
# --- LANGKAH 4: Crosstab - kepuasan sekolah per kelas ---
print("
=== Rata-rata Kepuasan per Kelas ===")
crosstab_kepuasan = df.groupby('kelas')['kepuasan_sekolah'].agg(['mean', 'std', 'count'])
print(crosstab_kepuasan.round(2))
# --- LANGKAH 5: Korelasi jam medsos vs motivasi belajar ---
corr = df['jam_medsos_harian'].corr(df['motivasi_belajar'])
print(f"
Korelasi jam medsos vs motivasi belajar: {corr:.3f}")
if abs(corr) < 0.1:
print("Interpretasi: hampir tidak ada hubungan linear")
elif abs(corr) < 0.3:
print("Interpretasi: hubungan lemah")
else:
print("Interpretasi: hubungan cukup kuat")
# --- LANGKAH 6: Deteksi outlier jam medsos (metode IQR) ---
Q1 = df['jam_medsos_harian'].quantile(0.25)
Q3 = df['jam_medsos_harian'].quantile(0.75)
IQR = Q3 - Q1
batas_bawah = Q1 - 1.5 * IQR
batas_atas = Q3 + 1.5 * IQR
outlier = df[(df['jam_medsos_harian'] < batas_bawah) | (df['jam_medsos_harian'] > batas_atas)]
print(f"
Outlier jam medsos: {len(outlier)} dari {n} responden")
print(outlier[['id', 'kelas', 'jam_medsos_harian']])
# --- LANGKAH 7: Visualisasi distribusi kepuasan ---
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
df['kepuasan_sekolah'].value_counts().sort_index().plot(
kind='bar', ax=axes[0], color='steelblue', edgecolor='white'
)
axes[0].set_title('Distribusi Kepuasan Sekolah')
axes[0].set_xlabel('Skor (1=Sangat Tidak Puas, 5=Sangat Puas)')
axes[0].set_ylabel('Jumlah Responden')
axes[0].tick_params(axis='x', rotation=0)
df.boxplot(column='jam_medsos_harian', by='kelas', ax=axes[1])
axes[1].set_title('Jam Medsos per Kelas')
axes[1].set_xlabel('Kelas')
axes[1].set_ylabel('Jam per hari')
plt.suptitle('') # hapus judul otomatis dari boxplot
plt.tight_layout()
plt.savefig('hasil_survey_sekolah.png', dpi=150, bbox_inches='tight')
print("
Chart disimpan sebagai hasil_survey_sekolah.png")
scipy.stats.kruskal karena data ordinal, bukan ANOVA biasa.
Jelaskan mengapa Kruskal-Wallis lebih tepat untuk data skala Likert.
Cronbach's Alpha mengukur konsistensi internal sebuah skala. Kalau Abbas membuat 5 pertanyaan untuk mengukur "motivasi belajar", alpha yang tinggi (di atas 0.7) berarti kelima pertanyaan itu benar-benar mengukur hal yang sama.
import pandas as pd
import numpy as np
# 5 pertanyaan motivasi belajar, 50 responden (skor 1-5)
np.random.seed(99)
n_resp = 50
# Simulasi: buat responden yang punya "motivasi sejati" laten
motivasi_laten = np.random.normal(3, 0.8, n_resp)
# Setiap pertanyaan mengukur motivasi + sedikit noise
data_skala = pd.DataFrame({
'Q1_target_belajar': (motivasi_laten + np.random.normal(0, 0.5, n_resp)).clip(1,5).round(),
'Q2_semangat_pagi': (motivasi_laten + np.random.normal(0, 0.6, n_resp)).clip(1,5).round(),
'Q3_tidak_mudah_nyerah': (motivasi_laten + np.random.normal(0, 0.4, n_resp)).clip(1,5).round(),
'Q4_suka_tantangan': (motivasi_laten + np.random.normal(0, 0.7, n_resp)).clip(1,5).round(),
'Q5_bangga_hasil': (motivasi_laten + np.random.normal(0, 0.5, n_resp)).clip(1,5).round(),
})
# Hitung Cronbach's Alpha
def cronbach_alpha(df):
k = df.shape[1] # jumlah item
var_items = df.var(axis=0, ddof=1) # varians tiap item
var_total = df.sum(axis=1).var(ddof=1) # varians skor total
alpha = (k / (k - 1)) * (1 - var_items.sum() / var_total)
return alpha
alpha = cronbach_alpha(data_skala)
print(f"Cronbach's Alpha: {alpha:.3f}")
# Interpretasi standar
if alpha >= 0.9:
print("Konsistensi: Excellent (sangat baik untuk instrumen klinis)")
elif alpha >= 0.8:
print("Konsistensi: Good (baik untuk riset akademis)")
elif alpha >= 0.7:
print("Konsistensi: Acceptable (cukup untuk riset eksplorasi)")
elif alpha >= 0.6:
print("Konsistensi: Questionable (perlu perbaikan pertanyaan)")
else:
print("Konsistensi: Poor (pertanyaan tidak mengukur hal yang sama)")
# Alpha jika item dihapus: bantu identifikasi pertanyaan lemah
print("
=== Alpha jika item dihapus ===")
for col in data_skala.columns:
df_tanpa = data_skala.drop(columns=[col])
alpha_tanpa = cronbach_alpha(df_tanpa)
delta = alpha_tanpa - alpha
tanda = "+" if delta > 0 else ""
print(f" Tanpa {col}: alpha={alpha_tanpa:.3f} ({tanda}{delta:.3f})")
if delta > 0.02:
print(f" -> Pertimbangkan hapus {col}, menurunkan konsistensi skala.")