Split words on boundary
<p>I have some tweets which I wish to split into words. Most of it works fine except when people combine words like: <code>trumpisamoron</code> or <code>makeamericagreatagain</code>. But then there are also things like <code>password</code> which shouldn't be split up into <code>pass</code> and <code>word</code>.</p>

<p>I know that the nltk package has a <code>punkt tokenizer</code> module which splits sentences up in a smart way. Is there something similar for words? Even if it isn't in the nltk package?</p>

<p>Note: The example of <code>password -&gt; pass + word</code> is much less of a problem than the splitting word problem.</p>

<p>Ref : My Answer on another Question - <a href="http://stackoverflow.com/questions/38621703/need-to-split-tags-to-text/38624458#38624458">Need to split #tags to text</a>.</p>

<p>Changes in this answer I made are - (1) Different corpus to get <code>WORDS</code> and (2) Added <code>def memo(f)</code> to speed up process. You may need to add/use corpus depending upon Domain you are working on. </p>

<p>Check - <a href="http://nbviewer.jupyter.org/url/norvig.com/ipython/How%20to%20Do%20Things%20with%20Words.ipynb" rel="nofollow">Word Segmentation Task</a> from <a href="http://norvig.com/" rel="nofollow">Norvig</a>'s work.</p>

<pre><code>from __future__ import division
from collections import Counter
import re, nltk
from datetime import datetime

WORDS = nltk.corpus.reuters.words() + nltk.corpus.words.words()
COUNTS = Counter(WORDS)

def memo(f):
    "Memoize function f, whose args must all be hashable."
    cache = {}
    def fmemo(*args):
        if args not in cache:
            cache[args] = f(*args)
        return cache[args]
    fmemo.cache = cache
    return fmemo

def pdist(counter):
    "Make a probability distribution, given evidence from a Counter."
    N = sum(counter.values())
    return lambda x: counter[x]/N

P = pdist(COUNTS)

def Pwords(words):
    "Probability of words, assuming each word is independent of others."
    return product(P(w) for w in words)

def product(nums):
    "Multiply the numbers together.  (Like `sum`, but with multiplication.)"
    result = 1
    for x in nums:
        result *= x
    return result

def splits(text, start=0, L=20):
    "Return a list of all (first, rest) pairs; start &lt;= len(first) &lt;= L."
    return [(text[:i], text[i:]) 
            for i in range(start, min(len(text), L)+1)]

@memo
def segment(text):
    "Return a list of words that is the most probable segmentation of text."
    if not text: 
        return []
    else:
        candidates = ([first] + segment(rest) 
                      for (first, rest) in splits(text, 1))
        return max(candidates, key=Pwords)

print segment('password')     # ['password']
print segment('makeamericagreatagain')     # ['make', 'america', 'great', 'again']
print segment('trumpisamoron')     # ['trump', 'is', 'a', 'moron']
print segment('narcisticidiots')     # ['narcistic', 'idiot', 's']
</code></pre>

<p>Sometimes, in case, word gets spilt into smaller token, there may be higher chances that word is not present in our <code>WORDS</code> Dictionary.</p>

<p>Here in last segment, it broke <code>narcisticidiots</code> into 3 tokens because token <code>idiots</code> was not there in our <code>WORDS</code>.</p>

<pre><code># Check for sample word 'idiots'
if 'idiots' in WORDS:
    print("YES")
else:
    print("NO")
</code></pre>

<p>You can add new user defined words to <code>WORDS</code>.</p>

<pre><code>.
.
user_words = []
user_words.append('idiots')

WORDS+=user_words
COUNTS = Counter(WORDS)
.
.
.
print segment('narcisticidiots')     # ['narcistic', 'idiots']
</code></pre>

<p>For better solution than this you can use bigram/trigram. </p>

<p>More examples at : <a href="http://nbviewer.jupyter.org/url/norvig.com/ipython/How%20to%20Do%20Things%20with%20Words.ipynb" rel="nofollow">Word Segmentation Task</a></p>

python nlp nltk