Last active
July 10, 2016 18:55
-
-
Save 2no/c7b708d5527493287a421f40747a6ff9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Playground - noun: a place where people can play | |
import UIKit | |
let dataset = [ | |
"山田": [ | |
"カレー": 2.5, | |
"ラーメン": 3.5, | |
"チャーハン": 3.0, | |
"寿司": 3.5, | |
"牛丼": 2.5, | |
"うどん": 3.0, | |
], | |
"田中": [ | |
"カレー": 3.0, | |
"ラーメン": 3.5, | |
"チャーハン": 1.5, | |
"寿司": 5.0, | |
"うどん": 3.0, | |
"牛丼": 3.5, | |
], | |
"佐藤": [ | |
"カレー": 2.5, | |
"ラーメン": 3.0, | |
"寿司": 3.5, | |
"うどん": 4.0, | |
], | |
"中村": [ | |
"ラーメン": 3.5, | |
"チャーハン": 3.0, | |
"うどん": 4.5, | |
"寿司": 4.0, | |
"牛丼": 2.5, | |
], | |
"川村": [ | |
"カレー": 3.0, | |
"ラーメン": 4.0, | |
"チャーハン": 2.0, | |
"寿司": 3.0, | |
"うどん": 3.0, | |
"牛丼": 2.0, | |
], | |
"鈴木": [ | |
"カレー": 3.0, | |
"ラーメン": 4.0, | |
"うどん": 3.0, | |
"寿司": 5.0, | |
"牛丼": 3.5, | |
], | |
"下林": [ | |
"ラーメン": 4.5, | |
"牛丼": 1.0, | |
"寿司": 4.0, | |
] | |
] | |
func calcSimilarityScore(person1: String, person2: String) -> Double { | |
var bothViewed = [String: Int]() | |
for item in dataset[person1]! { | |
if dataset[person2]![item.0] != nil { | |
bothViewed[item.0] = 1 | |
} | |
} | |
if bothViewed.count == 0 { | |
return 0 | |
} | |
var sumOfEclideanDistance: [Double] = [] | |
for item in dataset[person1]! { | |
if dataset[person2]![item.0] != nil { | |
sumOfEclideanDistance.append( | |
pow(dataset[person1]![item.0]! - dataset[person2]![item.0]!, 2)) | |
} | |
} | |
let totalOfEclideanDistance = sumOfEclideanDistance.reduce(0, combine: +) | |
return 1 / (1 + sqrt(totalOfEclideanDistance)) | |
} | |
func calcPearsonCorrelation(person1: String, person2: String) -> Double { | |
var bothRated = [String: Int]() | |
for item in dataset[person1]! { | |
if dataset[person2]![item.0] != nil { | |
bothRated[item.0] = 1 | |
} | |
} | |
let numberOfRatings = Double(bothRated.count) | |
if numberOfRatings == 0 { | |
return 0 | |
} | |
var person1PreferencesSum = 0.0 | |
var person2PreferencesSum = 0.0 | |
var person1SquarePreferencesSum = 0.0 | |
var person2SquarePreferencesSum = 0.0 | |
var productSumOfBothUsers = 0.0 | |
for item in bothRated { | |
person1PreferencesSum += dataset[person1]![item.0]! | |
person2PreferencesSum += dataset[person2]![item.0]! | |
person1SquarePreferencesSum += pow(dataset[person1]![item.0]!, 2) | |
person2SquarePreferencesSum += pow(dataset[person2]![item.0]!, 2) | |
productSumOfBothUsers += dataset[person1]![item.0]! * dataset[person2]![item.0]! | |
} | |
let denominatorValue = sqrt((person1SquarePreferencesSum - pow(person1PreferencesSum, 2) / numberOfRatings) * (person2SquarePreferencesSum - pow(person2PreferencesSum, 2) / numberOfRatings)) | |
if denominatorValue == 0 { | |
return 0 | |
} | |
let numeratorValue = productSumOfBothUsers - (person1PreferencesSum * person2PreferencesSum / numberOfRatings) | |
return numeratorValue / denominatorValue | |
} | |
func getMostSimilarUsers(person: String, numberOfUsers: Int) -> [(String, Double)] { | |
var scores = [String: Double]() | |
for item in dataset { | |
if person != item.0 { | |
scores[item.0] = calcPearsonCorrelation(person, person2: item.0) | |
} | |
} | |
return Array(scores.sort{$0.1 > $1.1}[0..<numberOfUsers]) | |
} | |
func getUserRecommendations(person: String) -> [String] { | |
var totals = [String: Double]() | |
var simSums = [String: Double]() | |
for other in dataset { | |
if other.0 == person { | |
continue | |
} | |
let sim = calcPearsonCorrelation(person, person2: other.0) | |
if sim <= 0 { | |
continue | |
} | |
for item in dataset[other.0]! { | |
if dataset[person]![item.0] == nil || dataset[person]![item.0] == 0 { | |
if totals[item.0] == nil { | |
totals[item.0] = 0 | |
} | |
totals[item.0] = totals[item.0]! + item.1 * sim | |
if simSums[item.0] == nil { | |
simSums[item.0] = 0 | |
} | |
simSums[item.0] = simSums[item.0]! + sim | |
} | |
} | |
} | |
// 正規化されたリストを作成 | |
var rankings = [String: Double]() | |
for item in totals { | |
rankings[item.0] = item.1 / simSums[item.0]! | |
} | |
return rankings.sort{$0.1 > $1.1}.map{$0.0} | |
} | |
print("山田さんと鈴木さんの類似度 (ユークリッド距離)", calcSimilarityScore("山田", person2: "鈴木")) | |
print("山田さんと田中さんの類似度 (ピアソン相関係数)", calcPearsonCorrelation("山田", person2: "田中")) | |
dump(getMostSimilarUsers("山田", numberOfUsers: 3)) | |
dump(getUserRecommendations("下林")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
山田さんと鈴木さんの類似度 (ユークリッド距離) 0.340542426583167 | |
山田さんと田中さんの類似度 (ピアソン相関係数) 0.39605901719067 | |
▿ 3 elements | |
▿ [0]: (2 elements) | |
- .0: 下林 | |
- .1: 0.99124070716193 | |
▿ [1]: (2 elements) | |
- .0: 鈴木 | |
- .1: 0.747017880833996 | |
▿ [2]: (2 elements) | |
- .0: 川村 | |
- .1: 0.594088525786004 | |
▿ 3 elements | |
- [0]: うどん | |
- [1]: カレー | |
- [2]: チャーハン |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment