cmfrec 0.1.0 → 0.1.1

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: 2e9f45e0c3826b90788782ac8a0838476fc7849e75d91f2d5949b08b382a28c3
4
- data.tar.gz: 2f44b988001bf2b23e3c5938b4c5e7b9089e8943d86aa42c86e72be0ff558ea1
3
+ metadata.gz: 2091fae505c3d98468c6cfa08e0f80c6e97804fe8558bea97191336fa0179423
4
+ data.tar.gz: b66cece9f8659a6f79ac6ee302a1d2068d9a8b89b8903b2182f0fa8be6838869
5
5
  SHA512:
6
- metadata.gz: 592ef4363bc016da1a35a958f99dd13a0fc7e900ecbe77b3ba95fc4b4cbbe151397924ff90aab2e326ac3369ac07c0380c76b1d3a2ac71b390664118de0f8611
7
- data.tar.gz: 27d3e1ce80e88af9e8b062837f68329b10718372be15c08d4cd2e056619d1cd9e5ed906e6070798c32993ccea2f617b706af5e6afb7cba4f3cfc70ea22393ba6
6
+ metadata.gz: 5feed4c89f6249646b61d0713bcbc83725561942437ba3abdf2ca70a82a8f76d4a614b4f47a2c07e158a7155442f31b730b1d759234520bd34e50cae7e723b16
7
+ data.tar.gz: 649ce14693b2b7cfcc6039d4e39d94c3e3dce695e32b62f6c8c94c2751621622c519e5b536c917446e781b1acbadd3a511789efaae3ffc4aa2062738697f975f
@@ -1,3 +1,7 @@
1
+ ## 0.1.1 (2020-11-28)
2
+
3
+ - Added `predict` method
4
+
1
5
  ## 0.1.0 (2020-11-27)
2
6
 
3
7
  - First release
data/README.md CHANGED
@@ -6,8 +6,6 @@
6
6
  - Works with explicit and implicit feedback
7
7
  - Uses high-performance matrix factorization
8
8
 
9
- Not available for Windows yet
10
-
11
9
  [![Build Status](https://github.com/ankane/cmfrec/workflows/build/badge.svg?branch=master)](https://github.com/ankane/cmfrec/actions)
12
10
 
13
11
  ## Installation
@@ -18,6 +16,8 @@ Add this line to your application’s Gemfile:
18
16
  gem 'cmfrec'
19
17
  ```
20
18
 
19
+ Not available for Windows yet
20
+
21
21
  ## Getting Started
22
22
 
23
23
  Create a recommender
@@ -48,7 +48,7 @@ recommender.fit([
48
48
 
49
49
  > Use `value` instead of rating for implicit feedback
50
50
 
51
- Get recommendations - “users like you also liked”
51
+ Get recommendations for a user in the training data
52
52
 
53
53
  ```ruby
54
54
  recommender.user_recs(user_id)
@@ -69,10 +69,10 @@ Use the `count` option to specify the number of recommendations (default is 5)
69
69
  recommender.user_recs(user_id, count: 3)
70
70
  ```
71
71
 
72
- Get predicted ratings for specific items
72
+ Get predicted ratings for specific users and items
73
73
 
74
74
  ```ruby
75
- recommender.user_recs(user_id, item_ids: [1, 2, 3])
75
+ recommender.predict([{user_id: 1, item_id: 2}, {user_id: 2, item_id: 4}])
76
76
  ```
77
77
 
78
78
  ## Side Information
@@ -81,12 +81,12 @@ Add side information about users, items, or both
81
81
 
82
82
  ```ruby
83
83
  user_info = [
84
- {user_id: 1, a: 1, b: 1},
85
- {user_id: 2, a: 1, b: 1},
84
+ {user_id: 1, cats: 1, dogs: 0},
85
+ {user_id: 2, cats: 2, dogs: 1},
86
86
  ]
87
87
  item_info = [
88
- {item_id: 1, c: 1, d: 1},
89
- {item_id: 2, c: 1, d: 1},
88
+ {item_id: 1, genre_comedy: 1, genre_drama: 0},
89
+ {item_id: 2, genre_comedy: 0, genre_drama: 1},
90
90
  ]
91
91
  recommender.fit(ratings, user_info: user_info, item_info: item_info)
92
92
  ```
@@ -98,13 +98,13 @@ ratings = [
98
98
  {item_id: 1, rating: 5},
99
99
  {item_id: 2, rating: 3}
100
100
  ]
101
- recommender.new_user_recs(ratings, user_info: {a: 1, b: 1})
101
+ recommender.new_user_recs(ratings, user_info: {cats: 0, dogs: 2})
102
102
  ```
103
103
 
104
104
  Get recommendations with only side information
105
105
 
106
106
  ```ruby
107
- recommender.new_user_recs([], user_info: {a: 1, b: 1})
107
+ recommender.new_user_recs([], user_info: {cats: 0, dogs: 2})
108
108
  ```
109
109
 
110
110
  ## Options
@@ -172,6 +172,49 @@ module Cmfrec
172
172
  self
173
173
  end
174
174
 
175
+ def predict(data)
176
+ check_fit
177
+
178
+ data = to_dataset(data)
179
+
180
+ u = data.map { |v| @user_map[v[:user_id]] || -1 }
181
+ i = data.map { |v| @item_map[v[:item_id]] || -1 }
182
+
183
+ pred_a = int_ptr(u)
184
+ pred_b = int_ptr(i)
185
+ nnz = data.size
186
+ outp = Fiddle::Pointer.malloc(nnz * Fiddle::SIZEOF_DOUBLE)
187
+
188
+ FFI.predict_multiple(
189
+ @a, @k_user,
190
+ @b, @k_item,
191
+ @bias_a, @bias_b,
192
+ @global_mean,
193
+ @k, @k_main,
194
+ @m, @n,
195
+ pred_a, pred_b, nnz,
196
+ outp,
197
+ @nthreads
198
+ )
199
+
200
+ predictions = real_array(outp)
201
+
202
+ nan_index = predictions.each_index.select { |j| predictions[j].nan? }
203
+ if nan_index.any?
204
+ # TODO improve performance
205
+ user_bias = send(:user_bias)
206
+ item_bias = send(:item_bias)
207
+ nan_index.each do |j|
208
+ v = @global_mean
209
+ v += user_bias[u[j]] if user_bias && u[j] != -1
210
+ v += item_bias[i[j]] if item_bias && i[j] != -1
211
+ predictions[j] = v
212
+ end
213
+ end
214
+
215
+ predictions
216
+ end
217
+
175
218
  def user_recs(user_id, count: 5, item_ids: nil)
176
219
  check_fit
177
220
  user = @user_map[user_id]
@@ -181,24 +224,9 @@ module Cmfrec
181
224
  # remove missing ids
182
225
  item_ids = item_ids.select { |v| @item_map[v] }
183
226
 
184
- pred_a = int_ptr([@user_map[user_id]] * item_ids.size)
185
- pred_b = int_ptr(item_ids.map { |v| @item_map[v] })
186
- nnz = item_ids.size
187
- outp = Fiddle::Pointer.malloc(nnz * Fiddle::SIZEOF_DOUBLE)
188
-
189
- FFI.predict_multiple(
190
- @a, @k_user,
191
- @b, @k_item,
192
- @bias_a, @bias_b,
193
- @global_mean,
194
- @k, @k_main,
195
- @m, @n,
196
- pred_a, pred_b, nnz,
197
- outp,
198
- @nthreads
199
- )
200
-
201
- scores = real_array(outp)
227
+ data = item_ids.map { |v| {user_id: user_id, item_id: v} }
228
+ scores = predict(data)
229
+
202
230
  item_ids.zip(scores).map do |item_id, score|
203
231
  {item_id: item_id, score: score}
204
232
  end
@@ -1,3 +1,3 @@
1
1
  module Cmfrec
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmfrec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane