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.
Files changed (80) hide show
  1. package/.dockerignore +2 -0
  2. package/.idea/misc.xml +6 -0
  3. package/.idea/modules.xml +8 -0
  4. package/.idea/natural.iml +12 -0
  5. package/.idea/vcs.xml +6 -0
  6. package/.idea/workspace.xml +511 -0
  7. package/.travis.yml +8 -1
  8. package/README.md +331 -40
  9. package/bayes_classifier.json +1 -1
  10. package/classifier.json +418 -0
  11. package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tagger.js +8 -9
  12. package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tester.js +5 -5
  13. package/lib/natural/brill_pos_tagger/lib/Brill_POS_Trainer.js +10 -6
  14. package/lib/natural/brill_pos_tagger/lib/Corpus.js +21 -16
  15. package/lib/natural/brill_pos_tagger/lib/Lexicon.js +22 -13
  16. package/lib/natural/brill_pos_tagger/lib/Predicate.js +2 -2
  17. package/lib/natural/brill_pos_tagger/lib/RuleTemplate.js +2 -2
  18. package/lib/natural/brill_pos_tagger/lib/RuleTemplates.js +132 -146
  19. package/lib/natural/brill_pos_tagger/lib/Sentence.js +41 -0
  20. package/lib/natural/brill_pos_tagger/lib/TransformationRule.js +10 -11
  21. package/lib/natural/classifiers/classifier.js +5 -3
  22. package/lib/natural/classifiers/maxent/Classifier.js +125 -0
  23. package/lib/natural/classifiers/maxent/Context.js +33 -0
  24. package/lib/natural/classifiers/maxent/Distribution.js +145 -0
  25. package/lib/natural/classifiers/maxent/Element.js +34 -0
  26. package/lib/natural/classifiers/maxent/Feature.js +88 -0
  27. package/lib/natural/classifiers/maxent/FeatureSet.js +67 -0
  28. package/lib/natural/classifiers/maxent/GISScaler.js +148 -0
  29. package/lib/natural/classifiers/maxent/POS/ME_Corpus.js +56 -0
  30. package/lib/natural/classifiers/maxent/POS/ME_Sentence.js +73 -0
  31. package/lib/natural/classifiers/maxent/POS/POS_Element.js +172 -0
  32. package/lib/natural/classifiers/maxent/Sample.js +141 -0
  33. package/lib/natural/classifiers/maxent/SimpleExample/SE_Element.js +49 -0
  34. package/lib/natural/distance/hamming_distance.js +52 -0
  35. package/lib/natural/distance/jaro-winkler_distance.js +16 -6
  36. package/lib/natural/distance/levenshtein_distance.js +2 -2
  37. package/lib/natural/index.js +21 -2
  38. package/lib/natural/inflectors/noun_inflector.js +5 -3
  39. package/lib/natural/ngrams/ngrams.js +83 -25
  40. package/lib/natural/sentiment/Basque/senticon_eu.json +21557 -0
  41. package/lib/natural/sentiment/Catalan/senticon_ca.json +36352 -0
  42. package/lib/natural/sentiment/Dutch/negations_du.json +3 -0
  43. package/lib/natural/sentiment/Dutch/pattern-sentiment-nl.json +39650 -0
  44. package/lib/natural/sentiment/English/negations_en.json +3 -0
  45. package/lib/natural/sentiment/English/pattern-sentiment-en.json +16024 -0
  46. package/lib/natural/sentiment/English/senticon_en.json +124197 -0
  47. package/lib/natural/sentiment/French/pattern-sentiment-fr.json +40906 -0
  48. package/lib/natural/sentiment/Galician/senticon_gl.json +24427 -0
  49. package/lib/natural/sentiment/Italian/pattern-sentiment-it.json +24522 -0
  50. package/lib/natural/sentiment/SentimentAnalyzer.js +116 -0
  51. package/lib/natural/sentiment/Spanish/afinnShortSortedSpanish.json +1655 -0
  52. package/lib/natural/sentiment/Spanish/negations_es.json +3 -0
  53. package/lib/natural/sentiment/Spanish/senticon_es.json +56722 -0
  54. package/lib/natural/sentiment/tools/README.md +31 -0
  55. package/lib/natural/sentiment/tools/XmlParser4PatternData.js +31 -0
  56. package/lib/natural/sentiment/tools/sentimentXmlParser.js +30 -0
  57. package/lib/natural/stemmers/indonesian/data/kata-dasar.json +29935 -0
  58. package/lib/natural/stemmers/indonesian/prefix_rules.js +10 -9
  59. package/lib/natural/stemmers/indonesian/stemmer_id.js +16 -15
  60. package/lib/natural/stemmers/indonesian/suffix_rules.js +2 -2
  61. package/lib/natural/stemmers/porter_stemmer_es.js +265 -199
  62. package/lib/natural/stemmers/porter_stemmer_fr.js +1 -1
  63. package/lib/natural/stemmers/porter_stemmer_nl.js +362 -0
  64. package/lib/natural/stemmers/stemmer_es.js +18 -12
  65. package/lib/natural/stemmers/stemmer_nl.js +64 -0
  66. package/lib/natural/tfidf/tfidf.js +27 -4
  67. package/lib/natural/tokenizers/aggressive_tokenizer_sv.js +1 -1
  68. package/lib/natural/tokenizers/aggressive_tokenizer_vi.js +37 -0
  69. package/lib/natural/tokenizers/orthography_matchers.js +33 -0
  70. package/lib/natural/tokenizers/regexp_tokenizer.js +24 -4
  71. package/lib/natural/tokenizers/sentence_tokenizer.js +1 -1
  72. package/lib/natural/wordnet/wordnet_file.js +7 -1
  73. package/lr_classifier.json +1 -1
  74. package/package.json +35 -23
  75. package/.npmignore +0 -4
  76. package/.vscode/temp.sql +0 -0
  77. package/Makefile +0 -38
  78. package/data/kata-dasar.txt +0 -29933
  79. package/package-lock.json +0 -79
  80. package/tests/stemmer_id_test.js +0 -361
package/README.md CHANGED
@@ -3,6 +3,7 @@ natural
3
3
 
4
4
  [![NPM version](https://img.shields.io/npm/v/natural.svg)](https://www.npmjs.com/package/natural)
5
5
  [![Build Status](https://travis-ci.org/NaturalNode/natural.png?branch=master)](https://travis-ci.org/NaturalNode/natural)
6
+ [![Slack](https://slack.bri.im/badge.svg)](https://slack.bri.im)
6
7
 
7
8
  "Natural" is a general natural language facility for nodejs. Tokenizing,
8
9
  stemming, classification, phonetics, tf-idf, WordNet, string similarity,
@@ -14,10 +15,9 @@ contributions and the like.
14
15
  Note that many algorithms from Rob Ellis's [node-nltools](https://github.com/NaturalNode/node-nltools) are
15
16
  being merged into this project and will be maintained from here onward.
16
17
 
17
- At the moment, most of the algorithms are English-specific, but in the long-term, some diversity
18
- will be in order. Thanks to Polyakov Vladimir, Russian stemming has been added!, Thanks to David Przybilla, Spanish stemming has been added!.
18
+ While most of the algorithms are English-specific, contributors have implemented support for other languages. Thanks to Polyakov Vladimir, Russian stemming has been added! Thanks to David Przybilla, Spanish stemming has been added! Thanks to [even more contributors](https://github.com/NaturalNode/natural/graphs/contributors), stemming and tokenizing in more languages have been added.
19
19
 
20
- Aside from this README, the only documentation is [this DZone article](http://www.dzone.com/links/r/using_natural_a_nlp_module_for_nodejs.html), [this free course on Egghead.io](https://egghead.io/courses/natural-language-processing-in-javascript-with-natural), and [here on my blog](http://www.chrisumbel.com/article/node_js_natural_language_porter_stemmer_lancaster_bayes_naive_metaphone_soundex), which is a bit older.
20
+ Aside from this README, the only documentation is [this DZone article](http://www.dzone.com/links/r/using_natural_a_nlp_module_for_nodejs.html), [this course on Egghead.io](https://egghead.io/courses/natural-language-processing-in-javascript-with-natural), and [here on my blog](http://www.chrisumbel.com/article/node_js_natural_language_porter_stemmer_lancaster_bayes_naive_metaphone_soundex). The README is up to date, the other sources are somewhat outdated.
21
21
 
22
22
  ### TABLE OF CONTENTS
23
23
 
@@ -27,6 +27,9 @@ Aside from this README, the only documentation is [this DZone article](http://ww
27
27
  * [Approximate String Matching](#approximate-string-matching)
28
28
  * [Stemmers](#stemmers)
29
29
  * [Classifiers](#classifiers)
30
+ * [Bayesian and logistic regression](#bayesian-and-logistic-regression)
31
+ * [Maximum Entropy Classifier](#maximum-entropy-classifier)
32
+ * [Sentiment Analysis](#sentiment-analysis)
30
33
  * [Phonetics](#phonetics)
31
34
  * [Inflectors](#inflectors)
32
35
  * [N-Grams](#n-grams)
@@ -38,7 +41,6 @@ Aside from this README, the only documentation is [this DZone article](http://ww
38
41
  * [WordNet](#wordnet)
39
42
  * [Spellcheck](#spellcheck)
40
43
  * [POS Tagger](#pos-tagger)
41
- * [Acknowledgements/references](#acknowledgements-and-references)
42
44
  * [Development](#development)
43
45
  * [License](#license)
44
46
 
@@ -55,7 +57,7 @@ means fork away!
55
57
 
56
58
  ## Tokenizers
57
59
 
58
- Word, Regexp, and [Treebank tokenizers](http://www.cis.upenn.edu/~treebank/tokenization.html) are provided for breaking text up into
60
+ Word, Regexp, and [Treebank tokenizers](ftp://ftp.cis.upenn.edu/pub/treebank/public_html/tokenization.html) are provided for breaking text up into
59
61
  arrays of tokens:
60
62
 
61
63
  ```javascript
@@ -79,16 +81,64 @@ console.log(tokenizer.tokenize("flea-dog"));
79
81
  tokenizer = new natural.WordPunctTokenizer();
80
82
  console.log(tokenizer.tokenize("my dog hasn't any fleas."));
81
83
  // [ 'my', 'dog', 'hasn', '\'', 't', 'any', 'fleas', '.' ]
82
- ```
84
+
85
+ tokenizer = new natural.OrthographyTokenizer({language: "fi"});
86
+ console.log(tokenizer.tokenize("Mikä sinun nimesi on?"));
87
+ // [ 'Mikä', 'sinun', 'nimesi', 'on' ]
88
+ ```
89
+
90
+ Overview of available tokenizers:
91
+
92
+ | Tokenizer | Language | Explanation |
93
+ |:-----------------------|:------------|:------------------------------------------------------------------------|
94
+ | WordTokenizer | Any | Splits on anything except alphabetic characters, digits and underscore |
95
+ | WordPunctTokenizer | Any | Splits on anything except alphabetic characters, digits, punctuation and underscore |
96
+ | SentenceTokenizer | Any | Break string up in to parts based on punctation and quotation marks |
97
+ | CaseTokenizer | Any? | If lower and upper case are the same, the character is assumed to be whitespace or something else (punctuation) |
98
+ | RegexpTokenizer | Any | Splits on a regular expression that either defines sequences of word characters or gap characters |
99
+ | OrthographyTokenizer | Finnish | Splits on anything except alpabetic characters, digits and underscore |
100
+ | TreebankWordTokenizer | Any | |
101
+ | AggressiveTokenizer | English | |
102
+ | AggressiveTokenizerFa | Farsi | |
103
+ | AggressiveTokenizerFr | French | |
104
+ | AggressiveTokenizerRu | Russian | |
105
+ | AggressiveTokenizerEs | Spanish | |
106
+ | AggressiveTokenizerIt | Italian | |
107
+ | AggressiveTokenizerPl | Polish | |
108
+ | AggressiveTokenizerPt | Portugese | |
109
+ | AggressiveTokenizerNo | Norwegian | |
110
+ | AggressiveTokenizerSv | Swedish | |
111
+ | AggressiveTokenizerVi | Vietnamese | |
112
+ | TokenizerJa | Japanese | | |
113
+
114
+
83
115
 
84
116
  ## String Distance
85
117
 
86
- Natural provides an implementation of the [Jaro–Winkler](http://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance) string distance measuring algorithm.
87
- This will return a number between 0 and 1 which tells how closely the strings match (0 = not at all, 1 = exact match):
118
+ Natural provides an implementation of three algorithms for calculating string distance: Hamming distance, Jaro-Winkler, Levenshtein distance, and Dice coefficient.
88
119
 
120
+ [Hamming distance](https://en.wikipedia.org/wiki/Hamming_distance) measures the distance between two strings of equal length by counting the number of different characters. The third parameter indicates whether case should be ignored. By default the algorithm is case sensitive.
89
121
  ```javascript
90
122
  var natural = require('natural');
91
- console.log(natural.JaroWinklerDistance("dixon","dicksonx"))
123
+ console.log(natural.HammingDistance("karolin", "kathrin", false));
124
+ console.log(natural.HammingDistance("karolin", "kerstin", false));
125
+ // If strings differ in length -1 is returned
126
+ console.log(natural.HammingDistance("short string", "longer string", false));
127
+ ```
128
+
129
+ Output:
130
+ ```javascript
131
+ 3
132
+ 3
133
+ -1
134
+ ```
135
+
136
+
137
+ The [Jaro–Winkler](http://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance) string distance measuring algorithm will return a number between 0 and 1 which tells how closely the strings match (0 = not at all, 1 = exact match):
138
+
139
+ ```javascript
140
+ var natural = require('natural');
141
+ console.log(natural.JaroWinklerDistance("dixon","dicksonx"));
92
142
  console.log(natural.JaroWinklerDistance('not', 'same'));
93
143
  ```
94
144
 
@@ -99,7 +149,14 @@ Output:
99
149
  0
100
150
  ```
101
151
 
102
- Natural also offers support for Levenshtein distances:
152
+ If the distance between the strings is already known you can pass it as a third parameter. And you can force the algorithm to ignore case by passing a fourth parameter as follows:
153
+ ```javascript
154
+ var natural = require('natural');
155
+ console.log(natural.JaroWinklerDistance("dixon","dicksonx", undefined, true));
156
+ ```
157
+
158
+
159
+ Natural also offers support for [Levenshtein](https://en.wikipedia.org/wiki/Levenshtein_distance) distances:
103
160
 
104
161
  ```javascript
105
162
  var natural = require('natural');
@@ -169,7 +226,7 @@ console.log(natural.DamerauLevenshteinDistance('CA', 'ABC', { restricted: false
169
226
  1
170
227
  ```
171
228
 
172
- And Dice's co-efficient:
229
+ And [Dice's co-efficient](https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient):
173
230
 
174
231
  ```javascript
175
232
  var natural = require('natural');
@@ -205,7 +262,7 @@ The following
205
262
 
206
263
  ## Stemmers
207
264
 
208
- Currently stemming is supported via the [Porter](http://tartarus.org/martin/PorterStemmer/index.html) and [Lancaster](http://www.comp.lancs.ac.uk/computing/research/stemming/) (Paice/Husk) algorithms.
265
+ Currently stemming is supported via the [Porter](http://tartarus.org/martin/PorterStemmer/index.html) and [Lancaster](http://www.comp.lancs.ac.uk/computing/research/stemming/) (Paice/Husk) algorithms. The Indonesian and Japanese stemmers do not follow a known algorithm.
209
266
 
210
267
  ```javascript
211
268
  var natural = require('natural');
@@ -229,6 +286,24 @@ console.log(natural.PorterStemmerRu.stem("падший"));
229
286
  console.log(natural.PorterStemmerEs.stem("jugaría"));
230
287
  ```
231
288
 
289
+ The following stemmers are available:
290
+
291
+ | Language | Porter | Lancaster | Other | Module |
292
+ |:------------- |:-----------:|:---------:|:---------:|:------------------|
293
+ | Dutch | X | | | `PorterStemmerNl` |
294
+ | English | X | | | `PorterStemmer` |
295
+ | English | | X | | `LancasterStemmer` |
296
+ | Farsi (in progress) | X | | | `PorterStemmerFa` |
297
+ | French | X | | | `PorterStemmerFr` |
298
+ | Indonesian | | | X | `StemmerId` |
299
+ | Italian | X | | | `PorterStemmerIt` |
300
+ | Japanese | | | X | `StemmerJa` |
301
+ | Norwegian | X | | | `PorterStemmerNo` |
302
+ | Portugese | X | | | `PorterStemmerPt` |
303
+ | Russian | X | | | `PorterStemmerRu` |
304
+ | Swedish | X | | | `PorterStemmerSv` |
305
+
306
+
232
307
  `attach()` patches `stem()` and `tokenizeAndStem()` to String as a shortcut to
233
308
  `PorterStemmer.stem(token)`. `tokenizeAndStem()` breaks text up into single words
234
309
  and returns an array of stemmed tokens.
@@ -239,7 +314,7 @@ console.log("i am waking up to the sounds of chainsaws".tokenizeAndStem());
239
314
  console.log("chainsaws".stem());
240
315
  ```
241
316
 
242
- the same thing can be done with a Lancaster stemmer:
317
+ The same thing can be done with a Lancaster stemmer:
243
318
 
244
319
  ```javascript
245
320
  natural.LancasterStemmer.attach();
@@ -249,6 +324,8 @@ console.log("chainsaws".stem());
249
324
 
250
325
  ## Classifiers
251
326
 
327
+ ### Bayesian and logistic regression
328
+
252
329
  Two classifiers are currently supported, [Naive Bayes](http://en.wikipedia.org/wiki/Naive_Bayes_classifier) and [logistic regression](http://en.wikipedia.org/wiki/Logistic_regression).
253
330
  The following examples use the BayesClassifier class, but the
254
331
  LogisticRegressionClassifier class could be substituted instead.
@@ -306,7 +383,7 @@ classifier.addDocument(['sell', 'gold'], 'sell');
306
383
  ```
307
384
 
308
385
  The training process can be monitored by subscribing to the event `trainedWithDocument` that's emitted by the classifier, this event's emitted each time a document is finished being trained against:
309
-
386
+ ```javascript
310
387
  classifier.events.on('trainedWithDocument', function (obj) {
311
388
  console.log(obj);
312
389
  /* {
@@ -316,7 +393,7 @@ The training process can be monitored by subscribing to the event `trainedWithDo
316
393
  * }
317
394
  */
318
395
  });
319
-
396
+ ```
320
397
  A classifier can also be persisted and recalled so you can reuse a training
321
398
 
322
399
  ```javascript
@@ -357,8 +434,216 @@ const PorterStemmerRu = require('./node_modules/natural/lib/natural/stemmers/por
357
434
  var classifier = new natural.BayesClassifier(PorterStemmerRu);
358
435
  ```
359
436
 
360
- ## Phonetics
437
+ ### Maximum Entropy Classifier
438
+ This module provides a classifier based on maximum entropy modelling. The central idea to maximum entropy modelling is to estimate a probability distribution that that has maximum entropy subject to the evidence that is available. This means that the distribution follows the data it has "seen" but does not make any assumptions beyond that.
439
+
440
+ The module is not specific to natural language processing, or any other application domain. There are little requirements with regard to the data structure it can be trained on. For training, it needs a sample that consists of elements. These elements have two parts:
441
+ * part a: the class of the element
442
+ * part b: the context of the element
443
+ The classifier will, once trained, return the most probable class for a particular context.
444
+
445
+ We start with an explanation of samples and elements. You have to create your own specialisation of the Element class. Your element class should implement the generateFeatures method for inferring feature functions from the sample.
446
+
447
+ #### Samples and elements
448
+ Elements and contexts are created as follows:
449
+
450
+ ```javascript
451
+ var MyElement = require('MyElementClass');
452
+ var Context = require('Context');
453
+ var Sample = require('Sample');
454
+
455
+ var x = new MyElementClass("x", new Context("0"));
456
+ // A sample is created from an array of elements
457
+ var sample = new Sample();
458
+ sample.addElement(x);
459
+ ```
460
+ A class is a string, contexts may be as complex as you want (as long as it can be serialised).
461
+
462
+ A sample can be saved to and loaded from a file:
463
+ ```javascript
464
+ sample.save('sample.json', function(error, sample) {
465
+ ...
466
+ });
467
+ ```
468
+ A sample can be read from a file as follows.
469
+
470
+ ```javascript
471
+ sample.load('sample.json', MyElementClass, function(err, sample) {
472
+
473
+ });
474
+ ```
475
+ You have to pass the element class to the load method so that the right element objects can be created from the data.
476
+
477
+ #### Features and feature sets
478
+ Features are functions that map elements to zero or one. Features are defined as follows:
479
+ ```javascript
480
+ var Feature = require('Feature');
481
+
482
+ function f(x) {
483
+ if (x.b === "0") {
484
+ return 1;
485
+ }
486
+ return 0;
487
+ }
488
+
489
+ var feature = new Feature(f, name, parameters);
490
+ ```
491
+ <code>name</code> is a string for the name of the feature function, <code>parameters</code> is an array of strings for the parameters of the feature function. The combination of name and parameters should uniquely distinguish features from each other. Features that are added to a feature set are tested for uniqueness using these properties.
492
+
493
+ A feature set is created like this
494
+ ```javascript
495
+ var FeatureSet = require('FeatureSet');
496
+
497
+ var set = new FeatureSet();
498
+ set.addFeature(f, "f", ["0"]);
499
+ ```
500
+
501
+ In most cases you will generate feature functions using closures. For instance, when you generate feature functions in a loop that iterates through an array
502
+ ```javascript
503
+ var FeatureSet = require('FeatureSet');
504
+ var Feature = require('Feature');
505
+
506
+ var listOfTags = ['NN', 'DET', 'PREP', 'ADJ'];
507
+ var featureSet = new FeatureSet();
361
508
 
509
+ listofTags.forEach(function(tag) {
510
+ function isTag(x) {
511
+ if (x.b.data.tag === tag) {
512
+ return 1
513
+ }
514
+ return 0;
515
+ }
516
+ featureSet.addFeature(new Feature(isTag, "isTag", [tag]));
517
+ });
518
+ ```
519
+ In this example you create feature functions that each have a different value for <code>tag</code> in their closure.
520
+
521
+ #### Setting up and training the classifier
522
+ A classifier needs the following parameter:
523
+ * Classes: an array of classes (strings)
524
+ * Features: an array of feature functions
525
+ * Sample: a sample of elements for training the classifier
526
+
527
+ A classifier can be created as follows:
528
+ ```javascript
529
+ var Classifier = require('Classifier');
530
+ var classifier = new Classifier(classes, featureSet, sample);
531
+ ```
532
+ And it starts training with:
533
+ ```javascript
534
+ var maxIterations = 100;
535
+ var minImprovement = .01;
536
+ var p = classifier.train(maxIterations, minImprovement);
537
+ ```
538
+ Training is finished when either <code>maxIterations</code> is reached or the improvement in likelihood (of the sample) becomes smaller than <code>minImprovement</code>. It returns a probability distribution that can be stored and retrieved for later usage:
539
+ ```javascript
540
+ classifier.save('classifier.json', function(err, c) {
541
+ if (err) {
542
+ console.log(err);
543
+ }
544
+ else {
545
+ // Continue using the classifier
546
+ }
547
+ });
548
+
549
+ classifier.load('classifier.json', function(err, c) {
550
+ if (err) {
551
+ console.log(err);
552
+ }
553
+ else {
554
+ // Use the classifier
555
+ }
556
+ });
557
+ ```
558
+
559
+ The training algorithm is based on Generalised Iterative Scaling.
560
+
561
+ #### Applying the classifier
562
+ The classifier can be used to classify contexts in two ways. To get the probabilities for all classes:
563
+ ```javascript
564
+ var classifications = classifier.getClassifications(context);
565
+ classifications.forEach(function(classPlusProbability) {
566
+ console.log('Class ' + classPlusProbability.label + ' has score ' + classPlusProbability.value);
567
+ });
568
+ ```
569
+ This returns a map from classes to probabilities.
570
+ To get the highest scoring class:
571
+ ```javascript
572
+ var class = classifier.classify(context);
573
+ console.log(class);
574
+ ```
575
+
576
+ #### Simple example of maximum entropy modelling
577
+ A test is added to the spec folder based on simple elements that have contexts that are either "0" or "1", and classes are "x" and "y".
578
+ ```javascript
579
+ {
580
+ "a": "x",
581
+ "b": {
582
+ "data": "0"
583
+ }
584
+ }
585
+ ```
586
+ In the SE_Element class that inherits from Element, the method generateFeatures is implemented. It creates a feature function that tests for context "0".
587
+
588
+ After setting up your own element class, the classifier can be created and trained.
589
+
590
+ #### Application to POS tagging
591
+ A more elaborate example of maximum entropy modelling is provided for part of speech tagging. The following steps are taken to create a classifier and apply it to a test set:
592
+ * A new element class POS_Element is created that has a word window and a tag window around the word to be tagged.
593
+ * From the Brown corpus a sample is generated consisting of POS elements.
594
+ * Feature functions are generated from the sample.
595
+ * A classifier is created and trained.
596
+ * The classifier is applied to a test set. Results are compared to a simple lexicon-based tagger.
597
+
598
+ #### References
599
+ * Adwait RatnaParkhi, Maximum Entropy Models For Natural Language Ambiguity Resolution, University of Pennsylvania, 1998, URL: http://repository.upenn.edu/cgi/viewcontent.cgi?article=1061&context=ircs_reports
600
+ * Darroch, J.N.; Ratcliff, D. (1972). Generalized iterative scaling for log-linear models, The Annals of Mathematical Statistics, Institute of Mathematical Statistics, 43 (5): 1470–1480.
601
+
602
+ ## Sentiment Analysis
603
+ This is a simple sentiment analysis algorithm based on a vocabulary that assigns polarity to words. The algorithm calculates the sentiment of a piece of text by summing the polarity of each word and normalizing with the length of the sentence. If a negation occurs the result is made negative. It is used as follows:
604
+ ```javascript
605
+ var Analyzer = require('natural').SentimentAnalyzer;
606
+ var stemmer = require('natural').PorterStemmer;
607
+ var analyzer = new Analyzer("English", stemmer, "afinn");
608
+ // getSentiment expects an array of strings
609
+ console.log(analyzer.getSentiment(["I", "like", "cherries"]));
610
+ // 0.6666666666666666
611
+ ```
612
+ The constructor has three parameters:
613
+ * Language: see below for supported languages.
614
+ * Stemmer: to increase the coverage of the sentiment analyzer a stemmer may be provided. May be `null`.
615
+ * Vocabulary: sets the type of vocabulary, `"afinn"`, `"senticon"` or `"pattern"` are valid values.
616
+
617
+ Currently, the following languages are supported with type of vocabulary and availability of negations (in alphabetic order):
618
+
619
+ | Language | AFINN | Senticon | Pattern | Negations |
620
+ | ------------- |:-----------:|:---------:|:---------:|:---------:|
621
+ | Basque | | X | | |
622
+ | Catalan | | X | | |
623
+ | Dutch | | | X | X |
624
+ | English | X | X | X | X |
625
+ | French | | | X | |
626
+ | Galician | | X | | |
627
+ | Italian | | | X | |
628
+ | Spanish | X | X | | X |
629
+
630
+ More languages can be added by adding vocabularies and extending the map `languageFiles` in `SentimentAnalyzer.js`. In the tools folder below `lib/natural/sentiment` some tools are provided for transforming vocabularies in Senticon and Pattern format into a JSON format.
631
+
632
+
633
+
634
+ ### Acknowledgements and References
635
+ Thanks to Domingo Martín Mancera for providing the basis for this sentiment analyzer in his repo [Lorca](https://github.com/dmarman/lorca).
636
+
637
+ AFINN is a list of English words rated for valence with an integer
638
+ between minus five (negative) and plus five (positive). The words have
639
+ been manually labeled by Finn Årup Nielsen in 2009-2011. Scientific reference can be found [here](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6010). We used [afinn-165](https://github.com/words/afinn-165) which is available as nodejs module.
640
+
641
+ The senticon vocabulary is based on work by Fermin L. Cruz and others:
642
+ Cruz, Fermín L., José A. Troyano, Beatriz Pontes, F. Javier Ortega. Building layered, multilingual sentiment lexicons at synset and lemma levels, Expert Systems with Applications, 2014.
643
+
644
+ The Pattern vocabularies are from the [Pattern project](https://github.com/clips/pattern) of the CLiPS Research Center of University of Antwerpen. These have a PDDL license.
645
+
646
+ ## Phonetics
362
647
  Phonetic matching (sounds-like) matching can be done with the [SoundEx](http://en.wikipedia.org/wiki/Soundex),
363
648
  [Metaphone](http://en.wikipedia.org/wiki/Metaphone) or [DoubleMetaphone](http://en.wikipedia.org/wiki/Metaphone#Double_Metaphone) algorithms
364
649
 
@@ -624,7 +909,11 @@ console.log(NGramsZH.ngrams(['一', '个', '中', '文', '测',
624
909
  ## tf-idf
625
910
 
626
911
  [Term Frequency–Inverse Document Frequency (tf-idf)](http://en.wikipedia.org/wiki/Tf%E2%80%93idf) is implemented to determine how important a word (or words) is to a
627
- document relative to a corpus. The following example will add four documents to
912
+ document relative to a corpus. The following formulas are used for calculating tf and idf:
913
+ * tf(t, d) is a so-called raw count, so just the count of the term in the document
914
+ * idf(t, D) uses the following formula: 1 + ln(N / (1 + n_t)) where N is the number of documents, and n_t the number of documents in which the term appears. The 1 + in the denominator is for handling the possibility that n_t is 0.
915
+
916
+ The following example will add four documents to
628
917
  a corpus and determine the weight of the word "node" and then the weight of the
629
918
  word "ruby" in each document.
630
919
 
@@ -1033,9 +1322,19 @@ var rules = new natural.RuleSet(rulesFilename);
1033
1322
  var tagger = new natural.BrillPOSTagger(lexicon, rules);
1034
1323
 
1035
1324
  var sentence = ["I", "see", "the", "man", "with", "the", "telescope"];
1036
- console.log(JSON.stringify(tagger.tag(sentence)));
1037
- // [["I","NN"],["see","VB"],["the","DT"],["man","NN"],["with","IN"],["the","DT"],["telescope","NN"]]
1038
-
1325
+ console.log(tagger.tag(sentence));
1326
+ ```
1327
+ This outputs the following:
1328
+ ```
1329
+ Sentence {
1330
+ taggedWords:
1331
+ [ { token: 'I', tag: 'NN' },
1332
+ { token: 'see', tag: 'VB' },
1333
+ { token: 'the', tag: 'DT' },
1334
+ { token: 'man', tag: 'NN' },
1335
+ { token: 'with', tag: 'IN' },
1336
+ { token: 'the', tag: 'DT' },
1337
+ { token: 'telescope', tag: 'NN' } ] }
1039
1338
  ```
1040
1339
 
1041
1340
  ### Lexicon
@@ -1076,22 +1375,18 @@ VBD NN PREV-TAG DT
1076
1375
  Here the category of the previous word must be <code>DT</code> for the rule to be applied.
1077
1376
 
1078
1377
  ### Algorithm
1079
- The tagger applies transformation rules that may change the category of words. The input sentence must be split into words which are assigned with categories. The tagged sentence is then processed from left to right. At each step all rules are applied once; rules are applied in the order in which they are specified. Algorithm:
1378
+ The tagger applies transformation rules that may change the category of words. The input sentence is a Sentence object with tagged words. The tagged sentence is processed from left to right. At each step all rules are applied once; rules are applied in the order in which they are specified. Algorithm:
1080
1379
  ```javascript
1081
- function(sentence) {
1082
- var tagged_sentence = new Array(sentence.length);
1083
-
1084
- // snip
1085
-
1086
- // Apply transformation rules
1087
- for (var i = 0, size = sentence.length; i < size; i++) {
1088
- this.transformation_rules.forEach(function(rule) {
1089
- rule.apply(tagged_sentence, i);
1380
+ Brill_POS_Tagger.prototype.applyRules = function(sentence) {
1381
+ for (var i = 0, size = sentence.taggedWords.length; i < size; i++) {
1382
+ this.ruleSet.getRules().forEach(function(rule) {
1383
+ rule.apply(sentence, i);
1090
1384
  });
1091
1385
  }
1092
- return(tagged_sentence);
1093
- }
1386
+ return sentence;
1387
+ };
1094
1388
  ```
1389
+ The output is a Sentence object just like the input sentence.
1095
1390
 
1096
1391
  ### Adding a predicate
1097
1392
  Predicates are defined in module <code>lib/RuleTemplates.js</code>. In that file
@@ -1119,13 +1414,13 @@ A typical entry for a rule templates looks like this:
1119
1414
  "parameter1Values": nextTagParameterValues
1120
1415
  }
1121
1416
  ```
1122
- A predicate function accepts a tagged sentence, the current position in the
1417
+ A predicate function accepts a Sentence object, the current position in the
1123
1418
  sentence that should be tagged, and the outcome(s) of the predicate.
1124
1419
  An example of a predicate that checks the category of the current word:
1125
1420
  ```javascript
1126
- function next_tag_is(tagged_sentence, i, parameter) {
1127
- if (i < tagged_sentence.length - 1) {
1128
- return(tagged_sentence[i+1][1] === parameter);
1421
+ function next_tag_is(sentence, i, parameter) {
1422
+ if (i < sentence.taggedWords.length - 1) {
1423
+ return(sentence.taggedWords[i + 1][1] === parameter);
1129
1424
  }
1130
1425
  else {
1131
1426
  return(false);
@@ -1145,10 +1440,6 @@ function nextTagParameterValues(sentence, i) {
1145
1440
  }
1146
1441
  }
1147
1442
  ```
1148
- Please note that these functions work with a different data type. Here, a
1149
- sentence is an array of tokens and tokens are maps that have at least a
1150
- token (word) and a tag.
1151
-
1152
1443
 
1153
1444
  ### Training
1154
1445
  The trainer allows to learn a new set of transformation rules from a corpus.
@@ -1 +1 @@
1
- {"classifier":{"classFeatures":{"computing":{"0":2,"1":2,"2":2,"3":3,"4":2,"5":2},"literature":{"2":2,"6":4,"7":2,"8":2}},"classTotals":{"computing":4,"literature":4},"totalExamples":7,"smoothing":1},"docs":[{"label":"computing","text":["fix","box"]},{"label":"computing","text":["write","code"]},{"label":"computing","text":["nasti","script","code"]},{"label":"literature","text":["write","book"]},{"label":"literature","text":["read","book"]},{"label":"literature","text":["studi","book"]}],"features":{"fix":1,"box":1,"write":2,"code":2,"nasti":1,"script":1,"book":3,"read":1,"studi":1},"stemmer":{},"lastAdded":6,"events":{"domain":null,"_events":{},"_eventsCount":0}}
1
+ {"classifier":{"classFeatures":{"computing":{"0":2,"1":2,"2":2,"3":3,"4":2,"5":2},"literature":{"2":2,"6":4,"7":2,"8":2}},"classTotals":{"computing":4,"literature":4},"totalExamples":7,"smoothing":1},"docs":[{"label":"computing","text":["fix","box"]},{"label":"computing","text":["write","code"]},{"label":"computing","text":["nasti","script","code"]},{"label":"literature","text":["write","book"]},{"label":"literature","text":["read","book"]},{"label":"literature","text":["studi","book"]}],"features":{"fix":1,"box":1,"write":2,"code":2,"nasti":1,"script":1,"book":3,"read":1,"studi":1},"stemmer":{},"lastAdded":6,"events":{"_events":{},"_eventsCount":0}}