├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Collaborative │ ├── Base.php │ ├── EuclideanCollaborative.php │ ├── RankingCollaborative.php │ └── SlopeOneCollaborative.php ├── Configuration │ └── StandardKey.php ├── Creator │ └── CollaborativeCreator.php ├── Factories │ └── CollaborativeFactory.php ├── Interfaces │ └── CollaborativeInterface.php ├── Recommend.php └── Traits │ └── OperationTrait.php └── tests ├── DataArrayTrait.php └── RecommendTest.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ['tigoCaval'] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://www.paypal.com/donate?hosted_button_id=PPDESEV98R8KS', 'https://www.paypal.com/donate?hosted_button_id=S7FBV5N6ZTRXQ'] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tiago Ap Cavalcante Pereira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Recommendation algorithm 2 | 3 | Collaborative filtering recommendation system 4 | - Ranking algorithm using likes / dislikes or star-based rating 5 | - This package can be used in any PHP application or with any framework. 6 | - Download package: ```composer require tigo/recommendation``` 7 | - MIT license. ***Feel free to use this project***. ***Leave a star :star: or make a fork !*** 8 | 9 | *If you found this project useful, consider making a donation to support the developer.* 10 | 11 | [![paypal](https://www.paypalobjects.com/pt_BR/BR/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?hosted_button_id=S7FBV5N6ZTRXQ) 12 | | 13 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?hosted_button_id=PPDESEV98R8KS) 14 | 15 | [![](https://github.com/tigoCaval/images/blob/main/web/recommend.gif)](https://github.com/tigoCaval/recommendation-algorithm) 16 | 17 | ### Getting started 18 | Starting with composer 19 | 1. Install composer 20 | 2. Download package: ```composer require tigo/recommendation``` 21 | 3. PHP >= 7.0; Versions that have been tested: 7.2.25, 7.3.23 e 8.0.1. 22 | 23 | ```php 24 | //Somewhere in your project, you may need to use autoload 25 | include __DIR__ ."/vendor/autoload.php"; 26 | ``` 27 | ### Algorithms 28 | - ranking 29 | - euclidean 30 | - slope one 31 | ### Introduction 32 | Recommend a product using collaborative filtering 33 | ```php 34 | /** 35 | $table gets the array from the database. 36 | $user is the foreign key that represents the user who will receive the recommendation. 37 | **/ 38 | use Tigo\Recommendation\Recommend; // import class 39 | $client = new Recommend(); 40 | $client->ranking($table,$user); //optional third parameter refers to the score not accepted 41 | $client->euclidean($table,$user); //optional third parameter refers to the minimum accepted score 42 | $client->slopeOne($table, $user); //optional third parameter refers to the minimum accepted score 43 | ``` 44 | 45 | ### Configuration 46 | Sometimes, it may be necessary to rename the value of the constants (According to your database table). 47 | 48 | [![example](https://github.com/tigoCaval/images/blob/main/web/table_r1.jpg)](https://github.com/tigoCaval/recommendation-algorithm) 49 | 50 | - Configure: standard key (Directory: ```./src/configuration/StandardKey.php```) 51 | ```php 52 | const SCORE = 'score'; //score 53 | const PRODUCT_ID = 'product_id'; //Foreign key 54 | const USER_ID = 'user_id'; //Foreign key 55 | ``` 56 | ### Example 57 | A simple didactic demonstration of the algorithm 58 | ```php 59 | /** 60 | Example using "rating: liked and disliked" 61 | like: score = 1; dislike: score = 0 62 | **/ 63 | $table = [ 64 | ['product_id'=> 'A', 65 | 'score'=> 1, 66 | 'user_id'=> 'Pedro' 67 | ], 68 | ['product_id'=> 'B', 69 | 'score'=> 1, 70 | 'user_id'=> 'Pedro' 71 | ], 72 | ['product_id'=> 'A', 73 | 'score'=> 1, 74 | 'user_id'=> 'João' 75 | ], 76 | ['product_id'=> 'B', 77 | 'score'=> 1, 78 | 'user_id'=> 'João' 79 | ], 80 | ['product_id'=> 'C', 81 | 'score'=> 1, 82 | 'user_id'=> 'João' 83 | ] 84 | ]; 85 | use Tigo\Recommendation\Recommend; // import class 86 | $client = new Recommend(); 87 | print_r($client->ranking($table,"Pedro")); // result = ['C' => 2] 88 | print_r($client->ranking($table,"Pedro",1)); // result = []; 89 | 90 | print_r($client->euclidean($table,"Pedro")); // result = ['C' => 1] 91 | print_r($client->euclidean($table,"Pedro", 2)); // result = [] ; 92 | 93 | print_r($client->slopeOne($table,'Pedro')); // result = ['C' => 1] 94 | print_r($client->slopeOne($table,'Pedro', 2)); // result = [] 95 | ``` 96 | 97 | ### Supporting this project 98 | If you are interested in supporting this project, you can help in many ways. Leave a star :star: or make a donation of any value. 99 | 100 | ### Sponsor supporting this project 101 | - [] 102 | ### Contributors 103 | - [Tiago A C Pereira](https://github.com/tigoCaval) 104 | - [list of contributors](https://github.com/tigoCaval/recommendation-algorithm/graphs/contributors) 105 | ### License 106 | MIT license. See the archive [License](https://github.com/tigoCaval/recommendation-algorithm/blob/main/LICENSE) 107 | 108 | --- 109 | 110 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tigo/recommendation", 3 | "description": "collaborative filtering recommender systems", 4 | "license": "MIT", 5 | "keywords": [ 6 | "recommendation", 7 | "collaborative filtering", 8 | "euclidean distance", 9 | "slope one", 10 | "recommender system", 11 | "recommendation system", 12 | "recommendation algorithm", 13 | "recommender" 14 | ], 15 | "authors": [ 16 | { 17 | "name": "Tiago A C Pereira", 18 | "email": "tiagocavalcante57@gmail.com" 19 | } 20 | ], 21 | "require": { 22 | "php": ">=7.0" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Tigo\\Recommendation\\": "src/" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { "Tigo\\Recommendation\\Tests\\": "tests" } 31 | }, 32 | "require-dev": { 33 | "phpunit/phpunit": "^9.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | tests 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Collaborative/Base.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | abstract class Base extends StandardKey implements CollaborativeInterface 13 | { 14 | 15 | /** 16 | * User rated product. 17 | * @var array 18 | */ 19 | protected $product = []; 20 | 21 | /** 22 | * Product rated by other users. 23 | * @var array 24 | */ 25 | protected $other = []; 26 | 27 | /** 28 | * Get rated product. 29 | * @param array $table 30 | * @param mixed $user 31 | * 32 | * @return [type] 33 | */ 34 | protected function ratedProduct($table, $user) 35 | { 36 | foreach($table as $item){ 37 | $item[self::USER_ID] == $user ? $this->product[] = $item : $this->other[] = $item; 38 | } 39 | } 40 | 41 | 42 | /** 43 | * Get filter rating. 44 | * Remove product that the user has rated. 45 | * @param array $data 46 | * 47 | * @return array 48 | */ 49 | protected function filterRating($data) 50 | { 51 | $myRank = $data; 52 | $rank = $myRank; 53 | for($i = 0; $i < count($myRank); $i++){ 54 | foreach($this->product as $item){ 55 | if($item[self::PRODUCT_ID] == key($myRank)) 56 | unset($rank[key($myRank)]); // remove product 57 | } 58 | next($myRank); 59 | } 60 | arsort($rank); 61 | return $rank; 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /src/Collaborative/EuclideanCollaborative.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class EuclideanCollaborative extends Base 14 | { 15 | 16 | use OperationTrait; 17 | 18 | /** 19 | * Get recommend. 20 | * @param array $table 21 | * @param mixed $user 22 | * @param mixed $score 23 | * 24 | * @return array 25 | */ 26 | public function recommend($table, $user, $score = 0) 27 | { 28 | $data = $this->average($table, $user, $score); 29 | return $this->filterRating($data); 30 | } 31 | 32 | /** 33 | * Get users who rated the same product. 34 | * @param array $table 35 | * @param mixed $user 36 | * @param mixed $score 37 | * 38 | * @return array 39 | */ 40 | private function userRated($table, $user, $score) 41 | { 42 | $this->ratedProduct($table, $user); 43 | $rated = []; //get user rating 44 | foreach($this->product as $myProduct){ 45 | foreach($this->other as $item){ 46 | if($myProduct[self::PRODUCT_ID] == $item[self::PRODUCT_ID]){ 47 | if($myProduct[self::SCORE] >= $score && $item[self::SCORE] >= $score){ 48 | if(!in_array($item[self::USER_ID],$rated)) // check if user already exists 49 | $rated[] = $item[self::USER_ID]; //add user 50 | } 51 | } 52 | } 53 | } 54 | return $rated; 55 | } 56 | 57 | /** 58 | * Get operation|using part of the euclidean formula (p-q). 59 | * @param array $table 60 | * @param mixed $user 61 | * @param mixed $score 62 | * 63 | * @return array 64 | */ 65 | private function operation($table, $user, $score) 66 | { 67 | $rated = $this->userRated($table, $user, $score); 68 | $data = []; 69 | foreach ($this->product as $myProduct){ 70 | for($i = 0; $i < count($rated) ; $i++){ 71 | foreach($this->other as $itemOther){ 72 | if($itemOther[self::USER_ID] == $rated[$i] && 73 | $myProduct[self::PRODUCT_ID] == $itemOther[self::PRODUCT_ID] 74 | && $myProduct[self::SCORE] >= $score && $itemOther[self::SCORE] >= $score){ 75 | $data[$itemOther[self::USER_ID]][$myProduct[self::PRODUCT_ID]] = abs($itemOther[self::SCORE] - $myProduct[self::SCORE]); 76 | } 77 | } 78 | } 79 | } 80 | return $data; 81 | } 82 | 83 | /** 84 | * Using the metric distance formula and convert value to percentage. 85 | * @param array $table 86 | * @param mixed $user 87 | * @param mixed $score 88 | * 89 | * @return array 90 | */ 91 | private function metricDistance($table, $user, $score) 92 | { 93 | $data = $this->operation($table, $user, $score); 94 | $element = []; 95 | foreach($data as $item){ 96 | foreach($item as $value){ 97 | if(!isset($element[key($data)])) 98 | $element[key($data)] = 0; 99 | $element[key($data)] += pow($value,2); 100 | } 101 | $similarity = round(sqrt($element[key($data)]),2); //similarity rate 102 | $element[key($data)] = round(1/(1 + $similarity), 2); //convert value 103 | next($data); 104 | } 105 | return $element; 106 | } 107 | 108 | 109 | /** 110 | * Get weighted average. 111 | * @param array $table 112 | * @param mixed $user 113 | * @param mixed $score 114 | * 115 | * @return array 116 | */ 117 | private function average($table, $user, $score) 118 | { 119 | $metric = $this->metricDistance($table, $user, $score); 120 | $similarity = []; 121 | $element = []; 122 | foreach($metric as $itemMetric){ 123 | foreach($this->other as $itemOther){ 124 | if($itemOther[self::USER_ID] == key($metric) && $itemOther[self::SCORE] >= $score){ 125 | if(!isset($element[$itemOther[self::PRODUCT_ID]])){ 126 | $element[$itemOther[self::PRODUCT_ID]] = 0; 127 | $similarity[$itemOther[self::PRODUCT_ID]] = 0; 128 | } 129 | $element[$itemOther[self::PRODUCT_ID]] += ($itemMetric * $itemOther[self::SCORE]); 130 | $similarity[$itemOther[self::PRODUCT_ID]] += $itemMetric; 131 | } 132 | } 133 | next($metric); 134 | } 135 | return $this->division($element,$similarity); 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /src/Collaborative/RankingCollaborative.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class RankingCollaborative extends Base 16 | { 17 | 18 | /** 19 | * Get Recommend. 20 | * @param array $table 21 | * @param mixed $user 22 | * @param mixed $score 23 | * 24 | * @return array 25 | */ 26 | public function recommend($table, $user, $score = 0) 27 | { 28 | $data = $this->addRating($table, $user, $score); 29 | return $this->filterRating($data); 30 | } 31 | 32 | /** 33 | * Find similar users (Add weight score). 34 | * @param array $table 35 | * @param mixed $user 36 | * 37 | * @return array 38 | */ 39 | private function similarUser($table, $user) 40 | { 41 | $this->ratedProduct($table, $user); //get [product, other] 42 | $similar = []; //get users with similar tastes 43 | $rank = []; 44 | foreach($this->product as $myProduct){ 45 | foreach($this->other as $item){ 46 | if($myProduct[self::PRODUCT_ID] == $item[self::PRODUCT_ID]){ 47 | if($myProduct[self::SCORE] == $item[self::SCORE]){ 48 | if(!isset($similar[$item[self::USER_ID]])) 49 | $similar[$item[self::USER_ID]] = 0; // 50 | $similar[$item[self::USER_ID]] += 1; //assigning weight 51 | } 52 | } 53 | } 54 | } 55 | return $similar; 56 | } 57 | 58 | 59 | /** 60 | * Add Rating | Add a score (+value) for each recommended product. 61 | * @param array $table 62 | * @param mixed $user 63 | * @param mixed $score 64 | * 65 | * @return array 66 | */ 67 | private function addRating($table, $user, $score) 68 | { 69 | $similar = $this->similarUser($table, $user); 70 | $rank = []; 71 | foreach($this->other as $item){ 72 | foreach($similar as $value){ 73 | if($item[self::USER_ID] == key($similar) && $item[self::SCORE] > $score){ 74 | if(!isset($rank[$item[self::PRODUCT_ID]]) ) 75 | $rank[$item[self::PRODUCT_ID]] = 0; //assign value for calculation 76 | $rank[$item[self::PRODUCT_ID]] += $value; //add 77 | } 78 | next($similar); 79 | } 80 | reset($similar); 81 | } 82 | return $rank; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/Collaborative/SlopeOneCollaborative.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class SlopeOneCollaborative extends Base 12 | { 13 | 14 | /** 15 | * Get average weight. 16 | * @var array 17 | */ 18 | protected $weight = []; 19 | 20 | /** 21 | * Get items rated by other customers (difference matrix). 22 | * @var array 23 | */ 24 | protected $factor = []; 25 | 26 | /** 27 | * Get items rated by the client. 28 | * @var array 29 | */ 30 | protected $myProduct = []; 31 | 32 | /** 33 | * Get recommend. 34 | * @param array $table 35 | * @param mixed $user 36 | * @param mixed $score 37 | * 38 | * @return array 39 | */ 40 | public function recommend($table, $user, $score = 0) 41 | { 42 | return $this->filterRating($this->prediction($table, $user, $score)); 43 | } 44 | 45 | /** 46 | * Get customer rated product and update format. 47 | * @param array $table 48 | * @param mixed $user 49 | * 50 | * @return array 51 | */ 52 | private function update($table, $user) 53 | { 54 | $this->ratedProduct($table, $user); 55 | foreach($this->product as $item){ 56 | $this->myProduct[$item[self::USER_ID]][$item[self::PRODUCT_ID]] = $item[self::SCORE]; 57 | } 58 | foreach($this->other as $item){ 59 | $this->factor[$item[self::USER_ID]][$item[self::PRODUCT_ID]] = $item[self::SCORE]; 60 | } 61 | } 62 | 63 | /** 64 | * Get calculated items (f(x) = x + b). 65 | * @param array $table 66 | * @param mixed $user 67 | * 68 | * @return array 69 | */ 70 | private function calculateItems($table, $user) 71 | { 72 | $this->update($table, $user); 73 | $diff = $this->factor; 74 | $data = []; 75 | foreach($this->factor as $key1 => $item1){ 76 | foreach($item1 as $key2 => $value1){ 77 | foreach($diff as $key3 => $item2){ 78 | foreach($item2 as $key4 => $value2){ 79 | if(!isset($data[$key2][$key4]) && !isset($this->weight[$key2][$key4])){ 80 | $data[$key2][$key4] = 0; 81 | $this->weight[$key2][$key4] = 0; 82 | } 83 | if($key1 == $key3){ 84 | $data[$key2][$key4] += ($value1 - $value2); 85 | $this->weight[$key2][$key4] += 1; 86 | } 87 | } 88 | } 89 | } 90 | } 91 | return $data; 92 | } 93 | 94 | /** 95 | * Get average difference. 96 | * @param array $table 97 | * @param mixed $user 98 | * 99 | * @return array 100 | */ 101 | private function averageDifference($table, $user) 102 | { 103 | $component = $this->calculateItems($table, $user); 104 | $data = []; 105 | foreach($this->myProduct as $key1 => $item1){ 106 | foreach($item1 as $key2 => $value1){ 107 | foreach($component as $key3 => $item2){ 108 | foreach($item2 as $key4 => $value2){ 109 | if($key2 == $key4){ 110 | if(!isset($data[$key3][$key4])) 111 | $data[$key3][$key4] = 0; 112 | if($this->weight[$key3][$key4] > 0) 113 | $data[$key3][$key4] += ($value2/$this->weight[$key3][$key4])+$value1; 114 | } 115 | } 116 | } 117 | } 118 | } 119 | return $data; 120 | } 121 | 122 | /** 123 | * Get prediction. 124 | * @param array $table 125 | * @param mixed $user 126 | * @param mixed $score 127 | * 128 | * @return array 129 | */ 130 | private function prediction($table, $user, $score) 131 | { 132 | $component = $this->averageDifference($table, $user); 133 | $weight = []; 134 | $data = []; 135 | $result = []; 136 | foreach($component as $key1 => $item1){ 137 | foreach($item1 as $key2 => $value1){ 138 | if(!isset($weight[$key1]) && !isset($data[$key1]) ){ 139 | $weight[$key1] = 0; 140 | $data[$key1] = 0; 141 | } 142 | $weight[$key1] += $this->weight[$key1][$key2]; 143 | $data[$key1] += $value1 * $this->weight[$key1][$key2]; 144 | } 145 | } 146 | 147 | foreach($data as $key1 => $value){ 148 | $average = round($value/$weight[$key1],2); 149 | if($average >= $score) 150 | $result[$key1] = $average; 151 | } 152 | return $result; 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /src/Configuration/StandardKey.php: -------------------------------------------------------------------------------- 1 | factoryMethod($method, $table, $user, $score); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /src/Factories/CollaborativeFactory.php: -------------------------------------------------------------------------------- 1 | recommend($table, $user, $score); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Interfaces/CollaborativeInterface.php: -------------------------------------------------------------------------------- 1 | method = new CollaborativeFactory(); 23 | } 24 | 25 | /** 26 | * Get ranking | collaborative filtering algorithm. 27 | * @param array $table 28 | * @param mixed $user 29 | * @param mixed $score 30 | * 31 | * @return array 32 | */ 33 | public function ranking($table, $user, $score = 0) 34 | { 35 | return $this->method->doFactory(new RankingCollaborative(), $table, $user, $score); 36 | } 37 | 38 | /** 39 | * Get euclidean | collaborative filtering algorithm. 40 | * @param array $table 41 | * @param mixed $user 42 | * @param mixed $score 43 | * 44 | * @return array 45 | */ 46 | public function euclidean($table, $user, $score = 0) 47 | { 48 | return $this->method->doFactory(new EuclideanCollaborative(), $table, $user, $score); 49 | } 50 | 51 | /** 52 | * Get slope one | collaborative filtering algorithm. 53 | * @param array $table 54 | * @param mixed $user 55 | * @param mixed $score 56 | * 57 | * @return [type] 58 | */ 59 | public function slopeOne($table, $user, $score = 0) 60 | { 61 | return $this->method->doFactory(new SlopeOneCollaborative(), $table, $user, $score); 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /src/Traits/OperationTrait.php: -------------------------------------------------------------------------------- 1 | 10,'B'=>20]; $divisor = ['A'=>2,'B'=>10]; result ['A'=>5, 'B'=>2]. 15 | * @param array $dividend 16 | * @param array $divisor 17 | * 18 | * @return array 19 | */ 20 | public function division($dividend, $divisor) 21 | { 22 | $result = []; 23 | foreach($dividend as $item){ 24 | foreach($divisor as $div){ 25 | if(key($dividend) == key($divisor)){ 26 | if(!isset($result[key($dividend)])) 27 | $result[key($dividend)] = $item; 28 | $result[key($dividend)] = round($result[key($dividend)]/$div,2); 29 | } 30 | next($divisor); 31 | } 32 | reset($divisor); 33 | next($dividend); 34 | } 35 | arsort($result); 36 | return $result; 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /tests/DataArrayTrait.php: -------------------------------------------------------------------------------- 1 | 'A', 10 | 'score'=>1, 11 | 'user_id'=>'Pedro' 12 | ], 13 | [ 14 | 'product_id'=>'B', 15 | 'score'=>0, 16 | 'user_id'=>'Pedro' 17 | ], 18 | [ 19 | 'product_id'=>'A', 20 | 'score'=>0, 21 | 'user_id'=>'Maria' 22 | ], 23 | [ 24 | 'product_id'=>'B', 25 | 'score'=>1, 26 | 'user_id'=>'Maria' 27 | ], 28 | [ 29 | 'product_id'=>'C', 30 | 'score'=>1, 31 | 'user_id'=>'Joaquim' 32 | ], 33 | [ 34 | 'product_id'=>'A', 35 | 'score'=>1, 36 | 'user_id'=>'Joaquim' 37 | ], 38 | [ 39 | 'product_id'=>'A', 40 | 'score'=>1, 41 | 'user_id'=>'Beto' 42 | ], 43 | [ 44 | 'product_id'=>'B', 45 | 'score'=>0, 46 | 'user_id'=>'Luiz' 47 | ], 48 | [ 49 | 'product_id'=>'C', 50 | 'score'=>1, 51 | 'user_id'=>'Beto' 52 | ], 53 | [ 54 | 'product_id'=>'G', 55 | 'score'=>1, 56 | 'user_id'=>'Pedro' 57 | ], 58 | [ 59 | 'product_id'=>'A', 60 | 'score'=>1, 61 | 'user_id'=>'Rui' 62 | ], 63 | [ 64 | 'product_id'=>'B', 65 | 'score'=>1, 66 | 'user_id'=>'Beatriz' 67 | ], 68 | [ 69 | 'product_id'=>'C', 70 | 'score'=>0, 71 | 'user_id'=>'Rui' 72 | ], 73 | [ 74 | 'product_id'=>'G', 75 | 'score'=>1, 76 | 'user_id'=>'Maria' 77 | ], 78 | [ 79 | 'product_id'=>'F', 80 | 'score'=>1, 81 | 'user_id'=>'Beatriz' 82 | ], 83 | [ 84 | 'product_id'=>'B', 85 | 'score'=>0, 86 | 'user_id'=>'Joaquim' 87 | ], 88 | [ 89 | 'product_id'=>'F', 90 | 'score'=>1, 91 | 'user_id'=>'Pedro' 92 | ], 93 | [ 94 | 'product_id'=>'C', 95 | 'score'=>1, 96 | 'user_id'=>'Luana' 97 | ], 98 | [ 99 | 'product_id'=>'F', 100 | 'score'=>1, 101 | 'user_id'=>'Luana' 102 | ], 103 | [ 104 | 'product_id'=>'B', 105 | 'score'=>0, 106 | 'user_id'=>'Luana' 107 | ], 108 | [ 109 | 'product_id'=>'B', 110 | 'score'=>1, 111 | 'user_id'=>'Rui' 112 | ] 113 | ]; 114 | } -------------------------------------------------------------------------------- /tests/RecommendTest.php: -------------------------------------------------------------------------------- 1 | ranking($this->table,'Pedro'); 16 | $maria = $client->ranking($this->table,'Maria'); 17 | $joaquim = $client->ranking($this->table,'Joaquim'); 18 | $beto = $client->ranking($this->table,'Beto'); 19 | $luiz = $client->ranking($this->table,'Luiz'); 20 | $rui = $client->ranking($this->table,'Rui'); 21 | $beatriz = $client->ranking($this->table,'Beatriz'); 22 | $luana = $client->ranking($this->table,'Luana'); 23 | 24 | $this->assertEquals($pedro, ['C'=>5]); 25 | $this->assertEquals($maria, ['F'=>2]); 26 | $this->assertEquals($joaquim, ['F'=>4,'G'=>2]); 27 | $this->assertEquals($beto, ['F'=>2,'G'=>1,'B'=>1]); 28 | $this->assertEquals($luiz, ['A'=>2,'C'=>2,'F'=>2, 'G'=>1]); 29 | $this->assertEquals($rui, ['G'=>2,'F'=>2]); 30 | $this->assertEquals($beatriz, ['A'=>2,'G'=>2,'C'=>1]); 31 | $this->assertEquals($luana, ['A'=>5,'G'=>2]); 32 | } 33 | 34 | public function testEuclideanExpectedResult() 35 | { 36 | $client = new Recommend(); 37 | $pedro = $client->euclidean($this->table,'Pedro'); 38 | $maria = $client->euclidean($this->table,'Maria'); 39 | $joaquim = $client->euclidean($this->table,'Joaquim'); 40 | $beto = $client->euclidean($this->table,'Beto'); 41 | $luiz = $client->euclidean($this->table,'Luiz'); 42 | $rui = $client->euclidean($this->table,'Rui'); 43 | $beatriz = $client->euclidean($this->table,'Beatriz'); 44 | $luana = $client->euclidean($this->table,'Luana'); 45 | 46 | $this->assertEquals($pedro, ['C'=>0.86]); 47 | $this->assertEquals($maria, ['F'=>1,'C'=>0.74]); 48 | $this->assertEquals($joaquim, ['F'=>1,'G'=>1]); 49 | $this->assertEquals($beto, ['F'=>1,'G'=>1,'B'=>0.25]); 50 | $this->assertEquals($luiz, ['A'=>0.83,'C'=>0.8,'F'=>1, 'G'=>1]); 51 | $this->assertEquals($rui, ['G'=>1,'F'=>1]); 52 | $this->assertEquals($beatriz, ['A'=>0.67,'G'=>1,'C'=>0.5]); 53 | $this->assertEquals($luana, ['A'=>0.87,'G'=>1]); 54 | } 55 | 56 | public function testSlopeOneExpectedResult() 57 | { 58 | $client = new Recommend(); 59 | $pedro = $client->slopeOne($this->table,'Pedro'); 60 | $this->assertEquals($pedro,['C'=>0.57]); 61 | } 62 | 63 | 64 | } 65 | --------------------------------------------------------------------------------