natural 0.5.6 → 0.6.3
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/.dockerignore +2 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/natural.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/workspace.xml +511 -0
- package/.travis.yml +8 -1
- package/README.md +331 -40
- package/bayes_classifier.json +1 -1
- package/classifier.json +418 -0
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tagger.js +8 -9
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tester.js +5 -5
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Trainer.js +10 -6
- package/lib/natural/brill_pos_tagger/lib/Corpus.js +21 -16
- package/lib/natural/brill_pos_tagger/lib/Lexicon.js +22 -13
- package/lib/natural/brill_pos_tagger/lib/Predicate.js +2 -2
- package/lib/natural/brill_pos_tagger/lib/RuleTemplate.js +2 -2
- package/lib/natural/brill_pos_tagger/lib/RuleTemplates.js +132 -146
- package/lib/natural/brill_pos_tagger/lib/Sentence.js +41 -0
- package/lib/natural/brill_pos_tagger/lib/TransformationRule.js +10 -11
- package/lib/natural/classifiers/classifier.js +5 -3
- package/lib/natural/classifiers/maxent/Classifier.js +125 -0
- package/lib/natural/classifiers/maxent/Context.js +33 -0
- package/lib/natural/classifiers/maxent/Distribution.js +145 -0
- package/lib/natural/classifiers/maxent/Element.js +34 -0
- package/lib/natural/classifiers/maxent/Feature.js +88 -0
- package/lib/natural/classifiers/maxent/FeatureSet.js +67 -0
- package/lib/natural/classifiers/maxent/GISScaler.js +148 -0
- package/lib/natural/classifiers/maxent/POS/ME_Corpus.js +56 -0
- package/lib/natural/classifiers/maxent/POS/ME_Sentence.js +73 -0
- package/lib/natural/classifiers/maxent/POS/POS_Element.js +172 -0
- package/lib/natural/classifiers/maxent/Sample.js +141 -0
- package/lib/natural/classifiers/maxent/SimpleExample/SE_Element.js +49 -0
- package/lib/natural/distance/hamming_distance.js +52 -0
- package/lib/natural/distance/jaro-winkler_distance.js +16 -6
- package/lib/natural/distance/levenshtein_distance.js +2 -2
- package/lib/natural/index.js +21 -2
- package/lib/natural/inflectors/noun_inflector.js +5 -3
- package/lib/natural/ngrams/ngrams.js +83 -25
- package/lib/natural/sentiment/Basque/senticon_eu.json +21557 -0
- package/lib/natural/sentiment/Catalan/senticon_ca.json +36352 -0
- package/lib/natural/sentiment/Dutch/negations_du.json +3 -0
- package/lib/natural/sentiment/Dutch/pattern-sentiment-nl.json +39650 -0
- package/lib/natural/sentiment/English/negations_en.json +3 -0
- package/lib/natural/sentiment/English/pattern-sentiment-en.json +16024 -0
- package/lib/natural/sentiment/English/senticon_en.json +124197 -0
- package/lib/natural/sentiment/French/pattern-sentiment-fr.json +40906 -0
- package/lib/natural/sentiment/Galician/senticon_gl.json +24427 -0
- package/lib/natural/sentiment/Italian/pattern-sentiment-it.json +24522 -0
- package/lib/natural/sentiment/SentimentAnalyzer.js +116 -0
- package/lib/natural/sentiment/Spanish/afinnShortSortedSpanish.json +1655 -0
- package/lib/natural/sentiment/Spanish/negations_es.json +3 -0
- package/lib/natural/sentiment/Spanish/senticon_es.json +56722 -0
- package/lib/natural/sentiment/tools/README.md +31 -0
- package/lib/natural/sentiment/tools/XmlParser4PatternData.js +31 -0
- package/lib/natural/sentiment/tools/sentimentXmlParser.js +30 -0
- package/lib/natural/stemmers/indonesian/data/kata-dasar.json +29935 -0
- package/lib/natural/stemmers/indonesian/prefix_rules.js +10 -9
- package/lib/natural/stemmers/indonesian/stemmer_id.js +16 -15
- package/lib/natural/stemmers/indonesian/suffix_rules.js +2 -2
- package/lib/natural/stemmers/porter_stemmer_es.js +265 -199
- package/lib/natural/stemmers/porter_stemmer_fr.js +1 -1
- package/lib/natural/stemmers/porter_stemmer_nl.js +362 -0
- package/lib/natural/stemmers/stemmer_es.js +18 -12
- package/lib/natural/stemmers/stemmer_nl.js +64 -0
- package/lib/natural/tfidf/tfidf.js +27 -4
- package/lib/natural/tokenizers/aggressive_tokenizer_sv.js +1 -1
- package/lib/natural/tokenizers/aggressive_tokenizer_vi.js +37 -0
- package/lib/natural/tokenizers/orthography_matchers.js +33 -0
- package/lib/natural/tokenizers/regexp_tokenizer.js +24 -4
- package/lib/natural/tokenizers/sentence_tokenizer.js +1 -1
- package/lib/natural/wordnet/wordnet_file.js +7 -1
- package/lr_classifier.json +1 -1
- package/package.json +35 -23
- package/.npmignore +0 -4
- package/.vscode/temp.sql +0 -0
- package/Makefile +0 -38
- package/data/kata-dasar.txt +0 -29933
- package/package-lock.json +0 -79
- package/tests/stemmer_id_test.js +0 -361
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/*
|
|
2
|
+
GISScaler class that finds parameters of features
|
|
3
|
+
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var Element = require('./Element');
|
|
20
|
+
var Feature = require('./Feature');
|
|
21
|
+
var Distribution = require('./Distribution');
|
|
22
|
+
|
|
23
|
+
// classes is an array of classes
|
|
24
|
+
// features is an array of feature functions
|
|
25
|
+
function GISScaler(featureSet, sample) {
|
|
26
|
+
this.featureSet = featureSet;
|
|
27
|
+
this.sample = sample;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Returns true if a correction feature is necessary
|
|
31
|
+
GISScaler.prototype.calculateMaxSumOfFeatures = function() {
|
|
32
|
+
var that = this;
|
|
33
|
+
this.C = 0;
|
|
34
|
+
this.featureSums = {};
|
|
35
|
+
var listOfSumValues = [];
|
|
36
|
+
|
|
37
|
+
// Since feature functions are derived from the sample
|
|
38
|
+
// we can use the sample to calculate the max sum
|
|
39
|
+
// We look at each sample element only once
|
|
40
|
+
this.sample.elements.forEach(function(x) {
|
|
41
|
+
if (!that.featureSums[x.toString()]) {
|
|
42
|
+
var sum = 0;
|
|
43
|
+
that.featureSet.getFeatures().forEach(function(f) {
|
|
44
|
+
sum += f.apply(x);
|
|
45
|
+
});
|
|
46
|
+
if (sum > that.C) {
|
|
47
|
+
that.C = sum;
|
|
48
|
+
}
|
|
49
|
+
that.featureSums[x.toString()] = sum;
|
|
50
|
+
listOfSumValues.push(sum);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
//console.log("GISScaler:calculateMaxSumOfFeatures:maxSum is " + this.C);
|
|
54
|
+
|
|
55
|
+
// Check if a correction feature is necessary
|
|
56
|
+
listOfSumValues.sort(function(a, b) {
|
|
57
|
+
return a - b;
|
|
58
|
+
});
|
|
59
|
+
return(listOfSumValues[0] !== listOfSumValues[listOfSumValues.length - 1]);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
GISScaler.prototype.addCorrectionFeature = function() {
|
|
63
|
+
if (this.calculateMaxSumOfFeatures()) {
|
|
64
|
+
//console.log("GISScaler:addCorrectionFeature:C is " + this.C);
|
|
65
|
+
var that = this;
|
|
66
|
+
|
|
67
|
+
function f(x) {
|
|
68
|
+
if (that.featureSums[x.toString()] !== undefined) {
|
|
69
|
+
return that.C - that.featureSums[x.toString()];
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
var correctionFeature = new Feature(f, "Correction feature", []);
|
|
75
|
+
//console.log("GISScaler:addCorrectionFeature:correctionFeature " + JSON.stringify(correctionFeature));
|
|
76
|
+
this.featureSet.addFeature(correctionFeature);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
//console.log("Correction feature not needed");
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// This is the Generalised Iterative Scaling algorithm
|
|
84
|
+
// It ends if the improvement in likelihood of the distribution does not
|
|
85
|
+
// improve more than minImprovement or if the maximum number of iterations is
|
|
86
|
+
// reached.
|
|
87
|
+
GISScaler.prototype.run = function(maxIterations, minImprovement) {
|
|
88
|
+
this.iteration = 0;
|
|
89
|
+
this.improvement = 0;
|
|
90
|
+
|
|
91
|
+
this.addCorrectionFeature();
|
|
92
|
+
// Build up the distribution p
|
|
93
|
+
var alpha = new Array(this.featureSet.size());
|
|
94
|
+
for (var i = 0; i < alpha.length; i++) {
|
|
95
|
+
alpha[i] = 1;
|
|
96
|
+
}
|
|
97
|
+
var p = new Distribution(alpha, this.featureSet, this.sample);
|
|
98
|
+
//console.log("Distribution created");
|
|
99
|
+
p.prepare();
|
|
100
|
+
//console.log("Distribution prepared");
|
|
101
|
+
var likelihood = p.logLikelihood();
|
|
102
|
+
var KLDistance = p.KullbackLieblerDistance();
|
|
103
|
+
|
|
104
|
+
var newAlpha = new Array(this.featureSet.size());
|
|
105
|
+
var observedExpectation = 0;
|
|
106
|
+
var expectationApprox = 0;
|
|
107
|
+
do {
|
|
108
|
+
//console.log("Iteration " + this.iteration + " - Log likelihood of sample: " + likelihood + " - Entropy: " + p.entropy());
|
|
109
|
+
for (var i = 0; i < this.featureSet.size(); i++) {
|
|
110
|
+
observedExpectation = this.featureSet.getFeatures()[i].observedExpectation(this.sample);
|
|
111
|
+
expectationApprox = this.featureSet.getFeatures()[i].expectationApprox(p, this.sample);
|
|
112
|
+
//console.log("Iteration " + this.iteration + " - Feature " + i);
|
|
113
|
+
newAlpha[i] = p.alpha[i] * Math.pow(observedExpectation / expectationApprox, 1 / this.C);
|
|
114
|
+
|
|
115
|
+
//console.log("GISScaler.run: old alpha[" + i + "]: " + p.alpha[i]);
|
|
116
|
+
//console.log("GISScaler.run: new alpha[" + i + "]: " + newAlpha[i]);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Make the newly calculated parameters current parameters
|
|
120
|
+
newAlpha.forEach(function(newAlpha_j, j) {
|
|
121
|
+
p.alpha[j] = newAlpha_j;
|
|
122
|
+
});
|
|
123
|
+
// Recalculate a priori and a posteriori probabilities
|
|
124
|
+
p.prepare();
|
|
125
|
+
|
|
126
|
+
this.iteration++;
|
|
127
|
+
var newLikelihood = p.logLikelihood();
|
|
128
|
+
var newKLDistance = p.KullbackLieblerDistance();
|
|
129
|
+
this.improvement = KLDistance - newKLDistance;
|
|
130
|
+
//console.log("Iteration " + this.iteration + " - Old likelihood: " + likelihood + " - New likelihood: " + newLikelihood);
|
|
131
|
+
//console.log("Iteration " + this.iteration + " - Old KL: " + KLDistance + " - New KL: " + newKLDistance);
|
|
132
|
+
|
|
133
|
+
likelihood = newLikelihood;
|
|
134
|
+
KLDistance = newKLDistance;
|
|
135
|
+
} while ((this.iteration < maxIterations) && (this.improvement > minImprovement));
|
|
136
|
+
//} while (iteration < maxIterations);
|
|
137
|
+
/*
|
|
138
|
+
var that = this;
|
|
139
|
+
this.featureSet.getFeatures().forEach(function(f, j) {
|
|
140
|
+
console.log("Observed expectation of feature " + j + ": " + f.observedExpectation(that.sample) +
|
|
141
|
+
" - Expection of feature according to p: " + f.expectationApprox(p, that.sample));
|
|
142
|
+
});
|
|
143
|
+
*/
|
|
144
|
+
|
|
145
|
+
return p;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
module.exports = GISScaler;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Corpus class specific for MaxEnt modeling
|
|
3
|
+
Copyright (C) 2018 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var util = require('util');
|
|
20
|
+
var Sample = require('../Sample');
|
|
21
|
+
var Corpus = require('../../../brill_pos_tagger/lib/Corpus');
|
|
22
|
+
|
|
23
|
+
function ME_Corpus(data, BROWN, SentenceClass) {
|
|
24
|
+
ME_Corpus.super_.call(this, data, BROWN, SentenceClass);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
util.inherits(ME_Corpus, Corpus);
|
|
28
|
+
|
|
29
|
+
ME_Corpus.prototype.generateSample = function() {
|
|
30
|
+
var sample = new Sample([]);
|
|
31
|
+
this.sentences.forEach(function(sentence) {
|
|
32
|
+
sentence.generateSampleElements(sample);
|
|
33
|
+
});
|
|
34
|
+
return sample;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Splits the corpus in a training and testing set.
|
|
38
|
+
// percentageTrain is the size of the training corpus in percent
|
|
39
|
+
// Returns an array with two elements: training corpus, testing corpus
|
|
40
|
+
ME_Corpus.prototype.splitInTrainAndTest = function(percentageTrain) {
|
|
41
|
+
var corpusTrain = new ME_Corpus();
|
|
42
|
+
var corpusTest = new ME_Corpus();
|
|
43
|
+
|
|
44
|
+
var p = percentageTrain / 100;
|
|
45
|
+
this.sentences.forEach(function(sentence, i) {
|
|
46
|
+
if (Math.random() < p) {
|
|
47
|
+
corpusTrain.sentences.push(sentence);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
corpusTest.sentences.push(sentence);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return [corpusTrain, corpusTest];
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
module.exports = ME_Corpus;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Sentence class specific for MaxEnt modeling
|
|
3
|
+
Copyright (C) 2018 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var util = require('util');
|
|
20
|
+
var Context = require('../Context');
|
|
21
|
+
var Sentence = require('../../../brill_pos_tagger/lib/Sentence');
|
|
22
|
+
var Element = require('./POS_Element');
|
|
23
|
+
|
|
24
|
+
function ME_Sentence() {
|
|
25
|
+
ME_Sentence.super_.call(this);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
util.inherits(ME_Sentence, Sentence);
|
|
29
|
+
|
|
30
|
+
ME_Sentence.prototype.generateSampleElements = function(sample) {
|
|
31
|
+
var sentence = this.taggedWords;
|
|
32
|
+
sentence.forEach(function(token, index) {
|
|
33
|
+
var x = new Element(
|
|
34
|
+
token.tag,
|
|
35
|
+
new Context({
|
|
36
|
+
wordWindow: {},
|
|
37
|
+
tagWindow: {}
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Current word and tag
|
|
42
|
+
x.b.data.wordWindow["0"] = token.token;
|
|
43
|
+
x.b.data.tagWindow["0"] = sentence[index].tag;
|
|
44
|
+
|
|
45
|
+
// Previous bigram
|
|
46
|
+
if (index > 1) {
|
|
47
|
+
x.b.data.tagWindow["-2"] = sentence[index - 2].tag;
|
|
48
|
+
x.b.data.wordWindow["-2"] = sentence[index - 2].token;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Left bigram
|
|
52
|
+
if (index > 0) {
|
|
53
|
+
x.b.data.tagWindow["-1"] = sentence[index - 1].tag;
|
|
54
|
+
x.b.data.wordWindow["-1"] = sentence[index - 1].token;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Right bigram
|
|
58
|
+
if (index < sentence.length - 1) {
|
|
59
|
+
x.b.data.tagWindow["1"] = sentence[index + 1].tag;
|
|
60
|
+
x.b.data.wordWindow["1"] = sentence[index + 1].token;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Next bigram
|
|
64
|
+
if (index < sentence.length - 2) {
|
|
65
|
+
x.b.data.tagWindow["2"] = sentence[index + 2].tag;
|
|
66
|
+
x.b.data.wordWindow["2"] = sentence[index + 2].token;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
sample.addElement(x);
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
module.exports = ME_Sentence;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Element class for POS tagging
|
|
3
|
+
Copyright (C) 2018 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
var util = require('util');
|
|
21
|
+
var Element = require('../Element');
|
|
22
|
+
var Feature = require('../Feature');
|
|
23
|
+
|
|
24
|
+
function POS_Element(a, b) {
|
|
25
|
+
POS_Element.super_.call(this, a, b);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
util.inherits(POS_Element, Element);
|
|
29
|
+
|
|
30
|
+
POS_Element.prototype.generateFeatures = function(featureSet) {
|
|
31
|
+
var context = this.b.data;
|
|
32
|
+
var tag = this.a;
|
|
33
|
+
var token = context.wordWindow["0"];
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
// Feature for the current word
|
|
37
|
+
function currentWord(x) {
|
|
38
|
+
if ((x.b.data.wordWindow["0"] === token) &&
|
|
39
|
+
(x.a === tag)) {
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
featureSet.addFeature(new Feature(currentWord, "wordFeature", ["0", token, "0", tag]));
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// Feature for previous bigram (previous two tags), positions -2, -1
|
|
48
|
+
if (context.tagWindow["-2"]) {
|
|
49
|
+
var prevPrevTag = context.tagWindow["-2"];
|
|
50
|
+
var prevTag = context.tagWindow["-1"];
|
|
51
|
+
function prevBigram(x) {
|
|
52
|
+
if ((x.a === tag) &&
|
|
53
|
+
(x.b.data.tagWindow["-2"] === prevPrevTag) &&
|
|
54
|
+
(x.b.data.tagWindow["-1"] === prevTag)) {
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
featureSet.addFeature(new Feature(prevBigram, "prevBigram", ["0", tag, "-2", prevPrevTag, "-1", prevTag]));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
// Feature for left bigram, positions -1, 0
|
|
65
|
+
if (context.tagWindow["-1"]) {
|
|
66
|
+
var prevTag = context.tagWindow["-1"];
|
|
67
|
+
function leftBigram(x) {
|
|
68
|
+
if ((x.b.data.tagWindow["-1"] === prevTag) &&
|
|
69
|
+
(x.a === tag)) {
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
featureSet.addFeature(new Feature(leftBigram, "leftBigram", ["0", tag, "-1", prevTag]));
|
|
75
|
+
}
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
|
|
80
|
+
// Feature for right bigram, positions 0, 1
|
|
81
|
+
if (context.tagWindow["1"]) {
|
|
82
|
+
var nextTag = context.tagWindow["1"];
|
|
83
|
+
function rightBigram(x) {
|
|
84
|
+
if ((x.a === tag) &&
|
|
85
|
+
(x.b.data.tagWindow["1"] === nextTag)) {
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
featureSet.addFeature(new Feature(rightBigram, "rightBigram", ["0", tag, "1", nextTag]));
|
|
91
|
+
}
|
|
92
|
+
*/
|
|
93
|
+
/*
|
|
94
|
+
// Feature for next bigram (next two tags), positions 1 and 2
|
|
95
|
+
if (context.tagWindow["2"]) {
|
|
96
|
+
var nextTag = context.tagWindow["1"];
|
|
97
|
+
var nextNextTag = context.tagWindow["2"];
|
|
98
|
+
function nextBigram(x) {
|
|
99
|
+
if ((x.a === tag) &&
|
|
100
|
+
(x.b.data.tagWindow["1"] === nextTag) &&
|
|
101
|
+
(x.b.data.tagWindow["2"] === nextNextTag)) {
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
featureSet.addFeature(new Feature(nextBigram, "nextBigram", ["0", tag, "1", nextTag, "2", nextNextTag]));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Feature that looks at the left bigram words
|
|
110
|
+
if (context.wordWindow["-1"]) {
|
|
111
|
+
var prevWord = context.wordWindow["-1"];
|
|
112
|
+
function leftBigramWords(x) {
|
|
113
|
+
if ((x.a === tag) &&
|
|
114
|
+
(x.b.data.wordWindow["0"] === token) &&
|
|
115
|
+
(x.b.data.wordWindow["-1"] === prevWord)) {
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
featureSet.addFeature(new Feature(leftBigramWords, "leftBigramWords", ["0", tag, "0", token, "-1", prevWord]));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Feature that looks at the right bigram words
|
|
124
|
+
if (context.wordWindow["1"]) {
|
|
125
|
+
var nextWord = context.wordWindow["1"];
|
|
126
|
+
function rightBigramWords(x) {
|
|
127
|
+
if ((x.a === tag) &&
|
|
128
|
+
(x.b.data.wordWindow["0"] === token) &&
|
|
129
|
+
(x.b.data.wordWindow["1"] === nextWord)) {
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
featureSet.addFeature(new Feature(rightBigramWords, "rightBigramWords", ["0", tag, "0", token, "1", nextWord]));
|
|
135
|
+
}
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
// Feature that looks at the previous word and its category
|
|
139
|
+
if (context.wordWindow["-1"]) {
|
|
140
|
+
var prevWord = context.wordWindow["-1"];
|
|
141
|
+
var prevTag = context.tagWindow["-1"];
|
|
142
|
+
function prevWordAndCat(x) {
|
|
143
|
+
if ((x.a === tag) &&
|
|
144
|
+
(x.b.data.wordWindow["-1"] === prevWord) &&
|
|
145
|
+
(x.b.data.tagWindow["-1"] === prevTag)) {
|
|
146
|
+
return 1;
|
|
147
|
+
}
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
featureSet.addFeature(new Feature(prevWordAndCat, "prevWordAndCat", ["0", tag, "-1", prevWord, "-1", prevTag]));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
// Feature that looks at the next word and its category
|
|
156
|
+
if (context.wordWindow["1"]) {
|
|
157
|
+
var nextWord = context.wordWindow["1"];
|
|
158
|
+
var nextTag = context.tagWindow["1"];
|
|
159
|
+
function nextWordAndCat(x) {
|
|
160
|
+
if ((x.a === tag) &&
|
|
161
|
+
(x.b.data.wordWindow["1"] === nextWord) &&
|
|
162
|
+
(x.b.data.tagWindow["1"] === nextTag)) {
|
|
163
|
+
return 1;
|
|
164
|
+
}
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
featureSet.addFeature(new Feature(nextWordAndCat, "nextWordAndCat", ["0", tag, "1", nextWord, "1", nextTag]));
|
|
168
|
+
}
|
|
169
|
+
*/
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
module.exports = POS_Element;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Sample space of observed events
|
|
3
|
+
Copyright (C) 2018 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var Context = require('./Context');
|
|
20
|
+
|
|
21
|
+
var fs = require('fs');
|
|
22
|
+
|
|
23
|
+
function Sample(elements) {
|
|
24
|
+
this.frequencyOfContext = {};
|
|
25
|
+
this.frequency = {};
|
|
26
|
+
this.classes = [];
|
|
27
|
+
if (elements) {
|
|
28
|
+
this.elements = elements;
|
|
29
|
+
this.analyse();
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
this.elements = [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Extracts classes and frequencies
|
|
37
|
+
Sample.prototype.analyse = function() {
|
|
38
|
+
var that = this;
|
|
39
|
+
this.elements.forEach(function(x) {
|
|
40
|
+
if (this.classes.indexOf(x.a) === -1) {
|
|
41
|
+
this.classes.push(x.a);
|
|
42
|
+
}
|
|
43
|
+
if (!that.frequencyOfContext[x.b.toString()]) {
|
|
44
|
+
that.frequencyOfContext[x.b.toString()] = 0;
|
|
45
|
+
}
|
|
46
|
+
that.frequencyOfContext[x.b.toString()]++;
|
|
47
|
+
if (!that.frequency[x.toString()]) {
|
|
48
|
+
that.frequency[x.toString()] = 0;
|
|
49
|
+
}
|
|
50
|
+
that.frequency[x.toString()]++;
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
Sample.prototype.addElement = function(x) {
|
|
55
|
+
this.elements.push(x);
|
|
56
|
+
// Update frequencies
|
|
57
|
+
if (!this.frequencyOfContext[x.b.toString()]) {
|
|
58
|
+
this.frequencyOfContext[x.b.toString()] = 0;
|
|
59
|
+
}
|
|
60
|
+
this.frequencyOfContext[x.b.toString()]++;
|
|
61
|
+
if (!this.frequency[x.toString()]) {
|
|
62
|
+
this.frequency[x.toString()] = 0;
|
|
63
|
+
}
|
|
64
|
+
this.frequency[x.toString()]++;
|
|
65
|
+
// Update classes
|
|
66
|
+
if (this.classes.indexOf(x.a) === -1) {
|
|
67
|
+
this.classes.push(x.a);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
Sample.prototype.observedProbabilityOfContext = function(context) {
|
|
72
|
+
if (this.frequencyOfContext[context.toString()]) {
|
|
73
|
+
return this.frequencyOfContext[context.toString()] / this.elements.length;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
Sample.prototype.observedProbability = function(x) {
|
|
81
|
+
if (this.frequency[x.toString()]) {
|
|
82
|
+
return this.frequency[x.toString()] / this.elements.length;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
Sample.prototype.size = function() {
|
|
90
|
+
return this.elements.length;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
Sample.prototype.getClasses = function() {
|
|
94
|
+
return this.classes;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
Sample.prototype.generateFeatures = function(featureSet) {
|
|
98
|
+
this.elements.forEach(function(x) {
|
|
99
|
+
x.generateFeatures(featureSet);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
Sample.prototype.save = function(filename, callback) {
|
|
104
|
+
var sample = this;
|
|
105
|
+
var data = JSON.stringify(this, null, 2);
|
|
106
|
+
fs.writeFile(filename, data, 'utf8', function(err) {
|
|
107
|
+
//console.log('Sample written')
|
|
108
|
+
if(callback) {
|
|
109
|
+
callback(err, err ? null : sample);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Loads a sample from file and revives the right classes, i.e. Sample and
|
|
115
|
+
// Element classes.
|
|
116
|
+
Sample.prototype.load = function(filename, elementClass, callback) {
|
|
117
|
+
fs.readFile(filename, 'utf8', function(err, data) {
|
|
118
|
+
|
|
119
|
+
if(!err) {
|
|
120
|
+
var sampleData = JSON.parse(data);
|
|
121
|
+
var sample = new Sample();
|
|
122
|
+
sampleData.elements.forEach(function(elementData) {
|
|
123
|
+
var elt = new elementClass(elementData.a, new Context(elementData.b.data));
|
|
124
|
+
sample.addElement(elt);
|
|
125
|
+
});
|
|
126
|
+
if (!sample.frequency || !sample.frequencyOfContext) {
|
|
127
|
+
sample.analyse();
|
|
128
|
+
}
|
|
129
|
+
if (callback) {
|
|
130
|
+
callback(err, sample);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
if (callback) {
|
|
135
|
+
callback(err);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
module.exports = Sample;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Simple Example Element class
|
|
3
|
+
Copyright (C) 2018 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var util = require('util');
|
|
20
|
+
|
|
21
|
+
var Element = require('../Element');
|
|
22
|
+
var Feature = require('../Feature');
|
|
23
|
+
|
|
24
|
+
function SE_Element(a, b) {
|
|
25
|
+
SE_Element.super_.call(this, a, b);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
util.inherits(SE_Element, Element);
|
|
29
|
+
|
|
30
|
+
SE_Element.prototype.generateFeatures = function(featureSet) {
|
|
31
|
+
|
|
32
|
+
function isZero(x) {
|
|
33
|
+
if ((x.a === "x") && (x.b.data === "0")) {
|
|
34
|
+
return 1;
|
|
35
|
+
}
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
featureSet.addFeature(new Feature(isZero, "isZero", ["0"]));
|
|
39
|
+
|
|
40
|
+
function isOne(x) {
|
|
41
|
+
if ((x.a === "y") && (x.b.data === "1")) {
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
featureSet.addFeature(new Feature(isOne, "isOne", ["1"]));
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
module.exports = SE_Element;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2018, Shane Caldwell, Hugo 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
|
+
// Computes the Hamming distance between two string -- intrepreted from:
|
|
24
|
+
// https://en.wikipedia.org/wiki/Hamming_distance
|
|
25
|
+
// s1 is the first string to compare
|
|
26
|
+
// s2 is the second string to compare
|
|
27
|
+
// Strings should have equal length
|
|
28
|
+
function HammingDistance(s1, s2, ignoreCase) {
|
|
29
|
+
// Return -1 if one of the parameters is not a string
|
|
30
|
+
if (typeof(s1) != "string" || typeof(s2) != "string") {
|
|
31
|
+
return -1;
|
|
32
|
+
}
|
|
33
|
+
// Return -1 the lengths of the strings differ
|
|
34
|
+
if (s1.length != s2.length) {
|
|
35
|
+
return -1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (ignoreCase) {
|
|
39
|
+
s1 = s1.toLowerCase();
|
|
40
|
+
s2 = s2.toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
var diffs = 0;
|
|
44
|
+
for (var i = 0; i < s1.length; i++) {
|
|
45
|
+
if (s1[i] != s2[i]) {
|
|
46
|
+
diffs++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return diffs;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = HammingDistance;
|