coletivo 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
= coletivo
|
2
2
|
|
3
|
-
|
3
|
+
A simple Rails 3 recommendations engine.
|
4
|
+
Coletivo uses {Euclidean Distance}[http://en.wikipedia.org/wiki/Euclidean_distance] or {Pearson's Correlation Coefficient}[http://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient] to calculate the similarity between persons and their preferences.
|
4
5
|
|
5
6
|
== Installation:
|
6
7
|
|
@@ -18,15 +19,21 @@ At your Rails model that represents a person (can be an _User_, _Member_, or som
|
|
18
19
|
# ...
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
+
So, a person can rate things:
|
23
|
+
|
22
24
|
current_user = User.create(:name => 'Diogenes')
|
23
25
|
movie = Movie.create(:name => 'The Tourist', :year => 2010)
|
24
26
|
|
25
27
|
current_user.rate!(movie, 4.5)
|
26
28
|
|
27
|
-
|
29
|
+
And after a lot of ratings... *recommendations*:
|
30
|
+
|
28
31
|
Movie.find_recommendations_for(current_user) # => movies and more movies...
|
29
32
|
|
33
|
+
By default, the similarity strategy used is Euclidean Distance, but you can change that passing the _strategy_ option:
|
34
|
+
Movie.find_recommendations_for(current_user, :strategy => :pearson)
|
35
|
+
|
36
|
+
|
30
37
|
== Contributing to coletivo
|
31
38
|
|
32
39
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
@@ -41,4 +48,3 @@ At your Rails model that represents a person (can be an _User_, _Member_, or som
|
|
41
48
|
|
42
49
|
Copyright (c) 2011 Diógenes Falcão. See LICENSE.txt for
|
43
50
|
further details.
|
44
|
-
|
@@ -7,57 +7,15 @@ module Coletivo
|
|
7
7
|
end
|
8
8
|
|
9
9
|
module ClassMethods
|
10
|
-
def find_recommendations_for(
|
11
|
-
sim_totals, weighted_means = {}, {}
|
10
|
+
def find_recommendations_for(person, options = {})
|
12
11
|
preferences = options[:preferences] ||=
|
13
|
-
load_preferences_for_recommendation(
|
12
|
+
load_preferences_for_recommendation(person)
|
13
|
+
top = predict_highest_ratings(person, preferences, options)
|
14
|
+
ids = top.collect(&:last)
|
14
15
|
|
15
|
-
|
16
|
-
next if other == model
|
17
|
-
|
18
|
-
sim = model.similarity_with(other, options)
|
19
|
-
next if sim <= 0
|
20
|
-
|
21
|
-
other_prefs.each do |item, weight|
|
22
|
-
unless preferences[model.id].keys.include?(item)
|
23
|
-
sim_totals[item] ||= 0
|
24
|
-
weighted_means[item] ||= 0
|
25
|
-
|
26
|
-
sim_totals[item] += sim
|
27
|
-
weighted_means[item] += weight * sim
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# DESC sort by weighted mean of ratings
|
33
|
-
# e.g: [[5.35, "movie_2"], [2.0, "movie_4"]]
|
34
|
-
top = weighted_means.collect { |i, mean| [mean/sim_totals[i], i] }\
|
35
|
-
.sort { |t_one, t_other| t_other <=> t_one }
|
36
|
-
|
37
|
-
ids = top.collect(&:last) # e.g: ['movie_2', 'movie_4']
|
38
|
-
models = where(:id => ids)
|
39
|
-
|
40
|
-
top.collect { |weight, item| models.detect {|m| m.id == item } }\
|
41
|
-
.compact
|
16
|
+
where(:id => ids).limit(options[:limit]).all
|
42
17
|
end
|
43
18
|
|
44
|
-
# Map a preferences Hash by a collection of ratings.
|
45
|
-
#
|
46
|
-
# Rating objects have a +person+, a +rateable+ object and a +weight+.
|
47
|
-
#
|
48
|
-
# The preferences can be mapped for _persons_, like that:
|
49
|
-
# {
|
50
|
-
# :person_1 => {:movie_1 => 2.0, :movie_2 => 5.0},
|
51
|
-
# :person_2 => {:movie_1 => 3.0, :movie_2 => 4.0}
|
52
|
-
# }
|
53
|
-
#
|
54
|
-
# Or for _rateable_ items, like that:
|
55
|
-
# {
|
56
|
-
# :movie_1 => {:person_1 => 2.0, :person_2 => 3.0},
|
57
|
-
# :movie_2 => {:person_1 => 5.0, :person_2 => 4.0}
|
58
|
-
# }
|
59
|
-
#
|
60
|
-
# Expected keys are :person or :rateable
|
61
19
|
def map_ratings_to_preferences(ratings)
|
62
20
|
#TODO: (???) Item based mapping.
|
63
21
|
key, subkey = :person_id, :rateable_id
|
@@ -71,12 +29,40 @@ module Coletivo
|
|
71
29
|
preferences
|
72
30
|
end
|
73
31
|
|
74
|
-
def load_preferences_for_recommendation(
|
32
|
+
def load_preferences_for_recommendation(person)
|
75
33
|
r = Coletivo::Config.ratings_container\
|
76
|
-
.find_for_recommendation(
|
34
|
+
.find_for_recommendation(person, self)
|
77
35
|
|
78
36
|
map_ratings_to_preferences(r)
|
79
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def predict_highest_ratings(person, people_preferences, options)
|
42
|
+
data = {}
|
43
|
+
people_preferences.each do |other, other_prefs|
|
44
|
+
next if other == person
|
45
|
+
|
46
|
+
sim = person.similarity_with(other, options)
|
47
|
+
next if sim <= 0
|
48
|
+
|
49
|
+
other_prefs.each do |item, weight|
|
50
|
+
unless people_preferences[person.id].keys.include?(item)
|
51
|
+
data[item] ||= {:total_similarity => 0.0, :weighted_mean => 0.0}
|
52
|
+
data[item][:total_similarity] += sim
|
53
|
+
data[item][:weighted_mean] += weight * sim
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# e.g: [[5.35, "movie_2"], [2.0, "movie_4"]]
|
59
|
+
guessed_rating_and_id = lambda { |item, item_data|
|
60
|
+
[item_data[:weighted_mean] / item_data[:total_similarity], item]
|
61
|
+
}
|
62
|
+
|
63
|
+
# DESC sorting by weighted mean of ratings
|
64
|
+
data.collect(&guessed_rating_and_id).sort_by(&:first).reverse
|
65
|
+
end
|
80
66
|
end
|
81
67
|
|
82
68
|
module InstanceMethods
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coletivo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "Di\xC3\xB3genes Falc\xC3\xA3o"
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-09-20 00:00:00 -03:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|