This commit is contained in:
aarsyah0 2025-05-31 23:23:28 +07:00
parent faf884d9e1
commit edf4f3893d
1 changed files with 146 additions and 117 deletions

View File

@ -240,78 +240,100 @@
SECTION: Metrics Chart (Perbandingan Precision, F1, Accuracy) SECTION: Metrics Chart (Perbandingan Precision, F1, Accuracy)
================================================================ --}} ================================================================ --}}
@php @php
// Siapkan array untuk ringkasan metrik tiap ewallet // Daftar e-wallet yang ingin kita tampilkan
$metricsSummary = [ $wallets = ['dana', 'gopay', 'shopeepay'];
'dana' => ['precision' => 0, 'f1' => 0, 'accuracy' => 0],
'gopay' => ['precision' => 0, 'f1' => 0, 'accuracy' => 0],
'shopeepay' => ['precision' => 0, 'f1' => 0, 'accuracy' => 0],
];
foreach (array_keys($metricsSummary) as $key) { // Kita akan menyimpan struktur seperti:
// 1) Baca evaluation_metrics_full{key}.csv // $metricsDetail['dana'] = [
$metricsPath = storage_path("app/public/evaluation_metrics_full{$key}.csv"); // 'labels' => ['Netral','Positif','Negatif'],
$metricsData = []; // 'precision' => [0.812, 0.902, 0.748],
if (file_exists($metricsPath)) { // 'recall' => [0.800, 0.889, 0.765],
$metricsData = array_map('str_getcsv', file($metricsPath)); // 'f1' => [0.806, 0.895, 0.756],
// ];
$metricsDetail = [];
// Kelaskelas yang ingin kita plot urutannya: netral → positif → negatif
$kelasUrut = ['netral', 'positif', 'negatif'];
foreach ($wallets as $key) {
// Nama file CSV mirip: evaluation_metrics_full{key}.csv
// Note: untuk 'shopeepay', file Anda pakai 'shopee' bukan 'shopeepay'?
// Di contoh awal Anda, yang diupload adalah evaluation_metrics_fullshopee.csv,
// maka gunakan $fileKey='shopee' bukan 'shopeepay'.
$fileKey = $key;
$pathCsv = storage_path("app/public/evaluation_metrics_full{$fileKey}.csv");
$rows = [];
if (file_exists($pathCsv)) {
// Baca setiap baris kemudian pecah jadi array per kolom
$rows = array_map('str_getcsv', file($pathCsv));
} }
// Hitung MacroPrecision & MacroF1 (rata-rata precision/f1 tiap kelas) // Inisialisasi struktur kosong
$sumPrecision = 0.0; $metricsDetail[$key] = [
$sumF1 = 0.0; 'labels' => [],
$nClasses = 0; 'precision' => [],
if (count($metricsData) > 1) { 'recall' => [],
// Baris pertama header: [,precision,recall,f1score,support] 'f1' => [],
foreach (array_slice($metricsData, 1) as $row) { ];
$sumPrecision += (float) $row[1]; // precision
$sumF1 += (float) $row[3]; // f1score if (count($rows) > 1) {
$nClasses++; // Baris 0: header → abaikan. Mulai dari baris 1.
// Kita ingin memastikan tiap kelas ('netral','positif','negatif') ada, dan urutannya sesuai $kelasUrut.
// Bisa saja di CSV urutannya sudah sama, tapi kita paksa urut sesuai $kelasUrut.
// Buat map nama kelas ke indeks baris
$mapRowByKelas = [];
foreach (array_slice($rows, 1) as $r) {
// Asumsikan kolom 0 adalah nama kelas, kolom 2 precision, kolom 3 recall, kolom 4 f1-score
$namaKelas = strtolower(trim($r[0]));
$mapRowByKelas[$namaKelas] = $r;
} }
if ($nClasses > 0) {
$metricsSummary[$key]['precision'] = round($sumPrecision / $nClasses, 3);
$metricsSummary[$key]['f1'] = round($sumF1 / $nClasses, 3);
}
}
// 2) Hitung Accuracy dari confusion_matrix_{key}.csv foreach ($kelasUrut as $kelas) {
$confusionPath = storage_path("app/public/confusion_matrix_{$key}.csv"); if (isset($mapRowByKelas[$kelas])) {
$confusionData = []; $r = $mapRowByKelas[$kelas];
if (file_exists($confusionPath)) { // Ambil nilai ke2 (precision), ke3 (recall), ke4 (f1score)
$confusionData = array_map('str_getcsv', file($confusionPath)); // Hatihati: format CSV kadang ada kolom kosong di indeks 1 → kita gunakan r[2], r[3], r[4].
} $precision = isset($r[2]) ? (float) $r[2] : 0;
$recall = isset($r[3]) ? (float) $r[3] : 0;
$f1score = isset($r[4]) ? (float) $r[4] : 0;
if (count($confusionData) > 1) { // Push ke dalam array
$totalAll = 0; $metricsDetail[$key]['labels'][] = ucfirst($kelas);
$sumDiagonal = 0; $metricsDetail[$key]['precision'][] = round($precision, 3);
// Hitung total sampel $metricsDetail[$key]['recall'][] = round($recall, 3);
foreach (array_slice($confusionData, 1) as $r) { $metricsDetail[$key]['f1'][] = round($f1score, 3);
for ($c = 1; $c < count($r); $c++) { } else {
$totalAll += (int) $r[$c]; // Jika suatu kelas tidak ditemukan di CSV, isi 0.
$metricsDetail[$key]['labels'][] = ucfirst($kelas);
$metricsDetail[$key]['precision'][] = 0;
$metricsDetail[$key]['recall'][] = 0;
$metricsDetail[$key]['f1'][] = 0;
} }
} }
// Jumlah diagonal (misal 3 kelas → sel [1][1], [2][2], [3][3])
$sumDiagonal += (int) $confusionData[1][1];
$sumDiagonal += (int) $confusionData[2][2];
$sumDiagonal += (int) $confusionData[3][3];
if ($totalAll > 0) {
$metricsSummary[$key]['accuracy'] = round($sumDiagonal / $totalAll, 3);
}
} }
} }
@endphp @endphp
<div class="row mt-4 mb-5"> <div class="row mt-4 mb-5">
<div class="col-12"> @foreach ($wallets as $key)
<div class="card shadow-sm"> @php
<div class="card-header"> // Ubah key menjadi judul yang lebih enak (Dana, Gopay, Shopeepay)
<h6 class="mb-0">Perbandingan Metode Prediksi: Precision, F1Score, dan Akurasi</h6> $judul = ucfirst($key);
</div> @endphp
<div class="card-body p-3" style="height:350px;"> <div class="col-md-4 mb-4">
<canvas id="metricsChart" width="800" height="350"></canvas> <div class="card shadow-sm h-100">
<div class="card-header">
<h6 class="mb-0">Metrik Klasifikasi: {{ $judul }}</h6>
</div>
<div class="card-body" style="height: 300px;">
{{-- Canvas untuk Chart.js --}}
<canvas id="chart-{{ $key }}" width="400" height="250"></canvas>
</div>
</div> </div>
</div> </div>
</div> @endforeach
</div> </div>
{{-- ================================================================ {{-- ================================================================
SECTION: Top Features (Tiap ewallet dalam satu card terpisah) SECTION: Top Features (Tiap ewallet dalam satu card terpisah)
================================================================ --}} ================================================================ --}}
@ -477,91 +499,98 @@
sentiments.forEach(drawWC); sentiments.forEach(drawWC);
}); });
</script> </script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', function() {
// Ambil data ringkasan metrik dari PHP ke JS // Kita ingin memanggil Chart.js untuk masingmasing ewallet
const ewMetrics = @json($metricsSummary); const wallets = {!! json_encode($wallets) !!};
const labels = ['Dana', 'GoPay', 'ShopeePay'];
// Siapkan data Precision, F1, dan Accuracy dalam urutan yang sama wallets.forEach(function(key) {
const precisionData = labels.map(name => { // Ambil data PHP yang sudah kita siapkan di $metricsDetail
const key = name.toLowerCase(); const dataPHP = {!! json_encode($metricsDetail) !!};
return ewMetrics[key].precision;
});
const f1Data = labels.map(name => {
const key = name.toLowerCase();
return ewMetrics[key].f1;
});
const accuracyData = labels.map(name => {
const key = name.toLowerCase();
return ewMetrics[key].accuracy;
});
// Buat bar chart dengan tiga kelompok dataset // Labels kelas (Netral, Positif, Negatif)
new Chart(document.getElementById('metricsChart'), { const labels = dataPHP[key]['labels']; // contoh: ["Netral","Positif","Negatif"]
type: 'bar', const dataPrecision = dataPHP[key]['precision'];
data: { const dataRecall = dataPHP[key]['recall'];
const dataF1 = dataPHP[key]['f1'];
// Konfigurasi dataset untuk Chart.js
const chartData = {
labels: labels, labels: labels,
datasets: [{ datasets: [{
label: 'Precision (Macro)', label: 'Precision',
data: precisionData, data: dataPrecision,
backgroundColor: 'rgba(54, 162, 235, 0.6)', backgroundColor: 'rgba(54, 162, 235, 0.5)',
borderColor: 'rgba(54, 162, 235, 1)', borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1 borderWidth: 1
}, },
{ {
label: 'F1Score (Macro)', label: 'Recall',
data: f1Data, data: dataRecall,
backgroundColor: 'rgba(255, 206, 86, 0.6)', backgroundColor: 'rgba(255, 206, 86, 0.5)',
borderColor: 'rgba(255, 206, 86, 1)', borderColor: 'rgba(255, 206, 86, 1)',
borderWidth: 1 borderWidth: 1
}, },
{ {
label: 'Accuracy', label: 'F1Score',
data: accuracyData, data: dataF1,
backgroundColor: 'rgba(75, 192, 192, 0.6)', backgroundColor: 'rgba(75, 192, 192, 0.5)',
borderColor: 'rgba(75, 192, 192, 1)', borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1 borderWidth: 1
} }
] ]
}, };
options: {
responsive: true, const config = {
maintainAspectRatio: false, type: 'bar',
scales: { data: chartData,
yAxes: [{ options: {
ticks: { responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true, beginAtZero: true,
callback: value => (value * 100).toFixed(0) + max: 1,
'%' // Menampilkan dalam persen ticks: {
}, callback: function(value) {
scaleLabel: { return value.toFixed(2);
display: true, }
labelString: 'Nilai (%)' },
title: {
display: true,
text: 'Nilai'
}
} }
}], },
xAxes: [{ plugins: {
barPercentage: 0.6, tooltip: {
categoryPercentage: 0.6 callbacks: {
}] label: function(context) {
}, let label = context.dataset.label || '';
tooltips: { if (label) {
callbacks: { label += ': ';
label: function(tooltipItem, data) { }
let label = data.datasets[tooltipItem.datasetIndex].label || ''; if (context.parsed.y !== null) {
let val = tooltipItem.yLabel; label += context.parsed.y.toFixed(3);
return label + ': ' + (val * 100).toFixed(1) + '%'; }
return label;
}
}
},
legend: {
position: 'top',
} }
} }
},
legend: {
position: 'bottom'
} }
} };
// Buat chart pada canvas dengan id="chart-<key>"
const ctx = document.getElementById('chart-' + key).getContext('2d');
new Chart(ctx, config);
}); });
}); });
</script> </script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script>