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
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
//var logger = log4js.getLogger();
|
|
21
21
|
|
|
22
22
|
var Predicate = require("./Predicate");
|
|
23
|
+
var Sentence = require('./Sentence');
|
|
23
24
|
|
|
24
25
|
//logger.setLevel('INFO');
|
|
25
26
|
|
|
@@ -42,11 +43,11 @@ TransformationRule.prototype.key = function() {
|
|
|
42
43
|
return(this.literal.toString());
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
TransformationRule.prototype.apply = function(
|
|
46
|
-
if ((
|
|
46
|
+
TransformationRule.prototype.apply = function(sentence, position) {
|
|
47
|
+
if ((sentence.taggedWords[position].tag === this.old_category) ||
|
|
47
48
|
(this.old_category === category_wild_card)) {
|
|
48
|
-
if (this.predicate.evaluate(
|
|
49
|
-
|
|
49
|
+
if (this.predicate.evaluate(sentence, position)) {
|
|
50
|
+
sentence.taggedWords[position].tag = this.new_category;
|
|
50
51
|
//logger.debug('TransformationRule.apply: changed category ' +
|
|
51
52
|
//this.old_category + ' to ' + this.new_category +
|
|
52
53
|
//' at position ' + position);
|
|
@@ -62,13 +63,13 @@ TransformationRule.prototype.apply = function(tagged_sentence, position) {
|
|
|
62
63
|
// category to newTag
|
|
63
64
|
TransformationRule.prototype.isApplicableAt = function(sentence, taggedSentence, i) {
|
|
64
65
|
//logger.debug("TransformationRule.prototype.isApplicableAt: " + taggedSentence);
|
|
65
|
-
var applies = (taggedSentence[i]
|
|
66
|
+
var applies = (taggedSentence.taggedWords[i].tag === this.old_category) &&
|
|
66
67
|
this.predicate.evaluate(taggedSentence, i);
|
|
67
68
|
//logger.debug("TransformationRule.prototype.isApplicableAt: " + applies);
|
|
68
69
|
|
|
69
70
|
// Set newTag to let the trainer know what the new tag would become
|
|
70
71
|
if (applies) {
|
|
71
|
-
sentence[i].newTag = this.new_category;
|
|
72
|
+
sentence.taggedWords[i].newTag = this.new_category;
|
|
72
73
|
}
|
|
73
74
|
return(applies);
|
|
74
75
|
};
|
|
@@ -92,16 +93,14 @@ TransformationRule.prototype.prettyPrint = function() {
|
|
|
92
93
|
|
|
93
94
|
// Applies the rule the given location (if it applies)
|
|
94
95
|
TransformationRule.prototype.applyAt = function(sentence, i) {
|
|
95
|
-
var taggedSentence = sentence.
|
|
96
|
-
return [token.token, token.tag];
|
|
97
|
-
});
|
|
96
|
+
var taggedSentence = sentence.clone();
|
|
98
97
|
|
|
99
98
|
//logger.debug("TransformationRule.prototype.applyAt: input sentence length: " + sentence.length);
|
|
100
99
|
//logger.debug("TransformationRule.prototype.applyAt: tagged sentence length: " + taggedSentence.length);
|
|
101
100
|
|
|
102
|
-
this.apply(
|
|
101
|
+
this.apply(sentence, i);
|
|
103
102
|
// Assign the new tag to the corpus site
|
|
104
|
-
sentence[i].testTag = taggedSentence[i]
|
|
103
|
+
sentence.taggedWords[i].testTag = taggedSentence.taggedWords[i].tag;
|
|
105
104
|
};
|
|
106
105
|
|
|
107
106
|
// Calculate the net score of this rule
|
|
@@ -444,10 +444,12 @@ function setOptions(options){
|
|
|
444
444
|
Classifier.prototype.addDocument = addDocument;
|
|
445
445
|
Classifier.prototype.removeDocument = removeDocument;
|
|
446
446
|
Classifier.prototype.train = train;
|
|
447
|
-
|
|
448
|
-
Classifier.prototype.
|
|
447
|
+
if (Threads) {
|
|
448
|
+
Classifier.prototype.trainParallel = trainParallel;
|
|
449
|
+
Classifier.prototype.trainParallelBatches = trainParallelBatches;
|
|
450
|
+
Classifier.prototype.retrainParallel = retrainParallel;
|
|
451
|
+
}
|
|
449
452
|
Classifier.prototype.retrain = retrain;
|
|
450
|
-
Classifier.prototype.retrainParallel = retrainParallel;
|
|
451
453
|
Classifier.prototype.classify = classify;
|
|
452
454
|
Classifier.prototype.textToFeatures = textToFeatures;
|
|
453
455
|
Classifier.prototype.save = save;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Classifier class that provides functionality for training and
|
|
3
|
+
classification
|
|
4
|
+
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU General Public License
|
|
17
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
var fs = require('fs');
|
|
21
|
+
|
|
22
|
+
var Context = require('./Context');
|
|
23
|
+
var Element = require('./Element');
|
|
24
|
+
var Sample = require('./Sample');
|
|
25
|
+
var Scaler = require('./GISScaler');
|
|
26
|
+
var FeatureSet = require('./FeatureSet');
|
|
27
|
+
|
|
28
|
+
function Classifier(features, sample) {
|
|
29
|
+
if (features) {
|
|
30
|
+
this.features = features;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
this.features = new featureSet();
|
|
34
|
+
}
|
|
35
|
+
this.features = features;
|
|
36
|
+
if (sample) {
|
|
37
|
+
this.sample = sample;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
this.sample = new Sample();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Loads a classifier from file.
|
|
45
|
+
// Caveat: feature functions are generated from the sample elements. You need
|
|
46
|
+
// to create your own specialisation of the Element class that can generate
|
|
47
|
+
// your own specific feature functions
|
|
48
|
+
Classifier.prototype.load = function(filename, elementClass, callback) {
|
|
49
|
+
fs.readFile(filename, 'utf8', function(err, data) {
|
|
50
|
+
|
|
51
|
+
if(!err) {
|
|
52
|
+
classifierData = JSON.parse(data);
|
|
53
|
+
var sample = new Sample();
|
|
54
|
+
classifierData.sample.elements.forEach(function(elementData) {
|
|
55
|
+
var elt = new elementClass(elementData.a, new Context(elementData.b.data));
|
|
56
|
+
sample.addElement(elt);
|
|
57
|
+
});
|
|
58
|
+
var featureSet = new FeatureSet();
|
|
59
|
+
sample.generateFeatures(featureSet);
|
|
60
|
+
var classifier = new Classifier(featureSet, sample);
|
|
61
|
+
callback(err, classifier);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
if(callback) {
|
|
65
|
+
callback(err);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
Classifier.prototype.save = function(filename, callback) {
|
|
72
|
+
var data = JSON.stringify(this, null, 2);
|
|
73
|
+
var classifier = this;
|
|
74
|
+
fs.writeFile(filename, data, 'utf8', function(err) {
|
|
75
|
+
if(callback) {
|
|
76
|
+
callback(err, err ? null : classifier);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
Classifier.prototype.addElement = function(x) {
|
|
82
|
+
this.sample.addElement(x);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
Classifier.prototype.addDocument = function(context, classification, elementClass) {
|
|
86
|
+
Classifier.prototype.addElement(new elementClass(classification, context));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
Classifier.prototype.train = function(maxIterations, minImprovement, approxExpectation) {
|
|
90
|
+
this.scaler = new Scaler(this.features, this.sample);
|
|
91
|
+
this.p = this.scaler.run(maxIterations, minImprovement, approxExpectation);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
Classifier.prototype.getClassifications = function(b) {
|
|
95
|
+
var scores = [];
|
|
96
|
+
var that = this;
|
|
97
|
+
this.sample.getClasses().forEach(function(a) {
|
|
98
|
+
var x = new Element(a, b);
|
|
99
|
+
scores.push({
|
|
100
|
+
"label": a,
|
|
101
|
+
"value": that.p.calculateAPriori(x)
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
return scores;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
Classifier.prototype.classify = function(b) {
|
|
108
|
+
var scores = this.getClassifications(b);
|
|
109
|
+
// Sort the scores in an array
|
|
110
|
+
scores.sort(function(a, b) {
|
|
111
|
+
return b.value - a.value;
|
|
112
|
+
});
|
|
113
|
+
// Check if the classifier discriminates
|
|
114
|
+
var min = scores[scores.length - 1].value;
|
|
115
|
+
var max = scores[0].value;
|
|
116
|
+
if (min === max) {
|
|
117
|
+
return "";
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Return the highest scoring classes
|
|
121
|
+
return scores[0].label;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
module.exports = Classifier;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Context class
|
|
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 stringify = require('json-stable-stringify');
|
|
20
|
+
|
|
21
|
+
function Context(data) {
|
|
22
|
+
this.data = data;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Create a predictable key string for looking up in a hash
|
|
26
|
+
Context.prototype.toString = function() {
|
|
27
|
+
if (!this.key) {
|
|
28
|
+
this.key = stringify(this.data);
|
|
29
|
+
}
|
|
30
|
+
return this.key;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = Context;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Distribution class for probability distributions
|
|
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
|
+
|
|
21
|
+
function Distribution(alpha, featureSet, sample) {
|
|
22
|
+
this.alpha = alpha;
|
|
23
|
+
this.featureSet = featureSet;
|
|
24
|
+
this.sample = sample;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Returns the distribution as a string that can be stored for later usage
|
|
28
|
+
Distribution.prototype.toString = function() {
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Distribution.prototype.weight = function(x) {
|
|
33
|
+
var product = 1;
|
|
34
|
+
var that = this;
|
|
35
|
+
|
|
36
|
+
this.alpha.forEach(function(alpha_j, j) {
|
|
37
|
+
product *= Math.pow(alpha_j, that.featureSet.getFeatures()[j].apply(x));
|
|
38
|
+
});
|
|
39
|
+
return product;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
Distribution.prototype.calculateAPriori = function(x) {
|
|
43
|
+
if (!this.aPriorisBeforeNormalisation[x.toString()]) {
|
|
44
|
+
this.aPriorisBeforeNormalisation[x.toString()] = this.weight(x);
|
|
45
|
+
}
|
|
46
|
+
return this.aPriorisBeforeNormalisation[x.toString()];// / this.aPrioriNormalisationConstant;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Memoize a priori probabilities of sample elements
|
|
50
|
+
Distribution.prototype.prepareWeights = function() {
|
|
51
|
+
this.aPriorisBeforeNormalisation = {};
|
|
52
|
+
this.aPrioriNormalisationConstant = 0;
|
|
53
|
+
var sum = 0;
|
|
54
|
+
var that = this;
|
|
55
|
+
this.sample.elements.forEach(function(x) {
|
|
56
|
+
that.aPriorisBeforeNormalisation[x.toString()] = that.weight(x);
|
|
57
|
+
sum += that.aPriorisBeforeNormalisation[x.toString()];
|
|
58
|
+
});
|
|
59
|
+
this.aPrioriNormalisationConstant = sum;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
Distribution.prototype.calculateAPosteriori = function(x) {
|
|
63
|
+
if (!this.aPriorisBeforeNormalisation[x.toString()]) {
|
|
64
|
+
this.aPriorisBeforeNormalisation[x.toString()] = this.weight(x);
|
|
65
|
+
}
|
|
66
|
+
if (!this.aPosterioriNormalisationConstants[x.b.toString()]) {
|
|
67
|
+
this.aPosterioriNormalisationConstants[x.b.toString()] = this.aPosterioriNormalisation(x.b);
|
|
68
|
+
}
|
|
69
|
+
return this.aPriorisBeforeNormalisation[x] / this.aPosterioriNormalisationConstants[x.b.toString()];
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
Distribution.prototype.aPosterioriNormalisation = function(b) {
|
|
73
|
+
var sum = 0;
|
|
74
|
+
|
|
75
|
+
var that = this;
|
|
76
|
+
this.sample.getClasses().forEach(function(a) {
|
|
77
|
+
sum += that.weight(new Element(a, b));
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return(sum);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Memoize a posteriori probabilities of sample elements
|
|
84
|
+
Distribution.prototype.prepareAPosterioris = function() {
|
|
85
|
+
this.aPosterioriNormalisationConstants = {};
|
|
86
|
+
|
|
87
|
+
var contextSeen = {};
|
|
88
|
+
var that = this;
|
|
89
|
+
this.sample.elements.forEach(function(sampleElement) {
|
|
90
|
+
var context = sampleElement.b;
|
|
91
|
+
if (!contextSeen[context]) {
|
|
92
|
+
contextSeen[context] = true;
|
|
93
|
+
that.aPosterioriNormalisationConstants[context] =
|
|
94
|
+
that.aPosterioriNormalisation(context);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Memoize all probabilities of sample elements
|
|
100
|
+
Distribution.prototype.prepare = function() {
|
|
101
|
+
this.prepareWeights();
|
|
102
|
+
//console.log("Weights prepared");
|
|
103
|
+
this.prepareAPosterioris();
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Relative entropy between observered distribution and derived distribution
|
|
107
|
+
Distribution.prototype.KullbackLieblerDistance = function() {
|
|
108
|
+
var sum = 0;
|
|
109
|
+
var that = this;
|
|
110
|
+
this.sample.elements.forEach(function(x) {
|
|
111
|
+
sum += that.sample.observedProbability(x) * Math.log(that.sample.observedProbability(x) / that.calculateAPriori(x));
|
|
112
|
+
});
|
|
113
|
+
return sum;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
Distribution.prototype.logLikelihood = function() {
|
|
117
|
+
var sum = 0;
|
|
118
|
+
var that = this;
|
|
119
|
+
this.sample.elements.forEach(function(x) {
|
|
120
|
+
sum += that.sample.observedProbability(x) * Math.log(that.calculateAPriori(x));
|
|
121
|
+
});
|
|
122
|
+
return sum;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
Distribution.prototype.entropy = function() {
|
|
126
|
+
var sum = 0;
|
|
127
|
+
var that = this;
|
|
128
|
+
this.sample.elements.forEach(function(x) {
|
|
129
|
+
var p = that.calculateAPriori(x);
|
|
130
|
+
sum += p * Math.log(p);
|
|
131
|
+
});
|
|
132
|
+
return sum;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
Distribution.prototype.checkSum = function() {
|
|
136
|
+
var sum = 0;
|
|
137
|
+
var that = this;
|
|
138
|
+
this.sample.elements.forEach(function(x) {
|
|
139
|
+
sum += that.calculateAPriori(x);
|
|
140
|
+
});
|
|
141
|
+
//console.log("Distribution.checkSum is " + sum);
|
|
142
|
+
return sum;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = Distribution;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Element class for elements in the event space
|
|
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 Feature = require('./Feature');
|
|
20
|
+
|
|
21
|
+
// a is class and b is context
|
|
22
|
+
function Element(a, b) {
|
|
23
|
+
this.a = a;
|
|
24
|
+
this.b = b;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Element.prototype.toString = function() {
|
|
28
|
+
if (!this.key) {
|
|
29
|
+
this.key = this.a + this.b.toString();
|
|
30
|
+
}
|
|
31
|
+
return this.key;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
module.exports = Element;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Feature class for features that fire (or don't) on combinations of context
|
|
3
|
+
and class
|
|
4
|
+
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU General Public License
|
|
17
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
function Feature(f, name, parameters) {
|
|
22
|
+
this.evaluate = f;
|
|
23
|
+
this.name = name;
|
|
24
|
+
this.parameters = parameters;
|
|
25
|
+
|
|
26
|
+
var tmp = "";
|
|
27
|
+
parameters.forEach(function(par) {
|
|
28
|
+
tmp += par + "|";
|
|
29
|
+
});
|
|
30
|
+
this.parametersKey = tmp.substr(0, tmp.length - 1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Feature.prototype.apply = function(x) {
|
|
34
|
+
return this.evaluate(x);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
Feature.prototype.expectationApprox = function(p, sample) {
|
|
38
|
+
var totalSum = 0;
|
|
39
|
+
var that = this;
|
|
40
|
+
var sum = 0;
|
|
41
|
+
var seen = {};
|
|
42
|
+
var A = sample.getClasses();
|
|
43
|
+
sample.elements.forEach(function(sampleElement) {
|
|
44
|
+
var b_i = sampleElement.b;
|
|
45
|
+
|
|
46
|
+
if (!seen[b_i.toString()]) {
|
|
47
|
+
seen[b_i.toString()] = true;
|
|
48
|
+
var Element = require('./Element');
|
|
49
|
+
|
|
50
|
+
A.forEach(function(a) {
|
|
51
|
+
var x = new Element(a, b_i);
|
|
52
|
+
sum += sample.observedProbabilityOfContext(b_i) * p.calculateAPosteriori(x) * that.apply(x);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return sum;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Diect calculation of expected value of this feature according to distribution p
|
|
60
|
+
// In real-life applications with a lot of features this is not tractable
|
|
61
|
+
Feature.prototype.expectation = function(p, A, B) {
|
|
62
|
+
var sum = 0;
|
|
63
|
+
var that = this;
|
|
64
|
+
A.forEach(function(a) {
|
|
65
|
+
B.forEach(function(b) {
|
|
66
|
+
var x = new Element(a, b);
|
|
67
|
+
sum += (p.calculateAPriori(x) * that.apply(x));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
return sum;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Observed expectation of this feature in the sample
|
|
74
|
+
Feature.prototype.observedExpectation = function(sample) {
|
|
75
|
+
if (this.observedExpect) {
|
|
76
|
+
return this.observedExpect;
|
|
77
|
+
}
|
|
78
|
+
var N = sample.size();
|
|
79
|
+
var sum = 0;
|
|
80
|
+
var that = this;
|
|
81
|
+
sample.elements.forEach(function(x) {
|
|
82
|
+
sum += that.apply(x);
|
|
83
|
+
});
|
|
84
|
+
this.observedExpect = sum / N;
|
|
85
|
+
return this.observedExpect;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
module.exports = Feature;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Feature set class for administrating a set of unique feature
|
|
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
|
+
|
|
20
|
+
function FeatureSet() {
|
|
21
|
+
this.features = [];
|
|
22
|
+
this.map = {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Returns true if the feature did not exist and was added
|
|
26
|
+
FeatureSet.prototype.addFeature = function(feature) {
|
|
27
|
+
if (!this.featureExists(feature)) {
|
|
28
|
+
this.map[feature.name +" | " + feature.parametersKey] = true;
|
|
29
|
+
this.features.push(feature);
|
|
30
|
+
//console.log("FeatureSet.addFeature: feature added: " + feature.name + " - " + feature.parametersKey);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
FeatureSet.prototype.featureExists = function(feature) {
|
|
39
|
+
if (this.map[feature.name +" | " + feature.parametersKey]) {
|
|
40
|
+
//console.log("FeatureSet.featureExists: feature already exists: " +
|
|
41
|
+
// feature.name + " - " + feature.parameters);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Returns an array of features
|
|
50
|
+
// If the available array this.features is up to date it is returned immediately
|
|
51
|
+
FeatureSet.prototype.getFeatures = function() {
|
|
52
|
+
return this.features;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
FeatureSet.prototype.size = function() {
|
|
56
|
+
return this.features.length;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
FeatureSet.prototype.prettyPrint = function() {
|
|
60
|
+
var s = "";
|
|
61
|
+
Object.keys(this.map).forEach(function(key) {
|
|
62
|
+
s += key + "\n";
|
|
63
|
+
});
|
|
64
|
+
return s;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
module.exports = FeatureSet;
|