Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
713d379
Integrate emoji parsing
thorbert Jul 31, 2016
2598f63
Add emoji-regex to dependencies
thorbert Jul 31, 2016
ab614c1
Merge remote-tracking branch 'upstream/develop' into develop
thorbert Aug 3, 2016
cabcdb1
Separate AFINN.txt and emoji.txt; replace tokenizer regex;
thorbert Aug 4, 2016
d9075dc
Add some common emoji and guesses at scores to start with
thorbert Aug 5, 2016
0c2494a
Add spliddit dependency; Modify tokenizer to split out undelimited/re…
thorbert Sep 9, 2016
1d3e6a6
Make clear type of license
pisaacs Dec 24, 2016
9f14cc3
Merge pull request #91 from pisaacs/patch-1
thisandagain Dec 28, 2016
f58e3a0
Add license type to package. Re GH-91
thisandagain Dec 28, 2016
6555f1b
Merge branch 'develop' of github.com:thisandagain/sentiment into develop
thisandagain Dec 28, 2016
789a3f5
chore(package): update dependencies
greenkeeper[bot] Feb 9, 2017
1f995ed
docs(readme): add Greenkeeper badge
greenkeeper[bot] Feb 9, 2017
e1d860b
Merge pull request #102 from thisandagain/greenkeeper/initial
thisandagain Feb 9, 2017
ea125e1
Remove david-dm and add greenkeeper badge
thisandagain Feb 9, 2017
f7b6837
Add validation infrastructure and update README
thisandagain Feb 10, 2017
ed9c4f3
Remove unused 'tap' dependency
thisandagain Feb 10, 2017
caad947
Run validation as part of 'npm test'
thisandagain Feb 10, 2017
fb3dd86
Add test coverage and include alternate versions of common lexical it…
thisandagain Feb 10, 2017
facfd05
Bump major version number
thisandagain Feb 10, 2017
4fb58c7
Merge pull request #103 from thisandagain/feature/cv
thisandagain Feb 10, 2017
0f808bc
Merge branch 'develop' of github.com:thisandagain/sentiment into develop
thisandagain Feb 10, 2017
4c55876
Merge pull request #105 from thisandagain/develop
thisandagain Feb 10, 2017
c007a0b
Add 'isn't' to negators.
craigthomasfrost Feb 25, 2017
df73c26
Merge pull request #106 from craigthomasfrost/patch-1
thisandagain Feb 26, 2017
f867044
Add validation test instructions to README
thisandagain Feb 26, 2017
ee8843b
Prepare for release 3.0.1
thisandagain Feb 26, 2017
d9ad282
Merge pull request #108 from thisandagain/develop
thisandagain Feb 26, 2017
262ffe3
Merge upstream; Address some requested tweaks;
thorbert Mar 21, 2017
0a46aea
Move async to devDependencies
thorbert Mar 21, 2017
89d3f51
Add Emoji data from Novak, et. al.; Mod build to incorporate both AFI…
thorbert Mar 21, 2017
c5beca0
Remove unnecessary emoji.txt
thorbert Mar 23, 2017
54a2fa0
Remove unnecessary output after build
thorbert Mar 23, 2017
d6400ba
Put result key count in message to stderr after build.
thorbert Mar 23, 2017
fac8e05
Tokenize returns empty array when input is undefined
thorbert Mar 23, 2017
fb5b498
Use codepoint and String.fromCodePoint to parse emoji from backing data
thorbert Mar 26, 2017
3e83316
Correct build target in makefile
thorbert Mar 26, 2017
9b7c171
Replace AFINN.json build product after correcting builder
thorbert Mar 26, 2017
3c571cd
Tweak eslint settings for corpus to allow double quote and long line
thorbert Mar 31, 2017
84fca69
Remove spliddit dependency and reduce complexity in tokenizer
thorbert Mar 31, 2017
47d39fc
Add negative and positive text with emoji tests
thorbert Mar 31, 2017
9225d82
Change test name in async_negative.js
thorbert Mar 31, 2017
f6a1528
More paring back of tokenizer
thorbert Mar 31, 2017
5d415b2
Merge pull request #74 from thorbert/develop
thisandagain Apr 1, 2017
8af65cd
Fix case of “Makefile”
thisandagain Apr 1, 2017
d4ba32b
Move validation datasets to text fixutres
thisandagain Apr 1, 2017
46f8f8a
Rename build script and build results
thisandagain Apr 1, 2017
a1122ec
Clean-up tokenizer
thisandagain Apr 1, 2017
60140b3
Update README to include emoji dataset and updated benchmarks
thisandagain Apr 1, 2017
59f0b04
Fix issue with match for empty string
thisandagain Apr 1, 2017
c459144
Update version and prep for release
thisandagain Apr 1, 2017
4614928
Merge pull request #114 from thisandagain/develop
thisandagain Apr 1, 2017
c9c52b4
chore(package): update eslint to version 4.6.1
greenkeeper[bot] Sep 3, 2017
4934cea
Add package lock file
thisandagain Sep 3, 2017
1434d18
Ignore no-useless-escape eslint rule in tokenizer
thisandagain Sep 3, 2017
522d4f8
Cache node modules for CI
thisandagain Sep 3, 2017
471a53a
Merge pull request #120 from thisandagain/greenkeeper/eslint-4.6.1
thisandagain Sep 3, 2017
fa80367
Update package version
thisandagain Sep 3, 2017
a6ec3fd
Merge branch 'master' into develop
thisandagain Sep 3, 2017
f168dc6
Merge pull request #121 from thisandagain/develop
thisandagain Sep 3, 2017
fb35447
Split tokens on new line (#122)
rishpandey Oct 10, 2017
432cf66
Prepare for release
thisandagain Oct 10, 2017
adca3d1
Merge branch 'master' into develop
thisandagain Oct 10, 2017
6916ce0
Merge pull request #123 from thisandagain/develop
thisandagain Oct 10, 2017
d93e683
Add documentation to README. Resolves GH-112
rishpandey Oct 15, 2017
dd592ea
added one more negator
jerilops Oct 24, 2017
6c4c917
Add multilang support, fixes #104
elyas-bhy Dec 11, 2017
f311554
Move negators to separate file
elyas-bhy Dec 11, 2017
4a95466
Add support for language-specific negation strategies
elyas-bhy Dec 11, 2017
5084d4d
Add missing method documentation
elyas-bhy Dec 11, 2017
1b34049
Add link to supported languages
elyas-bhy Dec 12, 2017
b753ab7
chore(package): update tap to version 11.0.1
greenkeeper[bot] Dec 21, 2017
837ea54
Merge pull request #126 from jerilseb/develop
thisandagain Jan 15, 2018
4bf854c
Merge pull request #130 from thisandagain/greenkeeper/tap-11.0.1
thisandagain Jan 15, 2018
fc7b023
Bump package version to 4.2.0
thisandagain Jan 15, 2018
a791ddd
Update validation stats
thisandagain Jan 15, 2018
f59b4cb
Merge branch 'master' into develop
thisandagain Jan 15, 2018
6a22ea9
Merge pull request #131 from thisandagain/develop
thisandagain Jan 15, 2018
0a41a00
improve(development): makefile rules to npm-scripts
kevinchappell Feb 11, 2018
b412566
Merge pull request #134 from kevinchappell/improvement/npm-scripts
thisandagain Feb 15, 2018
037e22f
Update method signature to accept configuration object
elyas-bhy Feb 27, 2018
2dcb120
Add support for dynamically registring new languages
elyas-bhy Feb 28, 2018
16d09ca
Merge changes
elyas-bhy Feb 28, 2018
7fd7ae5
Update README.md
elyas-bhy Feb 28, 2018
d010408
Export a constructor from the provided module
elyas-bhy Apr 3, 2018
241bc87
Clean-up package.json and prepare for major release
thisandagain Apr 25, 2018
c5ad628
Resolve lint issues
thisandagain Apr 25, 2018
8b991e7
Update README to ensure performance, validation, and testing informat…
thisandagain Apr 25, 2018
3e9ace1
Use Node 8+ for CI testing
thisandagain Apr 25, 2018
3055552
Merge branch 'master' into develop
thisandagain Apr 25, 2018
17647bf
Merge pull request #139 from thisandagain/develop
thisandagain Apr 25, 2018
ee7f553
chore(package): update tap to version 12.0.1
greenkeeper[bot] May 17, 2018
2bd178a
Merge pull request #142 from thisandagain/greenkeeper/tap-12.0.1
thisandagain Jun 13, 2018
cea1a5c
Bump version
thisandagain Jun 13, 2018
251bea9
Merge branch 'master' into develop
thisandagain Jun 13, 2018
7c4a129
Merge pull request #143 from thisandagain/develop
thisandagain Jun 13, 2018
50e96a4
Fix for replacing punctuation in tokenize.js
jalners Jun 24, 2018
fff86a4
Fix for eslint max-len in tests
jalners Jun 24, 2018
13c7815
Fix for sync_corpus tests based on tokenize fixes
jalners Jun 24, 2018
b25180b
chore(package): update eslint to version 5.0.1
greenkeeper[bot] Jun 26, 2018
4ac95a7
Typo on #160, #164
kant Sep 21, 2018
c555526
Merge pull request #156 from thisandagain/greenkeeper/eslint-5.0.1
thisandagain Oct 4, 2018
1d9601b
Added calculation breakdown into results
blazergame Jan 29, 2019
b8ca59d
Updated readme
blazergame Jan 30, 2019
04924fb
Migrate from Travis to Circle CI
thisandagain Mar 28, 2019
74312d9
Merge branch 'develop' of github.com:thisandagain/sentiment into develop
thisandagain Mar 28, 2019
17c7b01
Update README CI status badge
thisandagain Mar 28, 2019
bdfe588
Escape question mark
oleoneto Jun 5, 2019
b5f262c
Update dependencies. Closes GH-168. Closes GH-165. Closes GH-163.
thisandagain Aug 22, 2019
dacb397
Merge pull request #169 from thisandagain/gk/201908
thisandagain Aug 22, 2019
222d52e
Merge pull request #149 from jalners/fix-tokenize-replace-punctuations
thisandagain Aug 22, 2019
1bdb021
Merge pull request #154 from kant/patch-1
thisandagain Aug 22, 2019
5438d4b
Merge branch 'develop' into patch-1
thisandagain Aug 22, 2019
442a822
Merge pull request #167 from oleoneto/patch-1
thisandagain Aug 22, 2019
ccee089
Fix integration test based on updates to tokenizer
thisandagain Aug 22, 2019
1660454
Update README.md
thisandagain Aug 22, 2019
c9a2f9b
Update index.js
thisandagain Aug 22, 2019
ece150d
Merge pull request #161 from blazergame/develop
thisandagain Aug 22, 2019
5a80774
Prepare for release
thisandagain Aug 22, 2019
35068b4
Merge branch 'master' into develop
thisandagain Aug 22, 2019
37666a7
Add editor config. Related to GH-153
thisandagain Aug 28, 2019
796cb32
Update dependencies
thisandagain Dec 24, 2019
719db1c
Bump package version
thisandagain Dec 25, 2019
8caf34e
Add codecov support for coverage reports. Related to GH-146
thisandagain Dec 25, 2019
98bc535
Use codecov orb for CircleCI integration
thisandagain Dec 25, 2019
3bc95e5
Add code coverage badge. Re GH-146
thisandagain Dec 25, 2019
809d61e
Specify relative path to lcov.info
thisandagain Dec 25, 2019
2c1eb1d
Clean-up formatting of badges in README
thisandagain Dec 25, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
version: 2.1
orbs:
codecov: codecov/codecov@1.0.2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/node:10

# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/mongo:3.4.4

working_directory: ~/repo

steps:
- checkout

# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-

# NPM install
- run:
name: Install
command: npm install

# Save cache
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}

# Run tests
- run:
name: Tests
command: npm test

# Generate coverage report
- run:
name: Code Coverage
command: npm run test:coverage

- store_artifacts:
path: coverage

- codecov/upload:
file: ./coverage/lcov.info
18 changes: 18 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org

root = true

[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.js]
indent_style = space
indent_size = 4

[*.md]
trim_trailing_whitespace = false
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.nyc_output
/coverage
/node_modules
8 changes: 0 additions & 8 deletions .travis.yml

This file was deleted.

4 changes: 3 additions & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
Copyright (c) 2012 Andrew Sliwinski.
The MIT License

Copyright (c) 2016 Andrew Sliwinski.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Expand Down
215 changes: 186 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,220 @@
## sentiment
#### AFINN-based sentiment analysis for Node.js
# sentiment
### AFINN-based sentiment analysis for Node.js

[![Build Status](https://travis-ci.org/thisandagain/sentiment.svg?branch=develop)](https://travis-ci.org/thisandagain/sentiment)
[![Coverage Status](https://coveralls.io/repos/thisandagain/sentiment/badge.svg?branch=develop&service=github)](https://coveralls.io/github/thisandagain/sentiment?branch=develop)
[![Dependency Status](https://david-dm.org/thisandagain/sentiment.svg)](https://david-dm.org/thisandagain/sentiment)
[![devDependency Status](https://david-dm.org/thisandagain/sentiment/dev-status.svg)](https://david-dm.org/thisandagain/sentiment#info=devDependencies)
[![CircleCI](https://circleci.com/gh/thisandagain/sentiment.svg?style=svg)](https://circleci.com/gh/thisandagain/sentiment)
[![codecov](https://codecov.io/gh/thisandagain/sentiment/branch/develop/graph/badge.svg)](https://codecov.io/gh/thisandagain/sentiment)
[![Greenkeeper badge](https://badges.greenkeeper.io/thisandagain/sentiment.svg)](https://greenkeeper.io/)

Sentiment is a Node.js module that uses the [AFINN-165](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6010) wordlist to perform [sentiment analysis](http://en.wikipedia.org/wiki/Sentiment_analysis) on arbitrary blocks of input text. Sentiment provides several things:
Sentiment is a Node.js module that uses the [AFINN-165](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6010) wordlist and [Emoji Sentiment Ranking](http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0144296) to perform [sentiment analysis](http://en.wikipedia.org/wiki/Sentiment_analysis) on arbitrary blocks of input text. Sentiment provides several things:

- Performance (see benchmarks below)
- The ability to append and overwrite word / value pairs from the AFINN wordlist
- A build process that makes updating sentiment to future versions of the AFINN word list trivial
- The ability to easily add support for new languages
- The ability to easily define custom strategies for negation, emphasis, etc. on a per-language basis

### Installation
## Table of contents

- [Installation](#installation)
- [Usage example](#usage-example)
- [Adding new languages](#adding-new-languages)
- [Adding and overwriting words](#adding-and-overwriting-words)
- [API Reference](#api-reference)
- [How it works](#how-it-works)
- [Benchmarks](#benchmarks)
- [Validation](#validation)
- [Testing](#testing)

## Installation
```bash
npm install sentiment
```

### Usage
```javascript
var sentiment = require('sentiment');
## Usage example
```js
var Sentiment = require('sentiment');
var sentiment = new Sentiment();
var result = sentiment.analyze('Cats are stupid.');
console.dir(result); // Score: -2, Comparative: -0.666
```

## Adding new languages
You can add support for a new language by registering it using the `registerLanguage` method:

```js
var frLanguage = {
labels: { 'stupide': -2 }
};
sentiment.registerLanguage('fr', frLanguage);

var r1 = sentiment('Cats are stupid.');
console.dir(r1); // Score: -2, Comparative: -0.666
var result = sentiment.analyze('Le chat est stupide.', { language: 'fr' });
console.dir(result); // Score: -2, Comparative: -0.5
```

You can also define custom scoring strategies to handle things like negation and emphasis on a per-language basis:
```js
var frLanguage = {
labels: { 'stupide': -2 },
scoringStrategy: {
apply: function(tokens, cursor, tokenScore) {
if (cursor > 0) {
var prevtoken = tokens[cursor - 1];
if (prevtoken === 'pas') {
tokenScore = -tokenScore;
}
}
return tokenScore;
}
}
};
sentiment.registerLanguage('fr', frLanguage);

var r2 = sentiment('Cats are totally amazing!');
console.dir(r2); // Score: 4, Comparative: 1
var result = sentiment.analyze('Le chat n\'est pas stupide', { language: 'fr' });
console.dir(result); // Score: 2, Comparative: 0.4
```

### Adding / overwriting words
## Adding and 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');

var result = sentiment('Cats are totally amazing!', {
var options = {
extras: {
'cats': 5,
'amazing': 2
});
'amazing': 2
}
};
var result = sentiment.analyze('Cats are totally amazing!', options);
console.dir(result); // Score: 7, Comparative: 1.75
```

## API Reference

#### `var sentiment = new Sentiment([options])`

| Argument | Type | Required | Description |
|----------|------------|----------|------------------------------------------------------------|
| options | `object` | `false` | Configuration options _(no options supported currently)_ |

---

#### `sentiment.analyze(phrase, [options], [callback])`

| Argument | Type | Required | Description |
|----------|------------|----------|-------------------------|
| phrase | `string` | `true` | Input phrase to analyze |
| options | `object` | `false` | Options _(see below)_ |
| callback | `function` | `false` | If specified, the result is returned using this callback function |


`options` object properties:

| Property | Type | Default | Description |
|----------|-----------|---------|---------------------------------------------------------------|
| language | `string` | `'en'` | Language to use for sentiment analysis |
| extras | `object` | `{}` | Set of labels and their associated values to add or overwrite |

---

#### `sentiment.registerLanguage(languageCode, language)`

| Argument | Type | Required | Description |
|--------------|----------|----------|---------------------------------------------------------------------|
| languageCode | `string` | `true` | International two-digit code for the language to add |
| language | `object` | `true` | Language module (see [Adding new languages](#adding-new-languages)) |

---

## How it works
### AFINN
AFINN is a list of words rated for valence with an integer between minus five (negative) and plus five (positive). Sentiment analysis is performed by cross-checking the string tokens (words, emojis) with the AFINN list and getting their respective scores. The comparative score is simply: `sum of each token / number of tokens`. So for example let's take the following:

`I love cats, but I am allergic to them.`

That string results in the following:
```javascript
{
score: 1,
comparative: 0.1111111111111111,
calculation: [ { allergic: -2 }, { love: 3 } ],
tokens: [
'i',
'love',
'cats',
'but',
'i',
'am',
'allergic',
'to',
'them'
],
words: [
'allergic',
'love'
],
positive: [
'love'
],
negative: [
'allergic'
]
}
```

* Returned Objects
* __Score__: Score calculated by adding the sentiment values of recognized words.
* __Comparative__: Comparative score of the input string.
* __Calculation__: An array of words that have a negative or positive valence with their respective AFINN score.
* __Token__: All the tokens like words or emojis found in the input string.
* __Words__: List of words from input string that were found in AFINN list.
* __Positive__: List of positive words in input string that were found in AFINN list.
* __Negative__: List of negative words in input string that were found in AFINN list.

In this case, love has a value of 3, allergic has a value of -2, and the remaining tokens are neutral with a value of 0. Because the string has 9 tokens the resulting comparative score looks like:
`(3 + -2) / 9 = 0.111111111`

This approach leaves you with a mid-point of 0 and the upper and lower bounds are constrained to positive and negative 5 respectively (the same as each token! 😸). For example, let's imagine an incredibly "positive" string with 200 tokens and where each token has an AFINN score of 5. Our resulting comparative score would look like this:

```
(max positive score * number of tokens) / number of tokens
(5 * 200) / 200 = 5
```

### Tokenization
Tokenization works by splitting the lines of input string, then removing the special characters, and finally splitting it using spaces. This is used to get list of words in the string.

---

### Benchmarks
The primary motivation for designing `sentiment` was performance. As such, it 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 a MacBook Pro with Node 0.12.7, `sentiment` is **twice as fast** as alternative implementations:
## Benchmarks
A primary motivation for designing `sentiment` was performance. As such, it 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 a MacBook Pro with Node v6.9.1, `sentiment` is nearly twice as fast as alternative implementations:

```bash
sentiment (Latest) x 544,714 ops/sec ±0.83% (99 runs sampled)
Sentimental (1.0.1) x 269,417 ops/sec ±1.06% (96 runs sampled)
sentiment (Latest) x 861,312 ops/sec ±0.87% (89 runs sampled)
Sentimental (1.0.1) x 451,066 ops/sec ±0.99% (92 runs sampled)
```

To run the benchmarks yourself, simply:
To run the benchmarks yourself:
```bash
make benchmark
npm run test:benchmark
```

---

## Validation
While the accuracy provided by AFINN is quite good considering it's computational performance (see above) there is always room for improvement. Therefore the `sentiment` module is open to accepting PRs which modify or amend the AFINN / Emoji datasets or implementation given that they improve accuracy and maintain similar performance characteristics. In order to establish this, we test the `sentiment` module against [three labelled datasets provided by UCI](https://archive.ics.uci.edu/ml/datasets/Sentiment+Labelled+Sentences).

To run the validation tests yourself:
```bash
npm run test:validate
```

### Rand Accuracy
```
Amazon: 0.726
IMDB: 0.765
Yelp: 0.696
```

---

### Testing
## Testing
```bash
npm test
```
Loading