1<?php 2 3/** 4 * This file is part of PHP K-Means 5 * 6 * Copyright (c) 2014 Benjamin Delespierre 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a copy 9 * of this software and associated documentation files (the "Software"), to deal 10 * in the Software without restriction, including without limitation the rights 11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 * copies of the Software, and to permit persons to whom the Software is furnished 13 * to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included in all 16 * copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 * THE SOFTWARE. 25 */ 26 27namespace KMeans; 28 29class Cluster extends Point implements \IteratorAggregate, \Countable 30{ 31 public const INIT_RANDOM = 1; 32 public const INIT_KMEANS_PLUS_PLUS = 2; 33 34 protected $space; 35 protected $points; 36 37 public function __construct(Space $space, array $coordinates) 38 { 39 parent::__construct($space, $coordinates); 40 $this->points = new \SplObjectStorage(); 41 } 42 43 public function toArray(): array 44 { 45 $points = []; 46 foreach ($this->points as $point) { 47 $points[] = $point->toArray(); 48 } 49 50 return [ 51 'centroid' => parent::toArray(), 52 'points' => $points, 53 ]; 54 } 55 56 public function attach(Point $point): Point 57 { 58 if ($point instanceof self) { 59 throw new \LogicException("cannot attach a cluster to another"); 60 } 61 62 $this->points->attach($point); 63 return $point; 64 } 65 66 public function detach(Point $point): Point 67 { 68 $this->points->detach($point); 69 return $point; 70 } 71 72 public function attachAll(\SplObjectStorage $points): void 73 { 74 $this->points->addAll($points); 75 } 76 77 public function detachAll(\SplObjectStorage $points): void 78 { 79 $this->points->removeAll($points); 80 } 81 82 public function updateCentroid(): void 83 { 84 if (!$count = count($this->points)) { 85 return; 86 } 87 88 $centroid = $this->space->newPoint(array_fill(0, $this->dimention, 0)); 89 90 foreach ($this->points as $point) { 91 for ($n = 0; $n < $this->dimention; $n++) { 92 $centroid->coordinates[$n] += $point->coordinates[$n]; 93 } 94 } 95 96 for ($n = 0; $n < $this->dimention; $n++) { 97 $this->coordinates[$n] = $centroid->coordinates[$n] / $count; 98 } 99 } 100 101 public function getIterator(): \Iterator 102 { 103 return $this->points; 104 } 105 106 public function count(): int 107 { 108 return count($this->points); 109 } 110} 111