fixing kecamatan, klaster, kmeans, sse

This commit is contained in:
daffarahman11 2025-05-13 23:59:17 +07:00
parent ae72f2717e
commit 7cfe92dcec
11 changed files with 323 additions and 242 deletions

View File

@ -4,6 +4,8 @@
use App\Models\Kecamatan; use App\Models\Kecamatan;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Services\KMeansService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Crypt;
class KecamatanController extends Controller class KecamatanController extends Controller
@ -30,14 +32,28 @@ public function create()
*/ */
public function store(Request $request) public function store(Request $request)
{ {
$validateData = $request->validate([
'nama_kecamatan' =>'required|max:255|unique:kecamatans,nama_kecamatan',
]);
try{ try{
$validateData = $request->validate([ DB::beginTransaction();
'nama_kecamatan' =>'required|max:255|unique:kecamatans,nama_kecamatan',
]);
Kecamatan::create($validateData); Kecamatan::create($validateData);
$serviceKMeans = new KMeansService();
$serviceKMeans->SSEElbowCuranmor();
$serviceKMeans->SSEElbowCuras();
$hasilKMeansCuras = $serviceKMeans->hitungKMeansCuras();
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras));
$hasilKMeansCuranmor = $serviceKMeans->hitungKMeansCuranmor();
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasilKMeansCuranmor));
DB::commit();
return redirect('/dashboard/kecamatan')->with('succes', 'Berhasil Menambahkan Data Kecamatan Baru'); return redirect('/dashboard/kecamatan')->with('succes', 'Berhasil Menambahkan Data Kecamatan Baru');
}catch (\Exception $e){ }catch (\Exception $e){
DB::rollBack();
return redirect('/dashboard/kecamatan')->with('error', 'Gagal Menambahkan Data Kecamatan Baru'); return redirect('/dashboard/kecamatan')->with('error', 'Gagal Menambahkan Data Kecamatan Baru');
} }
@ -71,14 +87,28 @@ public function edit(Kecamatan $kecamatan)
*/ */
public function update(Request $request, Kecamatan $kecamatan) public function update(Request $request, Kecamatan $kecamatan)
{ {
$validateData = $request->validate([
'nama_kecamatan' =>'required|max:255|unique:kecamatans,nama_kecamatan',
]);
try{ try{
$validateData = $request->validate([ DB::beginTransaction();
'nama_kecamatan' =>'required|max:255|unique:kecamatans,nama_kecamatan',
]);
Kecamatan::where('id', $kecamatan->id)->update($validateData); Kecamatan::where('id', $kecamatan->id)->update($validateData);
$serviceKMeans = new KMeansService();
$serviceKMeans->SSEElbowCuranmor();
$serviceKMeans->SSEElbowCuras();
$hasilKMeansCuras = $serviceKMeans->hitungKMeansCuras();
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras));
$hasilKMeansCuranmor = $serviceKMeans->hitungKMeansCuranmor();
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasilKMeansCuranmor));
DB::commit();
return redirect('/dashboard/kecamatan')->with('succes', 'Data Kecamatan Berhasil Di Ubah'); return redirect('/dashboard/kecamatan')->with('succes', 'Data Kecamatan Berhasil Di Ubah');
}catch (\Exception $e){ }catch (\Exception $e){
DB::rollBack();
return redirect('/dashboard/kecamatan')->with('error', 'Data Kecamatan Gagal Di Ubah'); return redirect('/dashboard/kecamatan')->with('error', 'Data Kecamatan Gagal Di Ubah');
} }
@ -91,10 +121,24 @@ public function update(Request $request, Kecamatan $kecamatan)
public function destroy(Kecamatan $kecamatan) public function destroy(Kecamatan $kecamatan)
{ {
try{ try{
DB::beginTransaction();
Kecamatan::destroy($kecamatan->id); Kecamatan::destroy($kecamatan->id);
$serviceKMeans = new KMeansService();
$serviceKMeans->SSEElbowCuranmor();
$serviceKMeans->SSEElbowCuras();
$hasilKMeansCuras = $serviceKMeans->hitungKMeansCuras();
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras));
$hasilKMeansCuranmor = $serviceKMeans->hitungKMeansCuranmor();
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasilKMeansCuranmor));
DB::commit();
return redirect('/dashboard/kecamatan')->with('succes', 'Data Kecamatan Berhasil Di Hapus'); return redirect('/dashboard/kecamatan')->with('succes', 'Data Kecamatan Berhasil Di Hapus');
}catch (\Exception $e){ }catch (\Exception $e){
DB::rollBack();
return redirect('/dashboard/kecamatan')->with('error', 'Data Kecamatan '. $kecamatan->nama_kecamatan .' Gagal Di Hapus | Hapus Data Curas Atau Curanmor Untuk Kecamatan '. $kecamatan->nama_kecamatan.' Terlebih Dahulu'); return redirect('/dashboard/kecamatan')->with('error', 'Data Kecamatan '. $kecamatan->nama_kecamatan .' Gagal Di Hapus | Hapus Data Curas Atau Curanmor Untuk Kecamatan '. $kecamatan->nama_kecamatan.' Terlebih Dahulu');
} }

View File

@ -5,6 +5,7 @@
use App\Models\Klaster; use App\Models\Klaster;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Services\KMeansService; use App\Services\KMeansService;
use Illuminate\Support\Facades\DB;
class KlasterController extends Controller class KlasterController extends Controller
{ {
@ -29,30 +30,35 @@ public function create()
*/ */
public function store(Request $request) public function store(Request $request)
{ {
$validateData = $request->validate([
'nama_klaster' =>'required|max:255|unique:klasters,nama_klaster',
'warna' =>'required|max:255',
]);
DB::beginTransaction();
try{ try{
$validateData = $request->validate([
'nama_klaster' =>'required|max:255|unique:klasters,nama_klaster', Klaster::create($validateData);
'warna' =>'required|max:255',
]);
$serviceKMeans = new KMeansService(); $serviceKMeans = new KMeansService();
$serviceKMeans->SSEElbowCuranmor(); $serviceKMeans->SSEElbowCuranmor();
$serviceKMeans->SSEElbowCuras(); $serviceKMeans->SSEElbowCuras();
$serviceKMeansCuras = new KMeansService(); $hasilKMeansCuras = $serviceKMeans->hitungKMeansCuras();
$hasilKMeansCuras = $serviceKMeansCuras->hitungKMeansCuras();
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras)); file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras));
$serviceKmeansCuranmor = new KMeansService(); $hasilKMeansCuranmor = $serviceKMeans->hitungKMeansCuranmor();
$hasilKMeansCuranmor = $serviceKmeansCuranmor->hitungKMeansCuranmor();
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasilKMeansCuranmor)); file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasilKMeansCuranmor));
Klaster::create($validateData); DB::commit();
return redirect('/dashboard/klaster')->with('succes', 'Berhasil Menambahkan Klaster Baru'); return redirect('/dashboard/klaster')->with('succes', 'Berhasil Menambahkan Klaster Baru');
}catch (\Exception $e){ }catch (\Exception $e){
DB::rollBack();
return redirect('/dashboard/klaster')->with('error', 'Gagal Menambahkan Klaster Baru ' .$e->getMessage()); return redirect('/dashboard/klaster')->with('error', 'Gagal Menambahkan Klaster Baru ' .$e->getMessage());
} }
} }
@ -85,17 +91,32 @@ public function edit(Klaster $klaster)
*/ */
public function update(Request $request, Klaster $klaster) public function update(Request $request, Klaster $klaster)
{ {
$validateData = $request->validate([
'nama_klaster' => 'sometimes|required|max:255|unique:klasters,nama_klaster,' . $klaster->id,
'warna' => 'sometimes|required|max:255',
]);
try { try {
$validateData = $request->validate([ DB::beginTransaction();
'nama_klaster' => 'sometimes|required|max:255|unique:klasters,nama_klaster,' . $klaster->id,
'warna' => 'sometimes|required|max:255',
]);
// Hanya update data yang diisi (tidak mengganti dengan null) // Hanya update data yang diisi (tidak mengganti dengan null)
Klaster::where('id', $klaster->id)->update(array_filter($validateData)); Klaster::where('id', $klaster->id)->update(array_filter($validateData));
$serviceKMeans = new KMeansService();
$serviceKMeans->SSEElbowCuranmor();
$serviceKMeans->SSEElbowCuras();
$hasilKMeansCuras = $serviceKMeans->hitungKMeansCuras();
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras));
$hasilKMeansCuranmor = $serviceKMeans->hitungKMeansCuranmor();
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasilKMeansCuranmor));
DB::commit();
return redirect('/dashboard/klaster')->with('success', 'Data Klaster Berhasil Diubah'); return redirect('/dashboard/klaster')->with('success', 'Data Klaster Berhasil Diubah');
} catch (\Exception $e) { } catch (\Exception $e) {
DB::rollBack();
return redirect('/dashboard/klaster')->with('error', 'Data Klaster Gagal Diubah'); return redirect('/dashboard/klaster')->with('error', 'Data Klaster Gagal Diubah');
} }
@ -107,11 +128,27 @@ public function update(Request $request, Klaster $klaster)
public function destroy(Klaster $klaster) public function destroy(Klaster $klaster)
{ {
try{ try{
DB::beginTransaction();
Klaster::destroy($klaster->id); Klaster::destroy($klaster->id);
DB::commit();
$serviceKMeans = new KMeansService();
$serviceKMeans->SSEElbowCuranmor();
$serviceKMeans->SSEElbowCuras();
$hasilKMeansCuras = $serviceKMeans->hitungKMeansCuras();
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras));
$hasilKMeansCuranmor = $serviceKMeans->hitungKMeansCuranmor();
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasilKMeansCuranmor));
return redirect('/dashboard/klaster')->with('succes', 'Data Klaster Berhasil Di Hapus'); return redirect('/dashboard/klaster')->with('succes', 'Data Klaster Berhasil Di Hapus');
}catch (\Exception $e){ }catch (\Exception $e){
return redirect('/dashboard/klaster')->with('error', 'Data Klaster '. $klaster->nama_kecamatan .' Gagal Di Hapus | Hapus Data Curas Atau Curanmor Untuk Klaster '. $klaster->nama_kecamatan.' Terlebih Dahulu');
DB::rollBack();
return redirect('/dashboard/klaster')->with('error', 'Data Klaster '. $klaster->nama_kecamatan .' Gagal Di Hapus | Hapus Data Curas Atau Curanmor Untuk Klaster '. $klaster->nama_kecamatan.' Terlebih Dahulu '. $e->getMessage());
} }
} }
} }

View File

@ -13,23 +13,14 @@ public function KMeansCuras()
{ {
$data = Curas::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curas')->orderBy('jumlah_curas', 'asc')->get(); $data = Curas::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curas')->orderBy('jumlah_curas', 'asc')->get();
$k = Klaster::count('id');
$maxIterasi = 100; $maxIterasi = 100;
$uniqueCount = $data->unique('jumlah_curas')->count();
if ($uniqueCount < $k) {
throw new \Exception("Jumlah nilai unik pada 'jumlah_curas' ($uniqueCount) kurang dari jumlah klaster ($k). Pastikan data memiliki variasi yang cukup.");
}
// Ambil centroid awal yang unik $centroidManual = [0, 1, 3];
// Gunakan centroid tetap $centroids = collect($centroidManual)->map(function ($value) {
return ['C' => $value];
$centroidValues = [0, 1, 3];
$centroids = collect($centroidValues)->map(function ($val) {
return ['jumlah_curas' => $val];
}); });
// Simpan centroid awal sebelum iterasi
$centroidAwal = $centroids->toArray(); $centroidAwal = $centroids->toArray();
$iterasi = []; $iterasi = [];
@ -43,13 +34,13 @@ public function KMeansCuras()
$jarak = []; $jarak = [];
foreach ($centroids as $idx => $centroid) { foreach ($centroids as $idx => $centroid) {
$dist = abs($item->jumlah_curas - $centroid['jumlah_curas']); $dist = abs($item->jumlah_curas - $centroid['C']);
$jarak["C" . ($idx + 1)] = $dist; $jarak["C" . ($idx + 1)] = $dist;
} }
$iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak); $iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak);
$minIndex = array_keys($jarak, min($jarak))[0]; // e.g. "jarakC2" $minIndex = array_keys($jarak, min($jarak))[0];
$clusterNumber = (int) str_replace("C", "", $minIndex); $clusterNumber = (int) str_replace("C", "", $minIndex);
$clustered[$clusterNumber][] = $item; $clustered[$clusterNumber][] = $item;
@ -57,65 +48,64 @@ public function KMeansCuras()
$currentAssignment[$item->id] = $clusterNumber; $currentAssignment[$item->id] = $clusterNumber;
} }
// ✨ Cek konvergensi: jika assignment sekarang == sebelumnya, break
if ($currentAssignment === $prevAssignment) { if ($currentAssignment === $prevAssignment) {
break; break;
} }
$prevAssignment = $currentAssignment; $prevAssignment = $currentAssignment;
// Update centroid berdasarkan rata-rata // Update centroid berdasarkan rata-rata
foreach ($clustered as $key => $group) { foreach ($clustered as $key => $group) {
$avg = collect($group)->avg('jumlah_curas'); $avg = collect($group)->avg('jumlah_curas');
$centroids = $centroids->map(function ($item, $index) use ($key, $avg) { $centroids = $centroids->map(function ($item, $index) use ($key, $avg) {
return $index === ($key - 1) return $index === ($key - 1)
? ['jumlah_curas' => $avg] ? ['C' => $avg]
: $item; : $item;
}); });
} }
} }
// Final mapping centroid ke klaster_id (aman/sedang/rawan) // Final mapping centroid ke klaster_id
$finalCentroids = $centroids->map(function ($item, $index) { $finalCentroids = $centroids->map(function ($item, $index) {
return ['index' => $index + 1, 'jumlah_curas' => $item['jumlah_curas']]; return ['index' => $index + 1, 'C' => $item['C']];
})->sortBy('jumlah_curas')->values(); })->sortBy('C')->values();
$centroidToKlaster = []; $availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
foreach ($finalCentroids as $i => $centroid) { foreach ($finalCentroids as $i => $centroid) {
// Klaster ID mulai dari 1 (asumsi klaster di DB bernomor 1, 2, 3, ...) $centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
$centroidToKlaster[$centroid['index']] = $i + 1;
} }
// Update ke database
foreach ($data as $item) { foreach ($data as $item) {
Curas::where('id', $item->id)->update([ Curas::where('id', $item->id)->update([
'klaster_id' => $centroidToKlaster[$item->temp_klaster], 'klaster_id' => $centroidToKlaster[$item->temp_klaster],
]); ]);
} }
// Format centroid awal
$centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) { $centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['jumlah_curas']]; return ['C' . ($index + 1) => $item['C']];
}); });
$hasil = [ // Format centroid akhir
$centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['C']];
});
$hasilKMeansCuras = [
'centroid_awal' => $centroidAwalFormatted, 'centroid_awal' => $centroidAwalFormatted,
'centroid_akhir' => $centroidAkhirFormatted,
'iterasi' => $iterasi 'iterasi' => $iterasi
]; ];
file_put_contents( file_put_contents(
storage_path('app/public/hasil_kmeans_curas.json'), storage_path('app/public/hasil_kmeans_curas.json'),
json_encode($hasil, JSON_PRETTY_PRINT) json_encode($hasilKMeansCuras, JSON_PRETTY_PRINT)
); );
return redirect('/dashboard/TampilHitungCuras'); return redirect('/dashboard/TampilHitungCuras');
} }
@ -123,20 +113,13 @@ public function KMeansCuranmor()
{ {
$data = Curanmor::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curanmor')->orderBy('jumlah_curanmor', 'asc')->get(); $data = Curanmor::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curanmor')->orderBy('jumlah_curanmor', 'asc')->get();
$k = Klaster::count('id');
$maxIterasi = 100; $maxIterasi = 100;
$uniqueCount = $data->unique('jumlah_curanmor')->count(); $centroidManual = [10, 20, 30];
if ($uniqueCount < $k) { $centroids = collect($centroidManual)->map(function ($value) {
throw new \Exception("Jumlah nilai unik pada 'jumlah_curanmor' ($uniqueCount) kurang dari jumlah klaster ($k). Pastikan data memiliki variasi yang cukup."); return ['C' => $value];
}
$centroidValues = [10, 20, 30];
$centroids = collect($centroidValues)->map(function ($val) {
return ['jumlah_curanmor' => $val];
}); });
// Simpan centroid awal sebelum iterasi
$centroidAwal = $centroids->toArray(); $centroidAwal = $centroids->toArray();
$iterasi = []; $iterasi = [];
@ -148,82 +131,84 @@ public function KMeansCuranmor()
foreach ($data as $item) { foreach ($data as $item) {
$jarak = []; $jarak = [];
foreach ($centroids as $idx => $centroid) { foreach ($centroids as $idx => $centroid) {
// Menggunakan Euclidean distance standar (akar kuadrat dari selisih kuadrat) $dist = abs($item->jumlah_curanmor - $centroid['C']);
$dist = sqrt(pow($item->jumlah_curanmor - $centroid['jumlah_curanmor'], 2));
$jarak["C" . ($idx + 1)] = $dist; $jarak["C" . ($idx + 1)] = $dist;
} }
$iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak); $iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak);
$minIndex = array_keys($jarak, min($jarak))[0]; // e.g. "jarakC2" $minIndex = array_keys($jarak, min($jarak))[0];
$clusterNumber = (int) str_replace("C", "", $minIndex); $clusterNumber = (int) str_replace("C", "", $minIndex);
$clustered[$clusterNumber][] = $item; $clustered[$clusterNumber][] = $item;
$item->temp_klaster = $clusterNumber; $item->temp_klaster = $clusterNumber;
$currentAssignment[$item->id] = $clusterNumber; $currentAssignment[$item->id] = $clusterNumber;
} }
// ✨ Cek konvergensi: jika assignment sekarang == sebelumnya, break
if ($currentAssignment === $prevAssignment) { if ($currentAssignment === $prevAssignment) {
break; break;
} }
$prevAssignment = $currentAssignment; $prevAssignment = $currentAssignment;
// Update centroid berdasarkan rata-rata // Update centroid berdasarkan rata-rata
foreach ($clustered as $key => $group) { foreach ($clustered as $key => $group) {
$avg = collect($group)->avg('jumlah_curanmor'); $avg = collect($group)->avg('jumlah_curanmor');
$centroids = $centroids->map(function ($item, $index) use ($key, $avg) { $centroids = $centroids->map(function ($item, $index) use ($key, $avg) {
return $index === ($key - 1) return $index === ($key - 1)
? ['jumlah_curanmor' => $avg] ? ['C' => $avg]
: $item; : $item;
}); });
} }
} }
// Final mapping centroid ke klaster_id (aman/sedang/rawan) // Final mapping centroid ke klaster_id
$finalCentroids = $centroids->map(function ($item, $index) { $finalCentroids = $centroids->map(function ($item, $index) {
return ['index' => $index + 1, 'jumlah_curanmor' => $item['jumlah_curanmor']]; return ['index' => $index + 1, 'C' => $item['C']];
})->sortBy('jumlah_curanmor')->values(); })->sortBy('C')->values();
$centroidToKlaster = []; $availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
foreach ($finalCentroids as $i => $centroid) { foreach ($finalCentroids as $i => $centroid) {
// Klaster ID mulai dari 1 (asumsi klaster di DB bernomor 1, 2, 3, ...) $centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
$centroidToKlaster[$centroid['index']] = $i + 1;
} }
// Update ke database // Update ke database
foreach ($data as $item) { foreach ($data as $item) {
Curanmor::where('id', $item->id)->update([ Curanmor::where('id', $item->id)->update([
'klaster_id' => $centroidToKlaster[$item->temp_klaster], 'klaster_id' => $centroidToKlaster[$item->temp_klaster],
]); ]);
} }
// Format centroid awal
$centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) { $centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['jumlah_curanmor']]; return ['C' . ($index + 1) => $item['C']];
}); });
$hasil = [ // Format centroid akhir
$centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['C']];
});
$hasilKMeansCuranmor = [
'centroid_awal' => $centroidAwalFormatted, 'centroid_awal' => $centroidAwalFormatted,
'centroid_akhir' => $centroidAkhirFormatted,
'iterasi' => $iterasi 'iterasi' => $iterasi
]; ];
file_put_contents( file_put_contents(
storage_path('app/public/hasil_kmeans_curanmor.json'), storage_path('app/public/hasil_kmeans_curanmor.json'),
json_encode($hasil, JSON_PRETTY_PRINT) json_encode($hasilKMeansCuranmor, JSON_PRETTY_PRINT)
); );
return redirect('/dashboard/TampilHitungCuranmor'); return redirect('/dashboard/TampilHitungCuranmor');
} }
} }

View File

@ -21,15 +21,16 @@ public function hitungKMeansCuras()
$generated = collect(); $generated = collect();
while ($generated->count() < $k) { while ($generated->count() < $k) {
$randomFloat = round(mt_rand($minValue * 100, $maxValue * 100) / 100, 2); $random = mt_rand($minValue, $maxValue);
if (!$generated->contains($randomFloat)) { if (!$generated->contains($random)) {
$generated->push($randomFloat); $generated->push($random);
} }
} }
$centroids = $generated->map(function ($value) { $centroids = $generated->map(function ($value) {
return ['jumlah_curas' => $value]; return ['C' => $value];
}); });
$centroidAwal = $centroids->toArray(); $centroidAwal = $centroids->toArray();
$iterasi = []; $iterasi = [];
@ -43,7 +44,7 @@ public function hitungKMeansCuras()
$jarak = []; $jarak = [];
foreach ($centroids as $idx => $centroid) { foreach ($centroids as $idx => $centroid) {
$dist = abs($item->jumlah_curas - $centroid['jumlah_curas']); $dist = abs($item->jumlah_curas - $centroid['C']);
$jarak["C" . ($idx + 1)] = $dist; $jarak["C" . ($idx + 1)] = $dist;
} }
@ -68,7 +69,7 @@ public function hitungKMeansCuras()
$avg = collect($group)->avg('jumlah_curas'); $avg = collect($group)->avg('jumlah_curas');
$centroids = $centroids->map(function ($item, $index) use ($key, $avg) { $centroids = $centroids->map(function ($item, $index) use ($key, $avg) {
return $index === ($key - 1) return $index === ($key - 1)
? ['jumlah_curas' => $avg] ? ['C' => $avg]
: $item; : $item;
}); });
} }
@ -76,15 +77,20 @@ public function hitungKMeansCuras()
// Final mapping centroid ke klaster_id // Final mapping centroid ke klaster_id
$finalCentroids = $centroids->map(function ($item, $index) { $finalCentroids = $centroids->map(function ($item, $index) {
return ['index' => $index + 1, 'jumlah_curas' => $item['jumlah_curas']]; return ['index' => $index + 1, 'C' => $item['C']];
})->sortBy('jumlah_curas')->values(); })->sortBy('C')->values();
$availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
$centroidToKlaster = [];
foreach ($finalCentroids as $i => $centroid) { foreach ($finalCentroids as $i => $centroid) {
$centroidToKlaster[$centroid['index']] = $i + 1; $centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
} }
// Update ke database // Update ke database
foreach ($data as $item) { foreach ($data as $item) {
Curas::where('id', $item->id)->update([ Curas::where('id', $item->id)->update([
'klaster_id' => $centroidToKlaster[$item->temp_klaster], 'klaster_id' => $centroidToKlaster[$item->temp_klaster],
@ -93,12 +99,12 @@ public function hitungKMeansCuras()
// Format centroid awal // Format centroid awal
$centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) { $centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['jumlah_curas']]; return ['C' . ($index + 1) => $item['C']];
}); });
// Format centroid akhir // Format centroid akhir
$centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) { $centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['jumlah_curas']]; return ['C' . ($index + 1) => $item['C']];
}); });
return [ return [
@ -115,20 +121,22 @@ public function hitungKMeansCuranmor()
$k = Klaster::count('id'); $k = Klaster::count('id');
$maxIterasi = 100; $maxIterasi = 100;
// Ambil centroid awal yang unik
$minValue = $data->min('jumlah_curanmor'); $minValue = $data->min('jumlah_curanmor');
$maxValue = $data->max('jumlah_curanmor'); $maxValue = $data->max('jumlah_curanmor');
$generated = collect(); $generated = collect();
while ($generated->count() < $k) { while ($generated->count() < $k) {
$randomFloat = round(mt_rand($minValue * 100, $maxValue * 100) / 100, 2); $random = mt_rand($minValue, $maxValue);
if (!$generated->contains($randomFloat)) { if (!$generated->contains($random)) {
$generated->push($randomFloat); $generated->push($random);
} }
} }
$centroids = $generated->map(function ($value) { $centroids = $generated->map(function ($value) {
return ['jumlah_curanmor' => $value]; return ['C' => $value];
}); });
$centroidAwal = $centroids->toArray(); $centroidAwal = $centroids->toArray();
$iterasi = []; $iterasi = [];
@ -142,13 +150,13 @@ public function hitungKMeansCuranmor()
$jarak = []; $jarak = [];
foreach ($centroids as $idx => $centroid) { foreach ($centroids as $idx => $centroid) {
$dist = abs($item->jumlah_curanmor - $centroid['jumlah_curanmor']); $dist = abs($item->jumlah_curanmor - $centroid['C']);
$jarak["C" . ($idx + 1)] = $dist; $jarak["C" . ($idx + 1)] = $dist;
} }
$iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak); $iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak);
$minIndex = array_keys($jarak, min($jarak))[0]; // e.g. "jarakC2" $minIndex = array_keys($jarak, min($jarak))[0];
$clusterNumber = (int) str_replace("C", "", $minIndex); $clusterNumber = (int) str_replace("C", "", $minIndex);
$clustered[$clusterNumber][] = $item; $clustered[$clusterNumber][] = $item;
@ -156,77 +164,78 @@ public function hitungKMeansCuranmor()
$currentAssignment[$item->id] = $clusterNumber; $currentAssignment[$item->id] = $clusterNumber;
} }
// ✨ Cek konvergensi: jika assignment sekarang == sebelumnya, break
if ($currentAssignment === $prevAssignment) { if ($currentAssignment === $prevAssignment) {
break; break;
} }
$prevAssignment = $currentAssignment; $prevAssignment = $currentAssignment;
// Update centroid berdasarkan rata-rata // Update centroid berdasarkan rata-rata
foreach ($clustered as $key => $group) { foreach ($clustered as $key => $group) {
$avg = collect($group)->avg('jumlah_curanmor'); $avg = collect($group)->avg('jumlah_curanmor');
$centroids = $centroids->map(function ($item, $index) use ($key, $avg) { $centroids = $centroids->map(function ($item, $index) use ($key, $avg) {
return $index === ($key - 1) return $index === ($key - 1)
? ['jumlah_curanmor' => $avg] ? ['C' => $avg]
: $item; : $item;
}); });
} }
} }
// Final mapping centroid ke klaster_id (aman/sedang/rawan) // Final mapping centroid ke klaster_id
$finalCentroids = $centroids->map(function ($item, $index) { $finalCentroids = $centroids->map(function ($item, $index) {
return ['index' => $index + 1, 'jumlah_curanmor' => $item['jumlah_curanmor']]; return ['index' => $index + 1, 'C' => $item['C']];
})->sortBy('jumlah_curanmor')->values(); })->sortBy('C')->values();
$centroidToKlaster = []; $availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
foreach ($finalCentroids as $i => $centroid) { foreach ($finalCentroids as $i => $centroid) {
// Klaster ID mulai dari 1 (asumsi klaster di DB bernomor 1, 2, 3, ...) $centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
$centroidToKlaster[$centroid['index']] = $i + 1;
} }
// Update ke database // Update ke database
foreach ($data as $item) { foreach ($data as $item) {
Curanmor::where('id', $item->id)->update([ Curanmor::where('id', $item->id)->update([
'klaster_id' => $centroidToKlaster[$item->temp_klaster], 'klaster_id' => $centroidToKlaster[$item->temp_klaster],
]); ]);
} }
// Format centroid awal
$centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) { $centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['jumlah_curanmor']]; return ['C' . ($index + 1) => $item['C']];
}); });
// Format centroid akhir
$centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['C']];
});
return [ return [
'centroid_awal' => $centroidAwalFormatted, 'centroid_awal' => $centroidAwalFormatted,
'centroid_akhir' => $centroidAkhirFormatted,
'iterasi' => $iterasi 'iterasi' => $iterasi
]; ];
} }
public function SSEElbowCuras() public function SSEElbowCuranmor()
{ {
$data = Curas::select('id', 'jumlah_curas')->get(); $data = Curanmor::select('id', 'jumlah_curanmor')->get();
$maxK = 10; $maxK = 10;
$maxIterasi = 100; $maxIterasi = 100;
$elbowData = []; $elbowData = [];
// Ambil nilai minimum dan maksimum dari jumlah_curas $min = $data->min('jumlah_curanmor');
$min = $data->min('jumlah_curas'); $max = $data->max('jumlah_curanmor');
$max = $data->max('jumlah_curas');
// Loop untuk setiap nilai k dari 2 hingga maxK
for ($k = 2; $k <= $maxK; $k++) { for ($k = 2; $k <= $maxK; $k++) {
// Inisialisasi centroid awal sebagai float acak dalam rentang min-max
$centroids = collect(range(1, $k))->map(function () use ($min, $max) { $centroids = collect(range(1, $k))->map(function () use ($min, $max) {
return ['jumlah_curas' => mt_rand($min * 100, $max * 100) / 100]; return ['jumlah_curanmor' => mt_rand($min, $max)];
}); });
$centroidAwal = $centroids->pluck('jumlah_curas')->toArray();
$prevAssignment = []; $prevAssignment = [];
for ($iter = 0; $iter < $maxIterasi; $iter++) { for ($iter = 0; $iter < $maxIterasi; $iter++) {
@ -236,22 +245,104 @@ public function SSEElbowCuras()
foreach ($data as $item) { foreach ($data as $item) {
$jarak = []; $jarak = [];
// Hitung jarak antara data dan setiap centroid
foreach ($centroids as $idx => $centroid) { foreach ($centroids as $idx => $centroid) {
$dist = abs($item->jumlah_curas - $centroid['jumlah_curas']); $dist = abs($item->jumlah_curanmor - $centroid['jumlah_curanmor']);
$jarak[$idx] = $dist; $jarak[$idx] = $dist;
} }
// Tentukan cluster dengan jarak terdekat
$minIndex = array_keys($jarak, min($jarak))[0]; $minIndex = array_keys($jarak, min($jarak))[0];
$clustered[$minIndex][] = $item; $clustered[$minIndex][] = $item;
$currentAssignment[$item->id] = $minIndex; $currentAssignment[$item->id] = $minIndex;
} }
// Jika tidak ada perubahan cluster, break
if ($currentAssignment === $prevAssignment) { if ($currentAssignment === $prevAssignment) {
break; break;
} }
$prevAssignment = $currentAssignment; $prevAssignment = $currentAssignment;
// Update centroid dengan rata-rata cluster
foreach ($clustered as $key => $group) {
$avg = collect($group)->avg('jumlah_curanmor');
$centroids = $centroids->map(function ($centroid, $idx) use ($key, $avg) {
return $idx == $key
? ['jumlah_curanmor' => $avg]
: $centroid;
});
}
}
// Hitung SSE (Sum of Squared Errors)
$sse = 0;
foreach ($clustered as $key => $group) {
$centroidVal = $centroids[$key]['jumlah_curanmor'];
foreach ($group as $item) {
$sse += pow($item->jumlah_curanmor - $centroidVal, 2);
}
}
// Simpan SSE untuk nilai k
$elbowData[] = [
'k' => $k,
'sse' => round($sse, 4)
];
}
// Simpan hasil SSE untuk setiap k ke file JSON
file_put_contents(
storage_path('app/public/sse_elbow_curanmor.json'),
json_encode($elbowData, JSON_PRETTY_PRINT)
);
}
public function SSEElbowCuras()
{
$data = Curas::select('id', 'jumlah_curas')->get();
$maxK = 10;
$maxIterasi = 100;
$elbowData = [];
$min = $data->min('jumlah_curas');
$max = $data->max('jumlah_curas');
// Loop untuk setiap nilai k dari 2 hingga maxK
for ($k = 2; $k <= $maxK; $k++) {
$centroids = collect(range(1, $k))->map(function () use ($min, $max) {
return ['jumlah_curas' => mt_rand($min, $max)];
});
$prevAssignment = [];
for ($iter = 0; $iter < $maxIterasi; $iter++) {
$clustered = [];
$currentAssignment = [];
foreach ($data as $item) {
$jarak = [];
// Hitung jarak antara data dan setiap centroid
foreach ($centroids as $idx => $centroid) {
$dist = abs($item->jumlah_curas - $centroid['jumlah_curas']);
$jarak[$idx] = $dist;
}
// Tentukan cluster dengan jarak terdekat
$minIndex = array_keys($jarak, min($jarak))[0];
$clustered[$minIndex][] = $item;
$currentAssignment[$item->id] = $minIndex;
}
// Jika tidak ada perubahan cluster, break
if ($currentAssignment === $prevAssignment) {
break;
}
$prevAssignment = $currentAssignment;
// Update centroid dengan rata-rata cluster
foreach ($clustered as $key => $group) { foreach ($clustered as $key => $group) {
$avg = collect($group)->avg('jumlah_curas'); $avg = collect($group)->avg('jumlah_curas');
$centroids = $centroids->map(function ($centroid, $idx) use ($key, $avg) { $centroids = $centroids->map(function ($centroid, $idx) use ($key, $avg) {
@ -262,7 +353,7 @@ public function SSEElbowCuras()
} }
} }
// Hitung SSE // Hitung SSE (Sum of Squared Errors)
$sse = 0; $sse = 0;
foreach ($clustered as $key => $group) { foreach ($clustered as $key => $group) {
$centroidVal = $centroids[$key]['jumlah_curas']; $centroidVal = $centroids[$key]['jumlah_curas'];
@ -271,96 +362,22 @@ public function SSEElbowCuras()
} }
} }
// Simpan SSE untuk nilai k
$elbowData[] = [ $elbowData[] = [
'k' => $k, 'k' => $k,
'sse' => $sse, 'sse' => round($sse, 4)
'centroid_awal' => $centroidAwal
]; ];
} }
// Simpan hasil SSE untuk setiap k ke file JSON
file_put_contents( file_put_contents(
storage_path('app/public/sse_elbow_curas.json'), storage_path('app/public/sse_elbow_curas.json'),
json_encode($elbowData, JSON_PRETTY_PRINT) json_encode($elbowData, JSON_PRETTY_PRINT)
); );
} }
public function SSEElbowCuranmor()
{
$data = Curanmor::select('id', 'jumlah_curanmor')->get();
$maxK = 10;
$maxIterasi = 100;
$elbowData = [];
for ($k = 2; $k <= $maxK; $k++) {
// 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');
$centroids = $centroids->map(function ($centroid, $idx) use ($key, $avg) {
return $idx == $key
? ['jumlah_curanmor' => $avg]
: $centroid;
});
}
}
// Hitung SSE untuk k saat ini
$sse = 0;
foreach ($clustered as $key => $group) {
$centroidVal = $centroids[$key]['jumlah_curanmor'];
foreach ($group as $item) {
$sse += pow($item->jumlah_curanmor - $centroidVal, 2);
}
}
$elbowData[] = [
'k' => $k,
'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)
);
}
} }

View File

@ -17,7 +17,7 @@ public function up(): void
table: 'kecamatans', indexName: 'curas_kecamatan_id'); table: 'kecamatans', indexName: 'curas_kecamatan_id');
$table->float('jumlah_curas'); $table->float('jumlah_curas');
$table->foreignId('klaster_id')->nullable()->constrained( $table->foreignId('klaster_id')->nullable()->constrained(
table: 'klasters', indexName: 'curas_klaster_id'); table: 'klasters', indexName: 'curas_klaster_id')->nullOnDelete();
$table->timestamps(); $table->timestamps();
}); });
} }

View File

@ -17,7 +17,7 @@ public function up(): void
table: 'kecamatans', indexName: 'curanmor_kecamatan_id'); table: 'kecamatans', indexName: 'curanmor_kecamatan_id');
$table->float('jumlah_curanmor'); $table->float('jumlah_curanmor');
$table->foreignId('klaster_id')->nullable()->constrained( $table->foreignId('klaster_id')->nullable()->constrained(
table: 'klasters', indexName: 'klaster_kecamatan_id'); table: 'klasters', indexName: 'klaster_kecamatan_id')->nullOnDelete();
$table->timestamps(); $table->timestamps();
}); });
} }

View File

@ -5213,12 +5213,11 @@ if (jQuery("#editor").length) {
valueAxis.title.text = "Nilai SSE"; valueAxis.title.text = "Nilai SSE";
let lineSeries = chart.series.push(new am4charts.LineSeries()); let lineSeries = chart.series.push(new am4charts.LineSeries());
lineSeries.dataFields.valueY = "sse"; lineSeries.dataFields.valueY = "sse";
lineSeries.dataFields.categoryX = "k"; lineSeries.dataFields.categoryX = "k";
lineSeries.name = "SSE"; lineSeries.name = "SSE";
lineSeries.strokeWidth = 2; lineSeries.strokeWidth = 2;
lineSeries.tooltipText = "K={categoryX}\nSSE={valueY}\nCentroid Awal: {centroid_awal}"; lineSeries.tooltipText = "K={categoryX}\nSSE={valueY}"; // Tooltip tanpa centroid_awal
lineSeries.tensionX = 1; lineSeries.tensionX = 1;
let bullet = lineSeries.bullets.push(new am4charts.CircleBullet()); let bullet = lineSeries.bullets.push(new am4charts.CircleBullet());
@ -5236,20 +5235,19 @@ if (jQuery("#editor").length) {
fetch(`/storage/sse_elbow_${type}.json`) fetch(`/storage/sse_elbow_${type}.json`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
// Format data untuk chart
chart.data = data.map(item => ({ chart.data = data.map(item => ({
k: item.k, k: item.k,
sse: item.sse, sse: item.sse
centroid_awal: item.centroid_awal.join(", ")
})); }));
console.log("Data yang dimuat ke chart:", chart.data); console.log("Data yang dimuat ke chart:", chart.data);
}); });
} }
// Load data default saat pertama kali (curas) // Load data default saat pertama kali (curanmor)
loadChartData('curanmor'); loadChartData('curanmor');
// Event listener dropdown // Event listener dropdown untuk memilih tipe data
document.querySelectorAll('.chart-option').forEach(item => { document.querySelectorAll('.chart-option').forEach(item => {
item.addEventListener('click', function(e) { item.addEventListener('click', function(e) {
e.preventDefault(); e.preventDefault();

View File

@ -11,7 +11,8 @@
</div> </div>
<a href="/dashboard/curas/create" class="btn btn-primary add-list"><i class="las la-plus mr-3"></i>Tambah Kasus Curas</a> <a href="/dashboard/curas/create" class="btn btn-primary add-list"><i class="las la-plus mr-3"></i>Tambah Kasus Curas</a>
</div> </div>
@if (session()->has('succes')) </div>
@if (session()->has('succes'))
<div class="alert alert-success" role="alert"> <div class="alert alert-success" role="alert">
{{ session('succes') }} {{ session('succes') }}
</div> </div>
@ -21,7 +22,7 @@
{{ session('error') }} {{ session('error') }}
</div> </div>
@endif @endif
</div>
<div class="col-lg-12"> <div class="col-lg-12">
<div class="table-responsive rounded mb-3"> <div class="table-responsive rounded mb-3">
<table class="data-table table mb-0 tbl-server-info"> <table class="data-table table mb-0 tbl-server-info">

View File

@ -35,7 +35,7 @@
</div> </div>
</th> </th>
<th>No</th> <th>No</th>
<th>Nama Kecamatan</th> <th class="text-center">Nama Kecamatan</th>
<th>Action</th> <th>Action</th>
</tr> </tr>
</thead> </thead>
@ -50,7 +50,7 @@
</td> </td>
<td>{{ $kecamatan -> id }}</td> <td>{{ $kecamatan -> id }}</td>
<td>{{ $kecamatan -> nama_kecamatan }}</td> <td>{{ $kecamatan -> nama_kecamatan }}</td>
<td> <td class="text-center">
<div class="d-flex align-items-center list-action"> <div class="d-flex align-items-center list-action">
<a class="badge bg-success mr-2" data-toggle="tooltip" data-placement="top" title="" <a class="badge bg-success mr-2" data-toggle="tooltip" data-placement="top" title=""
href="/dashboard/kecamatan/{{ $kecamatan ->id}}/edit"><i class="ri-pencil-line mr-0"></i></a> href="/dashboard/kecamatan/{{ $kecamatan ->id}}/edit"><i class="ri-pencil-line mr-0"></i></a>

View File

@ -13,15 +13,15 @@
</div> </div>
</div> </div>
@if (session()->has('succes')) @if (session()->has('succes'))
<div class="alert alert-success" role="alert"> <div class="alert alert-success" role="alert">
{{ session('succes') }} {{ session('succes') }}
</div> </div>
@endif @endif
@if (session()->has('error')) @if (session()->has('error'))
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
{{ session('error') }} {{ session('error') }}
</div> </div>
@endif @endif
<div class="col-lg-12"> <div class="col-lg-12">
<div class="table-responsive rounded mb-3"> <div class="table-responsive rounded mb-3">
<table class="data-table table mb-0 tbl-server-info"> <table class="data-table table mb-0 tbl-server-info">

View File

@ -357,12 +357,11 @@ class="rounded profile-img img-fluid avatar-70">
<div class="row"> <div class="row">
<div class="col-lg-6"> <div class="col-lg-6">
<ul class="list-inline mb-0"> <ul class="list-inline mb-0">
<li class="list-inline-item"><a href="../backend/privacy-policy.html">Privacy Policy</a></li> <li class="list-inline-item">Designed By : <a target="blank" href="https://iqonic.design/">Iconic.Design</a></li>
<li class="list-inline-item"><a href="../backend/terms-of-service.html">Terms of Use</a></li>
</ul> </ul>
</div> </div>
<div class="col-lg-6 text-right"> <div class="col-lg-6 text-right">
<span class="mr-1"><script>document.write(new Date().getFullYear())</script>©</span> <a href="#" class="">POS Dash</a>. <span class="mr-1"><script>document.write(new Date().getFullYear())</script>©</span> <a href="/" class="">KProtect</a>.
</div> </div>
</div> </div>
</div> </div>