recommendation 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
@@ -0,0 +1,5 @@
1
+ === 0.1.3 / 2013-10-18
2
+
3
+ * First release.
4
+
5
+
@@ -0,0 +1,8 @@
1
+ You can redistribute it and/or modify it under either the terms of the GPL
2
+ version 3, or LGPL version 3 (Dual License).
3
+
4
+ See the file doc/COPYING or doc/COPYING.LESSER.
5
+
6
+ Copyright (c) 774 All Rights Reserved.
7
+ Web: http://id774.net
8
+ E-Mail: 774@id774.net
@@ -0,0 +1,16 @@
1
+ recommendation
2
+
3
+ Name
4
+ Collaborative filtering for recommender system
5
+
6
+ Syntax
7
+ require 'recommendation'
8
+
9
+ Description
10
+ http://en.wikipedia.org/wiki/Recommender_system
11
+
12
+ Installation
13
+ $ gem install recommendation
14
+
15
+ Tutorial
16
+ See spec files.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ module Recommendation
5
+ VERSION = "0.1.3"
6
+ require File.dirname(__FILE__) + "/recommendation/supervisor"
7
+ require File.dirname(__FILE__) + "/recommendation/engine"
8
+ end
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ module Recommendation
5
+ class Engine
6
+ def recommendation(table, user, similarity=:sim_pearson)
7
+ totals_h = Hash.new(0)
8
+ sim_sums_h = Hash.new(0)
9
+ table.each do |other, val|
10
+ next if other == user
11
+ sim = __send__(similarity, table, user, other)
12
+ next if sim <= 0
13
+ table[other].each do |item, val|
14
+ if !table[user].keys.include?(item) || table[user][item] == 0
15
+ totals_h[item] += table[other][item]*sim
16
+ sim_sums_h[item] += sim
17
+ end
18
+ end
19
+ end
20
+
21
+ rankings = Hash.new
22
+ totals_h.each do |item, total|
23
+ rankings[item] = total/sim_sums_h[item]
24
+ end
25
+
26
+ Hash[rankings.sort_by{|k, v| -v}]
27
+ end
28
+
29
+ def top_matches(table, user, n=5, similarity=:sim_pearson)
30
+ scores = Array.new
31
+ table.each do |key, value|
32
+ if key != user
33
+ scores << [__send__(similarity, table, user,key), key]
34
+ end
35
+ end
36
+ result = Hash.new
37
+ scores.sort.reverse[0,n].each do |k, v|
38
+ result[v] = k
39
+ end
40
+ result
41
+ end
42
+
43
+ private
44
+
45
+ def sim_pearson(table, user1, user2)
46
+ shared_items_a = shared_items_a(table, user1, user2)
47
+
48
+ n = shared_items_a.size
49
+ return 0 if n == 0
50
+
51
+ sum1 = shared_items_a.inject(0) {|result, si|
52
+ result + table[user1][si]
53
+ }
54
+ sum2 = shared_items_a.inject(0) {|result, si|
55
+ result + table[user2][si]
56
+ }
57
+
58
+ sum1_sq = shared_items_a.inject(0) {|result, si|
59
+ result + table[user1][si]**2
60
+ }
61
+ sum2_sq = shared_items_a.inject(0) {|result, si|
62
+ result + table[user2][si]**2
63
+ }
64
+
65
+ sum_products = shared_items_a.inject(0) {|result, si|
66
+ result + table[user1][si]*table[user2][si]
67
+ }
68
+
69
+ num = sum_products - (sum1*sum2/n)
70
+ den = Math.sqrt((sum1_sq - sum1**2/n)*(sum2_sq - sum2**2/n))
71
+ return 0 if den == 0
72
+ return num/den
73
+ end
74
+
75
+ def shared_items(table, user1, user2)
76
+ shared_items_h = Hash.new
77
+ table[user1].each do |k, v|
78
+ shared_items_h[k] = 1 if table[user2].include?(k)
79
+ end
80
+ shared_items_h
81
+ end
82
+
83
+ def shared_items_a(table, user1, user2)
84
+ table[user1].nil? ? [] : table[user1].keys & table[user2].keys
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ module Recommendation
5
+ class Supervisor
6
+ def initialize(params = {})
7
+ @table = params
8
+ end
9
+
10
+ def table
11
+ @table
12
+ end
13
+
14
+ def train(params = {})
15
+ @table.merge!(params)
16
+ end
17
+
18
+ def transform_table
19
+ new_table = {}
20
+ @table.each do |key, value|
21
+ value.each do |new_key, new_value|
22
+ new_table[new_key] ||= Hash.new
23
+ new_table[new_key][key] = new_value
24
+ end
25
+ end
26
+ new_table
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,66 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: recommendation 0.1.3 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "recommendation"
9
+ s.version = "0.1.3"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.authors = ["id774"]
13
+ s.date = "2013-10-18"
14
+ s.description = "Collaborative filtering for recommender system"
15
+ s.email = "idnanashi@gmail.com"
16
+ s.extra_rdoc_files = [
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ "Gemfile",
21
+ "README.md",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "demo.rb",
25
+ "doc/AUTHORS",
26
+ "doc/COPYING",
27
+ "doc/COPYING.LESSER",
28
+ "doc/ChangeLog",
29
+ "doc/LICENSE",
30
+ "doc/README",
31
+ "lib/recommendation.rb",
32
+ "lib/recommendation/engine.rb",
33
+ "lib/recommendation/supervisor.rb",
34
+ "recommendation.gemspec",
35
+ "script/build",
36
+ "spec/lib/recommendation/engine_spec.rb",
37
+ "spec/lib/recommendation/supervisor_spec.rb",
38
+ "spec/lib/recommendation_spec.rb",
39
+ "spec/spec_helper.rb",
40
+ "vendor/.gitkeep"
41
+ ]
42
+ s.homepage = "http://github.com/id774/recommendation"
43
+ s.licenses = ["GPL"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = "2.1.5"
46
+ s.summary = "recommendation"
47
+
48
+ if s.respond_to? :specification_version then
49
+ s.specification_version = 4
50
+
51
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
52
+ s.add_development_dependency(%q<cucumber>, [">= 0"])
53
+ s.add_development_dependency(%q<bundler>, [">= 0"])
54
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
55
+ else
56
+ s.add_dependency(%q<cucumber>, [">= 0"])
57
+ s.add_dependency(%q<bundler>, [">= 0"])
58
+ s.add_dependency(%q<jeweler>, [">= 0"])
59
+ end
60
+ else
61
+ s.add_dependency(%q<cucumber>, [">= 0"])
62
+ s.add_dependency(%q<bundler>, [">= 0"])
63
+ s.add_dependency(%q<jeweler>, [">= 0"])
64
+ end
65
+ end
66
+
@@ -0,0 +1,32 @@
1
+ #!/bin/sh
2
+ #
3
+ ########################################################################
4
+ # Integration Build Script
5
+ #
6
+ # Maintainer: id774 <idnanashi@gmail.com>
7
+ #
8
+ # v1.2 4/17,2013
9
+ # Using simplecov for coverage.
10
+ # v1.1 3/14,2013
11
+ # Show ruby version.
12
+ # v1.0 3/16,2012
13
+ # First.
14
+ ########################################################################
15
+
16
+ kickstart() {
17
+ export RACK_ROOT="."
18
+ export RACK_ENV="test"
19
+ ruby -v
20
+ }
21
+
22
+ run_tests() {
23
+ rake simplecov
24
+ }
25
+
26
+ main() {
27
+ kickstart
28
+ run_tests
29
+ }
30
+
31
+ set -ex
32
+ main
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require File.dirname(__FILE__) + '/../../spec_helper'
5
+
6
+ describe 'Recommendation::Engine' do
7
+ describe 'recommendation' do
8
+ it 'should be suggesting interesting products' do
9
+ expected = {"The Night Listener"=>3.3477895267131017, "Lady in the Water"=>2.8325499182641614, "Just My Luck"=>2.530980703765565}
10
+
11
+
12
+ supervisor = Recommendation::Supervisor.new(visitors)
13
+ supervisor.train(new_comer)
14
+ engine = Recommendation::Engine.new
15
+
16
+ new_comer.keys[0].should be_eql 'Toby'
17
+ engine.recommendation(supervisor.table, new_comer.keys[0]).should be_eql expected
18
+ end
19
+ end
20
+
21
+ describe 'top_matches' do
22
+ it 'should be finding similar users' do
23
+ expected ={"Lisa Rose"=>0.9912407071619299, "Mick LaSalle"=>0.9244734516419049, "Claudia Puig"=>0.8934051474415647, "Jack Matthews"=>0.66284898035987, "Gene Seymour"=>0.38124642583151164}
24
+
25
+ supervisor = Recommendation::Supervisor.new(visitors)
26
+ supervisor.train(new_comer)
27
+ engine = Recommendation::Engine.new
28
+
29
+ new_comer.keys[0].should be_eql 'Toby'
30
+ engine.top_matches(supervisor.table, new_comer.keys[0]).should be_eql expected
31
+ end
32
+ end
33
+
34
+ describe 'transform_table ' do
35
+ it 'should return reversed critics' do
36
+ supervisor = Recommendation::Supervisor.new(visitors)
37
+ supervisor.train(new_comer)
38
+ engine = Recommendation::Engine.new
39
+
40
+ movies = supervisor.transform_table
41
+ movies.should be_eql reversed_critics
42
+ end
43
+ end
44
+
45
+ describe 'reversed critics' do
46
+ it 'should be found similar items' do
47
+ expected = {"You, Me and Dupree"=>0.6579516949597695, "Lady in the Water"=>0.4879500364742689, "Snake on the Plane"=>0.11180339887498941, "The Night Listener"=>-0.1798471947990544, "Just My Luck"=>-0.42289003161103106}
48
+
49
+ supervisor = Recommendation::Supervisor.new(visitors)
50
+ supervisor.train(new_comer)
51
+ engine = Recommendation::Engine.new
52
+
53
+ movies = supervisor.transform_table
54
+
55
+ new_comer.values[0].keys[2].should be_eql 'Superman Returns'
56
+ engine.top_matches(movies, new_comer.values[0].keys[2]).should be_eql expected
57
+ end
58
+ end
59
+
60
+ describe 'recommendation for the unexisting user' do
61
+ it 'should return empty array' do
62
+ expected = {}
63
+
64
+ supervisor = Recommendation::Supervisor.new(visitors)
65
+ supervisor.train(new_comer)
66
+ engine = Recommendation::Engine.new
67
+
68
+ engine.recommendation(supervisor.table, 'hoge').should be_eql expected
69
+ end
70
+ end
71
+
72
+ describe 'top_matches for the unexisting item' do
73
+ it 'should return all zero score' do
74
+ expected = {"Toby"=>0, "Mick LaSalle"=>0, "Michael Phillips"=>0, "Lisa Rose"=>0, "Jack Matthews"=>0}
75
+
76
+ supervisor = Recommendation::Supervisor.new(visitors)
77
+ supervisor.train(new_comer)
78
+ engine = Recommendation::Engine.new
79
+
80
+ engine.top_matches(supervisor.table, 'fuga').should be_eql expected
81
+ end
82
+ end
83
+ end
84
+
85
+ def new_comer
86
+ {
87
+ 'Toby' => {
88
+ 'Snake on the Plane' => 4.5,
89
+ 'You, Me and Dupree' => 1.0,
90
+ 'Superman Returns' => 4.0
91
+ }
92
+ }
93
+ end
94
+
95
+ def visitors
96
+ {
97
+ 'Lisa Rose' => {
98
+ 'Lady in the Water' => 2.5,
99
+ 'Snake on the Plane' => 3.5,
100
+ 'Just My Luck' => 3.0,
101
+ 'Superman Returns' => 3.5,
102
+ 'You, Me and Dupree' => 2.5,
103
+ 'The Night Listener' => 3.0
104
+ },
105
+
106
+ 'Gene Seymour' => {
107
+ 'Lady in the Water' => 3.0,
108
+ 'Snake on the Plane' => 3.5,
109
+ 'Just My Luck' => 1.5,
110
+ 'Superman Returns' => 5.0,
111
+ 'The Night Listener' => 3.0,
112
+ 'You, Me and Dupree' => 3.5
113
+ },
114
+
115
+ 'Michael Phillips' => {
116
+ 'Lady in the Water' => 2.5,
117
+ 'Snake on the Plane' => 3.0,
118
+ 'Superman Returns' => 3.5,
119
+ 'The Night Listener' => 4.0
120
+ },
121
+
122
+ 'Claudia Puig' => {
123
+ 'Snake on the Plane' => 3.5,
124
+ 'Just My Luck' => 3.0,
125
+ 'The Night Listener' => 4.5,
126
+ 'Superman Returns' => 4.0,
127
+ 'You, Me and Dupree' => 2.5
128
+ },
129
+
130
+ 'Mick LaSalle' => {
131
+ 'Lady in the Water' => 3.0,
132
+ 'Snake on the Plane' => 4.0,
133
+ 'Just My Luck' => 2.0,
134
+ 'Superman Returns' => 3.0,
135
+ 'The Night Listener' => 3.0,
136
+ 'You, Me and Dupree' => 2.0
137
+ },
138
+
139
+ 'Jack Matthews' => {
140
+ 'Lady in the Water' => 3.0,
141
+ 'Snake on the Plane' => 4.0,
142
+ 'The Night Listener' => 3.0,
143
+ 'Superman Returns' => 5.0,
144
+ 'You, Me and Dupree' => 3.5
145
+ }
146
+ }
147
+ end
148
+
149
+ def reversed_critics
150
+ {
151
+ "Lady in the Water" => {
152
+ "Lisa Rose" => 2.5,
153
+ "Gene Seymour" => 3.0,
154
+ "Michael Phillips" => 2.5,
155
+ "Mick LaSalle" => 3.0,
156
+ "Jack Matthews" => 3.0
157
+ },
158
+
159
+ "Snake on the Plane" => {
160
+ "Lisa Rose" => 3.5,
161
+ "Gene Seymour" => 3.5,
162
+ "Michael Phillips" => 3.0,
163
+ "Claudia Puig" => 3.5,
164
+ "Mick LaSalle" => 4.0,
165
+ "Jack Matthews" => 4.0,
166
+ "Toby" => 4.5
167
+ },
168
+
169
+ "Just My Luck" => {
170
+ "Lisa Rose" => 3.0,
171
+ "Gene Seymour" => 1.5,
172
+ "Claudia Puig" => 3.0,
173
+ "Mick LaSalle" => 2.0
174
+ },
175
+
176
+ "Superman Returns" => {
177
+ "Lisa Rose" => 3.5,
178
+ "Gene Seymour" => 5.0,
179
+ "Michael Phillips" => 3.5,
180
+ "Claudia Puig" => 4.0,
181
+ "Mick LaSalle" => 3.0,
182
+ "Jack Matthews" => 5.0,
183
+ "Toby" => 4.0
184
+ },
185
+
186
+ "You, Me and Dupree" => {
187
+ "Lisa Rose" => 2.5,
188
+ "Gene Seymour" => 3.5,
189
+ "Claudia Puig" => 2.5,
190
+ "Mick LaSalle" => 2.0,
191
+ "Jack Matthews" => 3.5,
192
+ "Toby" => 1.0
193
+ },
194
+
195
+ "The Night Listener" => {
196
+ "Lisa Rose" => 3.0,
197
+ "Gene Seymour" => 3.0,
198
+ "Michael Phillips" => 4.0,
199
+ "Claudia Puig" => 4.5,
200
+ "Mick LaSalle" => 3.0,
201
+ "Jack Matthews" => 3.0
202
+ }
203
+ }
204
+ end