Skip to content

Instantly share code, notes, and snippets.

@2no
Last active July 10, 2016 18:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 2no/c7b708d5527493287a421f40747a6ff9 to your computer and use it in GitHub Desktop.
Save 2no/c7b708d5527493287a421f40747a6ff9 to your computer and use it in GitHub Desktop.
//: 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("下林"))
山田さんと鈴木さんの類似度 (ユークリッド距離) 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