recommend_it 1.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: de2266a44910ede2906b3d2d14432400e1947c8730554d7421c64534ce2a5ffb
4
+ data.tar.gz: df6b2b1849fa821b9b9a0eb7a8e6138e4de8fdd2190a6b84d191ccc7ad6a2d0f
5
+ SHA512:
6
+ metadata.gz: a746eb1b0c828b1c9f652f0096cc87544c2c833f29da588de20fd1a9cd36982efdf4ef01e315e76558ab285e855d61ff4ca3a488764e89160312f0ab148d1cf1
7
+ data.tar.gz: 6e7747d78b0f8844383c586df8a70329aca144483add71691917c681c8e9594691a53b8bd71da2ca6bb57a984230b779ace2ee54667251607d3b6ab586f6221c
@@ -0,0 +1,9 @@
1
+ class User
2
+ def initialize(id)
3
+ @id = id
4
+ end
5
+
6
+ def id
7
+ return @id
8
+ end
9
+ end
data/lib/recommend.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'user_based_recommendation'
2
+
3
+ class Recommend
4
+ attr_reader :item, :order_details, :data
5
+ attr_accessor :type
6
+
7
+ # @param type = This is the type of recommendation to be performed e.g UserBasedRecommendation.new
8
+ # @param item = This is the object the recommendation will be performed for e.g user
9
+ # @param order_details = Hash containing array of users ordered menu items
10
+ # @param data = Matrix data of users against their purchase count of menu items
11
+ def initialize(type, item, order_details = [], data)
12
+ @type = type
13
+ @item = item
14
+ @order_details = order_details
15
+ @data = data
16
+ end
17
+
18
+ # Returns hash of user previous menus and new recommended menu items
19
+ def recommendation()
20
+ @type.recommendation(self)
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ require 'utils/similars'
2
+
3
+ class UserBasedRecommendation
4
+ include Similars
5
+
6
+ # Get recommendation
7
+ # user => current logged in user
8
+ # data => item purchase count matrix based on users
9
+ def recommendation(context)
10
+ user = context.item
11
+ data = context.data
12
+ order_details = context.order_details
13
+ similar_users = Similars.get_similar_users(user,data)
14
+ result = Hash.new
15
+ if similar_users.length > 0
16
+ user_menus = Array.new
17
+ user_menus = order_details[user.id]
18
+ similars_menus = get_similars_menus(similar_users,order_details)
19
+ recommend = Array.new
20
+ similars_menus.each do |s|
21
+ if !user_menus.include? s
22
+ recommend.push(s)
23
+ end
24
+ end
25
+ result[:user] = user_menus
26
+ result[:recommendation] = recommend
27
+ end
28
+ return result
29
+ end
30
+
31
+ # Returns all similars menu items
32
+ def get_similars_menus(similar_users,order_details)
33
+ number_of_users = similar_users.length
34
+ similars_menus = Array.new
35
+ similar_users.each do |user_id, sim_index|
36
+ get_all_similar_users_menu_items(order_details,user_id,similars_menus)
37
+ end
38
+ return similars_menus
39
+ end
40
+
41
+ # Returns an array containing all menu items ordered by similar users
42
+ def get_all_similar_users_menu_items(order_details,user_id,menus)
43
+ order_details[user_id].each do |menu|
44
+ if !menus.include? menu
45
+ menus.push(menu)
46
+ end
47
+ end
48
+ return menus
49
+ end
50
+
51
+ end
data/lib/utils/mock.rb ADDED
@@ -0,0 +1,98 @@
1
+ module Mock
2
+ # Mock data to use for testing purposes
3
+
4
+ # Returns document vector of valid length
5
+ def self.d1_same_size
6
+ return [1,2,4,5,1,6,4,4]
7
+ end
8
+
9
+ # Returns document vector of valid length
10
+ def self.d2_same_size
11
+ return [1,0,4,4,1,2,4,3]
12
+ end
13
+
14
+ # Returns a document vector of invalid length
15
+ def self.d1_invalid_size
16
+ return [1,2,4,5,4]
17
+ end
18
+
19
+ # Returns users that have similar order details
20
+ def self.order_details_similar
21
+ return {
22
+ 1 => [1,3,5,7],
23
+ 2 => [1,4,3],
24
+ 3 => [9,77]
25
+ }
26
+ end
27
+
28
+ # Returns users that have similar order details
29
+ def self.order_details_similar_12
30
+ return {
31
+ 1 => [1,3,5,7],
32
+ 2 => [1,4,3],
33
+ 3 => [9,77,44],
34
+ 4 => [1,5,7],
35
+ 5 => [1,4,3],
36
+ 6 => [9,4],
37
+ 7 => [1,3,5,7],
38
+ 8 => [1,4,3],
39
+ 9 => [2,77],
40
+ 10 => [1,3,7],
41
+ 11 => [1,4,3,13],
42
+ 12 => [9,5]
43
+ }
44
+ end
45
+
46
+ # Returns users that have different order details
47
+ def self.order_details_different
48
+ return {
49
+ 1 => [61,63,5,7],
50
+ 2 => [1,4,3],
51
+ 3 => [9,77]
52
+ }
53
+ end
54
+
55
+ # Returns users that have similar purchase count
56
+ def self.matrix_data
57
+ return {
58
+ 1 => [1,0,8,0,0,3,6],
59
+ 2 => [0,2,0,0,3,0,6],
60
+ 3 => [1,0,8,0,0,3,0]
61
+ }
62
+ end
63
+
64
+ # Returns12 similar purchase count for users
65
+ def self.matrix_data_12
66
+ return {
67
+ 1 => [1,0,8,0,2,3,6],
68
+ 2 => [0,2,0,0,3,0,6],
69
+ 3 => [1,0,8,0,0,3,0],
70
+ 4 => [1,0,8,3,2,3,0],
71
+ 5 => [1,0,8,0,0,3,0],
72
+ 6 => [1,0,0,0,0,3,0],
73
+ 7 => [1,0,3,0,0,0,0],
74
+ 8 => [1,0,8,1,0,3,4],
75
+ 9 => [0,0,8,0,0,3,0],
76
+ 10 => [1,0,8,0,0,3,0],
77
+ 11 => [1,0,8,0,0,3,0],
78
+ 12 => [1,0,8,0,0,3,0]
79
+ }
80
+ end
81
+
82
+ # Returns users that don't have similar purchase counts
83
+ def self.matrix_data_different
84
+ return {
85
+ 1 => [1,0,8,0,0,3,6],
86
+ 2 => [0,2,0,0,3,0,0],
87
+ 3 => [0,0,0,1,0,0,0]
88
+ }
89
+ end
90
+
91
+ # Returns an array of users with similarity indexes
92
+ def self.similarity_indexes
93
+ return {
94
+ 3 => 0.8201995322647244,
95
+ 2 => 0.490352188754876
96
+ }
97
+ end
98
+ end
@@ -0,0 +1,67 @@
1
+ module Similars
2
+
3
+ # Calculate similarity index using the cosine similarity model
4
+ # Formula is defined as cos(d1,d2) = (d1●d2)/||d1|| * ||d2||
5
+ # (d1●d2) is known as the dot product i.e multiplying each attributes of the document vectors by each other
6
+ # ||d1||, ||d2|| is defined as magnitude
7
+ # Document vector magnitude is calculated by squaring each document vector attribute and find the square root of the sum
8
+ def self.similarity_index(d1,d2)
9
+ d2 = fix_missing_values(d1,d2)
10
+ dot_product = calculate_dot_product(d1,d2)
11
+ d1_magnitude = calculate_vector_magnitude(d1)
12
+ d2_magnitude = calculate_vector_magnitude(d2)
13
+ return dot_product / (d1_magnitude * d2_magnitude) # convert to values to float before calculation so decimal places will not be lost
14
+ end
15
+
16
+ # Find dot product of the document vectors
17
+ # Dot product is done by mutliplying attribute by attribute of each document vector
18
+ def self.calculate_dot_product(d1,d2)
19
+ sum = 0
20
+ i = 0
21
+ while i < d1.length
22
+ sum = sum + d1[i] * d2[i]
23
+ i += 1
24
+ end
25
+ return sum.to_f
26
+ end
27
+
28
+ # Document vector magnitude is calculated by summing up the squares of each attribute and finding the square root of the sum
29
+ def self.calculate_vector_magnitude(vector)
30
+ sum = 0
31
+ i = 0
32
+ while i < vector.length
33
+ sum = sum + vector[i] * vector[i]
34
+ i += 1
35
+ end
36
+ return Math.sqrt(sum).to_f
37
+ end
38
+
39
+ # Returns an array of similarity index of users against the logged in user
40
+ # Sort the similarity indexes in descending order to get like users first
41
+ def self.get_similar_users(user,data)
42
+ sim = Hash.new
43
+ d1 = data[user.id]
44
+ data.each do |key, d2|
45
+ if key != user.id
46
+ index = similarity_index(d1, d2)
47
+ if index > 0 && !index.nan? # Only store users that have similarity index greater than 0 and is not NaN (NaN values are derived when document vector is filled with only zero values)
48
+ sim[key] = index
49
+ end
50
+ end
51
+ end
52
+ return sim.sort_by {|k,v| -v}.to_h # sort_by function converts hash to multidemsional array, so convert back to hash after sorting
53
+ end
54
+
55
+ # Fix vectors of varying length by adding zeros
56
+ def self.fix_missing_values(d1,d2)
57
+ i = 0
58
+ length_diff = d1.length - d2.length
59
+ if length_diff > 0
60
+ while i < length_diff
61
+ d2.push(0)
62
+ i += 1
63
+ end
64
+ end
65
+ return d2
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: recommend_it
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Weje Emmanuel Okechukwu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-11-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Meal recommedation engine based on collaborative filtering
14
+ email: wejemm@yahoo.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/models/user.rb
20
+ - lib/recommend.rb
21
+ - lib/user_based_recommendation.rb
22
+ - lib/utils/mock.rb
23
+ - lib/utils/similars.rb
24
+ homepage: http://rubygems.org/gems/recommend_it
25
+ licenses: []
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubygems_version: 3.0.3
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: Meal recommedation engine based on collaborative filtering
46
+ test_files: []