update k-means random bukan uniq lagi

This commit is contained in:
daffarahman11 2025-05-10 23:59:44 +07:00
parent 7b20b29157
commit ae72f2717e
6 changed files with 116 additions and 226 deletions

View File

@ -5,10 +5,11 @@
use App\Models\Klaster;
use App\Models\Curanmor;
use App\Models\Kecamatan;
use App\Models\Detail_Curanmor;
use Illuminate\Http\Request;
use App\Models\Detail_Curanmor;
use App\Services\KMeansService;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
class CuranmorController extends Controller
{
@ -35,13 +36,14 @@ public function create()
*/
public function store(Request $request)
{
$request->validate([
'kecamatan_id' => 'required|exists:kecamatans,id',
'jumlah_curanmor' => 'required|numeric',
]);
try{
$request->validate([
'kecamatan_id' => 'required|exists:kecamatans,id',
'jumlah_curanmor' => 'required|numeric',
]);
DB::beginTransaction();
$kecamatan_id = $request->kecamatan_id;
$tambahan_curanmor = $request->jumlah_curanmor;
@ -75,18 +77,11 @@ public function store(Request $request)
$serviceSSECuranmor = new KMeansService();
$serviceSSECuranmor->SSEElbowCuranmor();
// =====CODE TAMBAH SEBELUMNYA=========
// $validateData = $request->validate([
// 'kecamatan_id' =>'required|max:255|exists:kecamatans,id|unique:curanmors,kecamatan_id',
// 'jumlah_curanmor' =>'required',
// 'klaster_id' =>'required|max:255|exists:klasters,id',
// ]);
// Curanmor::create($validateData);
DB::commit();
return redirect('/dashboard/curanmor')->with('succes', 'Berhasil Menambahkan Data Curanmor Baru');
}catch (\Exception $e){
return redirect('/dashboard/curanmor')->with('error', 'Gagal Menambahkan Data Curanmor Baru');
DB::rollBack();
return redirect('/dashboard/curanmor')->with('error', 'Gagal Menambahkan Data Curanmor Baru '. $e->getMessage());
}
}
@ -103,18 +98,6 @@ public function show(Curanmor $curanmor)
*/
public function edit($curanmor)
{
try {
$edit = Curanmor::find($curanmor);
return view('admin.dashboardEditCuranmor', [
'curanmor' => $edit,
'kecamatans' => Kecamatan::all(),
'klasters' => Klaster::all(),
]);
} catch (\Exception $e) {
abort(404);
}
}
/**
@ -122,39 +105,7 @@ public function edit($curanmor)
*/
public function update(Request $request, Curanmor $curanmor)
{
try {
// Validasi input
$request->validate([
'kecamatan_id' => [
'required',
'exists:kecamatans,id',
Rule::unique('curanmors')->ignore($curanmor->id),
],
'klaster_id' => 'required|exists:klasters,id',
'jumlah_curanmor' => 'required|numeric|min:0',
]);
// Update data
$curanmor->update([
'kecamatan_id' => $request->kecamatan_id,
'klaster_id' => $request->klaster_id,
'jumlah_curanmor' => $request->jumlah_curanmor,
]);
$service = new KMeansService();
$hasil = $service->hitungKMeansCuranmor();
// 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) {
return redirect('/dashboard/curanmor')->with('error', 'Data Kecamatan Gagal Diubah: ' . $e->getMessage());
}
}
/**
@ -162,25 +113,6 @@ public function update(Request $request, Curanmor $curanmor)
*/
public function destroy($curanmor)
{
try {
// Cari data berdasarkan ID
$hapus = Curanmor::find($curanmor);
// Pastikan data ditemukan sebelum menghapus
if (!$hapus) {
return redirect('/dashboard/curanmor')->with('error', 'Data tidak ditemukan.');
}
// 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());
}
}
}

View File

@ -9,6 +9,7 @@
use Illuminate\Http\Request;
use App\Services\KMeansService;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
class CurasController extends Controller
{
@ -36,13 +37,14 @@ public function create()
*/
public function store(Request $request)
{
$request->validate([
'kecamatan_id' => 'required|exists:kecamatans,id',
'jumlah_curas' => 'required|numeric',
]);
try{
$request->validate([
'kecamatan_id' => 'required|exists:kecamatans,id',
'jumlah_curas' => 'required|numeric',
]);
DB::beginTransaction();
$kecamatan_id = $request->kecamatan_id;
$tambahan_curas = $request->jumlah_curas;
@ -74,16 +76,19 @@ public function store(Request $request)
$service = new KMeansService();
$hasil = $service->hitungKMeansCuras();
// simpan hasil ke file json
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasil));
$serviceSSECuras = new KMeansService();
$serviceSSECuras->SSEElbowCuras();
DB::commit();
return redirect('/dashboard/curas')->with('succes', 'Data curas berhasil ditambahkan.');
}catch (\Exception $e){
return redirect('/dashboard/curas')->with('error', 'Gagal Menambahkan Data Curas Baru');
DB::rollBack();
return redirect('/dashboard/curas')->with('error', 'Gagal Menambahkan Data Curas Baru'. $e->getMessage());
}
}
@ -100,18 +105,6 @@ public function show(Curas $curas)
*/
public function edit($curas)
{
try {
$edit = Curas::find($curas);
return view('admin.dashboardEditCuras', [
'curas' => $edit,
'kecamatans' => Kecamatan::all(),
'klasters' => Klaster::all(),
]);
} catch (\Exception $e) {
abort(404);
}
}
/**
@ -119,71 +112,13 @@ public function edit($curas)
*/
public function update(Request $request, $id)
{
try {
// Cari data berdasarkan ID yang dikirim
$curas = Curas::findOrFail($id);
// Debugging untuk memastikan data ditemukan
// dd($curas->toArray()); // Jika berhasil, ini akan menampilkan data curas
// Validasi input
$request->validate([
'kecamatan_id' => [
'required',
'exists:kecamatans,id',
Rule::unique('curas')->ignore($curas->id),
],
'klaster_id' => 'required|exists:klasters,id',
'jumlah_curas' => 'required|integer|min:0',
]);
// Update data
$curas->update([
'kecamatan_id' => $request->kecamatan_id,
'klaster_id' => $request->klaster_id,
'jumlah_curas' => $request->jumlah_curas,
]);
$service = new KMeansService();
$hasil = $service->hitungKMeansCuras();
// 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());
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy($curas)
{
try {
// Cari data berdasarkan ID
$hapus = Curas::find($curas);
// Pastikan data ditemukan sebelum menghapus
if (!$hapus) {
return redirect('/dashboard/curas')->with('error', 'Data tidak ditemukan.');
}
// Hapus data
$hapus->delete();
return redirect('/dashboard/curas')->with('succes', 'Data Curas Berhasil Dihapus');
} catch (\Exception $e) {
return redirect('/dashboard/curas')->with('error', 'Terjadi kesalahan: ' . $e->getMessage());
}
}

View File

@ -6,6 +6,7 @@
use Illuminate\Http\Request;
use App\Models\Detail_Curanmor;
use App\Services\KMeansService;
use Illuminate\Support\Facades\DB;
class DetailCuranmorController extends Controller
{
@ -65,6 +66,8 @@ public function update(Request $request, Detail_Curanmor $detail_Curanmor)
public function destroy($id)
{
try {
DB::beginTransaction();
$detail = Detail_Curanmor::findOrFail($id);
$curanmor = Curanmor::findOrFail($detail->curanmor_id);
@ -89,8 +92,12 @@ public function destroy($id)
$serviceSSECuranmor = new KMeansService();
$serviceSSECuranmor->SSEElbowCuranmor();
DB::commit();
return redirect('/dashboard/detail-curanmor')->with('succes', 'Data berhasil dihapus dan curanmor diperbarui.');
} catch (\Exception $e) {
DB::rollback();
return redirect('/dashboard/detail-curanmor')->with('error', 'Terjadi kesalahan Ketika Menghapus Data : ' . $e->getMessage());
}
}

View File

@ -6,6 +6,7 @@
use App\Models\Detail_Curas;
use Illuminate\Http\Request;
use App\Services\KMeansService;
use Illuminate\Support\Facades\DB;
class DetailCurasController extends Controller
{
@ -65,6 +66,9 @@ public function update(Request $request, Detail_Curas $detail_Curas)
public function destroy($id)
{
try {
DB::beginTransaction();
$detail = Detail_Curas::findOrFail($id);
// Ambil curas terkait
@ -92,8 +96,12 @@ public function destroy($id)
$serviceSSECuras = new KMeansService();
$serviceSSECuras->SSEElbowCuras();
DB::commit();
return redirect('/dashboard/detail-curas')->with('succes', 'Data berhasil dihapus dan curas diperbarui.');
} catch (\Exception $e) {
DB::rollBack();
return redirect('/dashboard/detail-curas')->with('error', 'Terjadi kesalahan Ketika Menghapus Data : ' . $e->getMessage());
}

View File

@ -4,6 +4,7 @@
use App\Models\Klaster;
use Illuminate\Http\Request;
use App\Services\KMeansService;
class KlasterController extends Controller
{
@ -34,12 +35,25 @@ public function store(Request $request)
'nama_klaster' =>'required|max:255|unique:klasters,nama_klaster',
'warna' =>'required|max:255',
]);
$serviceKMeans = new KMeansService();
$serviceKMeans->SSEElbowCuranmor();
$serviceKMeans->SSEElbowCuras();
$serviceKMeansCuras = new KMeansService();
$hasilKMeansCuras = $serviceKMeansCuras->hitungKMeansCuras();
file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras));
$serviceKmeansCuranmor = new KMeansService();
$hasilKMeansCuranmor = $serviceKmeansCuranmor->hitungKMeansCuranmor();
file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasilKMeansCuranmor));
Klaster::create($validateData);
return redirect('/dashboard/klaster')->with('succes', 'Berhasil Menambahkan Klaster Baru');
}catch (\Exception $e){
return redirect('/dashboard/klaster')->with('error', 'Gagal Menambahkan Klaster Baru');
return redirect('/dashboard/klaster')->with('error', 'Gagal Menambahkan Klaster Baru ' .$e->getMessage());
}
}

View File

@ -12,27 +12,24 @@ class KMeansService
public function hitungKMeansCuras()
{
$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
$minValue = $data->min('jumlah_curas');
$maxValue = $data->max('jumlah_curas');
$generated = collect();
while ($generated->count() < $k) {
$randomFloat = round(mt_rand($minValue * 100, $maxValue * 100) / 100, 2);
if (!$generated->contains($randomFloat)) {
$generated->push($randomFloat);
}
}
// Ambil centroid awal yang unik
$centroids = $data->unique('jumlah_curas') // Pastikan nilai unik
->shuffle() // Acak
->take($k) // Ambil k data
->values()
->map(function ($item) {
return [
'jumlah_curas' => $item->jumlah_curas,
];
});
// Simpan centroid awal sebelum iterasi
$centroids = $generated->map(function ($value) {
return ['jumlah_curas' => $value];
});
$centroidAwal = $centroids->toArray();
$iterasi = [];
@ -52,7 +49,7 @@ public function hitungKMeansCuras()
$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;
@ -60,15 +57,12 @@ public function hitungKMeansCuras()
$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');
@ -79,21 +73,17 @@ public function hitungKMeansCuras()
});
}
}
// 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();
$centroidToKlaster = [];
foreach ($finalCentroids as $i => $centroid) {
// Klaster ID mulai dari 1 (asumsi klaster di DB bernomor 1, 2, 3, ...)
$centroidToKlaster[$centroid['index']] = $i + 1;
}
// Update ke database
foreach ($data as $item) {
Curas::where('id', $item->id)->update([
@ -101,19 +91,23 @@ public function hitungKMeansCuras()
]);
}
// Format centroid awal
$centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['jumlah_curas']];
});
// Format centroid akhir
$centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) {
return ['C' . ($index + 1) => $item['jumlah_curas']];
});
return [
'centroid_awal' => $centroidAwalFormatted,
'centroid_akhir' => $centroidAkhirFormatted,
'iterasi' => $iterasi
];
}
public function hitungKMeansCuranmor()
{
$data = Curanmor::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curanmor')->orderBy('jumlah_curanmor', 'asc')->get();
@ -121,23 +115,20 @@ public function hitungKMeansCuranmor()
$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.");
$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);
}
}
// Ambil centroid awal yang unik
$centroids = $data->unique('jumlah_curanmor') // Pastikan nilai unik
->shuffle() // Acak
->take($k) // Ambil k data
->values()
->map(function ($item) {
return [
'jumlah_curanmor' => $item->jumlah_curanmor,
];
});
// Simpan centroid awal sebelum iterasi
$centroids = $generated->map(function ($value) {
return ['jumlah_curanmor' => $value];
});
$centroidAwal = $centroids->toArray();
$iterasi = [];
@ -224,42 +215,43 @@ public function SSEElbowCuras()
$maxK = 10;
$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];
// Ambil nilai minimum dan maksimum dari jumlah_curas
$min = $data->min('jumlah_curas');
$max = $data->max('jumlah_curas');
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];
});
// 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');
$centroids = $centroids->map(function ($centroid, $idx) use ($key, $avg) {
@ -269,8 +261,8 @@ public function SSEElbowCuras()
});
}
}
// Hitung SSE untuk k saat ini
// Hitung SSE
$sse = 0;
foreach ($clustered as $key => $group) {
$centroidVal = $centroids[$key]['jumlah_curas'];
@ -278,20 +270,22 @@ public function SSEElbowCuras()
$sse += pow($item->jumlah_curas - $centroidVal, 2);
}
}
$elbowData[] = [
'k' => $k,
'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()
@ -301,7 +295,7 @@ public function SSEElbowCuranmor()
$maxIterasi = 100;
$elbowData = [];
for ($k = 1; $k <= $maxK; $k++) {
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];