Permalink
Browse files

Added support k/v injection into the AFINN word list

  • Loading branch information...
1 parent 0031bdc commit 15995f5d1878070f3d4319e4f1685e95499d4bf4 @thisandagain committed Dec 7, 2012
Showing with 167 additions and 42 deletions.
  1. +14 −1 README.md
  2. +45 −36 lib/index.js
  3. +3 −2 package.json
  4. +0 −3 test/index.js
  5. +105 −0 test/inject.js
View
@@ -26,13 +26,26 @@ sentiment('Cats are totally amazing!', function (err, result) {
});
```
+### Adding / overwriting words
+You can append and/or overwrite values from AFINN by simply injecting key/value pairs into a sentiment method call:
+```javascript
+var sentiment = require('sentiment');
+
+sentiment('Cats are totally amazing!', {
+ 'cats': 5,
+ 'amazing': 2
+}, function (err, result) {
+ console.dir(result); // Score: 7, Comparative: 1.75
+});
+```
+
### Testing
```bash
npm test
```
### Benchmarks
-The primary motivation for designing `sentiment` was performance. As such, `sentiment` includes a benchmark script within the test directory that compares it against the [Sentimental](https://github.com/thinkroth/Sentimental) module which provides a nearly equivalent interface and approach. Based on these benchmarks running on an older MacBook Air with Node 0.8.9, `sentiment` is about 33 times faster than the alternative implementation:
+The primary motivation for designing `sentiment` was performance. As such, `sentiment` includes a benchmark script within the test directory that compares it against the [Sentimental](https://github.com/thinkroth/Sentimental) module which provides a nearly equivalent interface and approach. Based on these benchmarks, running on an older MacBook Air with Node 0.8.9, `sentiment` is about 33 times faster than the alternative implementation:
```bash
sentiment (v0.1.0)
1000 operations | 33ms
View
@@ -8,7 +8,8 @@
/**
* Dependencies
*/
-var async = require('async'),
+var _ = require('lodash'),
+ async = require('async'),
afinn = require('../build/AFINN.json');
/**
@@ -23,47 +24,55 @@ function tokenize (input) {
.replace(/[^a-zA-Z ]+/g, '')
.replace('/ {2,}/',' ')
.toLowerCase()
- .split(" ");
+ .split(' ');
}
/**
- * Constructor
+ * Performs sentiment analysis on the provided input "phrase".
+ *
+ * @param {String} Input phrase
+ * @param {Object} Optional sentiment additions to AFINN (hash of word/value pairs)
+ *
+ * @return {Object}
*/
-module.exports = function () {
-
- /**
- * Performs sentiment analysis on the provided input "phrase".
- *
- * @param {String} Input phrase
- *
- * @return {Object}
- */
- function sentiment (phrase, callback) {
- var tokens = tokenize(phrase),
- score = 0,
- words = [];
-
- // Iterate over tokens
- async.forEach(tokens, function (obj, callback) {
- var item = afinn[obj];
- if (typeof item === 'undefined') return callback();
+module.exports = function (phrase, inject, callback) {
+ // Parse arguments
+ if (typeof callback === 'undefined') {
+ callback = inject;
+ inject = null;
+ }
- words.push(obj);
- score += item;
- callback();
- }, function (err) {
- callback(err, {
- score: score,
- comparative: score / tokens.length,
- tokens: tokens,
- words: words
- });
- });
+ // Merge
+ if (inject !== null) {
+ afinn = _.extend(afinn, inject);
}
- // ---------------------------------
- // ---------------------------------
+ // Storage objects
+ var tokens = tokenize(phrase),
+ score = 0,
+ words = [],
+ positive = [],
+ negative = [];
- return sentiment;
+ // Iterate over tokens
+ async.forEach(tokens, function (obj, callback) {
+ var item = afinn[obj];
+ if (typeof item === 'undefined') return callback();
-}();
+ words.push(obj);
+ if (item > 0) positive.push(obj);
+ if (item < 0) negative.push(obj);
+
+ score += item;
+ callback();
+ }, function (err) {
+ callback(err, {
+ score: score,
+ comparative: score / tokens.length,
+ tokens: tokens,
+ words: words,
+ positive: positive,
+ negative: negative
+ });
+ });
+};
View
@@ -2,7 +2,7 @@
"author": "Andrew Sliwinski <andrewsliwinski@acm.org> (http://andrewsliwinski.com)",
"name": "sentiment",
"description": "AFINN-based sentiment analysis for Node.js",
- "version": "0.1.0",
+ "version": "0.2.0",
"homepage": "https://github.com/thisandagain/sentiment",
"repository": {
"type": "git",
@@ -13,7 +13,8 @@
"test": "tap ./test/*.js"
},
"dependencies": {
- "async": "~0.1.22"
+ "async": "~0.1.22",
+ "lodash": "~1.0.0"
},
"devDependencies": {
"tap": "~0.3.1",
View
@@ -106,9 +106,6 @@ async.auto({
t.end();
});
- console.dir(obj.n2);
- console.dir(obj.p2);
-
test('BB.net test', function (t) {
t.type(obj.bb, 'object', 'Results should be an object');
t.equal(obj.bb.length, 6, 'Expected score');
View
@@ -0,0 +1,105 @@
+/**
+ * Test suite
+ *
+ * @package sentiment
+ * @author Andrew Sliwinski <andrew@diy.org>
+ */
+
+/**
+ * Dependencies
+ */
+var async = require('async'),
+ test = require('tap').test,
+
+ target = require(__dirname + '/../lib/index.js');
+
+/**
+ * Suite
+ */
+async.auto({
+
+ negative: function (callback) {
+ target('Hey you worthless scumbag', {
+ worthless: -5,
+ scumbag: -5
+ }, callback);
+ },
+
+ positive: function (callback) {
+ target('This is so cool', {
+ cool: 5
+ }, callback);
+ },
+
+ blacklist: function (callback) {
+ target('I really like your badword cat!', {
+ badword: -100
+ }, callback);
+ },
+
+ mix: function (callback) {
+ target('I really like your badword cat! It is super cool and amazing.', {
+ badword: -100
+ }, callback);
+ },
+
+ p1: function (callback) {
+ target('Cats are totally amazing!', {
+ 'cats': 5,
+ 'amazing': 2
+ }, callback);
+ },
+
+ test: ['negative', 'positive', 'blacklist', 'mix', 'p1', function (callback, obj) {
+ console.dir(obj);
+
+ test('Component definition', function (t) {
+ t.type(target, 'function', 'Component should be a function');
+ t.end();
+ });
+
+ test('negative', function (t) {
+ t.type(obj.negative, 'object', 'Results should be an object');
+ t.equal(obj.negative.score, -10, 'Expected score');
+ t.equal(obj.negative.comparative, -2.5, 'Expected comparative score');
+ t.equal(obj.negative.tokens.length, 4, 'Expected tokens length');
+ t.equal(obj.negative.words.length, 2, 'Expected match length');
+ t.end();
+ });
+
+ test('positive', function (t) {
+ t.type(obj.positive, 'object', 'Results should be an object');
+ t.equal(obj.positive.score, 5, 'Expected score');
+ t.equal(obj.positive.comparative, 1.25, 'Expected comparative score');
+ t.equal(obj.positive.tokens.length, 4, 'Expected tokens length');
+ t.equal(obj.positive.words.length, 1, 'Expected match length');
+ t.end();
+ });
+
+ test('blacklist', function (t) {
+ t.type(obj.blacklist, 'object', 'Results should be an object');
+ t.equal(obj.blacklist.score, -98, 'Expected score');
+ t.equal(obj.blacklist.comparative, -16.333333333333332, 'Expected comparative score');
+ t.equal(obj.blacklist.tokens.length, 6, 'Expected tokens length');
+ t.equal(obj.blacklist.words.length, 2, 'Expected match length');
+ t.end();
+ });
+
+ test('mix', function (t) {
+ t.type(obj.mix, 'object', 'Results should be an object');
+ t.equal(obj.mix.score, -86, 'Expected score');
+ t.equal(obj.mix.comparative, -7.166666666666667, 'Expected comparative score');
+ t.equal(obj.mix.tokens.length, 12, 'Expected tokens length');
+ t.equal(obj.mix.words.length, 5, 'Expected match length');
+ t.end();
+ });
+
+ callback();
+ }]
+
+}, function (err, obj) {
+ test('Catch errors', function (t) {
+ t.equal(err, null, 'Errors should be null');
+ t.end();
+ });
+});

0 comments on commit 15995f5

Please sign in to comment.