disco 0.2.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ed4e5ff1d50d49068cfc9dc6b26df6be24aca1827b3f3724c3e351b06d768ac
4
- data.tar.gz: 24756addf552956497889a9898eb9f93cdeec996c2a8a3cc1808c61425ea3d39
3
+ metadata.gz: e9b8792d465e2bd894ce9aaa5dabf79dd89e93337d838917c709ac7747b85772
4
+ data.tar.gz: 9d34a5124dc26f8a2ecb7e2ed3cbf524fe586c37c693d885e668974e24dfaf0a
5
5
  SHA512:
6
- metadata.gz: 50d5a9ba262c8c751f77fb2037235d9186e7616af55c653518752b2300d0c0b1a70d59dd935f0eaed9e1bd09881e547c66446dd0af8bfe641a679acae76fddd0
7
- data.tar.gz: 00b260249dbc2831ad4948d531176c53df965b28f317b0e46987c5be7209da63126033cf75116b01f78ceefd8da977a9fb14bf014e67ae71e18bb634a9852335
6
+ metadata.gz: 658b48b75994a295382eb22908d4a5f1825b01bfc26f52428e993802c42f7ebb435a59e7f1262d17400d65eda886f1a4f38edff82cdeda96c2a0ce280602742f
7
+ data.tar.gz: c9acce77cae8a575c5814456247600367d5fea4eb85a52309e26cac643d107bc03c42c99ce094c2f9ec46a329fd2dfa97d2f58f0020b337feb88b56346630942
@@ -1,3 +1,13 @@
1
+ ## 0.2.3 (2020-11-28)
2
+
3
+ - Added `predict` method
4
+ - Fixed bad recommendations and scores with `user_recs` and explicit feedback
5
+ - Fixed `item_ids` option for `user_recs`
6
+
7
+ ## 0.2.2 (n/a)
8
+
9
+ - Not available (released by previous gem owner)
10
+
1
11
  ## 0.2.1 (2020-10-28)
2
12
 
3
13
  - Fixed issue with `user_recs` returning rated items
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  - Works with explicit and implicit feedback
7
7
  - Uses high-performance matrix factorization
8
8
 
9
- [![Build Status](https://travis-ci.org/ankane/disco.svg?branch=master)](https://travis-ci.org/ankane/disco)
9
+ [![Build Status](https://github.com/ankane/disco/workflows/build/badge.svg?branch=master)](https://github.com/ankane/disco/actions)
10
10
 
11
11
  ## Installation
12
12
 
@@ -64,10 +64,10 @@ Use the `count` option to specify the number of recommendations (default is 5)
64
64
  recommender.user_recs(user_id, count: 3)
65
65
  ```
66
66
 
67
- Get predicted ratings for specific items
67
+ Get predicted ratings for specific users and items
68
68
 
69
69
  ```ruby
70
- recommender.user_recs(user_id, item_ids: [1, 2, 3])
70
+ recommender.predict([{user_id: 1, item_id: 2}, {user_id: 2, item_id: 4}])
71
71
  ```
72
72
 
73
73
  Get similar users
@@ -71,12 +71,31 @@ module Disco
71
71
  @item_index = nil
72
72
  end
73
73
 
74
+ # generates a prediction even if a user has already rated the item
75
+ def predict(data)
76
+ data = to_dataset(data)
77
+
78
+ u = data.map { |v| @user_map[v[:user_id]] }
79
+ i = data.map { |v| @item_map[v[:item_id]] }
80
+
81
+ new_index = data.each_index.select { |index| u[index].nil? || i[index].nil? }
82
+ new_index.each do |j|
83
+ u[j] = 0
84
+ i[j] = 0
85
+ end
86
+
87
+ predictions = @user_factors[u, true].inner(@item_factors[i, true])
88
+ predictions.inplace.clip(@min_rating, @max_rating) if @min_rating
89
+ predictions[new_index] = @global_mean
90
+ predictions.to_a
91
+ end
92
+
74
93
  def user_recs(user_id, count: 5, item_ids: nil)
94
+ check_fit
75
95
  u = @user_map[user_id]
76
96
 
77
97
  if u
78
- predictions = @global_mean + @item_factors.dot(@user_factors[u, true])
79
- predictions.inplace.clip(@min_rating, @max_rating) if @min_rating
98
+ predictions = @item_factors.inner(@user_factors[u, true])
80
99
 
81
100
  predictions =
82
101
  @item_map.keys.zip(predictions).map do |item_id, pred|
@@ -85,7 +104,7 @@ module Disco
85
104
 
86
105
  if item_ids
87
106
  idx = item_ids.map { |i| @item_map[i] }.compact
88
- predictions.values_at(*idx)
107
+ predictions = predictions.values_at(*idx)
89
108
  else
90
109
  @rated[u].keys.sort_by { |v| -v }.each do |i|
91
110
  predictions.delete_at(i)
@@ -94,6 +113,15 @@ module Disco
94
113
 
95
114
  predictions.sort_by! { |pred| -pred[:score] } # already sorted by id
96
115
  predictions = predictions.first(count) if count && !item_ids
116
+
117
+ # clamp *after* sorting
118
+ # also, only needed for returned predictions
119
+ if @min_rating
120
+ predictions.each do |pred|
121
+ pred[:score] = pred[:score].clamp(@min_rating, @max_rating)
122
+ end
123
+ end
124
+
97
125
  predictions
98
126
  else
99
127
  # no items if user is unknown
@@ -103,20 +131,24 @@ module Disco
103
131
  end
104
132
 
105
133
  def optimize_similar_items
134
+ check_fit
106
135
  @item_index = create_index(@item_factors)
107
136
  end
108
137
  alias_method :optimize_item_recs, :optimize_similar_items
109
138
 
110
139
  def optimize_similar_users
140
+ check_fit
111
141
  @user_index = create_index(@user_factors)
112
142
  end
113
143
 
114
144
  def similar_items(item_id, count: 5)
145
+ check_fit
115
146
  similar(item_id, @item_map, @item_factors, item_norms, count, @item_index)
116
147
  end
117
148
  alias_method :item_recs, :similar_items
118
149
 
119
150
  def similar_users(user_id, count: 5)
151
+ check_fit
120
152
  similar(user_id, @user_map, @user_factors, user_norms, count, @user_index)
121
153
  end
122
154
 
@@ -203,6 +235,10 @@ module Disco
203
235
  raise ArgumentError, "No training data" if train_set.empty?
204
236
  end
205
237
 
238
+ def check_fit
239
+ raise "Not fit" unless defined?(@implicit)
240
+ end
241
+
206
242
  def to_dataset(dataset)
207
243
  if defined?(Rover::DataFrame) && dataset.is_a?(Rover::DataFrame)
208
244
  # convert keys to symbols
@@ -1,3 +1,3 @@
1
1
  module Disco
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: disco
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-28 00:00:00.000000000 Z
11
+ date: 2020-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: libmf
@@ -38,118 +38,6 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: bundler
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rake
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: minitest
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '5'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '5'
83
- - !ruby/object:Gem::Dependency
84
- name: activerecord
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: sqlite3
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: daru
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
- - !ruby/object:Gem::Dependency
126
- name: rover-df
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- - !ruby/object:Gem::Dependency
140
- name: ngt
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: 0.3.0
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: 0.3.0
153
41
  description:
154
42
  email: andrew@chartkick.com
155
43
  executables: []