commendo 0.0.7 → 0.0.8

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
  SHA1:
3
- metadata.gz: e46e056067c3c4af5ab6315d8145b811286a465c
4
- data.tar.gz: 5392a1bbe9b1dbeb10f10f3a136d01d2a5b6f53d
3
+ metadata.gz: 62cfaa4fff44e045a7adf83a7bc3ccb29db84812
4
+ data.tar.gz: 8473e5cb764e1474f2aa541b195afd22ca123a84
5
5
  SHA512:
6
- metadata.gz: 38ff1874cd0fea432da1e3cc0303c674bef437c689cb4bf47e7f43d1072aaf6569dbc53727fdfffd9e7479f18912a86e49fd1c6f9350ac09c5a5d2acfa53aa7d
7
- data.tar.gz: a1c512f261e1b2b1cb704a5b6ba41b1d172b9191b17c04a031e54366e664f1485ecf1e71c2a6993b7cfa49b35ffd383c571e464703fb1eccdcfb088bad3c2c19
6
+ metadata.gz: 2e2911b2ab856cc6ce7514f289a02cf1fe3e29e9e4a546aca1a11a97da8c4f4b9a2518074ed8b1b4148ffde164c62a970d36f42cd1c6e56b9c49021f513b818f
7
+ data.tar.gz: f8664d3e2e048c9026865929c1c39375242b3f05d669b6b14212442f3ffaeb0aeca03a8a80091c42e1b59d6e305d7622b5ffb8eca516f083a4af991aaeaf8ef7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ # 0.0.8 / 2014-04-09
2
+ * [FEATURE] Allow degree of set membership to be included when adding data and incrementally increase scores when adding new data
3
+
1
4
  # 0.0.7 / 2014-04-01
2
5
  * [BUGFIX] Cope with sets of empty tags being set in TagSet
3
6
 
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ content_set_base_redis_key = ARGV[0]
4
+
5
+ require 'redis'
6
+ require 'commendo'
7
+
8
+ redis = Redis.new(db: 2)
9
+ #cs = Commendo::ContentSet.new(redis, content_set_base_redis_key)
10
+
11
+ distribution = {}
12
+ cursor = 0
13
+ begin
14
+ cursor, keys = redis.scan(cursor.to_i, match: "#{content_set_base_redis_key}*", count: 1000)
15
+ keys.each do |key|
16
+ left_resource = key.gsub(/^#{content_set_base_redis_key}/, '')
17
+ similar = redis.zrange(key, 0, -1, with_scores: true)
18
+ similar.each do |recommendation|
19
+ right_resource, score = recommendation
20
+ puts "#{left_resource}\t#{right_resource}" if score == 1 && left_resource < right_resource
21
+ end
22
+ end
23
+ end while cursor.to_i > 0
24
+
data/bin/commendo-load ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ redis_db = ARGV[0].to_i
4
+ content_set_base_redis_key = ARGV[1]
5
+ filename = ARGV[2]
6
+
7
+ require 'redis'
8
+ require 'commendo'
9
+
10
+ redis = Redis.new(db: 2)
11
+ cs = Commendo::ContentSet.new(redis, content_set_base_redis_key)
12
+
13
+ File.open(filename) do |f|
14
+
15
+ current_resource = nil
16
+ current_headings = []
17
+
18
+ f.each_line.with_index do |tsv, i|
19
+ next if i.zero?
20
+
21
+ resource, _classified, root, score, heading = tsv.split(/\t/)
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ content_set_base_redis_key = ARGV[0]
4
+
5
+ require 'redis'
6
+ require 'commendo'
7
+
8
+ redis = Redis.new(db: 2)
9
+ #cs = Commendo::ContentSet.new(redis, content_set_base_redis_key)
10
+
11
+ distribution = {}
12
+ cursor = 0
13
+ begin
14
+ cursor, keys = redis.scan(cursor.to_i, match: content_set_base_redis_key, count: 1000)
15
+ keys.each do |key|
16
+ count = redis.zcard(key)
17
+ distribution[count] ||= 0
18
+ distribution[count] += 1
19
+ end
20
+ end while cursor.to_i > 0
21
+
22
+ distribution.each do |score, count|
23
+ puts "#{score}\t#{count}"
24
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ content_set_base_redis_key = ARGV[0]
4
+
5
+ require 'redis'
6
+ require 'commendo'
7
+
8
+ redis = Redis.new(db: 2)
9
+ #cs = Commendo::ContentSet.new(redis, content_set_base_redis_key)
10
+
11
+ distribution = {}
12
+ cursor = 0
13
+ begin
14
+ cursor, keys = redis.scan(cursor.to_i, match: content_set_base_redis_key, count: 1000)
15
+ keys.each do |key|
16
+ similar = redis.zrange(key, 0, -1, with_scores: true)
17
+ similar.each do |recommendation|
18
+ resource, score = recommendation
19
+ distribution[score.round(1)] ||= 0
20
+ distribution[score.round(1)] += 1
21
+ end
22
+ end
23
+ end while cursor.to_i > 0
24
+
25
+ distribution.each do |score, count|
26
+ puts "#{score}\t#{count}"
27
+ end
@@ -9,33 +9,33 @@ module Commendo
9
9
  end
10
10
 
11
11
  def add_by_group(group, *resources)
12
- redis.sadd(group_key(group), resources)
13
12
  resources.each do |resource|
14
- redis.sadd(resource_key(resource), group)
13
+ if resource.kind_of?(Array)
14
+ add_single(resource[0], group, resource[1])
15
+ else
16
+ add_single(resource, group, 1)
17
+ end
15
18
  end
16
19
  end
17
20
 
18
21
  def add(resource, *groups)
19
- redis.sadd(resource_key(resource), groups)
20
22
  groups.each do |group|
21
- redis.sadd(group_key(group), resource)
23
+ if group.kind_of?(Array)
24
+ add_single(resource, group[0], group[1])
25
+ else
26
+ add_single(resource, group, 1)
27
+ end
22
28
  end
23
29
  end
24
30
 
31
+ def add_single(resource, group, score)
32
+ redis.zincrby(group_key(group), score, resource)
33
+ redis.zincrby(resource_key(resource), score, group)
34
+ end
35
+
25
36
  def add_and_calculate(resource, *groups)
26
37
  add(resource, *groups)
27
- groups = redis.smembers(resource_key(resource))
28
- group_keys = groups.map { |group| group_key(group) }
29
- resources = redis.sunion(*group_keys)
30
- resources.combination(2) do |l, r|
31
- intersect = redis.sinter(resource_key(l), resource_key(r))
32
- if (intersect.length > 0)
33
- union = redis.sunion(resource_key(l), resource_key(r))
34
- jaccard = intersect.length / union.length.to_f
35
- redis.zadd(similarity_key(l), jaccard, r)
36
- redis.zadd(similarity_key(r), jaccard, l)
37
- end
38
- end
38
+ calculate_similarity_for_resource(resource, 0)
39
39
  end
40
40
 
41
41
  def delete(resource)
@@ -48,25 +48,43 @@ module Commendo
48
48
  redis.del(resource_key(resource))
49
49
  end
50
50
 
51
+ SET_TOO_LARGE_FOR_LUA = 999
52
+
51
53
  def calculate_similarity(threshold = 0)
52
54
  #TODO make this use scan for scaling
53
55
  keys = redis.keys("#{resource_key_base}:*")
54
56
  keys.each_with_index do |key, i|
55
57
  yield(key, i, keys.length) if block_given?
56
- completed = redis.eval(similarity_lua, keys: [key], argv: [resource_key_base, similar_key_base, group_key_base, threshold])
57
- if completed == 999
58
- resource = key.gsub(/^#{resource_key_base}:/, '')
59
- groups = redis.smembers(resource_key(resource))
60
- group_keys = groups.map { |group| group_key(group) }
61
- resources = redis.sunion(*group_keys)
62
- resources.each do |to_compare|
63
- next if resource == to_compare
64
- redis.eval(pair_comparison_lua, keys: [key, resource_key(to_compare), similarity_key(resource), similarity_key(to_compare)], argv: [resource, to_compare, threshold])
65
- end
58
+ completed = redis.eval(similarity_lua, keys: [key], argv: [tmp_key_base, resource_key_base, similar_key_base, group_key_base, threshold])
59
+ if completed == SET_TOO_LARGE_FOR_LUA
60
+ calculate_similarity_for_key(key, threshold)
66
61
  end
67
62
  end
68
63
  end
69
64
 
65
+ def calculate_similarity_for_key(key, threshold)
66
+ resource = key.gsub(/^#{resource_key_base}:/, '')
67
+ calculate_similarity_for_key_resource(key, resource, threshold)
68
+ end
69
+
70
+ def calculate_similarity_for_resource(resource, threshold)
71
+ key = resource_key(resource)
72
+ calculate_similarity_for_key_resource(key, resource, threshold)
73
+ end
74
+
75
+ def calculate_similarity_for_key_resource(key, resource, threshold)
76
+ groups = redis.zrange(resource_key(resource), 0, -1)
77
+ group_keys = groups.map { |group| group_key(group) }
78
+ tmp_key = "#{tmp_key_base}:#{SecureRandom.uuid}"
79
+ redis.zunionstore(tmp_key, group_keys)
80
+ resources = redis.zrange(tmp_key, 0, -1)
81
+ redis.del(tmp_key)
82
+ resources.each do |to_compare|
83
+ next if resource == to_compare
84
+ redis.eval(pair_comparison_lua, keys: [key, resource_key(to_compare), similarity_key(resource), similarity_key(to_compare)], argv: [tmp_key_base, resource, to_compare, threshold])
85
+ end
86
+ end
87
+
70
88
  def similar_to(resource)
71
89
  if resource.kind_of? Array
72
90
  keys = resource.map do |res|
@@ -116,6 +134,10 @@ module Commendo
116
134
  file.read
117
135
  end
118
136
 
137
+ def tmp_key_base
138
+ "#{key_base}:tmp"
139
+ end
140
+
119
141
  def similar_key_base
120
142
  "#{key_base}:similar"
121
143
  end
@@ -3,9 +3,10 @@ local right_key = KEYS[2]
3
3
  local left_similarity_key = KEYS[3]
4
4
  local right_similarity_key = KEYS[4]
5
5
 
6
- local left = tonumber(ARGV[1])
7
- local right = tonumber(ARGV[2])
8
- local threshold = tonumber(ARGV[3])
6
+ local tmp_key_base = ARGV[1]
7
+ local left = tonumber(ARGV[2])
8
+ local right = tonumber(ARGV[3])
9
+ local threshold = tonumber(ARGV[4])
9
10
 
10
11
  local function round(num, idp)
11
12
  local mult = 10^(idp or 0)
@@ -14,10 +15,31 @@ end
14
15
 
15
16
  redis.log(redis.LOG_NOTICE, 'Running pair comparison for ' .. left_key .. ' ' .. right_key)
16
17
 
17
- local intersect = table.getn(redis.call('SINTER', left_key, right_key))
18
- if intersect > 0 then
19
- local union = table.getn(redis.call('SUNION', left_key, right_key))
20
- local similarity = round(intersect / union, 3)
18
+
19
+ --TODO change bar
20
+ local tmp_pair_intersect_key = tmp_key_base .. 'bar'
21
+ redis.call('ZINTERSTORE', tmp_pair_intersect_key, 2, left_key, right_key)
22
+ local intersect = redis.call('ZRANGE', tmp_pair_intersect_key, 0, -1, 'WITHSCORES')
23
+ redis.call('DEL', tmp_pair_intersect_key)
24
+
25
+ if table.getn(intersect) > 0 then
26
+ local intersect_score = 0
27
+ for i=1,#intersect,2 do
28
+ intersect_score = intersect_score + intersect[i+1]
29
+ end
30
+
31
+ --TODO change baz
32
+ local tmp_pair_union_key = tmp_key_base .. 'baz'
33
+ redis.call('ZUNIONSTORE', tmp_pair_union_key, 2, left_key, right_key)
34
+
35
+ local union = redis.call('ZRANGE', tmp_pair_union_key, 0, -1, 'WITHSCORES')
36
+ redis.call('DEL', tmp_pair_union_key)
37
+ local union_score = 0
38
+ for i=1,#union,2 do
39
+ union_score = union_score + union[i+1]
40
+ end
41
+
42
+ local similarity = round(intersect_score / union_score, 3)
21
43
  if similarity > threshold then
22
44
  redis.call('ZADD', left_similarity_key, similarity, right)
23
45
  redis.call('ZADD', right_similarity_key, similarity, left)
@@ -1,8 +1,9 @@
1
1
  local resource_key = KEYS[1]
2
- local resource_key_base = ARGV[1]
3
- local sim_key_base = ARGV[2]
4
- local group_key_base = ARGV[3]
5
- local threshold = tonumber(ARGV[4])
2
+ local tmp_key_base = ARGV[1]
3
+ local resource_key_base = ARGV[2]
4
+ local sim_key_base = ARGV[3]
5
+ local group_key_base = ARGV[4]
6
+ local threshold = tonumber(ARGV[5])
6
7
 
7
8
  local function round(num, idp)
8
9
  local mult = 10^(idp or 0)
@@ -12,7 +13,7 @@ end
12
13
  redis.log(redis.LOG_NOTICE, 'Running complete similarity for ' .. resource_key)
13
14
 
14
15
  local resource = resource_key:gsub('%' .. resource_key_base .. ':', '')
15
- local groups = redis.call('smembers', resource_key)
16
+ local groups = redis.call('ZRANGE', resource_key, 0, -1)
16
17
 
17
18
  if table.getn(groups) > 999 then
18
19
  redis.log(redis.LOG_NOTICE, 'Complete similarity too large for ' .. resource_key .. ', ' .. table.getn(groups))
@@ -25,24 +26,45 @@ for _,group in ipairs(groups) do
25
26
  end
26
27
  --redis.log(redis.LOG_NOTICE, 'Found ' .. table.getn(group_keys) .. ' group keys')
27
28
 
28
- local resources = redis.call('sunion', unpack(group_keys))
29
-
30
- --local resources = redis.call('sunion', unpack(group_keys))
31
- --redis.log(redis.LOG_NOTICE, 'Found ' .. table.getn(resources) .. ' resources')
29
+ --TODO change foo
30
+ local tmp_groups_union_key = tmp_key_base .. 'unionfoo'
31
+ redis.call('ZUNIONSTORE', tmp_groups_union_key, table.getn(group_keys), unpack(group_keys))
32
+ local resources = redis.call('ZRANGE', tmp_groups_union_key, 0, -1)
32
33
 
33
34
  local previous = 'foo'
34
35
  for _,to_compare in ipairs(resources) do
35
- -- redis.log(redis.LOG_NOTICE, 'Comparing ' .. resource .. ' and ' .. to_compare)
36
+ --redis.log(redis.LOG_NOTICE, 'Comparing ' .. resource .. ' and ' .. to_compare)
36
37
  if to_compare ~= previous then
37
38
  previous = to_compare
38
39
  if resource > to_compare then
39
- -- redis.log(redis.LOG_NOTICE, 'Calculating similarity for ' .. resource .. ' and ' .. to_compare)
40
- local intersect = table.getn(redis.call('SINTER', resource_key, resource_key_base .. ':' .. to_compare))
41
- if intersect > 0 then
42
- local union = table.getn(redis.call('SUNION', resource_key, resource_key_base .. ':' .. to_compare))
43
- local similarity = round(intersect / union, 3)
40
+ --redis.log(redis.LOG_NOTICE, 'Calculating similarity for ' .. resource .. ' and ' .. to_compare)
41
+
42
+ --TODO change bar
43
+ local tmp_pair_intersect_key = tmp_key_base .. 'bar'
44
+ redis.call('ZINTERSTORE', tmp_pair_intersect_key, 2, resource_key, resource_key_base .. ':' .. to_compare)
45
+ local intersect = redis.call('ZRANGE', tmp_pair_intersect_key, 0, -1, 'WITHSCORES')
46
+ redis.call('DEL', tmp_pair_intersect_key)
47
+
48
+ if table.getn(intersect) > 0 then
49
+ local intersect_score = 0
50
+ for i=1,#intersect,2 do
51
+ intersect_score = intersect_score + intersect[i+1]
52
+ end
53
+
54
+ --TODO change baz
55
+ local tmp_pair_union_key = tmp_key_base .. 'baz'
56
+ redis.call('ZUNIONSTORE', tmp_pair_union_key, 2, resource_key, resource_key_base .. ':' .. to_compare)
57
+
58
+ local union = redis.call('ZRANGE', tmp_pair_union_key, 0, -1, 'WITHSCORES')
59
+ redis.call('DEL', tmp_pair_union_key)
60
+ local union_score = 0
61
+ for i=1,#union,2 do
62
+ union_score = union_score + union[i+1]
63
+ end
64
+
65
+ local similarity = round(intersect_score / union_score, 3)
44
66
  if similarity > threshold then
45
- -- redis.log(redis.LOG_NOTICE, resource .. ' and ' .. to_compare .. ' scored ' .. similarity)
67
+ --redis.log(redis.LOG_NOTICE, resource .. ' and ' .. to_compare .. ' scored ' .. similarity)
46
68
  redis.call('ZADD', sim_key_base .. ':' .. resource, similarity, to_compare)
47
69
  redis.call('ZADD', sim_key_base .. ':' .. to_compare, similarity, resource)
48
70
  end
@@ -51,6 +73,12 @@ for _,to_compare in ipairs(resources) do
51
73
  end
52
74
  end
53
75
 
76
+ redis.call('DEL', tmp_groups_union_key)
77
+
54
78
  redis.log(redis.LOG_NOTICE, 'Finished running complete similarity for ' .. resource_key)
55
79
 
56
- return true
80
+ return true
81
+
82
+
83
+
84
+
@@ -1,3 +1,3 @@
1
1
  module Commendo
2
- VERSION = '0.0.7'
2
+ VERSION = '0.0.8'
3
3
  end
@@ -16,7 +16,7 @@ module Commendo
16
16
  assert_equal 'CommendoTests:similar:resource-1', cs.similarity_key('resource-1')
17
17
  end
18
18
 
19
- def test_stores_sets_by_resource
19
+ def test_recommends_when_added
20
20
  redis = Redis.new(db: 15)
21
21
  redis.flushdb
22
22
  key_base = 'CommendoTests'
@@ -25,33 +25,89 @@ module Commendo
25
25
  cs.add('resource-2', 'group-1')
26
26
  cs.add('resource-3', 'group-1', 'group-2')
27
27
  cs.add('resource-4', 'group-2')
28
- assert redis.sismember("#{key_base}:resources:resource-1", 'group-1')
29
- assert redis.sismember("#{key_base}:resources:resource-2", 'group-1')
30
- assert redis.sismember("#{key_base}:resources:resource-3", 'group-1')
31
- refute redis.sismember("#{key_base}:resources:resource-4", 'group-1')
32
-
33
- assert redis.sismember("#{key_base}:resources:resource-1", 'group-2')
34
- refute redis.sismember("#{key_base}:resources:resource-2", 'group-2')
35
- assert redis.sismember("#{key_base}:resources:resource-3", 'group-2')
36
- assert redis.sismember("#{key_base}:resources:resource-4", 'group-2')
28
+ cs.calculate_similarity
29
+ expected = [
30
+ {resource: 'resource-3', similarity: 1.0},
31
+ {resource: 'resource-4', similarity: 0.667},
32
+ {resource: 'resource-2', similarity: 0.667}
33
+ ]
34
+ assert_equal expected, cs.similar_to('resource-1')
37
35
  end
38
36
 
39
- def test_stores_sets_by_group
37
+ def test_recommends_when_added_with_scores
38
+ redis = Redis.new(db: 15)
39
+ redis.flushdb
40
+ key_base = 'CommendoTests'
41
+ cs = ContentSet.new(redis, key_base)
42
+ cs.add('resource-1', ['group-1', 2], ['group-2', 2])
43
+ cs.add('resource-2', ['group-1', 7])
44
+ cs.add('resource-3', ['group-1', 2], ['group-2', 2])
45
+ cs.add('resource-4', ['group-2', 3])
46
+ cs.calculate_similarity
47
+ expected = [
48
+ {resource: 'resource-3', similarity: 1.0},
49
+ {resource: 'resource-2', similarity: 0.818},
50
+ {resource: 'resource-4', similarity: 0.714}
51
+ ]
52
+ assert_equal expected, cs.similar_to('resource-1')
53
+ end
54
+
55
+ def test_recommends_when_extra_scores_added
56
+ test_recommends_when_added_with_scores
57
+ redis = Redis.new(db: 15)
58
+ key_base = 'CommendoTests'
59
+ cs = ContentSet.new(redis, key_base)
60
+ cs.add('resource-3', ['group-1', 1], ['group-3', 2])
61
+ cs.add('resource-4', ['group-2', 1])
62
+ cs.add_by_group('group-1', ['newource-9', 100], 'resource-2', 'resource-3')
63
+ cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
64
+ cs.calculate_similarity
65
+ expected = [
66
+ {resource: 'newource-9', similarity: 1.0},
67
+ {resource: 'resource-1', similarity: 0.769},
68
+ {resource: 'resource-3', similarity: 0.706}
69
+ ]
70
+ assert_equal expected, cs.similar_to('resource-2')
71
+ end
72
+
73
+ def test_recommends_when_added_by_group
40
74
  redis = Redis.new(db: 15)
41
75
  redis.flushdb
42
76
  key_base = 'CommendoTests'
43
77
  cs = ContentSet.new(redis, key_base)
44
78
  cs.add_by_group('group-1', 'resource-1', 'resource-2', 'resource-3')
45
79
  cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
46
- assert redis.sismember("#{key_base}:resources:resource-1", 'group-1')
47
- assert redis.sismember("#{key_base}:resources:resource-2", 'group-1')
48
- assert redis.sismember("#{key_base}:resources:resource-3", 'group-1')
49
- refute redis.sismember("#{key_base}:resources:resource-4", 'group-1')
50
-
51
- assert redis.sismember("#{key_base}:resources:resource-1", 'group-2')
52
- refute redis.sismember("#{key_base}:resources:resource-2", 'group-2')
53
- assert redis.sismember("#{key_base}:resources:resource-3", 'group-2')
54
- assert redis.sismember("#{key_base}:resources:resource-4", 'group-2')
80
+ cs.calculate_similarity
81
+ expected = [
82
+ {resource: 'resource-3', similarity: 1.0},
83
+ {resource: 'resource-4', similarity: 0.667},
84
+ {resource: 'resource-2', similarity: 0.667}
85
+ ]
86
+ assert_equal expected, cs.similar_to('resource-1')
87
+ end
88
+
89
+ def test_recommends_when_added_by_group_with_scores
90
+ redis = Redis.new(db: 15)
91
+ redis.flushdb
92
+ key_base = 'CommendoTests'
93
+ cs = ContentSet.new(redis, key_base)
94
+ cs.add_by_group('group-1', ['resource-1', 2], ['resource-2', 3], ['resource-3', 7])
95
+ cs.add_by_group('group-2', ['resource-1', 2], ['resource-3', 3], ['resource-4', 5])
96
+ cs.calculate_similarity
97
+ expected = [
98
+ {resource: 'resource-3', similarity: 1.0},
99
+ {resource: 'resource-4', similarity: 0.778},
100
+ {resource: 'resource-2', similarity: 0.714}
101
+ ]
102
+ assert_equal expected, cs.similar_to('resource-1')
103
+ end
104
+
105
+ def test_recommendations_are_isolated_by_key_base
106
+ skip
107
+ end
108
+
109
+ def test_recommendations_are_isolated_by_redis_db
110
+ skip
55
111
  end
56
112
 
57
113
  def test_calculates_similarity_scores
@@ -66,12 +122,12 @@ module Commendo
66
122
  end
67
123
  cs.calculate_similarity
68
124
  expected = [
69
- {resource: '9', similarity: 0.5},
70
- {resource: '6', similarity: 0.5},
71
- {resource: '12', similarity: 0.333},
72
- {resource: '3', similarity: 0.25},
73
- {resource: '21', similarity: 0.167},
74
- {resource: '15', similarity: 0.167}
125
+ {resource: '9', similarity: 0.667},
126
+ {resource: '6', similarity: 0.667},
127
+ {resource: '12', similarity: 0.5},
128
+ {resource: '3', similarity: 0.4},
129
+ {resource: '21', similarity: 0.286},
130
+ {resource: '15', similarity: 0.286}
75
131
  ]
76
132
  assert_equal expected, cs.similar_to(18)
77
133
  end
@@ -88,8 +144,9 @@ module Commendo
88
144
  end
89
145
  cs.calculate_similarity(0.4)
90
146
  expected = [
91
- {resource: '9', similarity: 0.5},
92
- {resource: '6', similarity: 0.5},
147
+ {resource: '9', similarity: 0.667},
148
+ {resource: '6', similarity: 0.667},
149
+ {resource: '12', similarity: 0.5}
93
150
  ]
94
151
  assert_equal expected, cs.similar_to(18)
95
152
  end
@@ -250,17 +307,17 @@ module Commendo
250
307
  end
251
308
  cs.calculate_similarity
252
309
  expected = [
253
- {resource: '18', similarity: 1.333},
254
- {resource: '3', similarity: 1.25},
255
- {resource: '6', similarity: 0.833},
256
- {resource: '12', similarity: 0.7},
257
- {resource: '21', similarity: 0.667},
258
- {resource: '15', similarity: 0.667},
259
- {resource: '9', similarity: 0.533},
260
- {resource: '4', similarity: 0.25},
261
- {resource: '8', similarity: 0.2},
262
- {resource: '16', similarity: 0.167},
263
- {resource: '20', similarity: 0.143}
310
+ {resource: '18', similarity: 1.834},
311
+ {resource: '3', similarity: 1.734},
312
+ {resource: '6', similarity: 1.167},
313
+ {resource: '21', similarity: 1.086},
314
+ {resource: '15', similarity: 1.086},
315
+ {resource: '12', similarity: 1.0},
316
+ {resource: '9', similarity: 0.833},
317
+ {resource: '4', similarity: 0.4},
318
+ {resource: '8', similarity: 0.333},
319
+ {resource: '16', similarity: 0.286},
320
+ {resource: '20', similarity: 0.25}
264
321
  ]
265
322
  actual = cs.similar_to([12, 6, 9])
266
323
  assert_equal expected, actual
@@ -19,9 +19,9 @@ module Commendo
19
19
  @cs3 = ContentSet.new(@redis, 'CommendoTests:ContentSet3', @tag_set)
20
20
  (3..23).each do |group|
21
21
  (3..23).each do |res|
22
- @cs1.add_by_group(group, res) if (res % group == 0) && (res % 2 == 0)
23
- @cs2.add_by_group(group, res) if (res % group == 0) && (res % 3 == 0)
24
- @cs3.add_by_group(group, res) if (res % group == 0) && (res % 6 == 0)
22
+ @cs1.add_by_group(group, res) if res.modulo(group).zero? && res.modulo(2).zero?
23
+ @cs2.add_by_group(group, res) if res.modulo(group).zero? && res.modulo(3).zero?
24
+ @cs3.add_by_group(group, res) if res.modulo(group).zero? && res.modulo(6).zero?
25
25
  @tag_set.add(res, 'mod3') if res.modulo(3).zero?
26
26
  @tag_set.add(res, 'mod4') if res.modulo(4).zero?
27
27
  @tag_set.add(res, 'mod5') if res.modulo(5).zero?
@@ -40,12 +40,12 @@ module Commendo
40
40
  {cs: @cs3, weight: 100.0}
41
41
  )
42
42
  expected = [
43
- {resource: '6', similarity: 55.5},
44
- {resource: '12', similarity: 36.963},
45
- {resource: '9', similarity: 5.0},
46
- {resource: '3', similarity: 2.5},
47
- {resource: '21', similarity: 1.67},
48
- {resource: '15', similarity: 1.67}
43
+ {resource: '6', similarity: 74.037},
44
+ {resource: '12', similarity: 55.5},
45
+ {resource: '9', similarity: 6.67},
46
+ {resource: '3', similarity: 4.0},
47
+ {resource: '21', similarity: 2.86},
48
+ {resource: '15', similarity: 2.86}
49
49
  ]
50
50
  assert_equal expected, weighted_group.similar_to(18)
51
51
  end
@@ -58,7 +58,7 @@ module Commendo
58
58
  {cs: @cs2, weight: 10.0},
59
59
  {cs: @cs3, weight: 100.0}
60
60
  )
61
- expected = [{resource: '15', similarity: 1.67}]
61
+ expected = [{resource: '15', similarity: 2.86}]
62
62
  weighted_group.tag_set = @tag_set
63
63
  assert_equal expected, weighted_group.filtered_similar_to(18, include: ['mod5'])
64
64
  end
@@ -72,10 +72,10 @@ module Commendo
72
72
  {cs: @cs3, weight: 100.0}
73
73
  )
74
74
  expected = [
75
- {resource: '6', similarity: 55.5},
76
- {resource: '12', similarity: 36.963},
77
- {resource: '9', similarity: 5.0},
78
- {resource: '3', similarity: 2.5}
75
+ {resource: '6', similarity: 74.037},
76
+ {resource: '12', similarity: 55.5},
77
+ {resource: '9', similarity: 6.67},
78
+ {resource: '3', similarity: 4.0}
79
79
  ]
80
80
  weighted_group.tag_set = @tag_set
81
81
  assert_equal expected, weighted_group.filtered_similar_to(18, exclude: ['mod5', 'mod7'])
@@ -90,9 +90,9 @@ module Commendo
90
90
  {cs: @cs3, weight: 1.0}
91
91
  )
92
92
  expected = [
93
- {resource: '16', similarity: 66.7},
94
- {resource: '4', similarity: 50.0},
95
- {resource: '12', similarity: 20.0}
93
+ {resource: '16', similarity: 80.0},
94
+ {resource: '4', similarity: 66.7},
95
+ {resource: '12', similarity: 33.3}
96
96
  ]
97
97
  weighted_group.tag_set = @tag_set
98
98
  assert_equal expected, weighted_group.filtered_similar_to(8, include: ['mod4'], exclude: ['mod5'])
@@ -107,16 +107,16 @@ module Commendo
107
107
  {cs: @cs3, weight: 1.0}
108
108
  )
109
109
  expected = [
110
- {resource: '12', similarity: 83.0},
111
- {resource: '18', similarity: 58.0},
112
- {resource: '8', similarity: 50.0},
113
- {resource: '16', similarity: 33.3},
114
- {resource: '20', similarity: 25.0},
115
- {resource: '9', similarity: 8.33},
116
- {resource: '21', similarity: 5.83},
117
- {resource: '15', similarity: 5.83},
118
- {resource: '6', similarity: 5.0},
119
- {resource: '3', similarity: 5.0}
110
+ {resource: '12', similarity: 118.037},
111
+ {resource: '18', similarity: 78.037},
112
+ {resource: '8', similarity: 66.7},
113
+ {resource: '16', similarity: 50.0},
114
+ {resource: '20', similarity: 40.0},
115
+ {resource: '9', similarity: 11.67},
116
+ {resource: '21', similarity: 9.0},
117
+ {resource: '15', similarity: 9.0},
118
+ {resource: '6', similarity: 6.67},
119
+ {resource: '3', similarity: 6.67}
120
120
  ]
121
121
  weighted_group.tag_set = @tag_set
122
122
  assert_equal expected, weighted_group.similar_to([3,4,5,6,7])
@@ -131,16 +131,9 @@ module Commendo
131
131
  {cs: @cs3, weight: 1.0}
132
132
  )
133
133
  expected = [
134
- {resource: '12', similarity: 83.0},
135
- #{resource: '18', similarity: 58.0},
136
- {resource: '8', similarity: 50.0},
137
- {resource: '16', similarity: 33.3},
138
- #{resource: '20', similarity: 25.0},
139
- #{resource: '9', similarity: 8.33},
140
- #{resource: '21', similarity: 5.83},
141
- #{resource: '15', similarity: 5.83},
142
- #{resource: '6', similarity: 5.0},
143
- #{resource: '3', similarity: 5.0}
134
+ {resource: '12', similarity: 118.037},
135
+ {resource: '8', similarity: 66.7},
136
+ {resource: '16', similarity: 50.0},
144
137
  ]
145
138
  weighted_group.tag_set = @tag_set
146
139
  assert_equal expected, weighted_group.filtered_similar_to([3,4,5,6,7], include: ['mod4'], exclude: ['mod5'])
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: commendo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Styles
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-01 00:00:00.000000000 Z
11
+ date: 2014-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -97,7 +97,11 @@ dependencies:
97
97
  description: A Jaccard-similarity recommender using Redis sets
98
98
  email:
99
99
  - rob.styles@dynamicorange.com
100
- executables: []
100
+ executables:
101
+ - commendo-find-identical-pairs
102
+ - commendo-load
103
+ - commendo-recommendations-distribution
104
+ - commendo-similarity-distribution
101
105
  extensions: []
102
106
  extra_rdoc_files: []
103
107
  files:
@@ -107,6 +111,10 @@ files:
107
111
  - LICENSE.txt
108
112
  - README.md
109
113
  - Rakefile
114
+ - bin/commendo-find-identical-pairs
115
+ - bin/commendo-load
116
+ - bin/commendo-recommendations-distribution
117
+ - bin/commendo-similarity-distribution
110
118
  - commendo.gemspec
111
119
  - lib/commendo.rb
112
120
  - lib/commendo/content_set.rb