Given a random set of numbers, the problem to solve here is to determine
a fixed number of intervals that best describe the distribution of the
initial dataset. Instead of trying to identify how many clusters exists
in the dataset (also possible, but outside the scope of this article), a
K-means clustering algorithm will always return a fixed (K) number of
subsets.
print_r(kmeans(array(1, 3, 2, 5, 6, 2, 3, 1, 30, 36, 45, 3, 15, 17), 3));
function kmeans($data, $k)
{
$cPositions = assign_initial_positions($data, $k);
$clusters = array();
while(true)
{
$changes = kmeans_clustering($data, $cPositions, $clusters);
if(!$changes)
{
return kmeans_get_cluster_values($clusters, $data);
}
$cPositions = kmeans_recalculate_cpositions($cPositions, $data, $clusters);
}
}
function kmeans_clustering($data, $cPositions, &$clusters)
{
$nChanges = 0;
foreach($data as $dataKey => $value)
{
$minDistance = null;
$cluster = null;
foreach($cPositions as $k => $position)
{
$distance = distance($value, $position);
if(is_null($minDistance) || $minDistance > $distance)
{
$minDistance = $distance;
$cluster = $k;
}
}
if(!isset($clusters[$dataKey]) || $clusters[$dataKey] != $cluster)
{
$nChanges++;
}
$clusters[$dataKey] = $cluster;
}
return $nChanges;
}
function kmeans_recalculate_cpositions($cPositions, $data, $clusters)
{
$kValues = kmeans_get_cluster_values($clusters, $data);
foreach($cPositions as $k => $position)
{
$cPositions[$k] = empty($kValues[$k]) ? 0 : kmeans_avg($kValues[$k]);
}
return $cPositions;
}
function kmeans_get_cluster_values($clusters, $data)
{
$values = array();
foreach($clusters as $dataKey => $cluster)
{
$values[$cluster][] = $data[$dataKey];
}
return $values;
}
function kmeans_avg($values)
{
$n = count($values);
$sum = array_sum($values);
return ($n == 0) ? 0 : $sum / $n;
}
function distance($v1, $v2)
{
return abs($v1-$v2);
}
function assign_initial_positions($data, $k)
{
$min = min($data);
$max = max($data);
$int = ceil(abs($max - $min) / $k);
while($k-- > 0)
{
$cPositions[$k] = $min + $int * $k;
}
return $cPositions;
}
Output
Array
(
[0] => Array
(
[0] => 1
[1] => 3
[2] => 2
[3] => 5
[4] => 6
[5] => 2
[6] => 3
[7] => 1
[8] => 3
)
[2] => Array
(
[0] => 30
[1] => 36
[2] => 45
)
[1] => Array
(
[0] => 15
[1] => 17
)
)
No comments:
Post a Comment