natural 7.0.2 → 7.0.5
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.
- package/.github/workflows/npm-publish.yml +2 -0
- package/.nycrc +9 -0
- package/lib/natural/analyzers/sentence_analyzer.js +115 -113
- package/lib/natural/spellcheck/spellcheck.js +63 -61
- package/lib/natural/util/bag.js +27 -25
- package/package.json +3 -4
- package/tsconfig.json +1 -1
- package/ts_spec/MaxEntClassifier_spec.ts +0 -130
- package/ts_spec/SentimentAnalyzer_spec.ts +0 -232
- package/ts_spec/WordPunctTokenizer_spec.ts +0 -57
- package/ts_spec/aggressive_tokenizer_de_spec.ts +0 -36
- package/ts_spec/aggressive_tokenizer_es_spec.ts +0 -39
- package/ts_spec/aggressive_tokenizer_fr_spec.ts +0 -111
- package/ts_spec/aggressive_tokenizer_hi_spec.ts +0 -70
- package/ts_spec/aggressive_tokenizer_nl_spec.ts +0 -39
- package/ts_spec/aggressive_tokenizer_pt_spec.ts +0 -48
- package/ts_spec/aggressive_tokenizer_spec.ts +0 -57
- package/ts_spec/aggressive_tokenizer_sv_spec.ts +0 -52
- package/ts_spec/aggressive_tokenizer_vi_spec.ts +0 -48
- package/ts_spec/bayes_classifier_spec.ts +0 -165
- package/ts_spec/brill_pos_tagger_spec.ts +0 -79
- package/ts_spec/brill_pos_trainer_spec.ts +0 -107
- package/ts_spec/carry_stemmer_fr_spec.ts +0 -97
- package/ts_spec/classifier_spec.ts +0 -104
- package/ts_spec/count_inflector_fr_spec.ts +0 -47
- package/ts_spec/count_inflector_spec.ts +0 -76
- package/ts_spec/damerau_levenshtein_spec.ts +0 -53
- package/ts_spec/dice_coefficient_spec.ts +0 -37
- package/ts_spec/double_metaphone_spec.ts +0 -739
- package/ts_spec/hamming_distance_spec.ts +0 -66
- package/ts_spec/jaro-winkler_spec.ts +0 -79
- package/ts_spec/lancaster_stemmer_spec.ts +0 -63
- package/ts_spec/levenshtein_spec.ts +0 -107
- package/ts_spec/logistic_regression_classifier_spec.ts +0 -93
- package/ts_spec/longest_path_tree_spec.ts +0 -111
- package/ts_spec/metaphone_spec.ts +0 -258
- package/ts_spec/ngram_spec.ts +0 -145
- package/ts_spec/ngram_zh_spec.ts +0 -124
- package/ts_spec/normalizer_ja_spec.ts +0 -165
- package/ts_spec/normalizer_no_spec.ts +0 -112
- package/ts_spec/normalizer_spec.ts +0 -141
- package/ts_spec/noun_inflector_fr_spec.ts +0 -472
- package/ts_spec/noun_inflector_ja_spec.ts +0 -116
- package/ts_spec/noun_inflector_spec.ts +0 -369
- package/ts_spec/orthography_tokenizer_spec.ts +0 -47
- package/ts_spec/porter_stemmer_de_spec.ts +0 -53
- package/ts_spec/porter_stemmer_es_spec.ts +0 -63
- package/ts_spec/porter_stemmer_fr_spec.ts +0 -103
- package/ts_spec/porter_stemmer_it_spec.ts +0 -56
- package/ts_spec/porter_stemmer_nl_spec.ts +0 -73
- package/ts_spec/porter_stemmer_no_spec.ts +0 -148
- package/ts_spec/porter_stemmer_pt_spec.ts +0 -50
- package/ts_spec/porter_stemmer_ru_spec.ts +0 -67
- package/ts_spec/porter_stemmer_spec.ts +0 -202
- package/ts_spec/porter_stemmer_sv_spec.ts +0 -41
- package/ts_spec/present_verb_inflector_spec.ts +0 -127
- package/ts_spec/remove_diacritics_spec.ts +0 -30
- package/ts_spec/sentence_analyzer_spec.ts +0 -293
- package/ts_spec/sentence_tokenizer_parser_spec.ts +0 -200
- package/ts_spec/sentence_tokenizer_spec.ts +0 -189
- package/ts_spec/shortest_path_tree_spec.ts +0 -141
- package/ts_spec/soundex_spec.ts +0 -162
- package/ts_spec/spellcheck_spec.ts +0 -112
- package/ts_spec/stemmer_id_spec.ts +0 -374
- package/ts_spec/stemmer_ja_spec.ts +0 -69
- package/ts_spec/stemmer_token_spec.ts +0 -163
- package/ts_spec/tfidf_spec.ts +0 -275
- package/ts_spec/tokenizer_case_spec.ts +0 -242
- package/ts_spec/tokenizer_ja_spec.ts +0 -63
- package/ts_spec/transliterator_ja_spec.ts +0 -105
- package/ts_spec/treebank_word_tokenizer_spec.ts +0 -38
- package/ts_spec/trie_spec.ts +0 -197
package/.nycrc
ADDED
|
@@ -25,7 +25,7 @@ THE SOFTWARE.
|
|
|
25
25
|
const _ = require('underscore')
|
|
26
26
|
|
|
27
27
|
/*
|
|
28
|
-
Sentences
|
|
28
|
+
Sentences Analyzer Class
|
|
29
29
|
From http://www.writingcentre.uottawa.ca/hypergrammar/sntpurps.html
|
|
30
30
|
|
|
31
31
|
Take a POS input and analyse it for
|
|
@@ -44,148 +44,150 @@ const _ = require('underscore')
|
|
|
44
44
|
- Show Preposition Phrases
|
|
45
45
|
*/
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
class SentenceAnalyzer {
|
|
48
|
+
constructor (pos, callbackFunction) {
|
|
49
|
+
this.posObj = pos
|
|
50
|
+
this.senType = null
|
|
51
|
+
callbackFunction(this)
|
|
52
|
+
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
part (callbackFunction) {
|
|
55
|
+
const subject = []
|
|
56
|
+
const predicat = []
|
|
57
|
+
let verbFound = false
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
this.prepositionPhrases()
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
verbFound = true
|
|
64
|
-
} else {
|
|
65
|
-
// We need to Test for any EX before the VB
|
|
66
|
-
if (this.posObj.tags[i - 1].pos !== 'EX') {
|
|
61
|
+
for (let i = 0; i < this.posObj.tags.length; i++) {
|
|
62
|
+
if (this.posObj.tags[i].pos === 'VB') {
|
|
63
|
+
if (i === 0) {
|
|
67
64
|
verbFound = true
|
|
68
65
|
} else {
|
|
69
|
-
|
|
66
|
+
// We need to Test for any EX before the VB
|
|
67
|
+
if (this.posObj.tags[i - 1].pos !== 'EX') {
|
|
68
|
+
verbFound = true
|
|
69
|
+
} else {
|
|
70
|
+
predicat.push(this.posObj.tags[i].token)
|
|
71
|
+
}
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
|
-
}
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
// Add Pronoun Phrase (pp) Or Subject Phrase (sp)
|
|
76
|
+
if (!verbFound) {
|
|
77
|
+
if (this.posObj.tags[i].pp !== true) { this.posObj.tags[i].spos = 'SP' }
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
subject.push(this.posObj.tags[i].token)
|
|
80
|
+
} else {
|
|
81
|
+
if (this.posObj.tags[i].pp !== true) { this.posObj.tags[i].spos = 'PP' }
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
predicat.push(this.posObj.tags[i].token)
|
|
84
|
+
}
|
|
83
85
|
}
|
|
84
|
-
}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
if (subject.length === 0) {
|
|
88
|
+
this.posObj.tags.push({ token: 'You', spos: 'SP', pos: 'PRP', added: true })
|
|
89
|
+
}
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
}
|
|
91
|
+
callbackFunction(this)
|
|
92
|
+
}
|
|
92
93
|
|
|
93
|
-
// Takes POS and removes IN to NN or NNS
|
|
94
|
-
// Adds a PP for each prepositionPhrases
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
// Takes POS and removes IN to NN or NNS
|
|
95
|
+
// Adds a PP for each prepositionPhrases
|
|
96
|
+
prepositionPhrases () {
|
|
97
|
+
let remove = false
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
for (let i = 0; i < this.posObj.tags.length; i++) {
|
|
100
|
+
if (this.posObj.tags[i].pos.match('IN')) {
|
|
101
|
+
remove = true
|
|
102
|
+
}
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
if (remove) {
|
|
105
|
+
this.posObj.tags[i].pp = true
|
|
106
|
+
}
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
if (this.posObj.tags[i].pos.match('NN')) {
|
|
109
|
+
remove = false
|
|
110
|
+
}
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
|
-
}
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
114
|
+
subjectToString () {
|
|
115
|
+
return this.posObj.tags.map(function (t) {
|
|
116
|
+
if (t.spos === 'SP' || t.spos === 'S') {
|
|
117
|
+
return t.token
|
|
118
|
+
} else return null
|
|
119
|
+
}).join(' ')
|
|
120
|
+
}
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
122
|
+
predicateToString () {
|
|
123
|
+
return this.posObj.tags.map(function (t) {
|
|
124
|
+
if (t.spos === 'PP' || t.spos === 'P') {
|
|
125
|
+
return t.token
|
|
126
|
+
} else return null
|
|
127
|
+
}).join(' ')
|
|
128
|
+
}
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
implicitYou () {
|
|
131
|
+
for (let i = 0; i < this.posObj.tags.length; i++) {
|
|
132
|
+
if (this.posObj.tags[i].added) {
|
|
133
|
+
return true
|
|
134
|
+
}
|
|
133
135
|
}
|
|
136
|
+
|
|
137
|
+
return false
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
|
|
137
|
-
}
|
|
140
|
+
toString () {
|
|
141
|
+
return this.posObj.tags.map(function (t) { return t.token }).join(' ')
|
|
142
|
+
}
|
|
138
143
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
144
|
+
// This is quick and incomplete.
|
|
145
|
+
type (cbf) {
|
|
146
|
+
const callbackFunction = cbf || false
|
|
142
147
|
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
} else if (_.contains(['PRP'], lastElement.pos)) {
|
|
163
|
-
// Sentences that end in a Personal pronoun are most likely questions
|
|
164
|
-
// eg. We should run away, should we [?]
|
|
165
|
-
// eg. You want to see that again, do you [?]
|
|
166
|
-
this.senType = 'INTERROGATIVE'
|
|
167
|
-
} else {
|
|
168
|
-
this.senType = 'UNKNOWN'
|
|
169
|
-
}
|
|
170
|
-
} else {
|
|
171
|
-
switch (lastElement.token) {
|
|
172
|
-
case '?':
|
|
148
|
+
// Check for implicit you before popping a tag.
|
|
149
|
+
const implicitYou = this.implicitYou()
|
|
150
|
+
|
|
151
|
+
// FIXME - punct seems useless
|
|
152
|
+
let lastElement = this.posObj.punct()
|
|
153
|
+
// console.log(lastElement)
|
|
154
|
+
lastElement = (lastElement.length !== 0) ? lastElement.pop() : this.posObj.tags.pop()
|
|
155
|
+
// console.log(lastElement)
|
|
156
|
+
|
|
157
|
+
if (lastElement.pos !== '.') {
|
|
158
|
+
if (implicitYou) {
|
|
159
|
+
this.senType = 'COMMAND'
|
|
160
|
+
} else if (_.contains(['WDT', 'WP', 'WP$', 'WRB'], this.posObj.tags[0].pos)) {
|
|
161
|
+
// Sentences that start with: who, what where when why and how, then they are questions
|
|
162
|
+
this.senType = 'INTERROGATIVE'
|
|
163
|
+
} else if (_.contains(['PRP'], lastElement.pos)) {
|
|
164
|
+
// Sentences that end in a Personal pronoun are most likely questions
|
|
165
|
+
// eg. We should run away, should we [?]
|
|
166
|
+
// eg. You want to see that again, do you [?]
|
|
173
167
|
this.senType = 'INTERROGATIVE'
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
168
|
+
} else {
|
|
169
|
+
this.senType = 'UNKNOWN'
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
switch (lastElement.token) {
|
|
173
|
+
case '?':
|
|
174
|
+
this.senType = 'INTERROGATIVE'
|
|
175
|
+
break
|
|
176
|
+
case '!':
|
|
177
|
+
this.senType = (implicitYou) ? 'COMMAND' : 'EXCLAMATORY'
|
|
178
|
+
break
|
|
179
|
+
case '.':
|
|
180
|
+
this.senType = (implicitYou) ? 'COMMAND' : 'DECLARATIVE'
|
|
181
|
+
break
|
|
182
|
+
}
|
|
181
183
|
}
|
|
182
|
-
}
|
|
183
184
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
if (callbackFunction && _.isFunction(callbackFunction)) {
|
|
186
|
+
callbackFunction(this)
|
|
187
|
+
} else {
|
|
188
|
+
return this.senType
|
|
189
|
+
}
|
|
188
190
|
}
|
|
189
191
|
}
|
|
190
192
|
|
|
191
|
-
module.exports =
|
|
193
|
+
module.exports = SentenceAnalyzer
|
|
@@ -27,79 +27,81 @@ const Trie = require('../trie/trie')
|
|
|
27
27
|
// Words that are an edit distance of n away from the mispelled word are considered infinitely more probable than words that are of an edit distance >n
|
|
28
28
|
|
|
29
29
|
// wordlist is a corpus (an array) from which word probabilities are calculated (so something like /usr/share/dict/words (on OSX) will work okay, but real world text will work better)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.word2frequency[wordlist[i]]
|
|
30
|
+
class Spellcheck {
|
|
31
|
+
constructor (wordlist) {
|
|
32
|
+
this.trie = new Trie()
|
|
33
|
+
this.trie.addStrings(wordlist)
|
|
34
|
+
this.word2frequency = {}
|
|
35
|
+
for (const i in wordlist) {
|
|
36
|
+
if (!this.word2frequency[wordlist[i]]) {
|
|
37
|
+
this.word2frequency[wordlist[i]] = 0
|
|
38
|
+
}
|
|
39
|
+
this.word2frequency[wordlist[i]]++
|
|
37
40
|
}
|
|
38
|
-
this.word2frequency[wordlist[i]]++
|
|
39
41
|
}
|
|
40
|
-
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
43
|
+
isCorrect (word) {
|
|
44
|
+
return this.trie.contains(word)
|
|
45
|
+
}
|
|
45
46
|
|
|
46
|
-
// Returns a list of suggested corrections, from highest to lowest probability
|
|
47
|
-
// maxDistance is the maximum edit distance
|
|
48
|
-
// According to Norvig, literature suggests that 80% to 95% of spelling errors are an edit distance of 1 away from the correct word. This is good, because there are roughly 54n+25 strings 1 edit distance away from any given string of length n. So after maxDistance = 2, this becomes very slow.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
// Returns a list of suggested corrections, from highest to lowest probability
|
|
48
|
+
// maxDistance is the maximum edit distance
|
|
49
|
+
// According to Norvig, literature suggests that 80% to 95% of spelling errors are an edit distance of 1 away from the correct word. This is good, because there are roughly 54n+25 strings 1 edit distance away from any given string of length n. So after maxDistance = 2, this becomes very slow.
|
|
50
|
+
getCorrections (word, maxDistance) {
|
|
51
|
+
const self = this
|
|
52
|
+
if (!maxDistance) maxDistance = 1
|
|
53
|
+
let edits = this.editsWithMaxDistance(word, maxDistance)
|
|
54
|
+
edits = edits.slice(1, edits.length)
|
|
55
|
+
edits = edits.map(function (editList) {
|
|
56
|
+
return editList.filter(function (word) { return self.isCorrect(word) })
|
|
57
|
+
.map(function (word) { return [word, self.word2frequency[word]] })
|
|
58
|
+
.sort(function (a, b) { return a[1] > b[1] ? -1 : 1 })
|
|
59
|
+
.map(function (wordscore) { return wordscore[0] })
|
|
60
|
+
})
|
|
61
|
+
let flattened = []
|
|
62
|
+
for (const i in edits) {
|
|
63
|
+
if (edits[i].length) {
|
|
64
|
+
flattened = flattened.concat(edits[i])
|
|
65
|
+
}
|
|
64
66
|
}
|
|
67
|
+
return flattened.filter(function (v, i, a) {
|
|
68
|
+
return a.indexOf(v) === i
|
|
69
|
+
})
|
|
65
70
|
}
|
|
66
|
-
return flattened.filter(function (v, i, a) {
|
|
67
|
-
return a.indexOf(v) === i
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
71
|
|
|
71
|
-
// Returns all edits that are 1 edit-distance away from the input word
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
72
|
+
// Returns all edits that are 1 edit-distance away from the input word
|
|
73
|
+
edits (word) {
|
|
74
|
+
const alphabet = 'abcdefghijklmnopqrstuvwxyz'
|
|
75
|
+
let edits = []
|
|
76
|
+
for (let i = 0; i < word.length + 1; i++) {
|
|
77
|
+
if (i > 0) edits.push(word.slice(0, i - 1) + word.slice(i, word.length)) // deletes
|
|
78
|
+
if (i > 0 && i < word.length + 1) edits.push(word.slice(0, i - 1) + word.slice(i, i + 1) + word.slice(i - 1, i) + word.slice(i + 1, word.length)) // transposes
|
|
79
|
+
for (let k = 0; k < alphabet.length; k++) {
|
|
80
|
+
if (i > 0) edits.push(word.slice(0, i - 1) + alphabet[k] + word.slice(i, word.length)) // replaces
|
|
81
|
+
edits.push(word.slice(0, i) + alphabet[k] + word.slice(i, word.length)) // inserts
|
|
82
|
+
}
|
|
81
83
|
}
|
|
84
|
+
// Deduplicate edits
|
|
85
|
+
edits = edits.filter(function (v, i, a) { return a.indexOf(v) === i })
|
|
86
|
+
return edits
|
|
82
87
|
}
|
|
83
|
-
// Deduplicate edits
|
|
84
|
-
edits = edits.filter(function (v, i, a) { return a.indexOf(v) === i })
|
|
85
|
-
return edits
|
|
86
|
-
}
|
|
87
88
|
|
|
88
|
-
// Returns all edits that are up to "distance" edit distance away from the input word
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
89
|
+
// Returns all edits that are up to "distance" edit distance away from the input word
|
|
90
|
+
editsWithMaxDistance (word, distance) {
|
|
91
|
+
return this.editsWithMaxDistanceHelper(distance, [[word]])
|
|
92
|
+
}
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
editsWithMaxDistanceHelper (distanceCounter, distance2edits) {
|
|
95
|
+
if (distanceCounter === 0) return distance2edits
|
|
96
|
+
const currentDepth = distance2edits.length - 1
|
|
97
|
+
const words = distance2edits[currentDepth]
|
|
98
|
+
// const edits = this.edits(words[0])
|
|
99
|
+
distance2edits[currentDepth + 1] = []
|
|
100
|
+
for (const i in words) {
|
|
101
|
+
distance2edits[currentDepth + 1] = distance2edits[currentDepth + 1].concat(this.edits(words[i]))
|
|
102
|
+
}
|
|
103
|
+
return this.editsWithMaxDistanceHelper(distanceCounter - 1, distance2edits)
|
|
101
104
|
}
|
|
102
|
-
return this.editsWithMaxDistanceHelper(distanceCounter - 1, distance2edits)
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
module.exports = Spellcheck
|
package/lib/natural/util/bag.js
CHANGED
|
@@ -22,31 +22,33 @@ THE SOFTWARE.
|
|
|
22
22
|
|
|
23
23
|
'use strict'
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
25
|
+
class Bag {
|
|
26
|
+
constructor () {
|
|
27
|
+
this.dictionary = []
|
|
28
|
+
this.nElement = 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
add (element) {
|
|
32
|
+
this.dictionary.push(element)
|
|
33
|
+
this.nElement++
|
|
34
|
+
return this
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
isEmpty () {
|
|
38
|
+
return this.nElement > 0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
contains (item) {
|
|
42
|
+
return this.dictionary.indexOf(item) >= 0
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* unpack the bag , and get all items
|
|
47
|
+
*/
|
|
48
|
+
unpack () {
|
|
49
|
+
// return a copy is better than original
|
|
50
|
+
return this.dictionary.slice()
|
|
51
|
+
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
module.exports = Bag
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natural",
|
|
3
3
|
"description": "General natural language (tokenizing, stemming (English, Russian, Spanish), part-of-speech tagging, sentiment analysis, classification, inflection, phonetics, tfidf, WordNet, jaro-winkler, Levenshtein distance, Dice's Coefficient) facilities for node.",
|
|
4
|
-
"version": "7.0.
|
|
4
|
+
"version": "7.0.5",
|
|
5
5
|
"homepage": "https://github.com/NaturalNode/natural",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -77,10 +77,9 @@
|
|
|
77
77
|
"benchmark": "node benchmarks",
|
|
78
78
|
"build": "tsc && ncp spec/test_data dist",
|
|
79
79
|
"clean": "rimraf *~ #* *classifier.json dist io_spec/tmp/*.json coverage",
|
|
80
|
-
"test": "cross-env NODE_PATH=. jasmine --random=false spec/*_spec.js",
|
|
81
80
|
"test_io": "jasmine --random=false io_spec/*_spec.js",
|
|
82
|
-
"
|
|
83
|
-
"coverage": "nyc --
|
|
81
|
+
"test": "cross-env NODE_PATH=.:./dist jasmine --random=false dist/spec/*_spec.js",
|
|
82
|
+
"coverage": "nyc --no-clean npm run test && nyc --no-clean npm run test_io",
|
|
84
83
|
"test_browser": "cross-env NODE_PATH=. node ./node_modules/gulp/bin/gulp.js",
|
|
85
84
|
"lint": "eslint . --ext .ts"
|
|
86
85
|
},
|
package/tsconfig.json
CHANGED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Unit test of Classifier
|
|
3
|
-
Copyright (C) 2018 Hugo W.L. ter Doest
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
all copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
THE SOFTWARE.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
'use strict'
|
|
25
|
-
|
|
26
|
-
import {
|
|
27
|
-
Context,
|
|
28
|
-
FeatureSet,
|
|
29
|
-
Sample,
|
|
30
|
-
MaxEntClassifier as Classifier,
|
|
31
|
-
SEElement
|
|
32
|
-
} from 'lib/natural'
|
|
33
|
-
|
|
34
|
-
import type { Element } from 'lib/natural'
|
|
35
|
-
|
|
36
|
-
const classifierFilename = 'classifier.json'
|
|
37
|
-
const minImprovement = 0.01
|
|
38
|
-
const nrIterations = 20
|
|
39
|
-
|
|
40
|
-
let sample: Sample
|
|
41
|
-
let featureSet: FeatureSet
|
|
42
|
-
let classifier: Classifier
|
|
43
|
-
|
|
44
|
-
const DEBUG = false
|
|
45
|
-
|
|
46
|
-
describe('The MaxEnt module', function () {
|
|
47
|
-
it('The Sample class creates a sample', function () {
|
|
48
|
-
sample = new Sample()
|
|
49
|
-
sample.addElement(new SEElement('x', new Context('0')))
|
|
50
|
-
sample.addElement(new SEElement('x', new Context('0')))
|
|
51
|
-
sample.addElement(new SEElement('x', new Context('0')))
|
|
52
|
-
sample.addElement(new SEElement('y', new Context('0')))
|
|
53
|
-
sample.addElement(new SEElement('y', new Context('0')))
|
|
54
|
-
sample.addElement(new SEElement('y', new Context('0')))
|
|
55
|
-
|
|
56
|
-
sample.addElement(new SEElement('x', new Context('1')))
|
|
57
|
-
sample.addElement(new SEElement('y', new Context('1')))
|
|
58
|
-
sample.addElement(new SEElement('y', new Context('1')))
|
|
59
|
-
sample.addElement(new SEElement('y', new Context('1')))
|
|
60
|
-
|
|
61
|
-
expect(sample.size()).toBe(10)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('The FeatureSet class creates a feature set', function () {
|
|
65
|
-
featureSet = new FeatureSet()
|
|
66
|
-
sample.generateFeatures(featureSet)
|
|
67
|
-
|
|
68
|
-
expect(featureSet.size()).toBe(2)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it('The Classifier class creates a classifier', function () {
|
|
72
|
-
// Create a classifier
|
|
73
|
-
classifier = new Classifier(featureSet, sample)
|
|
74
|
-
|
|
75
|
-
expect(classifier).toBeDefined()
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('Classifier does not need a correction feature', function () {
|
|
79
|
-
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('The classifier stops training after a specified number or iterations ' +
|
|
83
|
-
'or when the minimum improvement in likelihood is reached', function () {
|
|
84
|
-
classifier.train(nrIterations, minImprovement)
|
|
85
|
-
|
|
86
|
-
expect(classifier.scaler.iteration).toBeLessThan(nrIterations + 1)
|
|
87
|
-
if (classifier.scaler.iteration < nrIterations) {
|
|
88
|
-
expect(classifier.scaler.improvement).toBeLessThan(minImprovement)
|
|
89
|
-
}
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('Save classifer to a file', function (done) {
|
|
93
|
-
classifier.save(classifierFilename, function (err, c) {
|
|
94
|
-
if (err !== null) {
|
|
95
|
-
console.log(err)
|
|
96
|
-
} else {
|
|
97
|
-
DEBUG && console.log('Classifier saved to ' + classifierFilename)
|
|
98
|
-
}
|
|
99
|
-
done()
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
let newClassifier: Classifier
|
|
104
|
-
it('Load classifer', function (done) {
|
|
105
|
-
classifier.load(classifierFilename, SEElement as unknown as Element, function (err, c) {
|
|
106
|
-
if (err !== null) {
|
|
107
|
-
console.log(err)
|
|
108
|
-
} else if (c !== undefined) {
|
|
109
|
-
DEBUG && console.log('Classifier loaded from ' + classifierFilename)
|
|
110
|
-
newClassifier = c
|
|
111
|
-
}
|
|
112
|
-
done()
|
|
113
|
-
})
|
|
114
|
-
if (newClassifier !== undefined) {
|
|
115
|
-
classifier = newClassifier
|
|
116
|
-
}
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('The classifier classifies events', function () {
|
|
120
|
-
let context = new Context('0')
|
|
121
|
-
DEBUG && console.log('Classes plus scores ' + JSON.stringify(classifier.getClassifications(context)))
|
|
122
|
-
let classification = classifier.classify(context)
|
|
123
|
-
expect(classification).toBe('x')
|
|
124
|
-
|
|
125
|
-
context = new Context('1')
|
|
126
|
-
DEBUG && console.log('Classes plus scores ' + JSON.stringify(classifier.getClassifications(context)))
|
|
127
|
-
classification = classifier.classify(context)
|
|
128
|
-
expect(classification).toBe('y')
|
|
129
|
-
})
|
|
130
|
-
})
|