natural 0.5.4 → 0.6.1

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.
Files changed (82) hide show
  1. package/.dockerignore +2 -0
  2. package/.npmignore +1 -0
  3. package/.travis.yml +1 -1
  4. package/README.md +412 -89
  5. package/bayes_classifier.json +1 -1
  6. package/classifier.json +418 -0
  7. package/data/kata-dasar.txt +29933 -0
  8. package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tagger.js +8 -9
  9. package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tester.js +5 -5
  10. package/lib/natural/brill_pos_tagger/lib/Brill_POS_Trainer.js +10 -6
  11. package/lib/natural/brill_pos_tagger/lib/Corpus.js +21 -16
  12. package/lib/natural/brill_pos_tagger/lib/Lexicon.js +22 -13
  13. package/lib/natural/brill_pos_tagger/lib/Predicate.js +2 -2
  14. package/lib/natural/brill_pos_tagger/lib/RuleTemplate.js +2 -2
  15. package/lib/natural/brill_pos_tagger/lib/RuleTemplates.js +132 -146
  16. package/lib/natural/brill_pos_tagger/lib/Sentence.js +41 -0
  17. package/lib/natural/brill_pos_tagger/lib/TF_Parser.js +83 -76
  18. package/lib/natural/brill_pos_tagger/lib/TF_Parser.pegjs +19 -4
  19. package/lib/natural/brill_pos_tagger/lib/TransformationRule.js +10 -11
  20. package/lib/natural/classifiers/classifier.js +15 -8
  21. package/lib/natural/classifiers/maxent/Classifier.js +125 -0
  22. package/lib/natural/classifiers/maxent/Context.js +33 -0
  23. package/lib/natural/classifiers/maxent/Distribution.js +145 -0
  24. package/lib/natural/classifiers/maxent/Element.js +34 -0
  25. package/lib/natural/classifiers/maxent/Feature.js +88 -0
  26. package/lib/natural/classifiers/maxent/FeatureSet.js +67 -0
  27. package/lib/natural/classifiers/maxent/GISScaler.js +148 -0
  28. package/lib/natural/classifiers/maxent/POS/ME_Corpus.js +56 -0
  29. package/lib/natural/classifiers/maxent/POS/ME_Sentence.js +73 -0
  30. package/lib/natural/classifiers/maxent/POS/POS_Element.js +172 -0
  31. package/lib/natural/classifiers/maxent/Sample.js +141 -0
  32. package/lib/natural/classifiers/maxent/SimpleExample/SE_Element.js +49 -0
  33. package/lib/natural/distance/hamming_distance.js +52 -0
  34. package/lib/natural/distance/jaro-winkler_distance.js +16 -6
  35. package/lib/natural/distance/levenshtein_distance.js +164 -11
  36. package/lib/natural/index.js +26 -5
  37. package/lib/natural/inflectors/noun_inflector.js +15 -13
  38. package/lib/natural/ngrams/ngrams.js +83 -25
  39. package/lib/natural/normalizers/normalizer_sv.js +41 -0
  40. package/lib/natural/sentiment/Basque/senticon_eu.json +21557 -0
  41. package/lib/natural/sentiment/Catalan/senticon_ca.json +36352 -0
  42. package/lib/natural/sentiment/Dutch/negations_du.json +3 -0
  43. package/lib/natural/sentiment/Dutch/pattern-sentiment-nl.json +39650 -0
  44. package/lib/natural/sentiment/English/negations_en.json +3 -0
  45. package/lib/natural/sentiment/English/pattern-sentiment-en.json +16024 -0
  46. package/lib/natural/sentiment/English/senticon_en.json +124197 -0
  47. package/lib/natural/sentiment/French/pattern-sentiment-fr.json +40906 -0
  48. package/lib/natural/sentiment/Galician/senticon_gl.json +24427 -0
  49. package/lib/natural/sentiment/Italian/pattern-sentiment-it.json +24522 -0
  50. package/lib/natural/sentiment/SentimentAnalyzer.js +116 -0
  51. package/lib/natural/sentiment/Spanish/afinnShortSortedSpanish.json +1655 -0
  52. package/lib/natural/sentiment/Spanish/negations_es.json +3 -0
  53. package/lib/natural/sentiment/Spanish/senticon_es.json +56722 -0
  54. package/lib/natural/sentiment/tools/README.md +31 -0
  55. package/lib/natural/sentiment/tools/XmlParser4PatternData.js +31 -0
  56. package/lib/natural/sentiment/tools/sentimentXmlParser.js +30 -0
  57. package/lib/natural/stemmers/indonesian/base_stemmer_id.js +86 -0
  58. package/lib/natural/stemmers/indonesian/prefix_rules.js +1027 -0
  59. package/lib/natural/stemmers/indonesian/removal.js +49 -0
  60. package/lib/natural/stemmers/indonesian/stemmer_id.js +307 -0
  61. package/lib/natural/stemmers/indonesian/suffix_rules.js +75 -0
  62. package/lib/natural/stemmers/porter_stemmer_es.js +265 -199
  63. package/lib/natural/stemmers/porter_stemmer_fr.js +1 -1
  64. package/lib/natural/stemmers/porter_stemmer_nl.js +362 -0
  65. package/lib/natural/stemmers/porter_stemmer_sv.js +99 -0
  66. package/lib/natural/stemmers/stemmer_es.js +18 -12
  67. package/lib/natural/stemmers/stemmer_nl.js +64 -0
  68. package/lib/natural/stemmers/stemmer_sv.js +61 -0
  69. package/lib/natural/tfidf/tfidf.js +27 -4
  70. package/lib/natural/tokenizers/aggressive_tokenizer_id.js +44 -0
  71. package/lib/natural/tokenizers/aggressive_tokenizer_sv.js +40 -0
  72. package/lib/natural/tokenizers/aggressive_tokenizer_vi.js +37 -0
  73. package/lib/natural/tokenizers/orthography_matchers.js +33 -0
  74. package/lib/natural/tokenizers/regexp_tokenizer.js +23 -3
  75. package/lib/natural/tokenizers/sentence_tokenizer.js +1 -1
  76. package/lib/natural/util/stopwords_id.js +119 -0
  77. package/lib/natural/util/stopwords_no.js +1 -1
  78. package/lib/natural/util/stopwords_sv.js +70 -0
  79. package/lib/natural/wordnet/wordnet_file.js +7 -1
  80. package/lr_classifier.json +1 -1
  81. package/package-lock.json +378 -0
  82. package/package.json +29 -21
@@ -0,0 +1,362 @@
1
+ /*
2
+ Copyright (c) 2018, Hugo W.L. ter Doest
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+ */
22
+
23
+ /*
24
+ * Spec for the Dutch Porter Stemmer can be found at:
25
+ * http://snowball.tartarus.org/algorithms/dutch/stemmer.html
26
+ */
27
+ 'use strict'
28
+
29
+ var Stemmer = require('./stemmer_nl');
30
+
31
+ const DEBUG = false;
32
+ const vowels = "aeiouèy";
33
+
34
+
35
+ function isVowel(x) {
36
+ return vowels.indexOf(x) > -1;
37
+ }
38
+
39
+
40
+ // * Return longest matching suffixes for a token or '' if no suffix match
41
+ String.prototype.endsinArr = function(suffixes) {
42
+ var i, longest = '';
43
+ for (i = 0; i < suffixes.length; i++) {
44
+ if (this.endsin(suffixes[i]) && suffixes[i].length > longest.length)
45
+ longest = suffixes[i];
46
+ }
47
+
48
+ if (DEBUG && longest != "") {
49
+ console.log("Matched suffix: " + longest);
50
+ }
51
+ return longest;
52
+ };
53
+
54
+
55
+ // Returns true if token has suffix
56
+ String.prototype.endsin = function(suffix) {
57
+ if (this.length < suffix.length) return false;
58
+ return (this.slice(-suffix.length) == suffix);
59
+ };
60
+
61
+
62
+ // Removes a suffix of len characters and returns the string
63
+ String.prototype.removeSuffix = function(len) {
64
+ return this.substr(0, this.length - len);
65
+ };
66
+
67
+
68
+ // Define undoubling the ending as removing the last letter if the word ends kk, dd or tt.
69
+ String.prototype.undoubleEnding = function() {
70
+ if (this.substr(-2) == "kk" || this.substr(-2) == "tt" || this.substr(-2) == "dd") {
71
+ return this.substr(0, this.length - 1);
72
+ }
73
+ else {
74
+ return this;
75
+ }
76
+ }
77
+
78
+
79
+ class PorterStemmer extends Stemmer {
80
+ constructor() {
81
+ super();
82
+ }
83
+
84
+
85
+ replaceAccentedCharacters(word) {
86
+ var accentedCharactersMapping = {
87
+ "ä": "a",
88
+ "ë": "e",
89
+ "ï": "i",
90
+ "ö": "o",
91
+ "ü": "u",
92
+ "á": "a",
93
+ "é": "e",
94
+ "í": "i",
95
+ "ó": "o",
96
+ "ú": "u"
97
+ }
98
+ var result = word;
99
+ for (var x in accentedCharactersMapping) {
100
+ result = result.replace(new RegExp(x, "g"), accentedCharactersMapping[x]);
101
+ }
102
+ if (DEBUG) {
103
+ console.log("replaceAccentedCharacters: " + result);
104
+ }
105
+ return result;
106
+ }
107
+
108
+
109
+ //Put initial y, y after a vowel, and i between vowels into upper case.
110
+ handleYI(word) {
111
+ // Initial y
112
+ var result = word.replace(/^y/, "Y");
113
+ if (DEBUG) {
114
+ console.log("handleYI: initial y: " + result);
115
+ }
116
+ // y after vowel
117
+ result = result.replace(/([aeioué])y/g, "$1Y");
118
+ if (DEBUG) {
119
+ console.log("handleYI: y after vowel: " + result);
120
+ }
121
+ // i between vowels
122
+ var result = result.replace(/([aeioué])i([aeioué])/g, "$1I$2");
123
+ if (DEBUG) {
124
+ console.log("handleYI: i between vowels:" + result);
125
+ }
126
+ return result;
127
+ }
128
+
129
+
130
+ // Determines R1 and R2; adapted from the French Porter Stemmer
131
+ markRegions(token) {
132
+ var r1, r2, len;
133
+
134
+ r1 = r2 = len = token.length;
135
+
136
+ // R1 is the region after the first non-vowel following a vowel,
137
+ for (var i = 0; i < len - 1 && r1 == len; i++) {
138
+ if (isVowel(token[i]) && !isVowel(token[i + 1])) {
139
+ r1 = i + 2;
140
+ }
141
+ }
142
+ // Or is the null region at the end of the word if there is no such non-vowel.
143
+
144
+ // R1 is adjusted such that the region before it contains at least 3 characters
145
+ if (r1 != len) {
146
+ // R1 is not null
147
+ if (r1 < 3) {
148
+ // Region before does not contain at least 3 characters
149
+ if (len > 3) {
150
+ r1 = 3;
151
+ // Now R1 contains at least 3 characters
152
+ }
153
+ else {
154
+ // It is not possible to make the region before long enough
155
+ r1 = len;
156
+ }
157
+ }
158
+ }
159
+
160
+ // R2 is the region after the first non-vowel following a vowel in R1
161
+ for (i = r1; i < len - 1 && r2 == len; i++) {
162
+ if (isVowel(token[i]) && !isVowel(token[i + 1])) {
163
+ r2 = i + 2;
164
+ }
165
+ }
166
+ // Or is the null region at the end of the word if there is no such non-vowel.
167
+
168
+ if (DEBUG) {
169
+ console.log("Regions r1 = " + r1 + " r2 = " + r2);
170
+ }
171
+
172
+ this.r1 = r1;
173
+ this.r2 = r2;
174
+ }
175
+
176
+
177
+ prelude(word) {
178
+ var result = this.replaceAccentedCharacters(word);
179
+ result = this.handleYI(result);
180
+ this.markRegions(result);
181
+ if (DEBUG) {
182
+ console.log("Prelude: " + result);
183
+ }
184
+ return result;
185
+ }
186
+
187
+
188
+ // (1b) en ene => delete if in R1 and preceded by a valid en-ending, and then undouble the ending
189
+ // Define a valid en-ending as a non-vowel, and not gem.
190
+ // Define undoubling the ending as removing the last letter if the word ends kk, dd or tt.
191
+ step1b(word, suffixes) {
192
+ var result = word;
193
+
194
+ var match = result.endsinArr(suffixes);
195
+ if (match != "") {
196
+ var pos = result.length - match.length;
197
+ if (pos >= this.r1) {
198
+ // check the character before the matched en/ene AND check for gem
199
+ if (!isVowel(result[pos - 1]) && result.substr(pos - 3, 3) !== "gem") {
200
+ // delete
201
+ result = result.removeSuffix(match.length);
202
+ // Undouble the ending
203
+ result = result.undoubleEnding();
204
+ }
205
+ }
206
+ }
207
+ if (DEBUG) {
208
+ console.log("step 1b: " + result);
209
+ }
210
+ return result;
211
+ }
212
+
213
+
214
+ step1(word) {
215
+ var result = word;
216
+ // (1a) heden => replace with heid if in R1
217
+ if (result.endsin("heden") && result.length - 5 >= this.r1) {
218
+ result = result.removeSuffix(5);
219
+ result += "heid";
220
+ }
221
+ if (DEBUG) {
222
+ console.log("step 1a: " + result);
223
+ }
224
+
225
+ result = this.step1b(result, ["en", "ene"]);
226
+
227
+ // (1c) s se => delete if in R1 and preceded by a valid s-ending
228
+ // Define a valid s-ending as a non-vowel other than j.
229
+ var match = result.endsinArr(["se", "s"]);
230
+ if (match != "") {
231
+ var pos = result.length - match.length;
232
+ if (pos >= this.r1) {
233
+ // check the character before the matched s/se
234
+ // HtD: if there is a s before the s/se the suffix should stay
235
+ //if (!isVowel(result[pos - 1]) && result[pos - 1] != "j") {
236
+ if (!isVowel(result[pos - 1]) && !result.match(/[js]se?$/)) {
237
+ result = result.removeSuffix(match.length);
238
+ }
239
+ }
240
+ }
241
+ if (DEBUG) {
242
+ console.log("step 1c: " + result);
243
+ }
244
+ return result;
245
+ }
246
+
247
+
248
+ // Delete suffix e if in R1 and preceded by a non-vowel, and then undouble the ending
249
+ step2(word) {
250
+ var result = word;
251
+ if (result.endsin("e") && this.r1 <= result.length) {
252
+ if (result.length > 1 && !isVowel(result[result.length - 2])) {
253
+ // Delete
254
+ result = result.removeSuffix(1);
255
+ this.suffixeRemoved = true;
256
+ // Undouble the ending
257
+ result = result.undoubleEnding();
258
+ }
259
+ }
260
+
261
+
262
+ if (DEBUG) {
263
+ console.log("step2: " + result);
264
+ }
265
+ return result;
266
+ }
267
+
268
+
269
+ // Step 3a: heid => delete heid if in R2 and not preceded by c, and treat a preceding en as in step 1(b)
270
+ step3a(word) {
271
+ var result = word;
272
+ if (result.endsin("heid") && result.length - 4 >= this.r2 && result[result.length - 5] != "c") {
273
+ // Delete
274
+ result = result.removeSuffix(4);
275
+ // Treat a preceding en as in step 1b
276
+ result = this.step1b(result, ["en"]);
277
+ }
278
+ if (DEBUG) {
279
+ console.log("step 3a: " + result);
280
+ }
281
+ return result;
282
+ }
283
+
284
+
285
+ // d suffixes: Search for the longest among the following suffixes, and perform the action indicated.
286
+ step3b(word) {
287
+ var result = word;
288
+
289
+ // end ing => delete if in R2; if preceded by ig, delete if in R2 and not preceded by e, otherwise undouble the ending
290
+ var suf = "";
291
+ if (suf = result.endsinArr(["end", "ing"])) {
292
+ if ((result.length - 3) >= this.r2) {
293
+ // Delete suffix
294
+ result = result.removeSuffix(3);
295
+ //this.regions(result);
296
+ if (result.endsin("ig") && (result.length - 2 >= this.r2) && result[result.length - 3] != "e") {
297
+ // Delete suffix
298
+ result = result.removeSuffix(2);
299
+ }
300
+ else {
301
+ result = result.undoubleEnding();
302
+ }
303
+ }
304
+ }
305
+
306
+ // ig => delete if in R2 and not preceded by e
307
+ if (result.endsin("ig") && this.r2 <= result.length - 2 && result[result.length - 3] != "e") {
308
+ result = result.removeSuffix(2);
309
+ }
310
+
311
+ // lijk => delete if in R2, and then repeat step 2
312
+ if (result.endsin("lijk") && this.r2 <= result.length - 4) {
313
+ result = result.removeSuffix(4);
314
+ // repeat step 2
315
+ result = this.step2(result);
316
+ }
317
+
318
+ // baar => delete if in R2
319
+ if (result.endsin("baar") && this.r2 <= result.length - 4) {
320
+ result = result.removeSuffix(4);
321
+ }
322
+
323
+ // bar => delete if in R2 and if step 2 actually removed an e
324
+ if (result.endsin("bar") && this.r2 <= result.length - 3 && this.suffixeRemoved) {
325
+ result = result.removeSuffix(3);
326
+ }
327
+
328
+ if (DEBUG) {
329
+ console.log("step 3b: " + result);
330
+ }
331
+ return result;
332
+ }
333
+
334
+
335
+ // undouble vowel => If the words ends CVD, where C is a non-vowel,
336
+ // D is a non-vowel other than I, and V is double a, e, o or u,
337
+ // remove one of the vowels from V (for example, maan -> man, brood -> brod)
338
+ step4(word) {
339
+ var result = word;
340
+
341
+ if (result.match(/[bcdfghjklmnpqrstvwxz](aa|ee|oo|uu)[bcdfghjklmnpqrstvwxz]$/)) {
342
+ result = result.substr(0, result.length - 2) + result[result.length - 1];
343
+ }
344
+
345
+ if (DEBUG) {
346
+ console.log("step4: " + result);
347
+ }
348
+ return result;
349
+ }
350
+
351
+ // Turn I and Y back into lower case.
352
+ postlude(word) {
353
+ return word.toLowerCase();
354
+ }
355
+
356
+ stem(word) {
357
+ return this.postlude(this.step4(this.step3b(this.step3a(this.step2(this.step1(this.prelude(word)))))));
358
+ }
359
+ }
360
+
361
+
362
+ module.exports = new PorterStemmer();
@@ -0,0 +1,99 @@
1
+ /*
2
+ Copyright (c) 2017, Dogan Yazar
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+ */
22
+
23
+ var Stemmer = require('./stemmer_sv')
24
+
25
+ //Get R1 region
26
+ function getRegions(str) {
27
+ const match = str.match(/[aeiouyäåö][^aeiouyäåö]([a-zåäö]+)/)
28
+ let r1 = ''
29
+ if (match && match[1]) {
30
+ r1 = match[1]
31
+ if (match.index + 2 < 3) { //Not clear why we need this! Algorithm does not describe this part!
32
+ r1 = str.slice(3)
33
+ }
34
+ }
35
+ return {
36
+ r1,
37
+ rest: str.slice(0, str.length - r1.length)
38
+ }
39
+ }
40
+
41
+ function step1a(str, regions = getRegions(str)) {
42
+ const r1 = regions.r1
43
+ if (!r1) {
44
+ return str
45
+ }
46
+
47
+ const regEx = /(heterna|hetens|anden|andes|andet|arens|arnas|ernas|heten|heter|ornas|ande|ades|aren|arna|arne|aste|erna|erns|orna|ade|are|ast|ens|ern|het|ad|ar|as|at|en|er|es|or|a|e)$/
48
+ const match = r1.match(regEx)
49
+ return match ? regions.rest + r1.slice(0, match.index) : str
50
+ }
51
+
52
+ function step1b(str, regions = getRegions(str)) {
53
+ if (regions.r1 && str.match(/(b|c|d|f|g|h|j|k|l|m|n|o|p|r|t|v|y)s$/)) {
54
+ return str.slice(0, -1)
55
+ }
56
+
57
+ return str
58
+ }
59
+
60
+ function step1(str) {
61
+ const regions = getRegions(str)
62
+ const resA = step1a(str, regions)
63
+ const resB = step1b(str, regions)
64
+
65
+ return resA.length < resB.length ? resA : resB
66
+ }
67
+
68
+ function step2(str, regions = getRegions(str)) {
69
+ const r1 = regions.r1
70
+ if (r1 && r1.match(/(dd|gd|nn|dt|gt|kt|tt)$/)) {
71
+ return str.slice(0, -1)
72
+ }
73
+ return str
74
+ }
75
+
76
+ function step3(str, regions = getRegions(str)) {
77
+ const r1 = regions.r1
78
+ if (r1) {
79
+ if (r1.match(/(lös|full)t$/)) {
80
+ return str.slice(0, -1)
81
+ }
82
+
83
+ const match = r1.match(/(lig|ig|els)$/)
84
+ return match ? regions.rest + r1.slice(0, match.index) : str
85
+ }
86
+
87
+ return str
88
+ }
89
+
90
+ function stem(_str) {
91
+ const str = _str.toLowerCase()
92
+ return step3(step2(step1(str)))
93
+ }
94
+
95
+ var PorterStemmer = new Stemmer()
96
+ module.exports = PorterStemmer
97
+
98
+ // perform full stemming algorithm on a single word
99
+ PorterStemmer.stem = stem
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright (c) 2012, David Przybilla, Chris Umbel
2
+ Copyright (c) 2012, 2018 David Przybilla, Chris Umbel, Hugo W.L. ter Doest
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
@@ -23,21 +23,24 @@ THE SOFTWARE.
23
23
  var stopwords = require('../util/stopwords_es');
24
24
  var Tokenizer = require('../tokenizers/aggressive_tokenizer_es');
25
25
 
26
- module.exports = function() {
27
- var stemmer = this;
26
+ class Stemmer {
27
+ constructor() {
28
+
29
+ }
28
30
 
29
- stemmer.stem = function(token) {
31
+ stem(token) {
30
32
  return token;
31
33
  };
32
34
 
33
- stemmer.tokenizeAndStem = function(text, keepStops) {
35
+ tokenizeAndStem(text, keepStops) {
34
36
  var stemmedTokens = [];
35
37
 
38
+ var that = this;
36
39
  new Tokenizer().tokenize(text).forEach(function(token) {
37
40
  if (keepStops || stopwords.words.indexOf(token) == -1) {
38
41
  var resultToken = token.toLowerCase();
39
42
  if (resultToken.match(new RegExp('[a-záéíóúüñ0-9]+', 'gi'))) {
40
- resultToken = stemmer.stem(resultToken);
43
+ resultToken = that.stem(resultToken);
41
44
  }
42
45
  stemmedTokens.push(resultToken);
43
46
  }
@@ -46,13 +49,16 @@ module.exports = function() {
46
49
  return stemmedTokens;
47
50
  };
48
51
 
49
- stemmer.attach = function() {
50
- String.prototype.stem = function() {
51
- return stemmer.stem(this);
52
+ attach() {
53
+ var that = this;
54
+ String.prototype.stem = function() {
55
+ return that.stem(this);
52
56
  };
53
57
 
54
- String.prototype.tokenizeAndStem = function(keepStops) {
55
- return stemmer.tokenizeAndStem(this, keepStops);
56
- };
58
+ String.prototype.tokenizeAndStem = function(keepStops) {
59
+ return that.tokenizeAndStem(this, keepStops);
60
+ };
57
61
  };
58
62
  }
63
+
64
+ module.exports = Stemmer;
@@ -0,0 +1,64 @@
1
+ /*
2
+ Copyright (c) 2018 Hugo W.L. ter Doest
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+ */
22
+
23
+ var stopwords = require('../util/stopwords_nl');
24
+ var Tokenizer = require('../tokenizers/aggressive_tokenizer');
25
+
26
+ class Stemmer {
27
+ constructor() {
28
+
29
+ }
30
+
31
+ stem(token) {
32
+ return token;
33
+ };
34
+
35
+ tokenizeAndStem(text, keepStops) {
36
+ var stemmedTokens = [];
37
+
38
+ var that = this;
39
+ new Tokenizer().tokenize(text).forEach(function(token) {
40
+ if (keepStops || stopwords.words.indexOf(token) == -1) {
41
+ var resultToken = token.toLowerCase();
42
+ if (resultToken.match(new RegExp('[a-zäëïöüáéíóúè0-9]+', 'gi'))) {
43
+ resultToken = that.stem(resultToken);
44
+ }
45
+ stemmedTokens.push(resultToken);
46
+ }
47
+ });
48
+
49
+ return stemmedTokens;
50
+ };
51
+
52
+ attach() {
53
+ var that = this;
54
+ String.prototype.stem = function() {
55
+ return that.stem(this);
56
+ };
57
+
58
+ String.prototype.tokenizeAndStem = function(keepStops) {
59
+ return that.tokenizeAndStem(this, keepStops);
60
+ };
61
+ };
62
+ }
63
+
64
+ module.exports = Stemmer;
@@ -0,0 +1,61 @@
1
+ /*
2
+ Copyright (c) 2017, Dogan Yazar
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+ */
22
+
23
+ var stopwords = require('../util/stopwords_sv');
24
+ var Tokenizer = require('../tokenizers/aggressive_tokenizer_sv');
25
+
26
+ module.exports = function() {
27
+ var stemmer = this;
28
+
29
+ stemmer.stem = function(token) {
30
+ return token;
31
+ };
32
+
33
+ stemmer.addStopWord = function(stopWord) {
34
+ stopwords.words.push(stopWord);
35
+ };
36
+
37
+ stemmer.addStopWords = function(moreStopWords) {
38
+ stopwords.words = stopwords.words.concat(moreStopWords);
39
+ };
40
+
41
+ stemmer.tokenizeAndStem = function(text, keepStops) {
42
+ var stemmedTokens = [];
43
+
44
+ new Tokenizer().tokenize(text).forEach(function(token) {
45
+ if(keepStops || stopwords.words.indexOf(token.toLowerCase()) == -1)
46
+ stemmedTokens.push(stemmer.stem(token));
47
+ });
48
+
49
+ return stemmedTokens;
50
+ };
51
+
52
+ stemmer.attach = function() {
53
+ String.prototype.stem = function() {
54
+ return stemmer.stem(this);
55
+ };
56
+
57
+ String.prototype.tokenizeAndStem = function(keepStops) {
58
+ return stemmer.tokenizeAndStem(this, keepStops);
59
+ };
60
+ };
61
+ }
@@ -151,8 +151,9 @@ TfIdf.prototype.addFileSync = function(path, encoding, key, restoreCache) {
151
151
  TfIdf.prototype.tfidf = function(terms, d) {
152
152
  var _this = this;
153
153
 
154
- if(!_.isArray(terms))
154
+ if(!_.isArray(terms)) {
155
155
  terms = tokenizer.tokenize(terms.toString().toLowerCase());
156
+ }
156
157
 
157
158
  return terms.reduce(function(value, term) {
158
159
  var idf = _this.idf(term);
@@ -163,10 +164,16 @@ TfIdf.prototype.tfidf = function(terms, d) {
163
164
 
164
165
  TfIdf.prototype.listTerms = function(d) {
165
166
  var terms = [];
166
-
167
+ var _this = this;
167
168
  for(var term in this.documents[d]) {
168
- if(term != '__key')
169
- terms.push({term: term, tfidf: this.tfidf(term, d)});
169
+ if (this.documents[d]) {
170
+ if(term != '__key') {
171
+ terms.push({"term": term,
172
+ "tf": tf(term, _this.documents[d]),
173
+ "idf": _this.idf(term),
174
+ "tfidf": _this.tfidf(term, d)});
175
+ }
176
+ }
170
177
  }
171
178
 
172
179
  return terms.sort(function(x, y) { return y.tfidf - x.tfidf; });
@@ -191,3 +198,19 @@ TfIdf.prototype.setTokenizer = function(t) {
191
198
  throw new Error('Expected a valid Tokenizer');
192
199
  tokenizer = t;
193
200
  };
201
+
202
+ // Define a stopwords other than the default
203
+ TfIdf.prototype.setStopwords = function(customStopwords) {
204
+
205
+ if (!Array.isArray(customStopwords))
206
+ return false;
207
+
208
+ customStopwords.forEach(stopword => {
209
+ if ((typeof stopword) != 'string')
210
+ return false;
211
+ });
212
+
213
+ stopwords = customStopwords;
214
+ return true;
215
+
216
+ }