""" ============================================================= DASHBOARD VISUALIZATION ============================================================= Generate visualisasi untuk hasil clustering alumni: - Distribusi Kategori C0-C3 - Distribusi per Prodi - Distribusi per Tahun - Cluster Analysis - Pie Charts dan Bar Charts Output: PNG charts + HTML Dashboard ============================================================= """ import pandas as pd import numpy as np import json import os from datetime import datetime import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import seaborn as sns # Set style plt.style.use('seaborn-v0_8-whitegrid') sns.set_palette("husl") # Path configuration BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DATA_DIR = os.path.join(BASE_DIR, '..', 'data', 'processed') RESULT_DIR = os.path.join(BASE_DIR, '..', 'data', 'result') OUTPUT_DIR = os.path.join(BASE_DIR, '..', 'data', 'processed', 'dashboard') # Create output directory os.makedirs(OUTPUT_DIR, exist_ok=True) # Colors COLORS = { 'C0': '#2ecc71', # Green - Sesuai Profil 'C1': '#3498db', # Blue - Bidang Diketahui 'C2': '#f39c12', # Orange - Bidang Tidak Jelas 'C3': '#e74c3c', # Red - Belum Bekerja 'TIF': '#9b59b6', # Purple 'MIF': '#1abc9c', # Teal 'TKK': '#e67e22' # Orange } def load_data(): """Load semua data yang diperlukan.""" print(">> Loading data...") # Interpretasi results (from result folder) df = pd.read_csv(os.path.join(RESULT_DIR, 'interpretasi_results.csv'), encoding='utf-8-sig') # Processed dataset processed_df = pd.read_csv(os.path.join(DATA_DIR, 'processed_dataset.csv'), encoding='utf-8-sig') # Cluster statistics with open(os.path.join(DATA_DIR, 'cluster_statistics.json'), 'r') as f: cluster_stats = json.load(f) print(f" Total data: {len(df)}") return df, processed_df, cluster_stats def create_pie_chart_kategori(df, output_path): """Pie chart distribusi kategori C0-C3.""" print(">> Creating Pie Chart - Kategori C0-C3...") kategori_counts = df['kategori'].value_counts() labels = kategori_counts.index.tolist() sizes = kategori_counts.values colors = [COLORS.get(k, '#95a5a6') for k in labels] fig, ax = plt.subplots(figsize=(10, 8)) wedges, texts, autotexts = ax.pie( sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90, explode=[0.02] * len(labels) ) # Style for autotext in autotexts: autotext.set_fontsize(12) autotext.set_fontweight('bold') ax.set_title('Distribusi Kategori Alumni (C0-C3)\n', fontsize=16, fontweight='bold') # Add legend legend_labels = [ f'C0: Sesuai Profil ({kategori_counts.get("C0", 0)})', f'C1: Bidang Diketahui ({kategori_counts.get("C1", 0)})', f'C2: Bidang Tidak Jelas ({kategori_counts.get("C2", 0)})', f'C3: Belum Bekerja ({kategori_counts.get("C3", 0)})' ] ax.legend(wedges, legend_labels, title="Kategori", loc="center left", bbox_to_anchor=(1, 0, 0.5, 1)) plt.tight_layout() plt.savefig(output_path, dpi=150, bbox_inches='tight', facecolor='white') plt.close() print(f" Saved: {output_path}") def create_bar_chart_prodi(df, output_path): """Bar chart distribusi C0-C3 per prodi.""" print(">> Creating Bar Chart - Distribusi per Prodi...") # Map prodi codes prodi_map = {'55301': 'TIF', '57401': 'MIF', '56401': 'TKK'} df['prodi'] = df['kode_prodi'].astype(str).str.replace('.0', '', regex=False).str.strip() df['prodi'] = df['prodi'].map(prodi_map).fillna('Lainnya') # Group by prodi and kategori grouped = df.groupby(['prodi', 'kategori']).size().unstack(fill_value=0) fig, ax = plt.subplots(figsize=(12, 7)) categories = ['C0', 'C1', 'C2', 'C3'] colors = [COLORS[c] for c in categories] x = np.arange(len(grouped.index)) width = 0.2 for i, cat in enumerate(categories): if cat in grouped.columns: values = grouped[cat].values bars = ax.bar(x + i * width, values, width, label=cat, color=colors[i]) # Add value labels for bar in bars: height = bar.get_height() if height > 0: ax.annotate(f'{int(height)}', xy=(bar.get_x() + bar.get_width() / 2, height), xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9) ax.set_xlabel('Program Studi', fontsize=12) ax.set_ylabel('Jumlah Alumni', fontsize=12) ax.set_title('Distribusi Kategori C0-C3 per Program Studi\n', fontsize=16, fontweight='bold') ax.set_xticks(x + width * 1.5) ax.set_xticklabels(grouped.index) ax.legend(title='Kategori') plt.tight_layout() plt.savefig(output_path, dpi=150, bbox_inches='tight', facecolor='white') plt.close() print(f" Saved: {output_path}") def create_stacked_bar_tahun(df, output_path): """Stacked bar chart distribusi per tahun.""" print(">> Creating Stacked Bar - Distribusi per Tahun...") # Get tahun from processed dataset processed_df = pd.read_csv(os.path.join(DATA_DIR, 'processed_dataset.csv'), encoding='utf-8-sig') df = df.merge(processed_df[['nim', 'tahun_lulus']], on='nim', how='left') # Group by tahun and kategori grouped = df.groupby(['tahun_lulus', 'kategori']).size().unstack(fill_value=0) fig, ax = plt.subplots(figsize=(12, 7)) categories = ['C0', 'C1', 'C2', 'C3'] colors = [COLORS[c] for c in categories] bottom = np.zeros(len(grouped.index)) for i, cat in enumerate(categories): if cat in grouped.columns: values = grouped[cat].values ax.bar(grouped.index.astype(str), values, bottom=bottom, label=cat, color=colors[i]) bottom += values ax.set_xlabel('Tahun Lulus', fontsize=12) ax.set_ylabel('Jumlah Alumni', fontsize=12) ax.set_title('Distribusi Kategori C0-C3 per Tahun Lulus\n', fontsize=16, fontweight='bold') ax.legend(title='Kategori', loc='upper right') plt.tight_layout() plt.savefig(output_path, dpi=150, bbox_inches='tight', facecolor='white') plt.close() print(f" Saved: {output_path}") def create_donut_chart_cluster(df, output_path): """Donut chart distribusi cluster.""" print(">> Creating Donut Chart - Distribusi Cluster...") cluster_counts = df['cluster'].value_counts().sort_index() fig, ax = plt.subplots(figsize=(10, 8)) colors = ['#3498db', '#2ecc71', '#f39c12', '#9b59b6'] labels = [f'Cluster {i}' for i in cluster_counts.index] wedges, texts, autotexts = ax.pie( cluster_counts.values, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90, pctdistance=0.75, explode=[0.02] * len(labels) ) # Create donut hole centre_circle = plt.Circle((0, 0), 0.50, fc='white') ax.add_patch(centre_circle) # Style for autotext in autotexts: autotext.set_fontsize(11) autotext.set_fontweight('bold') ax.set_title('Distribusi Cluster K-Means (K=4)\n', fontsize=16, fontweight='bold') # Add legend legend_labels = [f'Cluster {i}: {cluster_counts.get(i, 0)}' for i in range(4)] ax.legend(wedges, legend_labels, title="Cluster", loc="center left", bbox_to_anchor=(1, 0, 0.5, 1)) plt.tight_layout() plt.savefig(output_path, dpi=150, bbox_inches='tight', facecolor='white') plt.close() print(f" Saved: {output_path}") def create_summary_cards(stats, output_path): """Generate summary cards as image.""" print(">> Creating Summary Cards...") fig, axes = plt.subplots(2, 4, figsize=(16, 8)) fig.patch.set_facecolor('white') # Total Alumni ax = axes[0, 0] ax.text(0.5, 0.7, '535', fontsize=36, ha='center', fontweight='bold', color='#2c3e50') ax.text(0.5, 0.3, 'Total Alumni', fontsize=12, ha='center', color='#7f8c8d') ax.axis('off') # C0 ax = axes[0, 1] ax.text(0.5, 0.7, '18', fontsize=36, ha='center', fontweight='bold', color=COLORS['C0']) ax.text(0.5, 0.3, 'C0 - Sesuai Profil', fontsize=10, ha='center', color='#7f8c8d') ax.axis('off') # C1 ax = axes[0, 2] ax.text(0.5, 0.7, '85', fontsize=36, ha='center', fontweight='bold', color=COLORS['C1']) ax.text(0.5, 0.3, 'C1 - Bidang Diketahui', fontsize=10, ha='center', color='#7f8c8d') ax.axis('off') # C2 ax = axes[0, 3] ax.text(0.5, 0.7, '416', fontsize=36, ha='center', fontweight='bold', color=COLORS['C2']) ax.text(0.5, 0.3, 'C2 - Bidang Tidak Jelas', fontsize=10, ha='center', color='#7f8c8d') ax.axis('off') # C3 ax = axes[1, 0] ax.text(0.5, 0.7, '16', fontsize=36, ha='center', fontweight='bold', color=COLORS['C3']) ax.text(0.5, 0.3, 'C3 - Belum Bekerja', fontsize=10, ha='center', color='#7f8c8d') ax.axis('off') # TIF ax = axes[1, 1] ax.text(0.5, 0.7, '225', fontsize=36, ha='center', fontweight='bold', color=COLORS['TIF']) ax.text(0.5, 0.3, 'TIF - Teknik Informatika', fontsize=10, ha='center', color='#7f8c8d') ax.axis('off') # MIF ax = axes[1, 2] ax.text(0.5, 0.7, '165', fontsize=36, ha='center', fontweight='bold', color=COLORS['MIF']) ax.text(0.5, 0.3, 'MIF - Manajemen Informatika', fontsize=10, ha='center', color='#7f8c8d') ax.axis('off') # TKK ax = axes[1, 3] ax.text(0.5, 0.7, '145', fontsize=36, ha='center', fontweight='bold', color=COLORS['TKK']) ax.text(0.5, 0.3, 'TKK - Teknik Komputer', fontsize=10, ha='center', color='#7f8c8d') ax.axis('off') plt.suptitle('Dashboard Summary - Sistem Clustering Alumni', fontsize=18, fontweight='bold', y=1.02) plt.tight_layout() plt.savefig(output_path, dpi=150, bbox_inches='tight', facecolor='white') plt.close() print(f" Saved: {output_path}") def create_html_dashboard(df, output_path): """Generate interactive HTML dashboard.""" print(">> Creating HTML Dashboard...") # Calculate stats kategori_counts = df['kategori'].value_counts() total = len(df) # Prodi distribution prodi_map = {'55301': 'TIF', '57401': 'MIF', '56401': 'TKK'} df['prodi'] = df['kode_prodi'].astype(str).str.replace('.0', '', regex=False).str.strip() df['prodi'] = df['prodi'].map(prodi_map).fillna('Lainnya') prodi_counts = df['prodi'].value_counts() html_content = f"""
Sistem Pengelompokan Pekerjaan Alumni dengan Kompetensi Lulusan - Jurusan Teknologi Informasi, Politeknik Negeri Jember