Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial import

  • Loading branch information...
commit 98310f8fa8b06c24afcd1cab825ba0bf7cac1b35 0 parents
@chrisumbel chrisumbel authored
2  .gitignore
@@ -0,0 +1,2 @@
+*~
+\#*
26 LICENSE.txt
@@ -0,0 +1,26 @@
+Copyright (c) 2011, Chris Umbel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+3. Neither the name of the PostgreSQL Global Development Group nor the names
+ of its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
29 Makefile
@@ -0,0 +1,29 @@
+# Copyright (c) 2011, Chris Umbel
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the PostgreSQL Global Development Group nor the names
+# of its contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+clean:
+ @rm -f **/*~ *~
5 README.md
@@ -0,0 +1,5 @@
+= natural
+
+General natural language facilities for nodejs
+
+=== Copyright (c) 2011 Chris Umbel
113 lib/bayes_classifier.js
@@ -0,0 +1,113 @@
+/*
+Copyright (c) 2011, Chris Umbel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+3. Neither the name of the PostgreSQL Global Development Group nor the names
+ of its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+require('./porter_stemmer').attach();
+
+var features = {};
+var classes = [];
+var words = [];
+var docCount = 0.0;
+
+// expose a training function. an array of objects that look like:
+// {classification: 'class name', text: 'full text of item'}
+// this function must be called before performing classification.
+exports.train = function(data) {
+ docCount = data.length;
+
+ // count up word occurrences for the classes of each doc
+ data.forEach(function(item) {
+ if(classes.indexOf(item.classification) < 0)
+ classes.push(item.classification);
+
+ item.text.tokenizeAndStem().forEach(function(word) {
+ if(words.indexOf(word) < 0)
+ words.push(word);
+
+ if(features[word])
+ if(features[word][item.classification])
+ features[word][item.classification]++;
+ else
+ features[word][item.classification] = 1;
+ else {
+ features[word] = {};
+ features[word][item.classification] = 1;
+ }
+ });
+ });
+
+ // determine the probability of each word for each class
+ words.forEach(function(word) {
+ classes.forEach(function(classification) {
+ if(features[word][classification]) {
+ features[word][classification] = features[word][classification] / docCount;
+ }
+ });
+ });
+};
+
+// expose a classification function. here's the money. general full text is
+// supplied and the classification is returned.
+exports.classify = function(text) {
+ classifications = {};
+
+ if(text instanceof Array)
+ tokens = text;
+ else
+ tokens = text.tokenizeAndStem();
+
+ // determine the probability that the given document matches each class
+ tokens.forEach(function(word) {
+ if(features[word]) {
+ classes.forEach(function(classification) {
+ // zero would short-circuit the whole operation. don't let that
+ // happen by using a multiplier approaching, but not equal to 0
+ // for a non-match.
+ if(!features[word][classification])
+ features[word][classification] = 0.0001;
+
+ if(classifications[classification])
+ classifications[classification] *= features[word][classification];
+ else
+ classifications[classification] = features[word][classification];
+ });
+ }
+ });
+
+ result = {className : "", value : null};
+
+ // find the class with the highest probability
+ classes.forEach(function(classification) {
+ if(classifications[classification] >= result.value) {
+ result.value = classifications[classification];
+ result.className = classification;
+ }
+ });
+
+ return result.className;
+}
217 lib/porter_stemmer.js
@@ -0,0 +1,217 @@
+/*
+Copyright (c) 2011, Chris Umbel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+3. Neither the name of the PostgreSQL Global Development Group nor the names
+ of its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+require('./tokenizers').attach();
+stopwords = require('./stopwords');
+
+// denote groups of consecutive consonants with a C and consecutive vowels
+// with a V.
+function categorizeGroups(token) {
+ return token.replace(/[^aeiou]+/g, 'C').replace(/[aeiouy]+/g, 'V');
+}
+
+// denote single consonants with a C and single vowels with a V
+function categorizeChars(token) {
+ return token.replace(/[^aeiou]/g, 'C').replace(/[aeiouy]/g, 'V');
+}
+
+// calculate the "measure" M of a word. M is the count of VC sequences dropping
+// an initial C if it exists and a trailing V if it exists.
+function measure(token) {
+ return categorizeGroups(token).replace(/^C/, '').replace(/V$/, '').length / 2;
+}
+
+// determine if a token end with a double consonant i.e. happ
+function endsWithDoublCons(token) {
+ return token.match(/([^aeiou])\1$/);
+}
+
+// replace a pattern in a word. if a replacement occurs an optional callback
+// can be called to post-process the result. if no match is made NULL is
+// returned.
+function attemptReplace(token, pattern, replacement, callback) {
+ var result = null;
+
+ if((typeof pattern == 'string') && token.substr(0 - pattern.length) == pattern)
+ result = token.replace(new RegExp(pattern + '$'), replacement);
+ else if((pattern instanceof RegExp) && token.match(pattern))
+ result = token.replace(pattern, replacement);
+
+ if(result && callback)
+ return callback(result);
+ else
+ return result;
+}
+
+// attempt to replace a list of patterns/replacements on a token for a minimum
+// measure M.
+function attemptReplacePatterns(token, replacements, measureThreshold) {
+ var replacement = null;
+
+ if(!measureThreshold || measure(token) > measureThreshold) {
+ for(var i = 0; i < replacements.length; i++) {
+ replacement = attemptReplace(token, replacements[i][0], replacements[i][1]);
+
+ if(replacement)
+ break;
+ }
+ }
+
+ return replacement;
+}
+
+// replace a list of patterns/replacements on a word. if no match is made return
+// the original token.
+function replacePatterns(token, replacements, measureThreshold) {
+ var result = attemptReplacePatterns(token, replacements, measureThreshold);
+ token = result == null ? token : result;
+
+ return token;
+}
+
+// step 1a as defined for the porter stemmer algorithm.
+function step1a(token) {
+ if(token.match(/(ss|i)es$/))
+ return token.replace(/(ss|i)es$/, '$1');
+
+ if(token.substr(-1) == 's' && token.substr(-2, 1) != 's')
+ return token.replace(/s?$/, '');
+
+ return token;
+}
+
+// step 1b as defined for the porter stemmer algorithm.
+function step1b(token) {
+ if(measure(token) > 0)
+ if(token.substr(-3) == 'eed')
+ return token.replace(/eed$/, 'ee');
+
+ if(categorizeGroups(token).substr(-2, 1) == 'V') {
+ result = attemptReplace(token, /ed|ing$/, '', function(token) {
+ var result = attemptReplacePatterns(token, [['at', 'ate'], ['bl', 'ble'], ['iz', 'ize']]);
+
+ if(result)
+ return result;
+ else {
+ if(endsWithDoublCons(token) && token.match(/[^lsz]$/))
+ return token.replace(/([^aeiou])\1$/, '$1');
+
+ if(measure(token) == 1 && categorizeChars(token).substr(-3) == 'CVC' && token.match(/[^wxy]$/))
+ return token + 'e';
+ }
+
+ return token;
+ });
+
+ if(result)
+ return result;
+ }
+
+ return token;
+}
+
+// step 1c as defined for the porter stemmer algorithm.
+function step1c(token) {
+ if(categorizeGroups(token).substr(-2, 1) == 'V') {
+ if(token.substr(-1) == 'y')
+ return token.replace(/y$/, 'i');
+ }
+
+ return token;
+}
+
+// step 2 as defined for the porter stemmer algorithm.
+function step2(token) {
+ return replacePatterns(token, [['ational', 'ate'], ['enci', 'ence'], ['anci', 'ance'],
+ ['izer', 'ize'], ['abli', 'able'], ['alli', 'all'], ['entli', 'ent'], ['eli', 'e'],
+ ['ousli', 'ous'], ['ization', 'ize'], ['ation', 'ate'], ['alism', 'al'],
+ ['iveness', 'ive'], ['fulness', 'ful'], ['ousness', 'ous'], ['aliti', 'al'],
+ ['iviti', 'ive'], ['biliti', 'ble']], 0);
+}
+
+// step 3 as defined for the porter stemmer algorithm.
+function step3(token) {
+ return replacePatterns(token, [['icate', 'ic'], ['ative', ''], ['alize', 'al'],
+ ['iciti', 'ic'], ['ful', ''], ['ness', '']], 0);
+}
+
+// step 4 as defined for the porter stemmer algorithm.
+function step4(token) {
+ return replacePatterns(token, [['al', ''], ['ance', ''], ['ence', ''], ['er', ''],
+ ['ic', ''], ['able', ''], ['ible', ''], ['ant', ''],
+ ['ement', ''], [/([st])ion/, '$1'], ['ou', ''], ['ism', ''],
+ ['ate', ''], ['iti', ''], ['ous', ''], ['ive', ''],
+ ['ize', '']], 1);
+}
+
+// step 5a as defined for the porter stemmer algorithm.
+function step5a(token) {
+ var m = measure(token);
+
+ if((m > 1 && token.substr(-1) == 'e') || (m == 1 && !(categorizeChars(token).substr(-4, 3) == 'CVC' && token.match(/[^wxy].$/))))
+ return token.replace(/e$/, '');
+
+ return token;
+}
+
+// step 5b as defined for the porter stemmer algorithm.
+function step5b(token) {
+ if(measure(token) > 1) {
+ if(endsWithDoublCons(token) && token.substr(-2) == 'll')
+ return token.replace(/ll$/, 'l');
+ }
+
+ return token;
+}
+
+// perform full stemming algorithm on a single word
+function stem(token) {
+ return step5b(step5a(step4(step3(step2(step1c(step1b(step1a(token.toLowerCase())))))))).toString();
+}
+
+// tell the rest of the world about the stem function
+exports.stem = stem;
+
+// expose an attach function that will patch String with stemming methods
+exports.attach = function() {
+ String.prototype.stem = function() {
+ return stem(this);
+ };
+
+ String.prototype.tokenizeAndStem = function(keepStops) {
+ var stemmedTokens = [];
+
+ this.tokenize().forEach(function(token) {
+ if(keepStops || stopwords.words.indexOf(token) == -1)
+ stemmedTokens.push(token.stem());
+ });
+
+ return stemmedTokens;
+ }
+};
48 lib/stopwords.js
@@ -0,0 +1,48 @@
+/*
+Copyright (c) 2011, Chris Umbel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+3. Neither the name of the PostgreSQL Global Development Group nor the names
+ of its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// a list of commonly used words that have little meaning and can be excluded
+// from analysis.
+var words = [
+ 'about', 'after', 'all', 'also', 'an', 'and', 'another', 'any', 'are', 'as', 'at', 'be',
+ 'because', 'been', 'before', 'being', 'between', 'both', 'but', 'by', 'came', 'can',
+ 'come', 'could', 'did', 'do', 'each', 'for', 'from', 'get', 'got', 'has', 'had',
+ 'he', 'have', 'her', 'here', 'him', 'himself', 'his', 'how', 'if', 'in', 'into',
+ 'is', 'it', 'like', 'make', 'many', 'me', 'might', 'more', 'most', 'much', 'must',
+ 'my', 'never', 'now', 'of', 'on', 'only', 'or', 'other', 'our', 'out', 'over',
+ 'said', 'same', 'see', 'should', 'since', 'some', 'still', 'such', 'take', 'than',
+ 'that', 'the', 'their', 'them', 'then', 'there', 'these', 'they', 'this', 'those',
+ 'through', 'to', 'too', 'under', 'up', 'very', 'was', 'way', 'we', 'well', 'were',
+ 'what', 'where', 'which', 'while', 'who', 'with', 'would', 'you', 'your',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '$', '1',
+ '2', '3', '4', '5', '6', '7', '8', '9', '0', '_'];
+
+// tell the world about the noise words.
+exports.words = words;
43 lib/tokenizers.js
@@ -0,0 +1,43 @@
+/*
+Copyright (c) 2011, Chris Umbel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+3. Neither the name of the PostgreSQL Global Development Group nor the names
+ of its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// break a string up into an array of tokens by anything non-word
+function tokenize(text) {
+ return text.split(/\W+/);
+}
+
+// expose the tokenize function
+exports.tokenize = tokenize;
+
+// expose an attach function that will patch String with a tokenize method
+exports.attach = function() {
+ String.prototype.tokenize = function() {
+ return tokenize(this);
+ }
+}
39 package.json
@@ -0,0 +1,39 @@
+/*
+Copyright (c) 2011, Chris Umbel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+3. Neither the name of the PostgreSQL Global Development Group nor the names
+ of its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+{
+ "name": "natural",
+ "version": "0.0.1",
+ "engines": {
+ "node": ">=0.2.6"
+ },
+ "author": "Chris Umbel <chris@chrisumbel.com>",
+ "keywords": ["natural", "language", "porter", "stemmer", "bayes", "classifier"],
+ "directories": { "lib" : "./lib" }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.