natural 6.8.0 → 6.9.0

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.
@@ -22,6 +22,8 @@
22
22
 
23
23
  'use strict'
24
24
 
25
+ const DEBUG = true
26
+
25
27
  // Afinn
26
28
  const englishAfinnVoca = require('afinn-165')
27
29
  const spanishAfinnVoca = require('./Spanish/afinnShortSortedSpanish.json')
@@ -87,6 +89,17 @@ class SentimentAnalyzer {
87
89
  // this.vocabulary must be a copy of the languageFiles object
88
90
  // or in subsequent execution the polarity will be undefined
89
91
  // shallow copy - requires ES6
92
+ if (languageFiles[type]) {
93
+ if (languageFiles[type][language]) {
94
+ if (languageFiles[type][language][0]) {
95
+ this.vocabulary = Object.create(languageFiles[type][language][0])
96
+ }
97
+ } else {
98
+ throw new Error('Type ' + type + ' for Language ' + language + ' not supported')
99
+ }
100
+ } else {
101
+ throw new Error('Type Language ' + type + ' not supported')
102
+ }
90
103
  this.vocabulary = Object.assign({}, languageFiles[type][language][0])
91
104
  Object.setPrototypeOf(this.vocabulary, null)
92
105
  if (type === 'senticon') {
@@ -98,7 +111,6 @@ class SentimentAnalyzer {
98
111
  Object.keys(this.vocabulary).forEach(word => {
99
112
  this.vocabulary[word] = this.vocabulary[word].polarity
100
113
  })
101
- // console.log(JSON.stringify(this.vocabulary, null, 2));
102
114
  }
103
115
  }
104
116
 
@@ -120,24 +132,24 @@ class SentimentAnalyzer {
120
132
  getSentiment (words) {
121
133
  let score = 0
122
134
  let negator = 1
123
- // let nrHits = 0
135
+ let nrHits = 0
124
136
 
125
137
  words.forEach((token) => {
126
138
  const lowerCased = token.toLowerCase()
127
139
  if (this.negations.indexOf(lowerCased) > -1) {
128
140
  negator = -1
129
- // nrHits++
141
+ DEBUG && nrHits++
130
142
  } else {
131
143
  // First try without stemming
132
144
  if (this.vocabulary[lowerCased] !== undefined) {
133
145
  score += negator * this.vocabulary[lowerCased]
134
- // nrHits++
146
+ DEBUG && nrHits++
135
147
  } else {
136
148
  if (this.stemmer) {
137
149
  const stemmedWord = this.stemmer.stem(lowerCased)
138
150
  if (this.vocabulary[stemmedWord] !== undefined) {
139
151
  score += negator * this.vocabulary[stemmedWord]
140
- // nrHits++
152
+ DEBUG && nrHits++
141
153
  }
142
154
  }
143
155
  }
@@ -145,7 +157,7 @@ class SentimentAnalyzer {
145
157
  })
146
158
 
147
159
  score = score / words.length
148
- // console.log("Number of hits: " + nrHits);
160
+ DEBUG && console.log('Number of hits: ' + nrHits)
149
161
 
150
162
  return score
151
163
  }
@@ -40,6 +40,7 @@ export let PorterStemmerIt: Stemmer
40
40
  export let PorterStemmerNl: Stemmer
41
41
  export let PorterStemmerNo: Stemmer
42
42
  export let PorterStemmerPt: Stemmer
43
+ export let PorterStemmerUk: Stemmer
43
44
  export let PorterStemmerRu: Stemmer
44
45
  export let PorterStemmerSv: Stemmer
45
46
  export let StemmerId: Stemmer
@@ -27,6 +27,7 @@ exports.PorterStemmerFa = require('./porter_stemmer_fa')
27
27
  exports.PorterStemmerFr = require('./porter_stemmer_fr')
28
28
  exports.CarryStemmerFr = require('./Carry')
29
29
  exports.PorterStemmerDe = require('./porter_stemmer_de')
30
+ exports.PorterStemmerUk = require('./porter_stemmer_uk')
30
31
  exports.PorterStemmerRu = require('./porter_stemmer_ru')
31
32
  exports.PorterStemmerEs = require('./porter_stemmer_es')
32
33
  exports.PorterStemmerIt = require('./porter_stemmer_it')
@@ -0,0 +1,129 @@
1
+ 'use strict'
2
+
3
+ const Stemmer = require('./stemmer_uk')
4
+
5
+ const PorterStemmer = new Stemmer()
6
+ module.exports = PorterStemmer
7
+
8
+ function attemptReplacePatterns (token, patterns) {
9
+ let replacement = null
10
+ let i = 0; let isReplaced = false
11
+ while ((i < patterns.length) && !isReplaced) {
12
+ if (patterns[i][0].test(token)) {
13
+ replacement = token.replace(patterns[i][0], patterns[i][1])
14
+ isReplaced = true
15
+ }
16
+ i++
17
+ }
18
+ return replacement
19
+ }
20
+
21
+ function perfectiveGerund (token) {
22
+ const result = attemptReplacePatterns(token, [
23
+ [/[ая]в(ши|шись)$/g, ''],
24
+ [/(ив|ивши|ившись)$/g, '']
25
+ ])
26
+ return result
27
+ }
28
+
29
+ function adjectival (token) {
30
+ let result = adjective(token)
31
+ if (result != null) {
32
+ const pariticipleResult = participle(result)
33
+ result = pariticipleResult || result
34
+ }
35
+ return result
36
+ }
37
+
38
+ function adjective (token) {
39
+ const result = attemptReplacePatterns(token, [
40
+ [/(ими|ій|ий|а|е|ова|ове|ів|є|їй|єє|еє|я|ім|ем|им|ім|их|іх|ою|йми|іми|у|ю|ого|ому|ої)$/g, '']
41
+ ])
42
+ return result
43
+ }
44
+
45
+ function participle (token) { // edit
46
+ const result = attemptReplacePatterns(token, [
47
+ [/(ий|ого|ому|им|ім|а|ій|у|ою|ій|і|их|йми|их)$/g, '']
48
+ ])
49
+ return result
50
+ }
51
+
52
+ function reflexive (token) {
53
+ const result = attemptReplacePatterns(token, [
54
+ [/(с[яьи])$/g, '']
55
+ ])
56
+ return result
57
+ }
58
+
59
+ function verb (token) {
60
+ const result = attemptReplacePatterns(token, [
61
+ [/(сь|ся|ив|ать|ять|у|ю|ав|али|учи|ячи|вши|ши|е|ме|ати|яти|є)$/g, '']
62
+ ])
63
+ return result
64
+ }
65
+
66
+ function noun (token) {
67
+ const result = attemptReplacePatterns(token, [
68
+ [/(а|ев|ов|е|ями|ами|еи|и|ей|ой|ий|й|иям|ям|ием|ем|ам|ом|о|у|ах|иях|ях|ы|ь|ию|ью|ю|ия|ья|я|і|ові|ї|ею|єю|ою|є|еві|ем|єм|ів|їв|ю)$/g, '']
69
+ ])
70
+ return result
71
+ }
72
+
73
+ function superlative (token) {
74
+ const result = attemptReplacePatterns(token, [
75
+ [/(ейш|ейше)$/g, '']
76
+ ])
77
+ return result
78
+ }
79
+
80
+ function derivational (token) {
81
+ const result = attemptReplacePatterns(token, [
82
+ [/[^аеиоуюяіїє][аеиоуюяіїє]+[^аеиоуюяіїє]+[аеиоуюяіїє].*(?<=о)сть?$/g, '']
83
+ ])
84
+ return result
85
+ }
86
+
87
+ // perform full stemming algorithm on a single word
88
+ PorterStemmer.stem = function (token) {
89
+ token = token.toLowerCase()
90
+ const volwesRegexp = /^(.*?[аеиоуюяіїє])(.*)$/g
91
+ let RV = volwesRegexp.exec(token)
92
+ if (!RV || RV.length < 3) {
93
+ return token
94
+ }
95
+ const head = RV[1]
96
+ RV = RV[2]
97
+ volwesRegexp.lastIndex = 0
98
+ const R2 = volwesRegexp.exec(RV)
99
+ let result = perfectiveGerund(RV)
100
+ if (result === null) {
101
+ const resultReflexive = reflexive(RV) || RV
102
+ result = adjectival(resultReflexive)
103
+ if (result === null) {
104
+ result = verb(resultReflexive)
105
+ if (result === null) {
106
+ result = noun(resultReflexive)
107
+ if (result === null) {
108
+ result = resultReflexive
109
+ }
110
+ }
111
+ }
112
+ }
113
+ result = result.replace(/и$/g, '')
114
+ let derivationalResult = result
115
+ if (R2 && R2[2]) {
116
+ derivationalResult = derivational(R2[2])
117
+ if (derivationalResult != null) {
118
+ derivationalResult = derivational(result)
119
+ } else {
120
+ derivationalResult = result
121
+ }
122
+ }
123
+
124
+ let superlativeResult = superlative(derivationalResult) || derivationalResult
125
+
126
+ superlativeResult = superlativeResult.replace(/(н)н/g, '$1')
127
+ superlativeResult = superlativeResult.replace(/ь$/g, '')
128
+ return head + superlativeResult
129
+ }
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const stopwords = require('../util/stopwords_uk')
4
+ const Tokenizer = require('../tokenizers/aggressive_tokenizer_uk')
5
+
6
+ module.exports = function () {
7
+ const stemmer = this
8
+
9
+ stemmer.stem = function (token) {
10
+ return token
11
+ }
12
+
13
+ stemmer.tokenizeAndStem = function (text, keepStops) {
14
+ const stemmedTokens = []
15
+
16
+ new Tokenizer().tokenize(text).forEach(function (token) {
17
+ if (keepStops || stopwords.words.indexOf(token) === -1) {
18
+ let resultToken = token.toLowerCase()
19
+ if (resultToken.match(/[а-яґєії0-9]+/gi)) {
20
+ resultToken = stemmer.stem(resultToken)
21
+ }
22
+ stemmedTokens.push(resultToken)
23
+ }
24
+ })
25
+
26
+ return stemmedTokens
27
+ }
28
+ }
@@ -0,0 +1,25 @@
1
+ 'use strict'
2
+
3
+ const Tokenizer = require('./tokenizer')
4
+ const util = require('util')
5
+
6
+ const AggressiveTokenizer = function () {
7
+ Tokenizer.call(this)
8
+ }
9
+
10
+ util.inherits(AggressiveTokenizer, Tokenizer)
11
+
12
+ module.exports = AggressiveTokenizer
13
+
14
+ AggressiveTokenizer.prototype.withoutEmpty = function (array) {
15
+ return array.filter(function (a) { return a })
16
+ }
17
+
18
+ AggressiveTokenizer.prototype.clearText = function (text) {
19
+ return text.replace(/[^a-zа-яґєії0-9]/gi, ' ').replace(/[\s\n]+/g, ' ').trim()
20
+ }
21
+
22
+ AggressiveTokenizer.prototype.tokenize = function (text) {
23
+ // break a string up into an array of tokens by anything non-word
24
+ return this.withoutEmpty(this.clearText(text).split(' '))
25
+ }
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ // a list of commonly used words that have little meaning and can be excluded
4
+ // from analysis.
5
+ const words = [
6
+ 'о', 'після', 'всі', 'також', 'і', 'інші', 'все', 'як', 'в', 'бути',
7
+ 'тому', 'був', 'до', 'є', 'між', 'вся', 'але', 'від', 'іди', 'можу',
8
+ 'підійди', 'міг', 'робив', 'роблю', 'кожен', 'для', 'звідки', 'мав',
9
+ 'він', 'має', 'її', 'тут', 'його', 'якщо', 'у', 'воно', 'за',
10
+ 'робити', 'багато', 'може бути', 'самий', 'повинен',
11
+ 'мій', 'ніколи', 'зараз', 'із', 'на', 'тільки', 'або', 'наше', 'поза',
12
+ 'кінець', 'сказав', 'сказала', 'бачив', 'з',
13
+ 'трохи', 'все ще', 'так', 'потім', 'той', 'їх', 'там', 'цей', 'вони', 'те',
14
+ 'через', 'теж', 'під', 'над', 'дуже', 'був', 'добре',
15
+ 'що', 'де', 'який', 'доки', 'хто', 'хотів би',
16
+ 'а', 'б', 'в', 'г', 'ґ', 'д', 'е', 'є', 'ж', 'з', 'и', 'і', 'й', 'ї', 'к',
17
+ 'л', 'м', 'н', 'o', 'п', 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ',
18
+ 'ь', 'ю', 'я', '$', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '_']
19
+
20
+ // tell the world about the noise words.
21
+ exports.words = words
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "natural",
3
3
  "description": "General natural language (tokenizing, stemming (English, Russian, Spanish), part-of-speech tagging, sentiment analysis, classification, inflection, phonetics, tfidf, WordNet, jaro-winkler, Levenshtein distance, Dice's Coefficient) facilities for node.",
4
- "version": "6.8.0",
4
+ "version": "6.9.0",
5
5
  "homepage": "https://github.com/NaturalNode/natural",
6
6
  "repository": {
7
7
  "type": "git",