-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnearest_neighbor.rb
More file actions
70 lines (64 loc) · 2.09 KB
/
Copy pathnearest_neighbor.rb
File metadata and controls
70 lines (64 loc) · 2.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
require 'gsl'
require 'sequel'
DB = Sequel.sqlite('MAL.db')
ALPHA = 2
BETA = 50
GAMMA = 0.015
K = 25
MIN_USERS = 40
def userRatings(userId)
ratingsDS = DB[:ratings]
ratings = {}
ratingsDS.filter(:user_id => userId).each do |rating|
ratings[rating[:anime_id]] = rating[:score]
end
return ratings
end
def regularize(correlation, numUsers, alpha, beta)
((numUsers * correlation) / (numUsers + beta)) ** alpha
end
def getNeighbors(ratings, animeId, min_users, k)
# grab all the correlations for animdId
correlationsDS = DB[:correlations]
filterString = "anime1_id = #{animeId} AND num_users > #{min_users}"
filteredCorrelationsDS = correlationsDS.filter(filterString)
if filteredCorrelationsDS.empty?
return nil
end
correlations = filteredCorrelationsDS.map {|r| r}
# keep animes seen by user
correlations.select! {|correlation| ratings.key? correlation[:anime2_id]}
onlyCorrelations = correlations.map {|correlation| correlation[:correlation]}
num = [k, correlations.size].min
if num <= 0
return nil
end
# keep largest num indices
neighborIndices = GSL::Vector.alloc(onlyCorrelations).sort_largest_index(num)
neighbors = Array.new
for i in 0...neighborIndices.size
index = neighborIndices.get i
correlationData = correlations[index]
correlation = correlationData[:correlation]
numUsers = correlationData[:num_users]
otherAnimeId = correlationData[:anime2_id]
neighbors << {:correlation => correlation, :numUsers => numUsers, :animeId => otherAnimeId}
end
return neighbors
end
def predict(userId, animeId, alpha, beta, gamma, k, min_users)
ratings = userRatings(userId)
neighbors = getNeighbors(ratings, animeId, min_users, k)
if neighbors.nil?
return -1
end
totalCorrelationSum = 0
subPrediction = neighbors.map do |neighbor|
regularizedCorrelation = regularize(neighbor[:correlation], neighbor[:numUsers], alpha, beta)
totalCorrelationSum += regularizedCorrelation
regularizedCorrelation * ratings[neighbor[:animeId]]
end
return subPrediction.reduce(:+) / (gamma + totalCorrelationSum)
end
def recommend()
end