Fixing -means v1
This commit is contained in:
parent
5dd6f4a297
commit
b309e56152
|
@ -72,6 +72,9 @@ public function store(Request $request)
|
|||
$hasil = $service->hitungKMeansCuranmor();
|
||||
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasil));
|
||||
|
||||
$serviceSSECuranmor = new KMeansService();
|
||||
$serviceSSECuranmor->SSEElbowCuranmor();
|
||||
|
||||
// =====CODE TAMBAH SEBELUMNYA=========
|
||||
// $validateData = $request->validate([
|
||||
// 'kecamatan_id' =>'required|max:255|exists:kecamatans,id|unique:curanmors,kecamatan_id',
|
||||
|
@ -144,6 +147,9 @@ public function update(Request $request, Curanmor $curanmor)
|
|||
|
||||
// simpan hasil ke file json
|
||||
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasil));
|
||||
|
||||
$serviceSSECuranmor = new KMeansService();
|
||||
$serviceSSECuranmor->SSEElbowCuranmor();
|
||||
|
||||
return redirect('/dashboard/curanmor')->with('succes', 'Data Kecamatan Berhasil Diubah');
|
||||
} catch (\Exception $e) {
|
||||
|
@ -168,6 +174,9 @@ public function destroy($curanmor)
|
|||
// Hapus data
|
||||
$hapus->delete();
|
||||
|
||||
$serviceSSECuranmor = new KMeansService();
|
||||
$serviceSSECuranmor->SSEElbowCuranmor();
|
||||
|
||||
return redirect('/dashboard/curanmor')->with('succes', 'Data Curanmor Berhasil Dihapus');
|
||||
} catch (\Exception $e) {
|
||||
return redirect('/dashboard/curanmor')->with('error', 'Terjadi kesalahan: ' . $e->getMessage());
|
||||
|
|
|
@ -77,6 +77,9 @@ public function store(Request $request)
|
|||
|
||||
// simpan hasil ke file json
|
||||
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasil));
|
||||
|
||||
$serviceSSECuras = new KMeansService();
|
||||
$serviceSSECuras->SSEElbowCuras();
|
||||
|
||||
return redirect('/dashboard/curas')->with('succes', 'Data curas berhasil ditambahkan.');
|
||||
}catch (\Exception $e){
|
||||
|
@ -147,6 +150,9 @@ public function update(Request $request, $id)
|
|||
// simpan hasil ke file json
|
||||
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasil));
|
||||
|
||||
$serviceSSECuras = new KMeansService();
|
||||
$serviceSSECuras->SSEElbowCuras();
|
||||
|
||||
return redirect('/dashboard/curas')->with('succes', 'Data Kecamatan Berhasil Diubah');
|
||||
} catch (\Exception $e) {
|
||||
return redirect('/dashboard/curas')->with('error', 'Data Kecamatan Gagal Diubah: ' . $e->getMessage());
|
||||
|
|
|
@ -86,6 +86,9 @@ public function destroy($id)
|
|||
// simpan hasil ke file json
|
||||
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasil));
|
||||
|
||||
$serviceSSECuranmor = new KMeansService();
|
||||
$serviceSSECuranmor->SSEElbowCuranmor();
|
||||
|
||||
return redirect('/dashboard/detail-curanmor')->with('succes', 'Data berhasil dihapus dan curanmor diperbarui.');
|
||||
} catch (\Exception $e) {
|
||||
return redirect('/dashboard/detail-curanmor')->with('error', 'Terjadi kesalahan Ketika Menghapus Data : ' . $e->getMessage());
|
||||
|
|
|
@ -89,6 +89,9 @@ public function destroy($id)
|
|||
// simpan hasil ke file json
|
||||
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasil));
|
||||
|
||||
$serviceSSECuras = new KMeansService();
|
||||
$serviceSSECuras->SSEElbowCuras();
|
||||
|
||||
return redirect('/dashboard/detail-curas')->with('succes', 'Data berhasil dihapus dan curas diperbarui.');
|
||||
} catch (\Exception $e) {
|
||||
return redirect('/dashboard/detail-curas')->with('error', 'Terjadi kesalahan Ketika Menghapus Data : ' . $e->getMessage());
|
||||
|
|
|
@ -148,21 +148,23 @@ public function KMeansCuranmor()
|
|||
|
||||
foreach ($data as $item) {
|
||||
$jarak = [];
|
||||
|
||||
|
||||
foreach ($centroids as $idx => $centroid) {
|
||||
$dist = abs($item->jumlah_curanmor - $centroid['jumlah_curanmor']);
|
||||
// Menggunakan Euclidean distance standar (akar kuadrat dari selisih kuadrat)
|
||||
$dist = sqrt(pow($item->jumlah_curanmor - $centroid['jumlah_curanmor'], 2));
|
||||
$jarak["C" . ($idx + 1)] = $dist;
|
||||
}
|
||||
|
||||
|
||||
$iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak);
|
||||
|
||||
|
||||
$minIndex = array_keys($jarak, min($jarak))[0]; // e.g. "jarakC2"
|
||||
$clusterNumber = (int) str_replace("C", "", $minIndex);
|
||||
|
||||
|
||||
$clustered[$clusterNumber][] = $item;
|
||||
$item->temp_klaster = $clusterNumber;
|
||||
$currentAssignment[$item->id] = $clusterNumber;
|
||||
}
|
||||
|
||||
|
||||
// ✨ Cek konvergensi: jika assignment sekarang == sebelumnya, break
|
||||
if ($currentAssignment === $prevAssignment) {
|
||||
|
|
|
@ -221,41 +221,44 @@ public function hitungKMeansCuranmor()
|
|||
public function SSEElbowCuras()
|
||||
{
|
||||
$data = Curas::select('id', 'jumlah_curas')->get();
|
||||
$maxK = 10;
|
||||
$maxK = 4;
|
||||
$maxIterasi = 100;
|
||||
$elbowData = [];
|
||||
|
||||
|
||||
for ($k = 1; $k <= $maxK; $k++) {
|
||||
// Inisialisasi centroid awal secara acak
|
||||
$centroids = $data->unique('jumlah_curas')->shuffle()->take($k)->values()->map(function ($item) {
|
||||
return ['jumlah_curas' => $item->jumlah_curas];
|
||||
});
|
||||
|
||||
|
||||
// Simpan centroid awal sebagai array angka
|
||||
$centroidAwal = $centroids->pluck('jumlah_curas')->toArray();
|
||||
|
||||
$prevAssignment = [];
|
||||
|
||||
|
||||
for ($iter = 0; $iter < $maxIterasi; $iter++) {
|
||||
$clustered = [];
|
||||
$currentAssignment = [];
|
||||
|
||||
|
||||
foreach ($data as $item) {
|
||||
$jarak = [];
|
||||
|
||||
|
||||
foreach ($centroids as $idx => $centroid) {
|
||||
$dist = abs($item->jumlah_curas - $centroid['jumlah_curas']);
|
||||
$jarak[$idx] = $dist;
|
||||
}
|
||||
|
||||
|
||||
$minIndex = array_keys($jarak, min($jarak))[0];
|
||||
$clustered[$minIndex][] = $item;
|
||||
$currentAssignment[$item->id] = $minIndex;
|
||||
}
|
||||
|
||||
|
||||
if ($currentAssignment === $prevAssignment) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$prevAssignment = $currentAssignment;
|
||||
|
||||
|
||||
// Update centroid
|
||||
foreach ($clustered as $key => $group) {
|
||||
$avg = collect($group)->avg('jumlah_curas');
|
||||
|
@ -266,7 +269,7 @@ public function SSEElbowCuras()
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Hitung SSE untuk k saat ini
|
||||
$sse = 0;
|
||||
foreach ($clustered as $key => $group) {
|
||||
|
@ -275,20 +278,21 @@ public function SSEElbowCuras()
|
|||
$sse += pow($item->jumlah_curas - $centroidVal, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$elbowData[] = [
|
||||
'k' => $k,
|
||||
'sse' => $sse
|
||||
'sse' => $sse,
|
||||
'centroid_awal' => $centroidAwal
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// Simpan ke file
|
||||
file_put_contents(
|
||||
storage_path('app/public/sse_elbow_curas.json'),
|
||||
json_encode($elbowData, JSON_PRETTY_PRINT)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function SSEElbowCuranmor()
|
||||
{
|
||||
|
@ -296,40 +300,41 @@ public function SSEElbowCuranmor()
|
|||
$maxK = 10;
|
||||
$maxIterasi = 100;
|
||||
$elbowData = [];
|
||||
|
||||
|
||||
for ($k = 1; $k <= $maxK; $k++) {
|
||||
|
||||
srand(time());
|
||||
// Inisialisasi centroid awal secara acak
|
||||
$centroids = $data->unique('jumlah_curanmor')->shuffle()->take($k)->values()->map(function ($item) {
|
||||
return ['jumlah_curanmor' => $item->jumlah_curanmor];
|
||||
});
|
||||
|
||||
|
||||
// Simpan centroid awal sebagai array angka
|
||||
$centroidAwal = $centroids->pluck('jumlah_curanmor')->toArray();
|
||||
|
||||
$prevAssignment = [];
|
||||
|
||||
|
||||
for ($iter = 0; $iter < $maxIterasi; $iter++) {
|
||||
$clustered = [];
|
||||
$currentAssignment = [];
|
||||
|
||||
|
||||
foreach ($data as $item) {
|
||||
$jarak = [];
|
||||
|
||||
|
||||
foreach ($centroids as $idx => $centroid) {
|
||||
$dist = abs($item->jumlah_curanmor - $centroid['jumlah_curanmor']);
|
||||
$jarak[$idx] = $dist;
|
||||
}
|
||||
|
||||
|
||||
$minIndex = array_keys($jarak, min($jarak))[0];
|
||||
$clustered[$minIndex][] = $item;
|
||||
$currentAssignment[$item->id] = $minIndex;
|
||||
}
|
||||
|
||||
|
||||
if ($currentAssignment === $prevAssignment) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$prevAssignment = $currentAssignment;
|
||||
|
||||
|
||||
// Update centroid
|
||||
foreach ($clustered as $key => $group) {
|
||||
$avg = collect($group)->avg('jumlah_curanmor');
|
||||
|
@ -340,7 +345,7 @@ public function SSEElbowCuranmor()
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Hitung SSE untuk k saat ini
|
||||
$sse = 0;
|
||||
foreach ($clustered as $key => $group) {
|
||||
|
@ -349,19 +354,19 @@ public function SSEElbowCuranmor()
|
|||
$sse += pow($item->jumlah_curanmor - $centroidVal, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$elbowData[] = [
|
||||
'k' => $k,
|
||||
'sse' => $sse
|
||||
'sse' => $sse,
|
||||
'centroid_awal' => $centroidAwal
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// Simpan ke file
|
||||
file_put_contents(
|
||||
storage_path('app/public/sse_elbow_curanmor.json'),
|
||||
json_encode($elbowData, JSON_PRETTY_PRINT)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,11 +25,9 @@ public function run(): void
|
|||
CuranmorSeeder::class,
|
||||
]);
|
||||
|
||||
$serviceKMeansCuras = new KMeansService();
|
||||
$hasilKMeansCuras = $serviceKMeansCuras->SSEElbowCuras();
|
||||
|
||||
$serviceKMeansCuras = new KMeansService();
|
||||
$hasilKMeansCuras = $serviceKMeansCuras->SSEElbowCuranmor();
|
||||
$serviceKMeans = new KMeansService();
|
||||
$serviceKMeans->SSEElbowCuranmor();
|
||||
$serviceKMeans->SSEElbowCuras();
|
||||
|
||||
$serviceKMeansCuras = new KMeansService();
|
||||
$hasilKMeansCuras = $serviceKMeansCuras->hitungKMeansCuras();
|
||||
|
|
|
@ -5193,136 +5193,64 @@ if (jQuery("#editor").length) {
|
|||
apexChartUpdate(chart, e.detail)
|
||||
})
|
||||
}
|
||||
if(jQuery('#layout1-chart-2').length){
|
||||
|
||||
if (jQuery('#layout1-chart-2').length) {
|
||||
am4core.ready(function() {
|
||||
|
||||
// Themes begin
|
||||
am4core.useTheme(am4themes_animated);
|
||||
// Themes end
|
||||
|
||||
// Create chart instance
|
||||
var chart = am4core.create("layout1-chart-2", am4charts.XYChart);
|
||||
chart.colors.list = [
|
||||
am4core.color("#32BDEA"),
|
||||
am4core.color("#32BDEA"),
|
||||
am4core.color("#32BDEA"),
|
||||
am4core.color("#32BDEA"),
|
||||
am4core.color("#32BDEA"),
|
||||
am4core.color("#32BDEA"),
|
||||
am4core.color("#32BDEA"),
|
||||
am4core.color("#32BDEA"),
|
||||
am4core.color("#32BDEA")
|
||||
];
|
||||
chart.scrollbarX = new am4core.Scrollbar();
|
||||
|
||||
// Add data
|
||||
chart.data = [{
|
||||
"country": "Jan",
|
||||
"visits": 3025
|
||||
}, {
|
||||
"country": "Feb",
|
||||
"visits": 1882
|
||||
}, {
|
||||
"country": "Mar",
|
||||
"visits": 1809
|
||||
}, {
|
||||
"country": "Apr",
|
||||
"visits": 1322
|
||||
}, {
|
||||
"country": "May",
|
||||
"visits": 1122
|
||||
}, {
|
||||
"country": "Jun",
|
||||
"visits": 1114
|
||||
}, {
|
||||
"country": "Jul",
|
||||
"visits": 984
|
||||
}, {
|
||||
"country": "Aug",
|
||||
"visits": 711
|
||||
}];
|
||||
|
||||
prepareParetoData();
|
||||
|
||||
function prepareParetoData(){
|
||||
var total = 0;
|
||||
|
||||
for(var i = 0; i < chart.data.length; i++){
|
||||
var value = chart.data[i].visits;
|
||||
total += value;
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for(var i = 0; i < chart.data.length; i++){
|
||||
var value = chart.data[i].visits;
|
||||
sum += value;
|
||||
chart.data[i].pareto = sum / total * 100;
|
||||
}
|
||||
}
|
||||
|
||||
// Create axes
|
||||
var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
|
||||
categoryAxis.dataFields.category = "country";
|
||||
categoryAxis.renderer.grid.template.location = 0;
|
||||
categoryAxis.renderer.minGridDistance = 60;
|
||||
categoryAxis.tooltip.disabled = true;
|
||||
|
||||
var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
|
||||
valueAxis.renderer.minWidth = 50;
|
||||
valueAxis.min = 0;
|
||||
valueAxis.cursorTooltipEnabled = false;
|
||||
|
||||
// Create series
|
||||
var series = chart.series.push(new am4charts.ColumnSeries());
|
||||
series.sequencedInterpolation = true;
|
||||
series.dataFields.valueY = "visits";
|
||||
series.dataFields.categoryX = "country";
|
||||
series.tooltipText = "[{categoryX}: bold]{valueY}[/]";
|
||||
series.columns.template.strokeWidth = 0;
|
||||
|
||||
series.tooltip.pointerOrientation = "vertical";
|
||||
|
||||
series.columns.template.column.cornerRadiusTopLeft = 10;
|
||||
series.columns.template.column.cornerRadiusTopRight = 10;
|
||||
series.columns.template.column.fillOpacity = 0.8;
|
||||
|
||||
// on hover, make corner radiuses bigger
|
||||
var hoverState = series.columns.template.column.states.create("hover");
|
||||
hoverState.properties.cornerRadiusTopLeft = 0;
|
||||
hoverState.properties.cornerRadiusTopRight = 0;
|
||||
hoverState.properties.fillOpacity = 1;
|
||||
|
||||
series.columns.template.adapter.add("fill", function(fill, target) {
|
||||
return chart.colors.getIndex(target.dataItem.index);
|
||||
})
|
||||
|
||||
|
||||
var paretoValueAxis = chart.yAxes.push(new am4charts.ValueAxis());
|
||||
paretoValueAxis.renderer.opposite = true;
|
||||
paretoValueAxis.min = 0;
|
||||
paretoValueAxis.max = 100;
|
||||
paretoValueAxis.strictMinMax = true;
|
||||
paretoValueAxis.renderer.grid.template.disabled = true;
|
||||
paretoValueAxis.numberFormatter = new am4core.NumberFormatter();
|
||||
paretoValueAxis.numberFormatter.numberFormat = "#'%'"
|
||||
paretoValueAxis.cursorTooltipEnabled = false;
|
||||
|
||||
var paretoSeries = chart.series.push(new am4charts.LineSeries())
|
||||
paretoSeries.dataFields.valueY = "pareto";
|
||||
paretoSeries.dataFields.categoryX = "country";
|
||||
paretoSeries.yAxis = paretoValueAxis;
|
||||
paretoSeries.tooltipText = "pareto: {valueY.formatNumber('#.0')}%[/]";
|
||||
paretoSeries.bullets.push(new am4charts.CircleBullet());
|
||||
paretoSeries.strokeWidth = 2;
|
||||
paretoSeries.stroke = new am4core.InterfaceColorSet().getFor("alternativeBackground");
|
||||
paretoSeries.strokeOpacity = 0.5;
|
||||
|
||||
// Cursor
|
||||
chart.cursor = new am4charts.XYCursor();
|
||||
chart.cursor.behavior = "panX";
|
||||
|
||||
}); // end am4core.ready()
|
||||
// Theme
|
||||
am4core.useTheme(am4themes_animated);
|
||||
|
||||
// Chart instance
|
||||
var chart = am4core.create("layout1-chart-2", am4charts.XYChart);
|
||||
|
||||
// Load data via Ajax
|
||||
fetch("/storage/sse_elbow_curanmor.json")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Format data untuk chart
|
||||
chart.data = data.map(item => ({
|
||||
k: item.k,
|
||||
sse: item.sse,
|
||||
centroid_awal: item.centroid_awal.join(", ") // Menggabungkan nilai centroid_awal menjadi string
|
||||
}));
|
||||
|
||||
// X Axis (kategori K)
|
||||
let categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
|
||||
categoryAxis.dataFields.category = "k";
|
||||
categoryAxis.renderer.grid.template.location = 0;
|
||||
categoryAxis.renderer.minGridDistance = 30;
|
||||
categoryAxis.title.text = "Jumlah Klaster (K)";
|
||||
|
||||
// Y Axis (nilai SSE)
|
||||
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
|
||||
valueAxis.title.text = "Nilai SSE";
|
||||
|
||||
// Line Series
|
||||
let lineSeries = chart.series.push(new am4charts.LineSeries());
|
||||
lineSeries.dataFields.valueY = "sse";
|
||||
lineSeries.dataFields.categoryX = "k";
|
||||
lineSeries.name = "SSE";
|
||||
lineSeries.strokeWidth = 2;
|
||||
lineSeries.tooltipText = "K={categoryX}\nSSE={valueY}\nCentroid Awal: {centroid_awal}";
|
||||
lineSeries.tensionX = 1; // untuk garis agak lengkung (opsional)
|
||||
|
||||
// Bullets pada titik data
|
||||
let bullet = lineSeries.bullets.push(new am4charts.CircleBullet());
|
||||
bullet.circle.radius = 4;
|
||||
|
||||
// Cursor
|
||||
chart.cursor = new am4charts.XYCursor();
|
||||
chart.cursor.behavior = "panX";
|
||||
chart.cursor.lineX.disabled = false;
|
||||
chart.cursor.lineY.disabled = false;
|
||||
|
||||
// Scrollbar (opsional)
|
||||
chart.scrollbarX = new am4core.Scrollbar();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (jQuery("#layout1-chart-3").length) {
|
||||
options = {
|
||||
series: [{
|
||||
|
|
|
@ -103,6 +103,31 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-header d-flex align-items-center justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title">Revenue Vs Cost</h4>
|
||||
</div>
|
||||
<div class="card-header-toolbar d-flex align-items-center">
|
||||
<div class="dropdown">
|
||||
<span class="dropdown-toggle dropdown-bg btn" id="dropdownMenuButton002"
|
||||
data-toggle="dropdown">
|
||||
This Month<i class="ri-arrow-down-s-line ml-1"></i>
|
||||
</span>
|
||||
<div class="dropdown-menu dropdown-menu-right shadow-none"
|
||||
aria-labelledby="dropdownMenuButton002">
|
||||
<a class="dropdown-item" href="#">Curas</a>
|
||||
<a class="dropdown-item" href="#">Curanmor</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="layout1-chart-2" style="min-height: 360px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Page end -->
|
||||
|
|
Loading…
Reference in New Issue