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 +7 -0
- data/lib/models/user.rb +9 -0
- data/lib/recommend.rb +22 -0
- data/lib/user_based_recommendation.rb +51 -0
- data/lib/utils/mock.rb +98 -0
- data/lib/utils/similars.rb +67 -0
- metadata +46 -0
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
|
data/lib/models/user.rb
ADDED
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: []
|