概要
閲覧数:1087
投稿日: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 } }