jekyll-related-posts 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +95 -0
- data/jekyll-related-posts.gemspec +38 -0
- data/lib/_config.yml +5 -0
- data/lib/jekyll-related-posts.rb +220 -0
- data/lib/related.html +12 -0
- metadata +188 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d2802d28e9109784b9d9aed5987459d7c085b29e
|
4
|
+
data.tar.gz: b4ec98641cbe32e590b2a6aa428013c5aa50b266
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 70ad01243ce8f17f133d3c56cf2d55bb5316881b2fb50d13c442fb0cb9953528a07a2dde7c304ce4b9ecda543c1bb83257923b96f628697b93804cd3af5320f1
|
7
|
+
data.tar.gz: d0c17c9d025e12c51df2ce7d4ddfe0a6d15baf63454a6e09052906342d4acdf8d5e1d4c2bfb1adf970eedf6eb0621a7f60cfcfb7c3a107da67c0ec0a380016b7
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Amadeusz Juskowiak
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# jekyll-related-posts
|
2
|
+
|
3
|
+
Proper related posts plugin for [Jekyll](http://jekyllrb.com) - uses document correlation matrix on TF-IDF (optionally with Latent Semantic Indexing).
|
4
|
+
|
5
|
+
## Example
|
6
|
+
|
7
|
+
Example is provided at http://jekyll-related-posts.dev.amadeusz.me - posts are
|
8
|
+
based on [Reuters-21578](https://archive.ics.uci.edu/ml/datasets/Reuters-21578+Text+Categorization+Collection) data set.
|
9
|
+
|
10
|
+
## Introduction
|
11
|
+
|
12
|
+
I am going to try to start blogging, again. Anyway I am studying at
|
13
|
+
Decision Support Systems Group and I have found document correlation
|
14
|
+
problem somehow interesting.
|
15
|
+
|
16
|
+
For my own purposes I have created related posts Jekyll plugin based on well
|
17
|
+
known algorithms such as [TFIDF](https://en.wikipedia.org/wiki/Tf–idf)
|
18
|
+
and [LSI](https://en.wikipedia.org/wiki/Latent_semantic_indexing).
|
19
|
+
|
20
|
+
## How to install
|
21
|
+
|
22
|
+
Initialy you had to install the plugin manually, however the plugin is a
|
23
|
+
gem now - follow instructions to install the plugin:
|
24
|
+
|
25
|
+
1. Install the gem `jekyll-related-posts`:
|
26
|
+
- if you are using bundler add `gem 'jekyll-related-posts'` to your
|
27
|
+
`Gemfile` and run `bundle install`,
|
28
|
+
- or install gem via `gem install jekyll-related-posts`.
|
29
|
+
2. Insert `gems: ['jekyll-related-posts']` to your `_config.yml`.
|
30
|
+
3. Insert `<related-posts />` somewhere in your `_layouts/post.html`
|
31
|
+
file.
|
32
|
+
4. Run `jekyll build`, don't forget to blog about the plugin!
|
33
|
+
|
34
|
+
### Customization
|
35
|
+
|
36
|
+
You can customize default related posts template by creating
|
37
|
+
`related.html` in your layouts directory. Plugin behaviour can be
|
38
|
+
altered by options in `_config.yml`, under `related:` section.
|
39
|
+
|
40
|
+
## Basis of operation
|
41
|
+
|
42
|
+
Each document is
|
43
|
+
[tokenized](https://en.wikipedia.org/wiki/Tokenization_(lexical_analysis))
|
44
|
+
and [stemmed](https://en.wikipedia.org/wiki/Stemming), every word found
|
45
|
+
is treated as keyword for analysis (except for some [stop
|
46
|
+
words](https://en.wikipedia.org/wiki/Stop_words)).
|
47
|
+
|
48
|
+
TF-IDF matrix for the whole site is calculated (including extra provided
|
49
|
+
weights), then if given accuraccy is lower than 1.0, LSI algorithm
|
50
|
+
is used to compute new simplified vector space. Document correlation
|
51
|
+
matrix is created using dot product of the matrix and its transpose.
|
52
|
+
|
53
|
+
For each of the post' related documents are inserted into priority queue
|
54
|
+
(sorted by score from document correlation matrix), assuming the score
|
55
|
+
is greater than minimal required score. Selected few bests related posts
|
56
|
+
are retrieven from the queue.
|
57
|
+
|
58
|
+
Liquid template for each post is rendered and `<related-posts />` is
|
59
|
+
replaced with the outcomes of algorithm.
|
60
|
+
|
61
|
+
## Configuration
|
62
|
+
|
63
|
+
In your `_config.yml` file (under `related:`) you can configure:
|
64
|
+
|
65
|
+
- `max_count: 5` - maximum number of related posts,
|
66
|
+
- `min_score: 0.1` - minimal required score to treat post as related,
|
67
|
+
- `accuracy: 0.75` - percentage of keywords used as basis for document
|
68
|
+
correlation matrix (if 1.0 then no LSI is computed, otherwise LSI is
|
69
|
+
computed and dimensions are reduced to `accuracy * |keywords|`)
|
70
|
+
|
71
|
+
### Weights
|
72
|
+
|
73
|
+
You can configure weights of words providing dictionary with them to
|
74
|
+
`weights`. In example weight of `2` means for term frequency algorithm
|
75
|
+
that the word occured twice as much in the document as in reality.
|
76
|
+
|
77
|
+
## Benchmark
|
78
|
+
|
79
|
+
For casual blogs, performance should not be an issue.
|
80
|
+
|
81
|
+
I did not benchmark the plugin, however for the dataset given in the
|
82
|
+
example (containing ~900 documents, ~7000 keywords) rendering time
|
83
|
+
(including Jekyll hoodoo stuff) is more less 70 seconds (on Xeon, using
|
84
|
+
750MB RAM). Computation related to this plugin is about 20 seconds
|
85
|
+
long. It should be noticed that I'm using OpenBLAS and standard LAPACK
|
86
|
+
distributed with Ubuntu (performance is similar on OS X using builtin
|
87
|
+
Acccelerate framework).
|
88
|
+
|
89
|
+
Unfortunately the plugin is not compatible with Jekyll 3.0 new
|
90
|
+
incremental builds, even though it requires at least Jekyll 3.0 (for the
|
91
|
+
plugin hooks).
|
92
|
+
|
93
|
+
## Authors
|
94
|
+
|
95
|
+
- Amadeusz Juskowiak - juskowiak[at]amadeusz.me
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "jekyll-related-posts"
|
7
|
+
spec.version = "0.1.1"
|
8
|
+
spec.authors = ["Amadeusz Juskowiak"]
|
9
|
+
spec.email = ["juskowiak@amadeusz.me"]
|
10
|
+
spec.summary = %q{Proper related posts plugin for Jekyll - uses document correlation matrix on TF-IDF (optionally with Latent Semantic Indexing).}
|
11
|
+
spec.description = %q{Proper related posts plugin for Jekyll - uses document correlation matrix on TF-IDF (optionally with Latent Semantic Indexing).
|
12
|
+
|
13
|
+
Each document is tokenized and stemmed, every word found is treated as keyword for analysis (except for some stop words).
|
14
|
+
|
15
|
+
TF-IDF matrix for the whole site is calculated (including extra provided weights), then if given accuraccy is lower than 1.0, LSI algorithm is used to compute new simplified vector space. Document correlation matrix is created using dot product of the matrix and its transpose.
|
16
|
+
|
17
|
+
For each of the post' related documents are inserted into priority queue (sorted by score from document correlation matrix), assuming the score is greater than minimal required score. Selected few bests related posts are retrieven from the queue.
|
18
|
+
|
19
|
+
Liquid template for each post is rendered and <related-posts /> is replaced with the outcomes of algorithm.}
|
20
|
+
spec.homepage = "https://github.com/alfanick/jekyll-related-posts"
|
21
|
+
spec.license = "MIT"
|
22
|
+
|
23
|
+
spec.files = `git ls-files -z`.split("\x0")
|
24
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
25
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
29
|
+
|
30
|
+
spec.add_runtime_dependency "jekyll", "~> 3.0"
|
31
|
+
spec.add_runtime_dependency "liquid", "~> 3.0"
|
32
|
+
spec.add_runtime_dependency "tokenizer", "~> 0.1"
|
33
|
+
spec.add_runtime_dependency "stopwords-filter", "~> 0.3"
|
34
|
+
spec.add_runtime_dependency "fast-stemmer", "~> 1.0"
|
35
|
+
spec.add_runtime_dependency "pqueue", "~> 2.1"
|
36
|
+
spec.add_runtime_dependency "nmatrix", "~> 0.2"
|
37
|
+
spec.add_runtime_dependency "nmatrix-lapacke", "~> 0.2"
|
38
|
+
end
|
data/lib/_config.yml
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'jekyll'
|
3
|
+
require 'singleton'
|
4
|
+
require 'tokenizer'
|
5
|
+
require 'yaml'
|
6
|
+
require 'liquid'
|
7
|
+
require 'fast_stemmer'
|
8
|
+
require 'stopwords'
|
9
|
+
require 'pqueue'
|
10
|
+
require 'nmatrix'
|
11
|
+
require 'nmatrix/lapacke'
|
12
|
+
|
13
|
+
module Amadeusz
|
14
|
+
module Jekyll
|
15
|
+
class RelatedPosts
|
16
|
+
include Singleton
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@posts = Array.new
|
20
|
+
@keywords = Array.new
|
21
|
+
@tokenizer = Tokenizer::Tokenizer.new(:en)
|
22
|
+
@stopwords_filter = Stopwords::Snowball::Filter.new('en')
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_post(post)
|
26
|
+
post = {
|
27
|
+
url: post.url,
|
28
|
+
title: post.data['title'].dup,
|
29
|
+
content: (stem(post.content) + stem(post.data['title']))
|
30
|
+
}
|
31
|
+
|
32
|
+
@posts << post
|
33
|
+
@keywords += post[:content]
|
34
|
+
@keywords.uniq!
|
35
|
+
end
|
36
|
+
|
37
|
+
def build!(site)
|
38
|
+
conf = config(site)
|
39
|
+
@weights = keywords_weights(conf['weights'])
|
40
|
+
related = find_releated(conf['max_count'], conf['min_score'], conf['accuracy'])
|
41
|
+
template = Liquid::Template.parse(File.read(template_path(site)))
|
42
|
+
|
43
|
+
@posts.each do |post|
|
44
|
+
filename = File.join(site.config['destination'], post[:url])
|
45
|
+
rendered = File.read(filename)
|
46
|
+
|
47
|
+
output = template.render('related_posts' => related[post])
|
48
|
+
|
49
|
+
rendered.gsub! '<related-posts />', output
|
50
|
+
File.write(filename, rendered)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def config(site)
|
57
|
+
builtin_file = File.join(File.absolute_path(File.dirname(__FILE__)), '_config.yml')
|
58
|
+
defaults = YAML.load_file(builtin_file)
|
59
|
+
|
60
|
+
defaults['related'].merge(site.config['related'] || {})
|
61
|
+
end
|
62
|
+
|
63
|
+
def template_path(site)
|
64
|
+
site_file = File.join(site.config['source'], site.config['layouts_dir'], 'related.html')
|
65
|
+
builtin_file = File.join(File.absolute_path(File.dirname(__FILE__)), 'related.html')
|
66
|
+
|
67
|
+
if File.exist? site_file
|
68
|
+
site_file
|
69
|
+
else
|
70
|
+
builtin_file
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def find_releated(count = 5, min_score = -10.0, accuracy = 1.0)
|
75
|
+
dc = document_correleation(accuracy)
|
76
|
+
result = Hash.new
|
77
|
+
count = [count, @posts.size].min
|
78
|
+
|
79
|
+
@posts.each_with_index do |post, index|
|
80
|
+
queue = PQueue.new(dc.row(index).each_with_index.select{|s,_| s>=min_score}) do |a, b|
|
81
|
+
a[0] > b[0]
|
82
|
+
end
|
83
|
+
|
84
|
+
result[post] = []
|
85
|
+
count.times do
|
86
|
+
score, id = queue.pop
|
87
|
+
break unless score
|
88
|
+
begin
|
89
|
+
result[post] << {
|
90
|
+
'score' => score,
|
91
|
+
'url' => @posts[id][:url],
|
92
|
+
'title' => @posts[id][:title]
|
93
|
+
}
|
94
|
+
rescue
|
95
|
+
break
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
return result
|
101
|
+
end
|
102
|
+
|
103
|
+
def lsi(matrix, accuracy)
|
104
|
+
degree = (@keywords.size * accuracy - 1).floor
|
105
|
+
u, sigma, vt = matrix.transpose.gesdd
|
106
|
+
|
107
|
+
u2 = u.slice(0..degree, 0..degree)
|
108
|
+
sigma_d = NMatrix.zeros([degree+1, @posts.size])
|
109
|
+
sigma.each_with_indices do |v, i, j|
|
110
|
+
break if i > degree
|
111
|
+
sigma_d[i, i] = v
|
112
|
+
end
|
113
|
+
|
114
|
+
return u2.dot(sigma_d).dot(vt).transpose
|
115
|
+
end
|
116
|
+
|
117
|
+
def document_correleation(accuracy = 1.0)
|
118
|
+
if accuracy == 1.0
|
119
|
+
scores = tfidf
|
120
|
+
else
|
121
|
+
scores = lsi(tfidf, accuracy)
|
122
|
+
end
|
123
|
+
|
124
|
+
result = scores.dot(scores.transpose)
|
125
|
+
|
126
|
+
result.each_with_indices do |_, u, v|
|
127
|
+
if u != v
|
128
|
+
result[u, v] /= (result[u, u] + result[v, v] - result[u, v])
|
129
|
+
else
|
130
|
+
result[u, v] = 0.0
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
return result
|
135
|
+
end
|
136
|
+
|
137
|
+
def bag_of_words
|
138
|
+
result = NMatrix.new([@posts.size, @keywords.size], 0.0)
|
139
|
+
@max = NMatrix.new([@posts.size], 0.0)
|
140
|
+
|
141
|
+
result.each_with_indices do |_, pi, ki|
|
142
|
+
result[pi, ki] = @posts[pi][:content].count(@keywords[ki])
|
143
|
+
|
144
|
+
if result[pi, ki] > @max[pi]
|
145
|
+
@max[pi] = result[pi, ki]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
@bag_of_words = result.dup
|
150
|
+
return result
|
151
|
+
end
|
152
|
+
|
153
|
+
def term_frequency
|
154
|
+
result = bag_of_words
|
155
|
+
|
156
|
+
result.rows.times do |r|
|
157
|
+
result[r, 0..-1] *= @weights
|
158
|
+
result[r, 0..-1] /= @max[r]
|
159
|
+
end
|
160
|
+
|
161
|
+
return result
|
162
|
+
end
|
163
|
+
|
164
|
+
def keywords_weights(weights)
|
165
|
+
result = NMatrix.new([1, @keywords.size], 1.0)
|
166
|
+
|
167
|
+
weights.each do |word, weight|
|
168
|
+
keyword = word.to_s.stem.to_sym
|
169
|
+
|
170
|
+
next unless @keywords.include? keyword
|
171
|
+
|
172
|
+
result[0, @keywords.index(keyword)] = weight
|
173
|
+
end
|
174
|
+
|
175
|
+
return result
|
176
|
+
end
|
177
|
+
|
178
|
+
def inverse_document_frequency
|
179
|
+
result = NMatrix.new([1, @keywords.size], 0.0)
|
180
|
+
|
181
|
+
@bag_of_words.each_column do |column|
|
182
|
+
occurences = column.reduce do |m, c|
|
183
|
+
m + (c > 0 ? 1.0 : 0.0)
|
184
|
+
end
|
185
|
+
|
186
|
+
result[0, column.offset[1]] = Math.log(column.size / occurences) if occurences > 0
|
187
|
+
end
|
188
|
+
|
189
|
+
return result
|
190
|
+
end
|
191
|
+
|
192
|
+
def tfidf
|
193
|
+
result = term_frequency
|
194
|
+
idf = inverse_document_frequency
|
195
|
+
|
196
|
+
result.rows.times do |r|
|
197
|
+
result[r, 0..-1] *= idf
|
198
|
+
end
|
199
|
+
|
200
|
+
return result
|
201
|
+
end
|
202
|
+
|
203
|
+
def stem(data)
|
204
|
+
tokenized = @tokenizer.tokenize(data.gsub(/[^a-z \t'_\-\n.,+]/i, '')).map(&:downcase)
|
205
|
+
filtered = @stopwords_filter.filter(tokenized)
|
206
|
+
stemmed = filtered.map(&:stem).select{|s| not s.empty?}.map(&:to_sym)
|
207
|
+
|
208
|
+
return stemmed
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
Jekyll::Hooks.register :posts, :pre_render do |post|
|
215
|
+
Amadeusz::Jekyll::RelatedPosts.instance.add_post(post)
|
216
|
+
end
|
217
|
+
|
218
|
+
Jekyll::Hooks.register :site, :post_write do |site|
|
219
|
+
Amadeusz::Jekyll::RelatedPosts.instance.build! site
|
220
|
+
end
|
data/lib/related.html
ADDED
metadata
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll-related-posts
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Amadeusz Juskowiak
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jekyll
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: liquid
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: tokenizer
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: stopwords-filter
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.3'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: fast-stemmer
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pqueue
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.1'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.1'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: nmatrix
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.2'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.2'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: nmatrix-lapacke
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.2'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.2'
|
139
|
+
description: |-
|
140
|
+
Proper related posts plugin for Jekyll - uses document correlation matrix on TF-IDF (optionally with Latent Semantic Indexing).
|
141
|
+
|
142
|
+
Each document is tokenized and stemmed, every word found is treated as keyword for analysis (except for some stop words).
|
143
|
+
|
144
|
+
TF-IDF matrix for the whole site is calculated (including extra provided weights), then if given accuraccy is lower than 1.0, LSI algorithm is used to compute new simplified vector space. Document correlation matrix is created using dot product of the matrix and its transpose.
|
145
|
+
|
146
|
+
For each of the post' related documents are inserted into priority queue (sorted by score from document correlation matrix), assuming the score is greater than minimal required score. Selected few bests related posts are retrieven from the queue.
|
147
|
+
|
148
|
+
Liquid template for each post is rendered and <related-posts /> is replaced with the outcomes of algorithm.
|
149
|
+
email:
|
150
|
+
- juskowiak@amadeusz.me
|
151
|
+
executables: []
|
152
|
+
extensions: []
|
153
|
+
extra_rdoc_files: []
|
154
|
+
files:
|
155
|
+
- ".gitignore"
|
156
|
+
- Gemfile
|
157
|
+
- LICENSE.txt
|
158
|
+
- README.md
|
159
|
+
- jekyll-related-posts.gemspec
|
160
|
+
- lib/_config.yml
|
161
|
+
- lib/jekyll-related-posts.rb
|
162
|
+
- lib/related.html
|
163
|
+
homepage: https://github.com/alfanick/jekyll-related-posts
|
164
|
+
licenses:
|
165
|
+
- MIT
|
166
|
+
metadata: {}
|
167
|
+
post_install_message:
|
168
|
+
rdoc_options: []
|
169
|
+
require_paths:
|
170
|
+
- lib
|
171
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
requirements: []
|
182
|
+
rubyforge_project:
|
183
|
+
rubygems_version: 2.2.2
|
184
|
+
signing_key:
|
185
|
+
specification_version: 4
|
186
|
+
summary: Proper related posts plugin for Jekyll - uses document correlation matrix
|
187
|
+
on TF-IDF (optionally with Latent Semantic Indexing).
|
188
|
+
test_files: []
|