Skip to content

Commit 6e6581b

Browse files
committed
update v0.0.2
1 parent 7fc14fc commit 6e6581b

4 files changed

Lines changed: 178 additions & 1 deletion

File tree

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"recommendation",
77
"collaborative filtering",
88
"euclidean distance",
9+
"slope one",
910
"recommender system",
1011
"recommendation system",
1112
"recommendation algorithm",
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
namespace Tigo\Recommendation\Collaborative;
3+
4+
use Tigo\Recommendation\Collaborative\Base;
5+
6+
/**
7+
* Slope One algorithm
8+
*
9+
* @author Tiago A C Pereira <tiagocavalcante57@gmail.com>
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/Recommend.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use Tigo\Recommendation\Collaborative\EuclideanCollaborative;
55
use Tigo\Recommendation\Collaborative\RankingCollaborative;
6+
use Tigo\Recommendation\Collaborative\SlopeOneCollaborative;
67
use Tigo\Recommendation\Factories\CollaborativeFactory;
78

89
/**
@@ -46,5 +47,18 @@ public function euclidean($table, $user, $score = 0)
4647
{
4748
return $this->method->doFactory(new EuclideanCollaborative(), $table, $user, $score);
4849
}
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+
5064
}

tests/RecommendTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,12 @@ public function testEuclideanExpectedResult()
5353
$this->assertEquals($luana, ['A'=>0.87,'G'=>1]);
5454
}
5555

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+
5663

5764
}

0 commit comments

Comments
 (0)