Tambah Normalisasi pada kmeans controller
This commit is contained in:
parent
df989d1551
commit
c3165955c1
|
@ -9,16 +9,30 @@
|
||||||
|
|
||||||
class KmeansController extends Controller
|
class KmeansController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
public function KMeansCuras()
|
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('kecamatan_id', 'asc')->get();
|
||||||
|
|
||||||
|
// Hitung min dan max untuk normalisasi
|
||||||
|
$min = $data->min('jumlah_curas');
|
||||||
|
$max = $data->max('jumlah_curas');
|
||||||
|
|
||||||
|
// Normalisasi jumlah_curas ke skala 0–1
|
||||||
|
$data = $data->map(function ($item) use ($min, $max) {
|
||||||
|
$item->jumlah_curas_normalized = $max == $min
|
||||||
|
? 1
|
||||||
|
: round(($item->jumlah_curas - $min) / ($max - $min), 2);
|
||||||
|
return $item;
|
||||||
|
});
|
||||||
|
|
||||||
$maxIterasi = 100;
|
$maxIterasi = 100;
|
||||||
|
|
||||||
|
// Centroid awal langsung dalam skala 0–1
|
||||||
$centroidManual = [0, 1, 3];
|
$centroidManual = [0.0, 0.5, 1];
|
||||||
$centroids = collect($centroidManual)->map(function ($value) {
|
$centroids = collect($centroidManual)->map(function ($value) {
|
||||||
return ['C' => $value];
|
return ['C' => round($value, 2)];
|
||||||
});
|
});
|
||||||
|
|
||||||
$centroidAwal = $centroids->toArray();
|
$centroidAwal = $centroids->toArray();
|
||||||
|
@ -34,11 +48,14 @@ public function KMeansCuras()
|
||||||
$jarak = [];
|
$jarak = [];
|
||||||
|
|
||||||
foreach ($centroids as $idx => $centroid) {
|
foreach ($centroids as $idx => $centroid) {
|
||||||
$dist = abs($item->jumlah_curas - $centroid['C']);
|
$dist = abs($item->jumlah_curas_normalized - $centroid['C']);
|
||||||
$jarak["C" . ($idx + 1)] = $dist;
|
$jarak["C" . ($idx + 1)] = round($dist, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
$iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak);
|
$iterasi[$i][] = array_merge([
|
||||||
|
'kecamatan_id' => $item->kecamatan_id,
|
||||||
|
'normal' => round($item->jumlah_curas_normalized, 2)
|
||||||
|
], $jarak);
|
||||||
|
|
||||||
$minIndex = array_keys($jarak, min($jarak))[0];
|
$minIndex = array_keys($jarak, min($jarak))[0];
|
||||||
$clusterNumber = (int) str_replace("C", "", $minIndex);
|
$clusterNumber = (int) str_replace("C", "", $minIndex);
|
||||||
|
@ -54,20 +71,20 @@ public function KMeansCuras()
|
||||||
|
|
||||||
$prevAssignment = $currentAssignment;
|
$prevAssignment = $currentAssignment;
|
||||||
|
|
||||||
// Update centroid berdasarkan rata-rata
|
// Update centroid dengan rata-rata nilai normalized
|
||||||
foreach ($clustered as $key => $group) {
|
foreach ($clustered as $key => $group) {
|
||||||
$avg = collect($group)->avg('jumlah_curas');
|
$avg = collect($group)->avg('jumlah_curas_normalized');
|
||||||
$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)
|
||||||
? ['C' => $avg]
|
? ['C' => round($avg, 2)]
|
||||||
: $item;
|
: $item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final mapping centroid ke klaster_id
|
// Mapping centroid ke klaster_id
|
||||||
$finalCentroids = $centroids->map(function ($item, $index) {
|
$finalCentroids = $centroids->map(function ($item, $index) {
|
||||||
return ['index' => $index + 1, 'C' => $item['C']];
|
return ['index' => $index + 1, 'C' => round($item['C'], 2)];
|
||||||
})->sortBy('C')->values();
|
})->sortBy('C')->values();
|
||||||
|
|
||||||
$availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
|
$availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
|
||||||
|
@ -82,20 +99,20 @@ public function KMeansCuras()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format centroid awal
|
|
||||||
$centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) {
|
$centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) {
|
||||||
return ['C' . ($index + 1) => $item['C']];
|
return ['C' . ($index + 1) => round($item['C'], 2)];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Format centroid akhir
|
|
||||||
$centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) {
|
$centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) {
|
||||||
return ['C' . ($index + 1) => $item['C']];
|
return ['C' . ($index + 1) => round($item['C'], 2)];
|
||||||
});
|
});
|
||||||
|
|
||||||
$hasilKMeansCuras = [
|
$hasilKMeansCuras = [
|
||||||
'centroid_awal' => $centroidAwalFormatted,
|
'centroid_awal' => $centroidAwalFormatted,
|
||||||
'centroid_akhir' => $centroidAkhirFormatted,
|
'centroid_akhir' => $centroidAkhirFormatted,
|
||||||
'iterasi' => $iterasi
|
'iterasi' => $iterasi,
|
||||||
|
'min' => $min,
|
||||||
|
'max' => $max
|
||||||
];
|
];
|
||||||
|
|
||||||
file_put_contents(
|
file_put_contents(
|
||||||
|
@ -103,21 +120,34 @@ public function KMeansCuras()
|
||||||
json_encode($hasilKMeansCuras, JSON_PRETTY_PRINT)
|
json_encode($hasilKMeansCuras, JSON_PRETTY_PRINT)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
return redirect('/dashboard/TampilHitungCuras');
|
return redirect('/dashboard/TampilHitungCuras');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function KMeansCuranmor()
|
public function KMeansCuranmor()
|
||||||
{
|
{
|
||||||
$data = Curanmor::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curanmor')->orderBy('jumlah_curanmor', 'asc')->get();
|
// Ambil data awal
|
||||||
|
$data = Curanmor::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curanmor')
|
||||||
|
->orderBy('kecamatan_id', 'asc')->get();
|
||||||
|
|
||||||
|
// Hitung min dan max untuk normalisasi Min-Max
|
||||||
|
$min = $data->min('jumlah_curanmor');
|
||||||
|
$max = $data->max('jumlah_curanmor');
|
||||||
|
|
||||||
|
// Normalisasi jumlah_curanmor ke skala 1-100
|
||||||
|
$data = $data->map(function ($item) use ($min, $max) {
|
||||||
|
$item->jumlah_curanmor_normalized = $max == $min
|
||||||
|
? 1
|
||||||
|
: round((($item->jumlah_curanmor - $min) / ($max - $min)), 2);
|
||||||
|
return $item;
|
||||||
|
});
|
||||||
|
|
||||||
$maxIterasi = 100;
|
$maxIterasi = 100;
|
||||||
|
|
||||||
$centroidManual = [10, 20, 30];
|
// Centroid awal dalam skala 1–100
|
||||||
|
$centroidManual = [0.2, 0.5, 0.8];
|
||||||
$centroids = collect($centroidManual)->map(function ($value) {
|
$centroids = collect($centroidManual)->map(function ($value) {
|
||||||
return ['C' => $value];
|
return ['C' => round($value, 2)];
|
||||||
});
|
});
|
||||||
|
|
||||||
$centroidAwal = $centroids->toArray();
|
$centroidAwal = $centroids->toArray();
|
||||||
|
@ -133,11 +163,14 @@ public function KMeansCuranmor()
|
||||||
$jarak = [];
|
$jarak = [];
|
||||||
|
|
||||||
foreach ($centroids as $idx => $centroid) {
|
foreach ($centroids as $idx => $centroid) {
|
||||||
$dist = abs($item->jumlah_curanmor - $centroid['C']);
|
$dist = abs($item->jumlah_curanmor_normalized - $centroid['C']);
|
||||||
$jarak["C" . ($idx + 1)] = $dist;
|
$jarak["C" . ($idx + 1)] = round($dist, 2); // Dua angka di belakang koma
|
||||||
}
|
}
|
||||||
|
|
||||||
$iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak);
|
$iterasi[$i][] = array_merge([
|
||||||
|
'kecamatan_id' => $item->kecamatan_id,
|
||||||
|
'normal' => round($item->jumlah_curanmor_normalized, 2)
|
||||||
|
], $jarak);
|
||||||
|
|
||||||
$minIndex = array_keys($jarak, min($jarak))[0];
|
$minIndex = array_keys($jarak, min($jarak))[0];
|
||||||
$clusterNumber = (int) str_replace("C", "", $minIndex);
|
$clusterNumber = (int) str_replace("C", "", $minIndex);
|
||||||
|
@ -147,18 +180,19 @@ public function KMeansCuranmor()
|
||||||
$currentAssignment[$item->id] = $clusterNumber;
|
$currentAssignment[$item->id] = $clusterNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cek konvergensi
|
||||||
if ($currentAssignment === $prevAssignment) {
|
if ($currentAssignment === $prevAssignment) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$prevAssignment = $currentAssignment;
|
$prevAssignment = $currentAssignment;
|
||||||
|
|
||||||
// Update centroid berdasarkan rata-rata
|
// Update centroid berdasarkan rata-rata nilai yang sudah dinormalisasi
|
||||||
foreach ($clustered as $key => $group) {
|
foreach ($clustered as $key => $group) {
|
||||||
$avg = collect($group)->avg('jumlah_curanmor');
|
$avg = collect($group)->avg('jumlah_curanmor_normalized');
|
||||||
$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)
|
||||||
? ['C' => $avg]
|
? ['C' => round($avg, 2)]
|
||||||
: $item;
|
: $item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -166,7 +200,7 @@ public function KMeansCuranmor()
|
||||||
|
|
||||||
// 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, 'C' => $item['C']];
|
return ['index' => $index + 1, 'C' => round($item['C'], 2)];
|
||||||
})->sortBy('C')->values();
|
})->sortBy('C')->values();
|
||||||
|
|
||||||
$availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
|
$availableKlasterIDs = Klaster::orderBy('id', 'asc')->pluck('id')->values();
|
||||||
|
@ -175,11 +209,7 @@ public function KMeansCuranmor()
|
||||||
$centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
|
$centroidToKlaster[$centroid['index']] = $availableKlasterIDs[$i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update hasil clustering 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],
|
||||||
|
@ -188,18 +218,20 @@ public function KMeansCuranmor()
|
||||||
|
|
||||||
// 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['C']];
|
return ['C' . ($index + 1) => round($item['C'], 2)];
|
||||||
});
|
});
|
||||||
|
|
||||||
// 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['C']];
|
return ['C' . ($index + 1) => round($item['C'], 2)];
|
||||||
});
|
});
|
||||||
|
|
||||||
$hasilKMeansCuranmor = [
|
$hasilKMeansCuranmor = [
|
||||||
'centroid_awal' => $centroidAwalFormatted,
|
'centroid_awal' => $centroidAwalFormatted,
|
||||||
'centroid_akhir' => $centroidAkhirFormatted,
|
'centroid_akhir' => $centroidAkhirFormatted,
|
||||||
'iterasi' => $iterasi
|
'iterasi' => $iterasi,
|
||||||
|
'min' => $min,
|
||||||
|
'max' => $max
|
||||||
];
|
];
|
||||||
|
|
||||||
file_put_contents(
|
file_put_contents(
|
||||||
|
@ -207,8 +239,10 @@ public function KMeansCuranmor()
|
||||||
json_encode($hasilKMeansCuranmor, JSON_PRETTY_PRINT)
|
json_encode($hasilKMeansCuranmor, JSON_PRETTY_PRINT)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
return redirect('/dashboard/TampilHitungCuranmor');
|
return redirect('/dashboard/TampilHitungCuranmor');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ public function runKmeans()
|
||||||
$serviceKMeans = new KMeansService();
|
$serviceKMeans = new KMeansService();
|
||||||
$serviceKMeans->SSEElbowCuranmor();
|
$serviceKMeans->SSEElbowCuranmor();
|
||||||
$serviceKMeans->SSEElbowCuras();
|
$serviceKMeans->SSEElbowCuras();
|
||||||
|
$serviceKMeans->hitungDBSCANManual();
|
||||||
|
$serviceKMeans->kmeansWithSilhouetteSingleMethod();
|
||||||
|
|
||||||
|
|
||||||
$serviceKMeansCuras = new KMeansService();
|
$serviceKMeansCuras = new KMeansService();
|
||||||
$hasilKMeansCuras = $serviceKMeansCuras->hitungKMeansCuras();
|
$hasilKMeansCuras = $serviceKMeansCuras->hitungKMeansCuras();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
use App\Models\Curas;
|
use App\Models\Curas;
|
||||||
use App\Models\Klaster;
|
use App\Models\Klaster;
|
||||||
use App\Models\Curanmor;
|
use App\Models\Curanmor;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
|
||||||
class KMeansService
|
class KMeansService
|
||||||
|
@ -388,6 +389,243 @@ public function SSEElbowCuras()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hitungDBSCANManual()
|
||||||
|
{
|
||||||
|
$eps = 1.5; // Jarak maksimum antar titik
|
||||||
|
$minPts = 3; // Minimum tetangga agar jadi core point
|
||||||
|
|
||||||
|
$data = Curas::select('jumlah_curas')->get()->pluck('jumlah_curas')->map(fn($v) => (float)$v)->toArray();
|
||||||
|
$n = count($data);
|
||||||
|
|
||||||
|
$visited = array_fill(0, $n, false);
|
||||||
|
$labels = array_fill(0, $n, null);
|
||||||
|
$clusterId = 0;
|
||||||
|
|
||||||
|
// Fungsi cari tetangga
|
||||||
|
$regionQuery = function ($pointIndex) use ($data, $eps) {
|
||||||
|
$neighbors = [];
|
||||||
|
foreach ($data as $i => $val) {
|
||||||
|
if (abs($val - $data[$pointIndex]) <= $eps) {
|
||||||
|
$neighbors[] = $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $neighbors;
|
||||||
|
};
|
||||||
|
|
||||||
|
// DBSCAN Proses
|
||||||
|
for ($i = 0; $i < $n; $i++) {
|
||||||
|
if ($visited[$i]) continue;
|
||||||
|
$visited[$i] = true;
|
||||||
|
|
||||||
|
$neighbors = $regionQuery($i);
|
||||||
|
if (count($neighbors) < $minPts) {
|
||||||
|
$labels[$i] = -1; // noise
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$labels[$i] = $clusterId;
|
||||||
|
$seeds = array_diff($neighbors, [$i]);
|
||||||
|
|
||||||
|
foreach ($seeds as $seed) {
|
||||||
|
if (!$visited[$seed]) {
|
||||||
|
$visited[$seed] = true;
|
||||||
|
$newNeighbors = $regionQuery($seed);
|
||||||
|
if (count($newNeighbors) >= $minPts) {
|
||||||
|
$seeds = array_unique(array_merge($seeds, $newNeighbors));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($labels[$seed] === null || $labels[$seed] === -1) {
|
||||||
|
$labels[$seed] = $clusterId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$clusterId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hitung silhouette (manual satu dimensi)
|
||||||
|
$silhouetteScores = [];
|
||||||
|
foreach ($data as $i => $val) {
|
||||||
|
$label = $labels[$i];
|
||||||
|
if ($label === -1) continue; // skip noise
|
||||||
|
|
||||||
|
$sameCluster = [];
|
||||||
|
$otherClusters = [];
|
||||||
|
|
||||||
|
foreach ($data as $j => $otherVal) {
|
||||||
|
if ($i === $j || $labels[$j] === -1) continue;
|
||||||
|
if ($labels[$j] === $label) {
|
||||||
|
$sameCluster[] = abs($val - $otherVal);
|
||||||
|
} else {
|
||||||
|
$otherClusters[$labels[$j]][] = abs($val - $otherVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$a = count($sameCluster) > 0 ? array_sum($sameCluster) / count($sameCluster) : 0;
|
||||||
|
$b = count($otherClusters) > 0 ? min(array_map(fn($d) => array_sum($d) / count($d), $otherClusters)) : 0;
|
||||||
|
|
||||||
|
$s = ($a === $b && $a === 0) ? 0 : ($b - $a) / max($a, $b);
|
||||||
|
$silhouetteScores[] = $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
$meanSilhouette = count($silhouetteScores) > 0 ? round(array_sum($silhouetteScores) / count($silhouetteScores), 4) : 0;
|
||||||
|
|
||||||
|
// Susun hasil anggota klaster
|
||||||
|
$anggotaKlaster = [];
|
||||||
|
foreach ($labels as $i => $label) {
|
||||||
|
$anggotaKlaster[$label][] = $data[$i];
|
||||||
|
}
|
||||||
|
|
||||||
|
$jumlahKlaster = count(array_filter(array_keys($anggotaKlaster), fn($k) => $k !== -1));
|
||||||
|
|
||||||
|
$hasil = [
|
||||||
|
'silhouette' => $meanSilhouette,
|
||||||
|
'jumlah_klaster' => $jumlahKlaster,
|
||||||
|
'anggota_klaster' => $anggotaKlaster,
|
||||||
|
];
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
storage_path('app/public/dbscan_curas.json'),
|
||||||
|
json_encode($hasil, JSON_PRETTY_PRINT)
|
||||||
|
);
|
||||||
|
return $hasil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function kmeansWithSilhouetteSingleMethod(int $k = 2, int $maxIter = 100): array
|
||||||
|
{
|
||||||
|
// Ambil data dari tabel curas
|
||||||
|
$data = \App\Models\Curas::select('kecamatan_id', 'jumlah_curas')
|
||||||
|
->get()
|
||||||
|
->pluck('jumlah_curas', 'kecamatan_id')
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
$ids = array_keys($data);
|
||||||
|
$values = array_values($data);
|
||||||
|
|
||||||
|
// Inisialisasi centroid secara random
|
||||||
|
// Ambil nilai min dan max dari data
|
||||||
|
$minValue = min($values);
|
||||||
|
$maxValue = max($values);
|
||||||
|
|
||||||
|
$centroids = [];
|
||||||
|
|
||||||
|
if ($k == 1) {
|
||||||
|
// Jika cuma 1 klaster, centroid random antara min dan max
|
||||||
|
$centroids[] = rand($minValue, $maxValue);
|
||||||
|
} else {
|
||||||
|
// Untuk k >= 2, inisialisasi centroid unik integer dalam rentang min-max
|
||||||
|
$centroids = [];
|
||||||
|
while (count($centroids) < $k) {
|
||||||
|
$randCentroid = rand($minValue, $maxValue);
|
||||||
|
if (!in_array($randCentroid, $centroids)) {
|
||||||
|
$centroids[] = $randCentroid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$clusters = [];
|
||||||
|
$iter = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$clusters = array_fill(0, $k, []);
|
||||||
|
|
||||||
|
// Assign ke klaster terdekat (jarak absolut 1 dimensi)
|
||||||
|
foreach ($values as $idx => $val) {
|
||||||
|
$distances = [];
|
||||||
|
foreach ($centroids as $cidx => $centroid) {
|
||||||
|
$distances[$cidx] = abs($val - $centroid);
|
||||||
|
}
|
||||||
|
asort($distances);
|
||||||
|
$nearestCluster = key($distances);
|
||||||
|
$clusters[$nearestCluster][] = $idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hitung centroid baru
|
||||||
|
$newCentroids = [];
|
||||||
|
foreach ($clusters as $cluster) {
|
||||||
|
if (count($cluster) > 0) {
|
||||||
|
$sum = 0;
|
||||||
|
foreach ($cluster as $i) {
|
||||||
|
$sum += $values[$i];
|
||||||
|
}
|
||||||
|
$newCentroids[] = $sum / count($cluster);
|
||||||
|
} else {
|
||||||
|
// Jika cluster kosong, pilih centroid random
|
||||||
|
$newCentroids[] = $values[array_rand($values)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$iter++;
|
||||||
|
if ($newCentroids === $centroids) break;
|
||||||
|
if ($iter >= $maxIter) break;
|
||||||
|
$centroids = $newCentroids;
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
// Fungsi hitung rata-rata jarak
|
||||||
|
$avgDistance = function(int $i, array $cluster) use ($values): float {
|
||||||
|
if (count($cluster) <= 1) return 0;
|
||||||
|
$sum = 0;
|
||||||
|
foreach ($cluster as $idx) {
|
||||||
|
if ($idx == $i) continue;
|
||||||
|
$sum += abs($values[$i] - $values[$idx]);
|
||||||
|
}
|
||||||
|
return $sum / (count($cluster) - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hitung silhouette untuk setiap data
|
||||||
|
$silhouetteScores = [];
|
||||||
|
for ($i = 0; $i < count($values); $i++) {
|
||||||
|
// Cari klaster data ke-i
|
||||||
|
$clusterIdx = null;
|
||||||
|
foreach ($clusters as $cidx => $cluster) {
|
||||||
|
if (in_array($i, $cluster)) {
|
||||||
|
$clusterIdx = $cidx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$a = $avgDistance($i, $clusters[$clusterIdx]);
|
||||||
|
$b = INF;
|
||||||
|
foreach ($clusters as $cidx => $cluster) {
|
||||||
|
if ($cidx == $clusterIdx) continue;
|
||||||
|
$dist = $avgDistance($i, $cluster);
|
||||||
|
if ($dist < $b) $b = $dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
$silhouetteScores[] = ($b - $a) / max($a, $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
$silhouette = array_sum($silhouetteScores) / count($silhouetteScores);
|
||||||
|
|
||||||
|
// Bentuk output anggota klaster dengan kecamatan_id
|
||||||
|
$clusterMembers = [];
|
||||||
|
foreach ($clusters as $cidx => $cluster) {
|
||||||
|
$clusterMembers[$cidx] = [];
|
||||||
|
foreach ($cluster as $idx) {
|
||||||
|
$clusterMembers[$cidx][] = $ids[$idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$hasil = [
|
||||||
|
'silhouette' => $silhouette,
|
||||||
|
'jumlah_klaster' => $k,
|
||||||
|
'anggota_klaster' => $clusterMembers,
|
||||||
|
'iterasi' => $iter,
|
||||||
|
'centroid' => $centroids,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Simpan ke file JSON
|
||||||
|
file_put_contents(
|
||||||
|
storage_path('app/public/silhoute_kmeans_curas.json'),
|
||||||
|
json_encode($hasil, JSON_PRETTY_PRINT)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $hasil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,28 +19,28 @@ public function run(): void
|
||||||
|
|
||||||
// Data jumlah curanmor untuk setiap kecamatan
|
// Data jumlah curanmor untuk setiap kecamatan
|
||||||
$dataCuranmor= [
|
$dataCuranmor= [
|
||||||
1 => 5,
|
1 => 11,
|
||||||
2 => 4,
|
2 => 14,
|
||||||
3 => 2,
|
3 => 21,
|
||||||
4 => 22,
|
4 => 59,
|
||||||
5 => 4,
|
5 => 8,
|
||||||
6 => 18,
|
6 => 42,
|
||||||
7 => 0,
|
7 => 13,
|
||||||
8 => 37,
|
8 => 188,
|
||||||
9 => 9,
|
9 => 30,
|
||||||
10 => 3,
|
10 => 13,
|
||||||
11 => 2,
|
11 => 4,
|
||||||
12 => 13,
|
12 => 33,
|
||||||
13 => 1,
|
13 => 3,
|
||||||
14 => 21,
|
14 => 48,
|
||||||
15 => 14,
|
15 => 53,
|
||||||
16 => 4,
|
16 => 12,
|
||||||
17 => 10,
|
17 => 30,
|
||||||
18 => 0,
|
18 => 5,
|
||||||
19 => 1,
|
19 => 4,
|
||||||
20 => 10,
|
20 => 10,
|
||||||
21 => 1,
|
21 => 5,
|
||||||
22 => 2,
|
22 => 7,
|
||||||
23 => 15,
|
23 => 15,
|
||||||
24 => 4,
|
24 => 4,
|
||||||
];
|
];
|
||||||
|
|
|
@ -17,20 +17,20 @@ public function run(): void
|
||||||
|
|
||||||
$dataCuras = [
|
$dataCuras = [
|
||||||
1 => 0,
|
1 => 0,
|
||||||
2 => 0,
|
2 => 1,
|
||||||
3 => 0,
|
3 => 2,
|
||||||
4 => 0,
|
4 => 1,
|
||||||
5 => 1,
|
5 => 1,
|
||||||
6 => 1,
|
6 => 1,
|
||||||
7 => 0,
|
7 => 0,
|
||||||
8 => 0,
|
8 => 1,
|
||||||
9 => 0,
|
9 => 1,
|
||||||
10 => 0,
|
10 => 0,
|
||||||
11 => 0,
|
11 => 0,
|
||||||
12 => 0,
|
12 => 0,
|
||||||
13 => 0,
|
13 => 0,
|
||||||
14 => 0,
|
14 => 0,
|
||||||
15 => 0,
|
15 => 1,
|
||||||
16 => 0,
|
16 => 0,
|
||||||
17 => 0,
|
17 => 0,
|
||||||
18 => 0,
|
18 => 0,
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
|
@ -1434,6 +1434,47 @@ class="img-fluid rounded-circle"
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<div class="team-box">
|
||||||
|
<div class="team-img text-center">
|
||||||
|
<img
|
||||||
|
src="{{ asset('assets/assetLanding/images/team/zahra.jpg') }}"
|
||||||
|
class="img-fluid rounded-circle"
|
||||||
|
alt="image"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="team-detail">
|
||||||
|
<a class="team-plus" href="#"><i class="fas fa-plus"></i></a>
|
||||||
|
<div class="team-info">
|
||||||
|
<h6 class="mb-0 text-white">
|
||||||
|
<a href="">Fatimatuzzahra <br></a>
|
||||||
|
</h6>
|
||||||
|
<span class="mb-0 text-white text-gray iq-fw-4"
|
||||||
|
>ANGGOTA PENGUJI</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="team-hover">
|
||||||
|
<p>
|
||||||
|
Dosen Politeknik Negeri Jember Program Studi D4 Teknik Informatika. Berperan sebagai Anggota Penguji pada Web SIG Pemetaan Daerah Rawan Curas dan Curanmor
|
||||||
|
</p>
|
||||||
|
<ul class="list-inline">
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="#sectionOurTeam"><i class="fab fa-facebook-f"></i></a>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="#sectionOurTeam"><i class="fab fa-twitter"></i></a>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="#sectionOurTeam"><i class="fab fa-linkedin-in"></i></a>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<a href="#sectionOurTeam"><i class="fab fa-instagram"></i></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue