natural 6.10.2 → 6.10.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/.github/workflows/node.js.yml +1 -1
- package/.github/workflows/npm-publish.yml +4 -4
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tagger.js +33 -31
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tester.js +33 -35
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Trainer.js +326 -324
- package/lib/natural/brill_pos_tagger/lib/Corpus.js +140 -138
- package/lib/natural/brill_pos_tagger/lib/Lexicon.js +78 -76
- package/lib/natural/brill_pos_tagger/lib/Predicate.js +19 -17
- package/lib/natural/brill_pos_tagger/lib/RuleSet.js +58 -56
- package/lib/natural/brill_pos_tagger/lib/RuleTemplate.js +11 -9
- package/lib/natural/brill_pos_tagger/lib/Sentence.js +21 -20
- package/lib/natural/brill_pos_tagger/lib/TransformationRule.js +58 -58
- package/lib/natural/classifiers/bayes_classifier.js +24 -29
- package/lib/natural/classifiers/classifier.js +136 -121
- package/lib/natural/classifiers/logistic_regression_classifier.js +33 -37
- package/lib/natural/classifiers/maxent/Classifier.js +81 -79
- package/lib/natural/classifiers/maxent/Context.js +11 -9
- package/lib/natural/classifiers/maxent/Distribution.js +107 -105
- package/lib/natural/classifiers/maxent/Element.js +12 -10
- package/lib/natural/classifiers/maxent/Feature.js +56 -54
- package/lib/natural/classifiers/maxent/FeatureSet.js +40 -38
- package/lib/natural/classifiers/maxent/GISScaler.js +110 -109
- package/lib/natural/classifiers/maxent/POS/ME_Corpus.js +26 -31
- package/lib/natural/classifiers/maxent/POS/ME_Sentence.js +37 -42
- package/lib/natural/classifiers/maxent/POS/POS_Element.js +119 -124
- package/lib/natural/classifiers/maxent/Sample.js +96 -94
- package/lib/natural/classifiers/maxent/SimpleExample/SE_Element.js +14 -20
- package/lib/natural/inflectors/fr/count_inflector.js +1 -1
- package/lib/natural/inflectors/fr/noun_inflector.js +179 -196
- package/lib/natural/inflectors/ja/noun_inflector.js +67 -84
- package/lib/natural/inflectors/noun_inflector.js +124 -139
- package/lib/natural/inflectors/present_verb_inflector.js +34 -48
- package/lib/natural/inflectors/singular_plural_inflector.js +62 -61
- package/lib/natural/tfidf/tfidf.js +118 -114
- package/lib/natural/trie/trie.js +161 -159
- package/lib/natural/util/directed_edge.js +49 -0
- package/lib/natural/util/edge_weighted_digraph.js +54 -74
- package/lib/natural/util/index.js +1 -0
- package/lib/natural/util/longest_path_tree.js +54 -52
- package/lib/natural/util/shortest_path_tree.js +56 -54
- package/lib/natural/util/topological.js +17 -15
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Classifier class that provides functionality for training and
|
|
3
3
|
classification
|
|
4
|
-
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
4
|
+
Copyright (C) 2017, 2023 Hugo W.L. ter Doest
|
|
5
5
|
|
|
6
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -32,96 +32,98 @@ const Sample = require('./Sample')
|
|
|
32
32
|
const Scaler = require('./GISScaler')
|
|
33
33
|
const FeatureSet = require('./FeatureSet')
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
class Classifier {
|
|
36
|
+
constructor (features, sample) {
|
|
37
|
+
if (features) {
|
|
38
|
+
this.features = features
|
|
39
|
+
} else {
|
|
40
|
+
this.features = new FeatureSet()
|
|
41
|
+
}
|
|
37
42
|
this.features = features
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
if (sample) {
|
|
44
|
+
this.sample = sample
|
|
45
|
+
} else {
|
|
46
|
+
this.sample = new Sample()
|
|
47
|
+
}
|
|
40
48
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
|
|
50
|
+
// Loads a classifier from file.
|
|
51
|
+
// Caveat: feature functions are generated from the sample elements. You need
|
|
52
|
+
// to create your own specialisation of the Element class that can generate
|
|
53
|
+
// your own specific feature functions
|
|
54
|
+
load (filename, ElementClass, callback) {
|
|
55
|
+
fs.readFile(filename, 'utf8', function (err, data) {
|
|
56
|
+
if (!err) {
|
|
57
|
+
const classifierData = JSON.parse(data)
|
|
58
|
+
const sample = new Sample()
|
|
59
|
+
classifierData.sample.elements.forEach(function (elementData) {
|
|
60
|
+
const elt = new ElementClass(elementData.a, new Context(elementData.b.data))
|
|
61
|
+
sample.addElement(elt)
|
|
62
|
+
})
|
|
63
|
+
const featureSet = new FeatureSet()
|
|
64
|
+
sample.generateFeatures(featureSet)
|
|
65
|
+
const classifier = new Classifier(featureSet, sample)
|
|
66
|
+
callback(err, classifier)
|
|
67
|
+
} else {
|
|
68
|
+
if (callback) {
|
|
69
|
+
callback(err)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
})
|
|
46
73
|
}
|
|
47
|
-
}
|
|
48
74
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Classifier.prototype.load = function (filename, ElementClass, callback) {
|
|
54
|
-
fs.readFile(filename, 'utf8', function (err, data) {
|
|
55
|
-
if (!err) {
|
|
56
|
-
const classifierData = JSON.parse(data)
|
|
57
|
-
const sample = new Sample()
|
|
58
|
-
classifierData.sample.elements.forEach(function (elementData) {
|
|
59
|
-
const elt = new ElementClass(elementData.a, new Context(elementData.b.data))
|
|
60
|
-
sample.addElement(elt)
|
|
61
|
-
})
|
|
62
|
-
const featureSet = new FeatureSet()
|
|
63
|
-
sample.generateFeatures(featureSet)
|
|
64
|
-
const classifier = new Classifier(featureSet, sample)
|
|
65
|
-
callback(err, classifier)
|
|
66
|
-
} else {
|
|
75
|
+
save (filename, callback) {
|
|
76
|
+
const data = JSON.stringify(this, null, 2)
|
|
77
|
+
const classifier = this
|
|
78
|
+
fs.writeFile(filename, data, 'utf8', function (err) {
|
|
67
79
|
if (callback) {
|
|
68
|
-
|
|
80
|
+
console.log('Saved classifier to ' + filename)
|
|
81
|
+
callback(err, err ? null : classifier)
|
|
69
82
|
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
Classifier.prototype.save = function (filename, callback) {
|
|
75
|
-
const data = JSON.stringify(this, null, 2)
|
|
76
|
-
const classifier = this
|
|
77
|
-
fs.writeFile(filename, data, 'utf8', function (err) {
|
|
78
|
-
if (callback) {
|
|
79
|
-
console.log('Saved classifier to ' + filename)
|
|
80
|
-
callback(err, err ? null : classifier)
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
}
|
|
83
|
+
})
|
|
84
|
+
}
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
86
|
+
addElement (x) {
|
|
87
|
+
this.sample.addElement(x)
|
|
88
|
+
}
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
90
|
+
addDocument (context, classification, ElementClass) {
|
|
91
|
+
Classifier.prototype.addElement(new ElementClass(classification, context))
|
|
92
|
+
}
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
94
|
+
train (maxIterations, minImprovement, approxExpectation) {
|
|
95
|
+
this.scaler = new Scaler(this.features, this.sample)
|
|
96
|
+
this.p = this.scaler.run(maxIterations, minImprovement, approxExpectation)
|
|
97
|
+
}
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
getClassifications (b) {
|
|
100
|
+
const scores = []
|
|
101
|
+
const that = this
|
|
102
|
+
this.sample.getClasses().forEach(function (a) {
|
|
103
|
+
const x = new Element(a, b)
|
|
104
|
+
scores.push({
|
|
105
|
+
label: a,
|
|
106
|
+
value: that.p.calculateAPriori(x)
|
|
107
|
+
})
|
|
106
108
|
})
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
109
|
+
return scores
|
|
110
|
+
}
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
112
|
+
classify (b) {
|
|
113
|
+
const scores = this.getClassifications(b)
|
|
114
|
+
// Sort the scores in an array
|
|
115
|
+
scores.sort(function (a, b) {
|
|
116
|
+
return b.value - a.value
|
|
117
|
+
})
|
|
118
|
+
// Check if the classifier discriminates
|
|
119
|
+
const min = scores[scores.length - 1].value
|
|
120
|
+
const max = scores[0].value
|
|
121
|
+
if (min === max) {
|
|
122
|
+
return ''
|
|
123
|
+
} else {
|
|
124
|
+
// Return the highest scoring classes
|
|
125
|
+
return scores[0].label
|
|
126
|
+
}
|
|
125
127
|
}
|
|
126
128
|
}
|
|
127
129
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Context class
|
|
3
|
-
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
3
|
+
Copyright (C) 2017, 2023 Hugo W.L. ter Doest
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -25,16 +25,18 @@ THE SOFTWARE.
|
|
|
25
25
|
|
|
26
26
|
const stringify = require('safe-stable-stringify')
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
class Context {
|
|
29
|
+
constructor (data) {
|
|
30
|
+
this.data = data
|
|
31
|
+
}
|
|
31
32
|
|
|
32
|
-
// Create a predictable key string for looking up in a hash
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
// Create a predictable key string for looking up in a hash
|
|
34
|
+
toString () {
|
|
35
|
+
if (!this.key) {
|
|
36
|
+
this.key = stringify(this.data)
|
|
37
|
+
}
|
|
38
|
+
return this.key
|
|
36
39
|
}
|
|
37
|
-
return this.key
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
module.exports = Context
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Distribution class for probability distributions
|
|
3
|
-
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
3
|
+
Copyright (C) 2017, 2023 Hugo W.L. ter Doest
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -25,128 +25,130 @@ THE SOFTWARE.
|
|
|
25
25
|
|
|
26
26
|
const Element = require('./Element')
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Returns the distribution as a string that can be stored for later usage
|
|
35
|
-
Distribution.prototype.toString = function () {
|
|
28
|
+
class Distribution {
|
|
29
|
+
constructor (alpha, featureSet, sample) {
|
|
30
|
+
this.alpha = alpha
|
|
31
|
+
this.featureSet = featureSet
|
|
32
|
+
this.sample = sample
|
|
33
|
+
}
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
// Returns the distribution as a string that can be stored for later usage
|
|
36
|
+
toString () {
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
let product = 1
|
|
41
|
-
const that = this
|
|
38
|
+
}
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
product
|
|
45
|
-
|
|
46
|
-
return product
|
|
47
|
-
}
|
|
40
|
+
weight (x) {
|
|
41
|
+
let product = 1
|
|
42
|
+
const that = this
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
this.alpha.forEach(function (alphaj, j) {
|
|
45
|
+
product *= Math.pow(alphaj, that.featureSet.getFeatures()[j].apply(x))
|
|
46
|
+
})
|
|
47
|
+
return product
|
|
52
48
|
}
|
|
53
|
-
return this.aPriorisBeforeNormalisation[x.toString()]// / this.aPrioriNormalisationConstant;
|
|
54
|
-
}
|
|
55
49
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.sample.elements.forEach(function (x) {
|
|
63
|
-
that.aPriorisBeforeNormalisation[x.toString()] = that.weight(x)
|
|
64
|
-
sum += that.aPriorisBeforeNormalisation[x.toString()]
|
|
65
|
-
})
|
|
66
|
-
this.aPrioriNormalisationConstant = sum
|
|
67
|
-
}
|
|
50
|
+
calculateAPriori (x) {
|
|
51
|
+
if (!this.aPriorisBeforeNormalisation[x.toString()]) {
|
|
52
|
+
this.aPriorisBeforeNormalisation[x.toString()] = this.weight(x)
|
|
53
|
+
}
|
|
54
|
+
return this.aPriorisBeforeNormalisation[x.toString()]// / this.aPrioriNormalisationConstant;
|
|
55
|
+
}
|
|
68
56
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.aPriorisBeforeNormalisation
|
|
57
|
+
// Memoize a priori probabilities of sample elements
|
|
58
|
+
prepareWeights () {
|
|
59
|
+
this.aPriorisBeforeNormalisation = {}
|
|
60
|
+
this.aPrioriNormalisationConstant = 0
|
|
61
|
+
let sum = 0
|
|
62
|
+
const that = this
|
|
63
|
+
this.sample.elements.forEach(function (x) {
|
|
64
|
+
that.aPriorisBeforeNormalisation[x.toString()] = that.weight(x)
|
|
65
|
+
sum += that.aPriorisBeforeNormalisation[x.toString()]
|
|
66
|
+
})
|
|
67
|
+
this.aPrioriNormalisationConstant = sum
|
|
72
68
|
}
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
|
|
70
|
+
calculateAPosteriori (x) {
|
|
71
|
+
if (!this.aPriorisBeforeNormalisation[x.toString()]) {
|
|
72
|
+
this.aPriorisBeforeNormalisation[x.toString()] = this.weight(x)
|
|
73
|
+
}
|
|
74
|
+
if (!this.aPosterioriNormalisationConstants[x.b.toString()]) {
|
|
75
|
+
this.aPosterioriNormalisationConstants[x.b.toString()] = this.aPosterioriNormalisation(x.b)
|
|
76
|
+
}
|
|
77
|
+
return this.aPriorisBeforeNormalisation[x] / this.aPosterioriNormalisationConstants[x.b.toString()]
|
|
75
78
|
}
|
|
76
|
-
return this.aPriorisBeforeNormalisation[x] / this.aPosterioriNormalisationConstants[x.b.toString()]
|
|
77
|
-
}
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
aPosterioriNormalisation (b) {
|
|
81
|
+
let sum = 0
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
const that = this
|
|
84
|
+
this.sample.getClasses().forEach(function (a) {
|
|
85
|
+
sum += that.weight(new Element(a, b))
|
|
86
|
+
})
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
}
|
|
88
|
+
return (sum)
|
|
89
|
+
}
|
|
89
90
|
|
|
90
|
-
// Memoize a posteriori probabilities of sample elements
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
91
|
+
// Memoize a posteriori probabilities of sample elements
|
|
92
|
+
prepareAPosterioris () {
|
|
93
|
+
this.aPosterioriNormalisationConstants = {}
|
|
94
|
+
|
|
95
|
+
const contextSeen = {}
|
|
96
|
+
const that = this
|
|
97
|
+
this.sample.elements.forEach(function (sampleElement) {
|
|
98
|
+
const context = sampleElement.b
|
|
99
|
+
if (!contextSeen[context]) {
|
|
100
|
+
contextSeen[context] = true
|
|
101
|
+
that.aPosterioriNormalisationConstants[context] =
|
|
102
|
+
that.aPosterioriNormalisation(context)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
}
|
|
105
106
|
|
|
106
|
-
// Memoize all probabilities of sample elements
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
107
|
+
// Memoize all probabilities of sample elements
|
|
108
|
+
prepare () {
|
|
109
|
+
this.prepareWeights()
|
|
110
|
+
// console.log("Weights prepared");
|
|
111
|
+
this.prepareAPosterioris()
|
|
112
|
+
}
|
|
112
113
|
|
|
113
|
-
// Relative entropy between observered distribution and derived distribution
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
114
|
+
// Relative entropy between observered distribution and derived distribution
|
|
115
|
+
KullbackLieblerDistance () {
|
|
116
|
+
let sum = 0
|
|
117
|
+
const that = this
|
|
118
|
+
this.sample.elements.forEach(function (x) {
|
|
119
|
+
sum += that.sample.observedProbability(x) * Math.log(that.sample.observedProbability(x) / that.calculateAPriori(x))
|
|
120
|
+
})
|
|
121
|
+
return sum
|
|
122
|
+
}
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
124
|
+
logLikelihood () {
|
|
125
|
+
let sum = 0
|
|
126
|
+
const that = this
|
|
127
|
+
this.sample.elements.forEach(function (x) {
|
|
128
|
+
sum += that.sample.observedProbability(x) * Math.log(that.calculateAPriori(x))
|
|
129
|
+
})
|
|
130
|
+
return sum
|
|
131
|
+
}
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
133
|
+
entropy () {
|
|
134
|
+
let sum = 0
|
|
135
|
+
const that = this
|
|
136
|
+
this.sample.elements.forEach(function (x) {
|
|
137
|
+
const p = that.calculateAPriori(x)
|
|
138
|
+
sum += p * Math.log(p)
|
|
139
|
+
})
|
|
140
|
+
return sum
|
|
141
|
+
}
|
|
141
142
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
143
|
+
checkSum () {
|
|
144
|
+
let sum = 0
|
|
145
|
+
const that = this
|
|
146
|
+
this.sample.elements.forEach(function (x) {
|
|
147
|
+
sum += that.calculateAPriori(x)
|
|
148
|
+
})
|
|
149
|
+
// console.log("Distribution.checkSum is " + sum);
|
|
150
|
+
return sum
|
|
151
|
+
}
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
module.exports = Distribution
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Element class for elements in the event space
|
|
3
|
-
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
3
|
+
Copyright (C) 2017, 2023 Hugo W.L. ter Doest
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -23,17 +23,19 @@ THE SOFTWARE.
|
|
|
23
23
|
|
|
24
24
|
'use strict'
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
class Element {
|
|
27
|
+
// a is class and b is context
|
|
28
|
+
constructor (a, b) {
|
|
29
|
+
this.a = a
|
|
30
|
+
this.b = b
|
|
31
|
+
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
toString () {
|
|
34
|
+
if (!this.key) {
|
|
35
|
+
this.key = this.a + this.b.toString()
|
|
36
|
+
}
|
|
37
|
+
return this.key
|
|
35
38
|
}
|
|
36
|
-
return this.key
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
module.exports = Element
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Feature class for features that fire (or don't) on combinations of context
|
|
3
3
|
and class
|
|
4
|
-
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
4
|
+
Copyright (C) 2017, 2023 Hugo W.L. ter Doest
|
|
5
5
|
|
|
6
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -26,69 +26,71 @@ THE SOFTWARE.
|
|
|
26
26
|
|
|
27
27
|
const Element = require('./Element')
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
class Feature {
|
|
30
|
+
constructor (f, name, parameters) {
|
|
31
|
+
this.evaluate = f
|
|
32
|
+
this.name = name
|
|
33
|
+
this.parameters = parameters
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
35
|
+
let tmp = ''
|
|
36
|
+
parameters.forEach(function (par) {
|
|
37
|
+
tmp += par + '|'
|
|
38
|
+
})
|
|
39
|
+
this.parametersKey = tmp.substr(0, tmp.length - 1)
|
|
40
|
+
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
42
|
+
apply (x) {
|
|
43
|
+
return this.evaluate(x)
|
|
44
|
+
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
expectationApprox (p, sample) {
|
|
47
|
+
const that = this
|
|
48
|
+
let sum = 0
|
|
49
|
+
const seen = {}
|
|
50
|
+
const A = sample.getClasses()
|
|
51
|
+
sample.elements.forEach(function (sampleElement) {
|
|
52
|
+
const bi = sampleElement.b
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
if (!seen[bi.toString()]) {
|
|
55
|
+
seen[bi.toString()] = true
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
57
|
+
A.forEach(function (a) {
|
|
58
|
+
const x = new Element(a, bi)
|
|
59
|
+
sum += sample.observedProbabilityOfContext(bi) * p.calculateAPosteriori(x) * that.apply(x)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
return sum
|
|
64
|
+
}
|
|
64
65
|
|
|
65
|
-
// Direct calculation of expected value of this feature according to distribution p
|
|
66
|
-
// In real-life applications with a lot of features this is not tractable
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
// Direct calculation of expected value of this feature according to distribution p
|
|
67
|
+
// In real-life applications with a lot of features this is not tractable
|
|
68
|
+
expectation (p, A, B) {
|
|
69
|
+
let sum = 0
|
|
70
|
+
const that = this
|
|
71
|
+
A.forEach(function (a) {
|
|
72
|
+
B.forEach(function (b) {
|
|
73
|
+
const x = new Element(a, b)
|
|
74
|
+
sum += (p.calculateAPriori(x) * that.apply(x))
|
|
75
|
+
})
|
|
74
76
|
})
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
77
|
+
return sum
|
|
78
|
+
}
|
|
78
79
|
|
|
79
|
-
// Observed expectation of this feature in the sample
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
// Observed expectation of this feature in the sample
|
|
81
|
+
observedExpectation (sample) {
|
|
82
|
+
if (this.observedExpect) {
|
|
83
|
+
return this.observedExpect
|
|
84
|
+
}
|
|
85
|
+
const N = sample.size()
|
|
86
|
+
let sum = 0
|
|
87
|
+
const that = this
|
|
88
|
+
sample.elements.forEach(function (x) {
|
|
89
|
+
sum += that.apply(x)
|
|
90
|
+
})
|
|
91
|
+
this.observedExpect = sum / N
|
|
82
92
|
return this.observedExpect
|
|
83
93
|
}
|
|
84
|
-
const N = sample.size()
|
|
85
|
-
let sum = 0
|
|
86
|
-
const that = this
|
|
87
|
-
sample.elements.forEach(function (x) {
|
|
88
|
-
sum += that.apply(x)
|
|
89
|
-
})
|
|
90
|
-
this.observedExpect = sum / N
|
|
91
|
-
return this.observedExpect
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
module.exports = Feature
|