fixing kecamatan, klaster, kmeans, sse
This commit is contained in:
parent
ae72f2717e
commit
7cfe92dcec
|
@ -4,6 +4,8 @@
|
|||
|
||||
use App\Models\Kecamatan;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\KMeansService;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class KecamatanController extends Controller
|
||||
|
@ -30,14 +32,28 @@ public function create()
|
|||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validateData = $request->validate([
|
||||
'nama_kecamatan' =>'required|max:255|unique:kecamatans,nama_kecamatan',
|
||||
]);
|
||||
|
||||
try{
|
||||
$validateData = $request->validate([
|
||||
'nama_kecamatan' =>'required|max:255|unique:kecamatans,nama_kecamatan',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
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');
|
||||
}catch (\Exception $e){
|
||||
DB::rollBack();
|
||||
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)
|
||||
{
|
||||
$validateData = $request->validate([
|
||||
'nama_kecamatan' =>'required|max:255|unique:kecamatans,nama_kecamatan',
|
||||
]);
|
||||
|
||||
try{
|
||||
$validateData = $request->validate([
|
||||
'nama_kecamatan' =>'required|max:255|unique:kecamatans,nama_kecamatan',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
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');
|
||||
}catch (\Exception $e){
|
||||
DB::rollBack();
|
||||
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)
|
||||
{
|
||||
try{
|
||||
DB::beginTransaction();
|
||||
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');
|
||||
|
||||
}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');
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use App\Models\Klaster;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\KMeansService;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class KlasterController extends Controller
|
||||
{
|
||||
|
@ -29,30 +30,35 @@ public function create()
|
|||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validateData = $request->validate([
|
||||
'nama_klaster' =>'required|max:255|unique:klasters,nama_klaster',
|
||||
'warna' =>'required|max:255',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try{
|
||||
$validateData = $request->validate([
|
||||
'nama_klaster' =>'required|max:255|unique:klasters,nama_klaster',
|
||||
'warna' =>'required|max:255',
|
||||
]);
|
||||
|
||||
Klaster::create($validateData);
|
||||
|
||||
|
||||
$serviceKMeans = new KMeansService();
|
||||
$serviceKMeans->SSEElbowCuranmor();
|
||||
$serviceKMeans->SSEElbowCuras();
|
||||
|
||||
$serviceKMeansCuras = new KMeansService();
|
||||
$hasilKMeansCuras = $serviceKMeansCuras->hitungKMeansCuras();
|
||||
$hasilKMeansCuras = $serviceKMeans->hitungKMeansCuras();
|
||||
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras));
|
||||
|
||||
$serviceKmeansCuranmor = new KMeansService();
|
||||
$hasilKMeansCuranmor = $serviceKmeansCuranmor->hitungKMeansCuranmor();
|
||||
$hasilKMeansCuranmor = $serviceKMeans->hitungKMeansCuranmor();
|
||||
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');
|
||||
}catch (\Exception $e){
|
||||
|
||||
DB::rollBack();
|
||||
|
||||
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)
|
||||
{
|
||||
$validateData = $request->validate([
|
||||
'nama_klaster' => 'sometimes|required|max:255|unique:klasters,nama_klaster,' . $klaster->id,
|
||||
'warna' => 'sometimes|required|max:255',
|
||||
]);
|
||||
|
||||
try {
|
||||
$validateData = $request->validate([
|
||||
'nama_klaster' => 'sometimes|required|max:255|unique:klasters,nama_klaster,' . $klaster->id,
|
||||
'warna' => 'sometimes|required|max:255',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
// Hanya update data yang diisi (tidak mengganti dengan null)
|
||||
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');
|
||||
} catch (\Exception $e) {
|
||||
|
||||
DB::rollBack();
|
||||
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)
|
||||
{
|
||||
try{
|
||||
|
||||
DB::beginTransaction();
|
||||
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');
|
||||
|
||||
}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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,23 +13,14 @@ public function KMeansCuras()
|
|||
{
|
||||
$data = Curas::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curas')->orderBy('jumlah_curas', 'asc')->get();
|
||||
|
||||
$k = Klaster::count('id');
|
||||
$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
|
||||
// Gunakan centroid tetap
|
||||
|
||||
$centroidValues = [0, 1, 3];
|
||||
$centroids = collect($centroidValues)->map(function ($val) {
|
||||
return ['jumlah_curas' => $val];
|
||||
$centroidManual = [0, 1, 3];
|
||||
$centroids = collect($centroidManual)->map(function ($value) {
|
||||
return ['C' => $value];
|
||||
});
|
||||
|
||||
// Simpan centroid awal sebelum iterasi
|
||||
$centroidAwal = $centroids->toArray();
|
||||
|
||||
$iterasi = [];
|
||||
|
@ -43,13 +34,13 @@ public function KMeansCuras()
|
|||
$jarak = [];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
$clustered[$clusterNumber][] = $item;
|
||||
|
@ -57,65 +48,64 @@ public function KMeansCuras()
|
|||
$currentAssignment[$item->id] = $clusterNumber;
|
||||
}
|
||||
|
||||
// ✨ Cek konvergensi: jika assignment sekarang == sebelumnya, break
|
||||
if ($currentAssignment === $prevAssignment) {
|
||||
break;
|
||||
}
|
||||
|
||||
$prevAssignment = $currentAssignment;
|
||||
|
||||
|
||||
|
||||
// Update centroid berdasarkan rata-rata
|
||||
foreach ($clustered as $key => $group) {
|
||||
$avg = collect($group)->avg('jumlah_curas');
|
||||
$centroids = $centroids->map(function ($item, $index) use ($key, $avg) {
|
||||
return $index === ($key - 1)
|
||||
? ['jumlah_curas' => $avg]
|
||||
? ['C' => $avg]
|
||||
: $item;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Final mapping centroid ke klaster_id (aman/sedang/rawan)
|
||||
// Final mapping centroid ke klaster_id
|
||||
$finalCentroids = $centroids->map(function ($item, $index) {
|
||||
return ['index' => $index + 1, 'jumlah_curas' => $item['jumlah_curas']];
|
||||
})->sortBy('jumlah_curas')->values();
|
||||
return ['index' => $index + 1, 'C' => $item['C']];
|
||||
})->sortBy('C')->values();
|
||||
|
||||
$centroidToKlaster = [];
|
||||
$availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
|
||||
|
||||
foreach ($finalCentroids as $i => $centroid) {
|
||||
// Klaster ID mulai dari 1 (asumsi klaster di DB bernomor 1, 2, 3, ...)
|
||||
$centroidToKlaster[$centroid['index']] = $i + 1;
|
||||
$centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
|
||||
}
|
||||
|
||||
|
||||
// Update ke database
|
||||
foreach ($data as $item) {
|
||||
Curas::where('id', $item->id)->update([
|
||||
'klaster_id' => $centroidToKlaster[$item->temp_klaster],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// Format centroid awal
|
||||
$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_akhir' => $centroidAkhirFormatted,
|
||||
'iterasi' => $iterasi
|
||||
];
|
||||
|
||||
|
||||
file_put_contents(
|
||||
storage_path('app/public/hasil_kmeans_curas.json'),
|
||||
json_encode($hasil, JSON_PRETTY_PRINT)
|
||||
json_encode($hasilKMeansCuras, JSON_PRETTY_PRINT)
|
||||
);
|
||||
|
||||
|
||||
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();
|
||||
|
||||
$k = Klaster::count('id');
|
||||
$maxIterasi = 100;
|
||||
|
||||
$uniqueCount = $data->unique('jumlah_curanmor')->count();
|
||||
if ($uniqueCount < $k) {
|
||||
throw new \Exception("Jumlah nilai unik pada 'jumlah_curanmor' ($uniqueCount) kurang dari jumlah klaster ($k). Pastikan data memiliki variasi yang cukup.");
|
||||
}
|
||||
|
||||
$centroidValues = [10, 20, 30];
|
||||
$centroids = collect($centroidValues)->map(function ($val) {
|
||||
return ['jumlah_curanmor' => $val];
|
||||
$centroidManual = [10, 20, 30];
|
||||
$centroids = collect($centroidManual)->map(function ($value) {
|
||||
return ['C' => $value];
|
||||
});
|
||||
|
||||
// Simpan centroid awal sebelum iterasi
|
||||
$centroidAwal = $centroids->toArray();
|
||||
|
||||
$iterasi = [];
|
||||
|
@ -148,82 +131,84 @@ public function KMeansCuranmor()
|
|||
|
||||
foreach ($data as $item) {
|
||||
$jarak = [];
|
||||
|
||||
|
||||
foreach ($centroids as $idx => $centroid) {
|
||||
// Menggunakan Euclidean distance standar (akar kuadrat dari selisih kuadrat)
|
||||
$dist = sqrt(pow($item->jumlah_curanmor - $centroid['jumlah_curanmor'], 2));
|
||||
$dist = abs($item->jumlah_curanmor - $centroid['C']);
|
||||
$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"
|
||||
|
||||
$minIndex = array_keys($jarak, min($jarak))[0];
|
||||
$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) {
|
||||
break;
|
||||
}
|
||||
|
||||
$prevAssignment = $currentAssignment;
|
||||
|
||||
|
||||
|
||||
// Update centroid berdasarkan rata-rata
|
||||
foreach ($clustered as $key => $group) {
|
||||
$avg = collect($group)->avg('jumlah_curanmor');
|
||||
$centroids = $centroids->map(function ($item, $index) use ($key, $avg) {
|
||||
return $index === ($key - 1)
|
||||
? ['jumlah_curanmor' => $avg]
|
||||
? ['C' => $avg]
|
||||
: $item;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Final mapping centroid ke klaster_id (aman/sedang/rawan)
|
||||
// Final mapping centroid ke klaster_id
|
||||
$finalCentroids = $centroids->map(function ($item, $index) {
|
||||
return ['index' => $index + 1, 'jumlah_curanmor' => $item['jumlah_curanmor']];
|
||||
})->sortBy('jumlah_curanmor')->values();
|
||||
return ['index' => $index + 1, 'C' => $item['C']];
|
||||
})->sortBy('C')->values();
|
||||
|
||||
$centroidToKlaster = [];
|
||||
$availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
|
||||
|
||||
foreach ($finalCentroids as $i => $centroid) {
|
||||
// Klaster ID mulai dari 1 (asumsi klaster di DB bernomor 1, 2, 3, ...)
|
||||
$centroidToKlaster[$centroid['index']] = $i + 1;
|
||||
$centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
|
||||
}
|
||||
|
||||
|
||||
// Update ke database
|
||||
|
||||
|
||||
|
||||
foreach ($data as $item) {
|
||||
Curanmor::where('id', $item->id)->update([
|
||||
'klaster_id' => $centroidToKlaster[$item->temp_klaster],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// Format centroid awal
|
||||
$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_akhir' => $centroidAkhirFormatted,
|
||||
'iterasi' => $iterasi
|
||||
];
|
||||
|
||||
|
||||
file_put_contents(
|
||||
storage_path('app/public/hasil_kmeans_curanmor.json'),
|
||||
json_encode($hasil, JSON_PRETTY_PRINT)
|
||||
json_encode($hasilKMeansCuranmor, JSON_PRETTY_PRINT)
|
||||
);
|
||||
|
||||
|
||||
return redirect('/dashboard/TampilHitungCuranmor');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,15 +21,16 @@ public function hitungKMeansCuras()
|
|||
|
||||
$generated = collect();
|
||||
while ($generated->count() < $k) {
|
||||
$randomFloat = round(mt_rand($minValue * 100, $maxValue * 100) / 100, 2);
|
||||
if (!$generated->contains($randomFloat)) {
|
||||
$generated->push($randomFloat);
|
||||
$random = mt_rand($minValue, $maxValue);
|
||||
if (!$generated->contains($random)) {
|
||||
$generated->push($random);
|
||||
}
|
||||
}
|
||||
|
||||
$centroids = $generated->map(function ($value) {
|
||||
return ['jumlah_curas' => $value];
|
||||
return ['C' => $value];
|
||||
});
|
||||
|
||||
$centroidAwal = $centroids->toArray();
|
||||
|
||||
$iterasi = [];
|
||||
|
@ -43,7 +44,7 @@ public function hitungKMeansCuras()
|
|||
$jarak = [];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -68,7 +69,7 @@ public function hitungKMeansCuras()
|
|||
$avg = collect($group)->avg('jumlah_curas');
|
||||
$centroids = $centroids->map(function ($item, $index) use ($key, $avg) {
|
||||
return $index === ($key - 1)
|
||||
? ['jumlah_curas' => $avg]
|
||||
? ['C' => $avg]
|
||||
: $item;
|
||||
});
|
||||
}
|
||||
|
@ -76,15 +77,20 @@ public function hitungKMeansCuras()
|
|||
|
||||
// Final mapping centroid ke klaster_id
|
||||
$finalCentroids = $centroids->map(function ($item, $index) {
|
||||
return ['index' => $index + 1, 'jumlah_curas' => $item['jumlah_curas']];
|
||||
})->sortBy('jumlah_curas')->values();
|
||||
return ['index' => $index + 1, 'C' => $item['C']];
|
||||
})->sortBy('C')->values();
|
||||
|
||||
$availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
|
||||
|
||||
$centroidToKlaster = [];
|
||||
foreach ($finalCentroids as $i => $centroid) {
|
||||
$centroidToKlaster[$centroid['index']] = $i + 1;
|
||||
$centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
|
||||
}
|
||||
|
||||
|
||||
// Update ke database
|
||||
|
||||
|
||||
|
||||
foreach ($data as $item) {
|
||||
Curas::where('id', $item->id)->update([
|
||||
'klaster_id' => $centroidToKlaster[$item->temp_klaster],
|
||||
|
@ -93,12 +99,12 @@ public function hitungKMeansCuras()
|
|||
|
||||
// Format centroid awal
|
||||
$centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) {
|
||||
return ['C' . ($index + 1) => $item['jumlah_curas']];
|
||||
return ['C' . ($index + 1) => $item['C']];
|
||||
});
|
||||
|
||||
// Format centroid akhir
|
||||
$centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) {
|
||||
return ['C' . ($index + 1) => $item['jumlah_curas']];
|
||||
return ['C' . ($index + 1) => $item['C']];
|
||||
});
|
||||
|
||||
return [
|
||||
|
@ -115,20 +121,22 @@ public function hitungKMeansCuranmor()
|
|||
$k = Klaster::count('id');
|
||||
$maxIterasi = 100;
|
||||
|
||||
// Ambil centroid awal yang unik
|
||||
$minValue = $data->min('jumlah_curanmor');
|
||||
$maxValue = $data->max('jumlah_curanmor');
|
||||
|
||||
$generated = collect();
|
||||
while ($generated->count() < $k) {
|
||||
$randomFloat = round(mt_rand($minValue * 100, $maxValue * 100) / 100, 2);
|
||||
if (!$generated->contains($randomFloat)) {
|
||||
$generated->push($randomFloat);
|
||||
$random = mt_rand($minValue, $maxValue);
|
||||
if (!$generated->contains($random)) {
|
||||
$generated->push($random);
|
||||
}
|
||||
}
|
||||
|
||||
$centroids = $generated->map(function ($value) {
|
||||
return ['jumlah_curanmor' => $value];
|
||||
return ['C' => $value];
|
||||
});
|
||||
|
||||
$centroidAwal = $centroids->toArray();
|
||||
|
||||
$iterasi = [];
|
||||
|
@ -142,13 +150,13 @@ public function hitungKMeansCuranmor()
|
|||
$jarak = [];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
$clustered[$clusterNumber][] = $item;
|
||||
|
@ -156,77 +164,78 @@ public function hitungKMeansCuranmor()
|
|||
$currentAssignment[$item->id] = $clusterNumber;
|
||||
}
|
||||
|
||||
// ✨ Cek konvergensi: jika assignment sekarang == sebelumnya, break
|
||||
if ($currentAssignment === $prevAssignment) {
|
||||
break;
|
||||
}
|
||||
|
||||
$prevAssignment = $currentAssignment;
|
||||
|
||||
|
||||
|
||||
// Update centroid berdasarkan rata-rata
|
||||
foreach ($clustered as $key => $group) {
|
||||
$avg = collect($group)->avg('jumlah_curanmor');
|
||||
$centroids = $centroids->map(function ($item, $index) use ($key, $avg) {
|
||||
return $index === ($key - 1)
|
||||
? ['jumlah_curanmor' => $avg]
|
||||
? ['C' => $avg]
|
||||
: $item;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Final mapping centroid ke klaster_id (aman/sedang/rawan)
|
||||
// Final mapping centroid ke klaster_id
|
||||
$finalCentroids = $centroids->map(function ($item, $index) {
|
||||
return ['index' => $index + 1, 'jumlah_curanmor' => $item['jumlah_curanmor']];
|
||||
})->sortBy('jumlah_curanmor')->values();
|
||||
return ['index' => $index + 1, 'C' => $item['C']];
|
||||
})->sortBy('C')->values();
|
||||
|
||||
$centroidToKlaster = [];
|
||||
$availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
|
||||
|
||||
foreach ($finalCentroids as $i => $centroid) {
|
||||
// Klaster ID mulai dari 1 (asumsi klaster di DB bernomor 1, 2, 3, ...)
|
||||
$centroidToKlaster[$centroid['index']] = $i + 1;
|
||||
$centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
|
||||
}
|
||||
|
||||
|
||||
// Update ke database
|
||||
|
||||
|
||||
|
||||
foreach ($data as $item) {
|
||||
Curanmor::where('id', $item->id)->update([
|
||||
'klaster_id' => $centroidToKlaster[$item->temp_klaster],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// Format centroid awal
|
||||
$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 [
|
||||
'centroid_awal' => $centroidAwalFormatted,
|
||||
'centroid_akhir' => $centroidAkhirFormatted,
|
||||
'iterasi' => $iterasi
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
public function SSEElbowCuras()
|
||||
public function SSEElbowCuranmor()
|
||||
{
|
||||
$data = Curas::select('id', 'jumlah_curas')->get();
|
||||
$data = Curanmor::select('id', 'jumlah_curanmor')->get();
|
||||
$maxK = 10;
|
||||
$maxIterasi = 100;
|
||||
$elbowData = [];
|
||||
|
||||
// Ambil nilai minimum dan maksimum dari jumlah_curas
|
||||
$min = $data->min('jumlah_curas');
|
||||
$max = $data->max('jumlah_curas');
|
||||
$min = $data->min('jumlah_curanmor');
|
||||
$max = $data->max('jumlah_curanmor');
|
||||
|
||||
// Loop untuk setiap nilai k dari 2 hingga maxK
|
||||
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) {
|
||||
return ['jumlah_curas' => mt_rand($min * 100, $max * 100) / 100];
|
||||
return ['jumlah_curanmor' => mt_rand($min, $max)];
|
||||
});
|
||||
|
||||
$centroidAwal = $centroids->pluck('jumlah_curas')->toArray();
|
||||
$prevAssignment = [];
|
||||
|
||||
for ($iter = 0; $iter < $maxIterasi; $iter++) {
|
||||
|
@ -236,22 +245,104 @@ public function SSEElbowCuras()
|
|||
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']);
|
||||
$dist = abs($item->jumlah_curanmor - $centroid['jumlah_curanmor']);
|
||||
$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) {
|
||||
$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) {
|
||||
$avg = collect($group)->avg('jumlah_curas');
|
||||
$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;
|
||||
foreach ($clustered as $key => $group) {
|
||||
$centroidVal = $centroids[$key]['jumlah_curas'];
|
||||
|
@ -271,96 +362,22 @@ public function SSEElbowCuras()
|
|||
}
|
||||
}
|
||||
|
||||
// Simpan SSE untuk nilai k
|
||||
$elbowData[] = [
|
||||
'k' => $k,
|
||||
'sse' => $sse,
|
||||
'centroid_awal' => $centroidAwal
|
||||
'sse' => round($sse, 4)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Simpan hasil SSE untuk setiap k ke file JSON
|
||||
file_put_contents(
|
||||
storage_path('app/public/sse_elbow_curas.json'),
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ public function up(): void
|
|||
table: 'kecamatans', indexName: 'curas_kecamatan_id');
|
||||
$table->float('jumlah_curas');
|
||||
$table->foreignId('klaster_id')->nullable()->constrained(
|
||||
table: 'klasters', indexName: 'curas_klaster_id');
|
||||
table: 'klasters', indexName: 'curas_klaster_id')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ public function up(): void
|
|||
table: 'kecamatans', indexName: 'curanmor_kecamatan_id');
|
||||
$table->float('jumlah_curanmor');
|
||||
$table->foreignId('klaster_id')->nullable()->constrained(
|
||||
table: 'klasters', indexName: 'klaster_kecamatan_id');
|
||||
table: 'klasters', indexName: 'klaster_kecamatan_id')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5213,12 +5213,11 @@ if (jQuery("#editor").length) {
|
|||
valueAxis.title.text = "Nilai SSE";
|
||||
|
||||
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.tooltipText = "K={categoryX}\nSSE={valueY}"; // Tooltip tanpa centroid_awal
|
||||
lineSeries.tensionX = 1;
|
||||
|
||||
let bullet = lineSeries.bullets.push(new am4charts.CircleBullet());
|
||||
|
@ -5236,20 +5235,19 @@ if (jQuery("#editor").length) {
|
|||
fetch(`/storage/sse_elbow_${type}.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(", ")
|
||||
sse: item.sse
|
||||
}));
|
||||
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');
|
||||
|
||||
// Event listener dropdown
|
||||
// Event listener dropdown untuk memilih tipe data
|
||||
document.querySelectorAll('.chart-option').forEach(item => {
|
||||
item.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
</div>
|
||||
<a href="/dashboard/curas/create" class="btn btn-primary add-list"><i class="las la-plus mr-3"></i>Tambah Kasus Curas</a>
|
||||
</div>
|
||||
@if (session()->has('succes'))
|
||||
</div>
|
||||
@if (session()->has('succes'))
|
||||
<div class="alert alert-success" role="alert">
|
||||
{{ session('succes') }}
|
||||
</div>
|
||||
|
@ -21,7 +22,7 @@
|
|||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="table-responsive rounded mb-3">
|
||||
<table class="data-table table mb-0 tbl-server-info">
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
</div>
|
||||
</th>
|
||||
<th>No</th>
|
||||
<th>Nama Kecamatan</th>
|
||||
<th class="text-center">Nama Kecamatan</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -50,7 +50,7 @@
|
|||
</td>
|
||||
<td>{{ $kecamatan -> id }}</td>
|
||||
<td>{{ $kecamatan -> nama_kecamatan }}</td>
|
||||
<td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex align-items-center list-action">
|
||||
<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>
|
||||
|
|
|
@ -13,15 +13,15 @@
|
|||
</div>
|
||||
</div>
|
||||
@if (session()->has('succes'))
|
||||
<div class="alert alert-success" role="alert">
|
||||
{{ session('succes') }}
|
||||
</div>
|
||||
@endif
|
||||
@if (session()->has('error'))
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
<div class="alert alert-success" role="alert">
|
||||
{{ session('succes') }}
|
||||
</div>
|
||||
@endif
|
||||
@if (session()->has('error'))
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
<div class="col-lg-12">
|
||||
<div class="table-responsive rounded mb-3">
|
||||
<table class="data-table table mb-0 tbl-server-info">
|
||||
|
|
|
@ -357,12 +357,11 @@ class="rounded profile-img img-fluid avatar-70">
|
|||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<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"><a href="../backend/terms-of-service.html">Terms of Use</a></li>
|
||||
<li class="list-inline-item">Designed By : <a target="blank" href="https://iqonic.design/">Iconic.Design</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<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>
|
||||
|
|
Loading…
Reference in New Issue