概要
閲覧数:1330
投稿日:2016-01-04
更新日:2016-01-04
書籍からの変更点
・sim_distance関数の最後の行
return 1/(1 + sum_of_squares)
↓
return 1/(1 + sqrt(sum_of_squares))
書籍と結果が異なる理由
・sqrtを追加しているため
リンク先からの変更点
・'Michael Phillips' の 'The Night Listener' 評価を 3.0 から 4.0 へ変更
コード
$critics = array(
'Lisa Rose' => array(
'Lady in the Water' => 2.5,
'Snakes on a Plane' => 3.5,
'Just My Luck' => 3.0,
'Superman Returns' => 3.5,
'You, Me and Dupree' => 2.5,
'The Night Listener' => 3.0,
),
'Gene Seymour' => array(
'Lady in the Water' => 3.0,
'Snakes on a Plane' => 3.5,
'Just My Luck' => 1.5,
'Superman Returns' => 5.0,
'You, Me and Dupree' => 3.5,
'The Night Listener' => 3.0,
),
'Michael Phillips' => array(
'Lady in the Water' => 2.5,
'Snakes on a Plane' => 3.0,
'Superman Returns' => 3.5,
'The Night Listener' => 4.0,
),
'Claudia Puig' => array(
'Snakes on a Plane' => 3.5,
'Just My Luck' => 3.0,
'Superman Returns' => 4.0,
'You, Me and Dupree' => 2.5,
'The Night Listener' => 4.5,
),
'Mick LaSalle' => array(
'Lady in the Water' => 3.0,
'Snakes on a Plane' => 4.0,
'Just My Luck' => 2.0,
'Superman Returns' => 3.0,
'You, Me and Dupree' => 2.0,
'The Night Listener' => 3.0,
),
'Jack Matthews' => array(
'Lady in the Water' => 3.0,
'Snakes on a Plane' => 4.0,
'Superman Returns' => 5.0,
'You, Me and Dupree' => 3.5,
'The Night Listener' => 3.0,
),
'Toby' => array(
'Snakes on a Plane' => 4.5,
'Superman Returns' => 4.0,
'You, Me and Dupree' => 1.0,
),
);
// person1とperson2の距離を基にした類似性スコアを返す
//返り値は0-1の範囲で1に近いほど類似性がある
function sim_distance($prefs, $person1, $person2){
$si = array();
//二人とも評価しているアイテムのリストを得る
foreach($prefs["$person1"] as $item => $val){
if(isset($prefs["$person2"]["$item"])){
$si["$item"] = 1;
}
}
if(count($si) == 0){ return 0;}
//すべての差の平方を足し合わせる
//上のループでできる
$sum_of_squares = 0;
foreach($prefs["$person1"] as $item => $val){
if(isset($prefs["$person2"]["$item"])){
$sum_of_squares += pow( ( $prefs["$person1"]["$item"] - $prefs["$person2"]["$item"]),2); }
}
return 1/(1 + sqrt($sum_of_squares));
}
//P1とP2のピアソン相関係数を返す。
function sim_pearson($prefs,$p1,$p2){
//両者が互いに評価しているアイテムのリストを取得
$si = array();
foreach($prefs["$p1"] as $item => $val){
if(isset($prefs["$p2"]["$item"])){
$si["$item"] = 1;
}
}
//要素の数を調べる。
$n = count($si);
//すべての嗜好,平方,積を合計する
//嗜好の合計
$sum1 = 0;
$sum2 = 0;
//平方の合計
$sum1Sq = 0;
$sum2Sq = 0;
//積の合計
$pSum = 0;
foreach($si as $item => $val){
$sum1 += $prefs["$p1"]["$item"];
$sum2 += $prefs["$p2"]["$item"];
$sum1Sq += pow($prefs["$p1"]["$item"],2);
$sum2Sq += pow($prefs["$p2"]["$item"],2);
$pSum += $prefs["$p1"]["$item"] * $prefs["$p2"]["$item"];
}
//ピアソンスコアを計算する
$num = $pSum - ($sum1 * $sum2 / $n);
$den = sqrt(($sum1Sq - pow($sum1,2) / $n) * ($sum2Sq - pow($sum2,2) / $n));
if($den == 0){ return 0;}
$r = $num / $den;
return $r;
}
//ディクショナリprefsからpersonにもっともマッチするものたちを返す
//結果の数と類似性関数はオプションのパラメータ
function topMatches($prefs, $person, $n=5, $similarity="sim_pearson"){
$scores = array();
foreach($prefs as $other => $list){
if($other != $person){
switch($similarity){
case "sim_distance":
$scores[] = array(sim_distance($prefs,$person,$other),$other);
break;
case "sim_pearson":
$scores[] = array(sim_pearson($prefs,$person,$other),$other);
break;
}
}
}
sort($scores);
rsort($scores);
$res = array();
for($i = 0;$i < $n;$i++){
$res[] = array_shift($scores);
}
return $res;
}
function transformPrefs($prefs){
$result = array();
foreach($prefs as $person => $items){
foreach($prefs["$person"] as $item => $scor){
$result["$item"]["$person"] = $prefs["$person"]["$item"];
}
}
return $result;
}
function calculateSimilarItems($prefs, $n=10){
//アイテムをキーとしてもち、それぞれのアイテムに似ている
//アイテムのリストを値として持つハッシュの作成
$result = array();
//嗜好の行列をアイテム中心な形に反転させる
$itemPrefs = transformPrefs($prefs);
$c = 0;
foreach ($itemPrefs as $item => $arr){
//巨大なデータセット用にステータスを表示
$c +=1;
if (($c % 100) == 0){
printf("%d / %d",$c,count($itemPrefs));
}
$scores = topMatches($itemPrefs, $item, $n,'sim_distance');
$result["$item"] = $scores;
}
return $result;
}
var_dump(calculateSimilarItems($critics));
結果
array(6) {
["Lady in the Water"]=>
array(10) {
[0]=>
array(2) {
[0]=>
float(0.44948974278318)
[1]=>
string(18) "You, Me and Dupree"
}
[1]=>
array(2) {
[0]=>
float(0.38742588672279)
[1]=>
string(18) "The Night Listener"
}
[2]=>
array(2) {
[0]=>
float(0.34833147735479)
[1]=>
string(17) "Snakes on a Plane"
}
[3]=>
array(2) {
[0]=>
float(0.34833147735479)
[1]=>
string(12) "Just My Luck"
}
[4]=>
array(2) {
[0]=>
float(0.24025307335204)
[1]=>
string(16) "Superman Returns"
}
[5]=>
NULL
[6]=>
NULL
[7]=>
NULL
[8]=>
NULL
[9]=>
NULL
}
["Snakes on a Plane"]=>
array(10) {
[0]=>
array(2) {
[0]=>
float(0.34833147735479)
[1]=>
string(17) "Lady in the Water"
}
[1]=>
array(2) {
[0]=>
float(0.32037724101704)
[1]=>
string(18) "The Night Listener"
}
[2]=>
array(2) {
[0]=>
float(0.30901699437495)
[1]=>
string(16) "Superman Returns"
}
[3]=>
array(2) {
[0]=>
float(0.25539679298969)
[1]=>
string(12) "Just My Luck"
}
[4]=>
array(2) {
[0]=>
float(0.18863786477265)
[1]=>
string(18) "You, Me and Dupree"
}
[5]=>
NULL
[6]=>
NULL
[7]=>
NULL
[8]=>
NULL
[9]=>
NULL
}
["Just My Luck"]=>
array(10) {
[0]=>
array(2) {
[0]=>
float(0.34833147735479)
[1]=>
string(17) "Lady in the Water"
}
[1]=>
array(2) {
[0]=>
float(0.32037724101704)
[1]=>
string(18) "You, Me and Dupree"
}
[2]=>
array(2) {
[0]=>
float(0.29893508442483)
[1]=>
string(18) "The Night Listener"
}
[3]=>
array(2) {
[0]=>
float(0.25539679298969)
[1]=>
string(17) "Snakes on a Plane"
}
[4]=>
array(2) {
[0]=>
float(0.20799159651348)
[1]=>
string(16) "Superman Returns"
}
[5]=>
NULL
[6]=>
NULL
[7]=>
NULL
[8]=>
NULL
[9]=>
NULL
}
["Superman Returns"]=>
array(10) {
[0]=>
array(2) {
[0]=>
float(0.30901699437495)
[1]=>
string(17) "Snakes on a Plane"
}
[1]=>
array(2) {
[0]=>
float(0.25265030858707)
[1]=>
string(18) "The Night Listener"
}
[2]=>
array(2) {
[0]=>
float(0.24025307335204)
[1]=>
string(17) "Lady in the Water"
}
[3]=>
array(2) {
[0]=>
float(0.20799159651348)
[1]=>
string(12) "Just My Luck"
}
[4]=>
array(2) {
[0]=>
float(0.19182536636347)
[1]=>
string(18) "You, Me and Dupree"
}
[5]=>
NULL
[6]=>
NULL
[7]=>
NULL
[8]=>
NULL
[9]=>
NULL
}
["You, Me and Dupree"]=>
array(10) {
[0]=>
array(2) {
[0]=>
float(0.44948974278318)
[1]=>
string(17) "Lady in the Water"
}
[1]=>
array(2) {
[0]=>
float(0.32037724101704)
[1]=>
string(12) "Just My Luck"
}
[2]=>
array(2) {
[0]=>
float(0.29429805508555)
[1]=>
string(18) "The Night Listener"
}
[3]=>
array(2) {
[0]=>
float(0.19182536636347)
[1]=>
string(16) "Superman Returns"
}
[4]=>
array(2) {
[0]=>
float(0.18863786477265)
[1]=>
string(17) "Snakes on a Plane"
}
[5]=>
NULL
[6]=>
NULL
[7]=>
NULL
[8]=>
NULL
[9]=>
NULL
}
["The Night Listener"]=>
array(10) {
[0]=>
array(2) {
[0]=>
float(0.38742588672279)
[1]=>
string(17) "Lady in the Water"
}
[1]=>
array(2) {
[0]=>
float(0.32037724101704)
[1]=>
string(17) "Snakes on a Plane"
}
[2]=>
array(2) {
[0]=>
float(0.29893508442483)
[1]=>
string(12) "Just My Luck"
}
[3]=>
array(2) {
[0]=>
float(0.29429805508555)
[1]=>
string(18) "You, Me and Dupree"
}
[4]=>
array(2) {
[0]=>
float(0.25265030858707)
[1]=>
string(16) "Superman Returns"
}
[5]=>
NULL
[6]=>
NULL
[7]=>
NULL
[8]=>
NULL
[9]=>
NULL
}
}