<jupyter_start><jupyter_text>What is NLP?Natural Language Processing (NLP) is often taught at the academic level from the perspective of computational linguists. However, as data scientists, we have a richer view of the natural language world - unstructured data that by its very nature has latent information that is important to humans. NLP practioners have benefitted from machine learning techniques to unlock meaning from large corpora, and in this tutorial we’ll explore how to do that using Python, the Natural Language Toolkit (NLTK) and Gensim. NLTK is an excellent library for machine-learning based NLP, written in Python by experts from both academia and industry. Python allows you to create rich data applications rapidly, iterating on hypotheses. The combination of Python + NLTK means that you can easily add language-aware data products to your larger analytical workflows and applications.  Quick Overview of NLTKNLTK was written by two eminent computational linguists, <NAME> (Senior Research Associate of the LDC and professor at the University of Melbourne) and <NAME> (Professor of Linguistics at Edinburgh University). The NTLK library provides a combination of natural language corpora, lexical resources, and example grammars with language processing algorithms, methodologies and demonstrations for a very Pythonic "batteries included" view of natural language processing.   As such, NLTK is perfect for research-driven (hypothesis-driven) workflows for agile data science.  Installing NLTKThis notebook has a few dependencies, most of which can be installed via the python package manger - `pip`. 1. Python 2.7+ or 3.5+ (Anaconda is ok)2. NLTK3. The NLTK corpora 4. The BeautifulSoup library5. The gensim libaryOnce you have Python and pip installed you can install NLTK from the terminal as follows:```bash~$ pip install nltk~$ pip install matplotlib~$ pip install beautifulsoup4~$ pip install gensim```Note that these will also install Numpy and Scipy if they aren't already installed.  What NLTK Includes- [tokenization](tokenize), [stemming](stemming), and [tagging](pos)- [chunking](chunk) and [parsing](parse)- language modeling- [classification](classify) and clustering- logical semanticsNLTK is a useful pedagogical resource for learning NLP with Python and serves as a starting place for producing production grade code that requires natural language analysis. It is also important to understand what NLTK is _not_. What NLTK is _Not_- Production ready out of the box- Lightweight- Generally applicable- MagicNLTK provides a variety of tools that can be used to explore the linguistic domain but is not a lightweight dependency that can be easily included in other workflows, especially those that require unit and integration testing or other build processes. This stems from the fact that NLTK includes a lot of added code but also a rich and complete library of corpora that power the built-in algorithms.  The Good Parts of NLTK- Preprocessing    - [segmentation](segment)    - [tokenization](tokenize)    - [Part-of-Speech (PoS) tagging](pos)- Word level processing    - [WordNet](wordnet)    - [Lemmatization](lemmatize)    - [Stemming](stemming)    - [NGrams](ngram)- Utilities    - Tree    - [FreqDist](freqdist)    - ConditionalFreqDist    - Streaming CorpusReaders- [Classification](classify)    - Maximum Entropy    - Naive Bayes    - Decision Tree- [Chunking](chunk)- [Named Entity Recognition](nerc)- [Parsers Galore!](parse) The Bad parts of NLTK- Syntactic Parsing    - No included grammar (not a black box)    - No Feature/Dependency Parsing    - No included feature grammar- The sem package        - Toy only (lambda-calculus & first order logic)- Lots of extra stuff (heavyweight dependency)    - papers, chat programs, alignments, etc.Knowing the good and the bad parts will help you explore NLTK further - looking into the source code to extract the material you need, then moving that code to production. We will explore NLTK in more detail in the rest of this notebook.  Obtaining and Exploring the NLTK CorporaNLTK ships with a variety of corpora, let's use a few of them to do some work. To download the NLTK corpora, open a Python interpreter:```pythonimport nltknltk.download()```This will open up a window with which you can download the various corpora and models to a specified location. For now, go ahead and download it all as we will be exploring as much of NLTK as we can. Also take note of the `download_directory` - you're going to want to know where that is so you can get a detailed look at the corpora that's included. I usually export an environment variable to track this. You can do this from your terminal:    ~$ export NLTK_DATA=/path/to/nltk_data<jupyter_code># Take a moment to explore what is in this directory
dir(nltk)<jupyter_output><empty_output><jupyter_text>Methods for Working with Sample NLTK CorporaTo explore much of the built-in corpus, use the following methods:<jupyter_code># Lists the various corpora and CorpusReader classes in the nltk.corpus module
for name in dir(nltk.corpus):
    print(name)
    if name.islower() and not name.startswith('_'): print(name)<jupyter_output>_LazyModule__lazymodule_globals
_LazyModule__lazymodule_import
_LazyModule__lazymodule_init
_LazyModule__lazymodule_loaded
_LazyModule__lazymodule_locals
_LazyModule__lazymodule_name
__class__
__delattr__
__dict__
__dir__
__doc__
__eq__
__format__
__ge__
__getattr__
__getattribute__
__gt__
__hash__
__init__
__le__
__lt__
__module__
__name__
__ne__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
__weakref__<jupyter_text>`fileids()`<jupyter_code># You can explore the titles with:
print(nltk.corpus.gutenberg.fileids())
# For a specific corpus, list the fileids that are available:
print(nltk.corpus.shakespeare.fileids())<jupyter_output>['a_and_c.xml', 'dream.xml', 'hamlet.xml', 'j_caesar.xml', 'macbeth.xml', 'merchant.xml', 'othello.xml', 'r_and_j.xml']<jupyter_text>`text.Text()`The `nltk.text.Text` class is a wrapper around a sequence of simple (string) tokens - intended only for _the initial exploration of text_ usually via the Python REPL. It has the following methods:- common_contexts- concordance- collocations- count- plot- findall- indexYou shouldn't use this class in production level systems, but it is useful to explore (small) snippets of text in a meaningful fashion. For example, you can get access to the text from Hamlet as follows:<jupyter_code>hamlet = nltk.text.Text(nltk.corpus.gutenberg.words('shakespeare-hamlet.txt'))<jupyter_output><empty_output><jupyter_text>`concordance()`The concordance function performs a search for the given token and then also provides the surrounding context.<jupyter_code>hamlet.concordance("king", 55, lines=10)<jupyter_output>Displaying 10 of 172 matches:
elfe Bar . Long liue the King Fran . Barnardo ? Bar . 
e same figure , like the King that ' s dead Mar . Thou
. Lookes it not like the King ? Marke it Horatio Hora 
Mar . Is it not like the King ? Hor . As thou art to t
isper goes so : Our last King , Whose Image euen but n
mpetent Was gaged by our King : which had return ' d T
Secunda . Enter Claudius King of Denmarke , Gertrude t
elia , Lords Attendant . King . Though yet of Hamlet o
er To businesse with the King , more then the scope Of
 , will we shew our duty King . We doubt it nothing ,<jupyter_text>`similar()`Given some context surrounding a word, we can discover similar words, e.g. words that that occur frequently in the same context and with a similar distribution: Distributional similarity: **Note** `ContextIndex.similar_words(word)` calculates the similarity score for each word as the sum of the products of frequencies in each context. `Text.similar()` simply counts the number of unique contexts the words share.http://bit.ly/2a2udIr<jupyter_code>print(hamlet.similar("marriage"))
austen = nltk.text.Text(nltk.corpus.gutenberg.words("austen-sense.txt"))
print()
print(austen.similar("marriage"))<jupyter_output>faculty that it paris loue wrath funerall together greefe time heauen
t thewes clouds reputation eare forme
None

mother family time life head brother side affection engagement
feelings judgment carriage manners ease daughters eyes regard sister
visit hand
None<jupyter_text>As you can see, this takes a bit of time to build the index in memory, one of the reasons it's not suggested to use this class in production code.  `common_contexts()`Now that we can do searching and similarity, we find the common contexts of a set of words.<jupyter_code>hamlet.common_contexts(["king", "father"])<jupyter_output>a_that my_and<jupyter_text>_your turn, go ahead and explore similar words and contexts - what does the common context mean?_ `dispersion_plot()`NLTK also uses matplotlib and pylab to display graphs and charts that can show dispersions and frequency. This is especially interesting for the corpus of innagural addresses given by U.S. presidents.<jupyter_code>inaugural = nltk.text.Text(nltk.corpus.inaugural.words())
inaugural.dispersion_plot(["citizens", "democracy", "freedom", "duty", "America"])<jupyter_output><empty_output><jupyter_text>Stopwords<jupyter_code>print(nltk.corpus.stopwords.fileids())
nltk.corpus.stopwords.words('english')

import string
print(string.punctuation)<jupyter_output>['danish', 'dutch', 'english', 'finnish', 'french', 'german', 'hungarian', 'italian', 'norwegian', 'portuguese', 'russian', 'spanish', 'swedish', 'turkish']
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~<jupyter_text>These corpora export several vital methods:- paras (iterate through each paragraph)- sents (iterate through each sentence)- words (iterate through each word)- raw   (get access to the raw text) `paras()`<jupyter_code>corpus = nltk.corpus.brown
print(corpus.paras())<jupyter_output>[[['The', 'Fulton', 'County', 'Grand', 'Jury', 'said', 'Friday', 'an', 'investigation', 'of', "Atlanta's", 'recent', 'primary', 'election', 'produced', '``', 'no', 'evidence', "''", 'that', 'any', 'irregularities', 'took', 'place', '.']], [['The', 'jury', 'further', 'said', 'in', 'term-end', 'presentments', 'that', 'the', 'City', 'Executive', 'Committee', ',', 'which', 'had', 'over-all', 'charge', 'of', 'the', 'election', ',', '``', 'deserves', 'the', 'praise', 'and', 'thanks', 'of', 'the', 'City', 'of', 'Atlanta', "''", 'for', 'the', 'manner', 'in', 'which', 'the', 'election', 'was', 'conducted', '.']], ...]<jupyter_text>`sents()`<jupyter_code>print(corpus.sents())<jupyter_output>[['The', 'Fulton', 'County', 'Grand', 'Jury', 'said', 'Friday', 'an', 'investigation', 'of', "Atlanta's", 'recent', 'primary', 'election', 'produced', '``', 'no', 'evidence', "''", 'that', 'any', 'irregularities', 'took', 'place', '.'], ['The', 'jury', 'further', 'said', 'in', 'term-end', 'presentments', 'that', 'the', 'City', 'Executive', 'Committee', ',', 'which', 'had', 'over-all', 'charge', 'of', 'the', 'election', ',', '``', 'deserves', 'the', 'praise', 'and', 'thanks', 'of', 'the', 'City', 'of', 'Atlanta', "''", 'for', 'the', 'manner', 'in', 'which', 'the', 'election', 'was', 'conducted', '.'], ...]<jupyter_text>`words()`<jupyter_code>print(corpus.words())<jupyter_output>['The', 'Fulton', 'County', 'Grand', 'Jury', 'said', ...]<jupyter_text>`raw()`Be careful!<jupyter_code>print(corpus.raw()[:200]) # Be careful!<jupyter_output>The/at Fulton/np-tl County/nn-tl Grand/jj-tl Jury/nn-tl said/vbd Friday/nr an/at investigation/nn of/in Atlanta's/np$ recent/jj primary/nn election/nn produced/vbd ``/`` no/at evidence/nn ''/'' tha<jupyter_text>_Your turn! Explore some of the text in the available corpora_ Frequency Analyses In statistical machine learning approaches to NLP, the very first thing we need to do is count things - especially the unigrams that appear in the text and their relationships to each other. NLTK provides two excellent classes to enable these frequency analyses:- `FreqDist`- `ConditionalFreqDist` And these two classes serve as the foundation for most of the probability and statistical analyses that we will conduct. Zipf's Law![Zipf's distribution](images/Zipf_distribution_PMF.png)Zipf's law states that given some corpus of natural language utterances, the frequency of any word is inversely proportional to its rank in the frequency table. Thus the most frequent word will occur approximately twice as often as the second most frequent word, three times as often as the third most frequent word, etc.: the rank-frequency distribution is an inverse relation. Read more on [Wikipedia](https://en.wikipedia.org/wiki/Zipf%27s_law).First we will compute the following:- The count of words- The vocabulary (unique words)- The lexical diversity (the ratio of word count to vocabulary)<jupyter_code>reuters = nltk.corpus.reuters # Corpus of news articles
counts  = nltk.FreqDist(reuters.words())
vocab   = len(counts.keys())
words   = sum(counts.values())
lexdiv  = float(words) / float(vocab)

print("Corpus has %i types and %i tokens for a lexical diversity of %0.3f" % (vocab, words, lexdiv))<jupyter_output>Corpus has 41600 types and 1720901 tokens for a lexical diversity of 41.368<jupyter_text>`counts()`<jupyter_code>counts.B()<jupyter_output><empty_output><jupyter_text>`most_common()`The _n_ most common tokens in the corpus<jupyter_code>print(counts.most_common(40))<jupyter_output>[('.', 94687), (',', 72360), ('the', 58251), ('of', 35979), ('to', 34035), ('in', 26478), ('said', 25224), ('and', 25043), ('a', 23492), ('mln', 18037), ('vs', 14120), ('-', 13705), ('for', 12785), ('dlrs', 11730), ("'", 11272), ('The', 10968), ('000', 10277), ('1', 9977), ('s', 9298), ('pct', 9093), ('it', 8842), (';', 8762), ('&', 8698), ('lt', 8694), ('on', 8556), ('from', 7986), ('cts', 7953), ('is', 7580), ('>', 7449), ('that', 7377), ('its', 7265), ('by', 6872), ('"', 6816), ('at', 6537), ('2', 6528), ('U', 6388), ('S', 6382), ('year', 6310), ('be', 6288), ('with', 5945)]<jupyter_text>`counts.max()`The most frequent token in the corpus.<jupyter_code>print(counts.max())<jupyter_output>.<jupyter_text>`counts.hapaxes()`A list of all _hapax legomena_ (words that only appear one time in the corpus).<jupyter_code>print(counts.hapaxes()[0:10])<jupyter_output>['72p', 'incurs', 'footwork', 'NEXL', 'arrogant', 'Formation', 'WJYE', 'simulation', 'auctioning', 'Chesapeake']<jupyter_text>`counts.freq()`The percentage of the corpus for the given token.<jupyter_code>counts.freq('stipulate') * 100<jupyter_output><empty_output><jupyter_text>`counts.plot()`Plot the frequencies of the _n_ most commonly occuring words.<jupyter_code>counts.plot(50, cumulative=False)
# By setting cumulative to True, we can visualize the cumulative counts of the _n_ most common words.
counts.plot(50, cumulative=True)<jupyter_output><empty_output><jupyter_text>`ConditionalFreqDist()`<jupyter_code>from itertools import chain 

brown = nltk.corpus.brown
categories = brown.categories()

counts = nltk.ConditionalFreqDist(chain(*[[(cat, word) for word in brown.words(categories=cat)] for cat in categories]))

for category, dist in counts.items():
    vocab  = len(dist.keys())
    tokens = sum(dist.values())
    lexdiv = float(tokens) / float(vocab)
    print("%s: %i types with %i tokens and lexical diversity of %0.3f" % (category, vocab, tokens, lexdiv))<jupyter_output>lore: 14503 types with 110299 tokens and lexical diversity of 7.605
religion: 6373 types with 39399 tokens and lexical diversity of 6.182
humor: 5017 types with 21695 tokens and lexical diversity of 4.324
hobbies: 11935 types with 82345 tokens and lexical diversity of 6.899
science_fiction: 3233 types with 14470 tokens and lexical diversity of 4.476
editorial: 9890 types with 61604 tokens and lexical diversity of 6.229
romance: 8452 types with 70022 tokens and lexical diversity of 8.285
belles_lettres: 18421 types with 173096 tokens and lexical diversity of 9.397
learned: 16859 types with 181888 tokens and lexical diversity of 10.789
fiction: 9302 types with 68488 tokens and lexical diversity of 7.363
mystery: 6982 types with 57169 tokens and lexical diversity of 8.188
reviews: 8626 types with 40704 tokens and lexical diversity of 4.719
government: 8181 types with 70117 tokens and lexical diversity of 8.571
adventure: 8874 types with 69342 tokens and lexical diversity of 7.814
news: 14[...]<jupyter_text>_Your turn: compute the conditional frequency distribution of bigrams in a corpus_Hint:<jupyter_code>for ngram in nltk.ngrams(["The", "bear", "walked", "in", "the", "woods", "at", "midnight"], 5):
    print(ngram)<jupyter_output>('The', 'bear', 'walked', 'in', 'the')
('bear', 'walked', 'in', 'the', 'woods')
('walked', 'in', 'the', 'woods', 'at')
('in', 'the', 'woods', 'at', 'midnight')<jupyter_text>Preprocessing TextNLTK is great at the preprocessing of raw text - it provides the following tools for dividing text into it's constituent parts:- `sent_tokenize`: a Punkt sentence tokenizer:    This tokenizer divides a text into a list of sentences, by using an unsupervised algorithm to build a model for abbreviation words, collocations, and words that start sentences.  It must be trained on a large collection of plaintext in the target language before it can be used.        However, Punkt is designed to learn parameters (a list of abbreviations, etc.) unsupervised from a corpus similar to the target domain. The pre-packaged models may therefore be unsuitable: use PunktSentenceTokenizer(text) to learn parameters from the given text.        - `word_tokenize`: a Treebank tokenizer     The Treebank tokenizer uses regular expressions to tokenize text as in Penn Treebank. This is the method that is invoked by ``word_tokenize()``.  It assumes that the text has already been segmented into sentences, e.g. using ``sent_tokenize()``.    - `pos_tag`: a maximum entropy tagger trained on the Penn Treebank    There are several other taggers including (notably) the BrillTagger as well as the BrillTrainer to train your own tagger or tagset.<jupyter_code>import bs4
from readability.readability import Document

# Tags to extract as paragraphs from the HTML text
TAGS = [
    'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7', 'p', 'li'
]

def read_html(path):
    with open(path, 'r') as f:

        # Transform the document into a readability paper summary
        html = Document(f.read()).summary()

        # Parse the HTML using BeautifulSoup
        soup = bs4.BeautifulSoup(html)

        # Extract the paragraph delimiting elements
        for tag in soup.find_all(TAGS):

            # Get the HTML node text
            yield tag.get_text()
for paragraph in read_html('fixtures/nrRB0.html'):
    print(paragraph + "\n")
text = u"Medical personnel returning to New York and New Jersey from the Ebola-riddled countries in West Africa will be automatically quarantined if they had direct contact with an infected person, officials announced Friday. New York Gov. <NAME> (D) and New Jersey Gov. <NAME> (R) announced the decision at a joint news conference Friday at 7 World Trade Center. “We have to do more,” Cuomo said. “It’s too serious of a situation to leave it to the honor system of compliance.” They said that public-health officials at <NAME> and Newark Liberty international airports, where enhanced screening for Ebola is taking place, would make the determination on who would be quarantined. Anyone who had direct contact with an Ebola patient in Liberia, Sierra Leone or Guinea will be quarantined. In addition, anyone who traveled there but had no such contact would be actively monitored and possibly quarantined, authorities said. This news came a day after a doctor who had treated Ebola patients in Guinea was diagnosed in Manhattan, becoming the fourth person diagnosed with the virus in the United States and the first outside of Dallas. And the decision came not long after a health-care worker who had treated Ebola patients arrived at Newark, one of five airports where people traveling from West Africa to the United States are encountering the stricter screening rules."

for sent in nltk.sent_tokenize(text): 
    print(sent)
    print()

for sent in nltk.sent_tokenize(text):
    print(list(nltk.wordpunct_tokenize(sent)))
    print()
for sent in nltk.sent_tokenize(text):
    print(list(nltk.pos_tag(nltk.word_tokenize(sent))))
    print()<jupyter_output>[('Medical', 'JJ'), ('personnel', 'NNS'), ('returning', 'VBG'), ('to', 'TO'), ('New', 'NNP'), ('York', 'NNP'), ('and', 'CC'), ('New', 'NNP'), ('Jersey', 'NNP'), ('from', 'IN'), ('the', 'DT'), ('Ebola-riddled', 'JJ'), ('countries', 'NNS'), ('in', 'IN'), ('West', 'NNP'), ('Africa', 'NNP'), ('will', 'MD'), ('be', 'VB'), ('automatically', 'RB'), ('quarantined', 'VBN'), ('if', 'IN'), ('they', 'PRP'), ('had', 'VBD'), ('direct', 'JJ'), ('contact', 'NN'), ('with', 'IN'), ('an', 'DT'), ('infected', 'JJ'), ('person', 'NN'), (',', ','), ('officials', 'NNS'), ('announced', 'VBD'), ('Friday', 'NNP'), ('.', '.')]

[('New', 'NNP'), ('York', 'NNP'), ('Gov', 'NNP'), ('.', '.')]

[('Andrew', 'NNP'), ('Cuomo', 'NNP'), ('(', '('), ('D', 'NNP'), (')', ')'), ('and', 'CC'), ('New', 'NNP'), ('Jersey', 'NNP'), ('Gov', 'NNP'), ('.', '.')]

[('Chris', 'NNP'), ('Christie', 'NNP'), ('(', '('), ('R', 'NNP'), (')', ')'), ('announced', 'VBD'), ('the', 'DT'), ('decision', 'NN'), ('at', 'IN'), ('a', 'DT'), ('joint', 'J[...]<jupyter_text>All of these taggers work _pretty_ well - but you can (and should train them on your own corpora).  Stemming and LemmatizationWe have an immense number of word forms as you can see from our various counts in the `FreqDist` above - it is helpful for many applications to normalize these word forms (especially applications like search) into some canonical word for further exploration. In English (and many other languages) - morphological context indicate gender, tense, quantity, etc. but these sublties might not be necessary:Stemming = chop off affixes to get the root stem of the word:    running --> run    flowers --> flower    geese   --> geese     Lemmatization = look up word form in a lexicon to get canonical lemma    women   --> woman    foxes   --> fox    sheep   --> sheep    There are several stemmers available:    - Lancaster (English, newer and aggressive)    - Porter (English, original stemmer)    - Snowball (Many languages, newest)        The Lemmatizer uses the WordNet lexicon<jupyter_code>from nltk.stem.snowball import SnowballStemmer
from nltk.stem.lancaster import LancasterStemmer
from nltk.stem.porter import PorterStemmer

text = list(nltk.word_tokenize("The women running in the fog passed bunnies working as computer scientists."))

snowball = SnowballStemmer('english')
lancaster = LancasterStemmer()
porter = PorterStemmer()

for stemmer in (snowball, lancaster, porter):
    stemmed_text = [stemmer.stem(t) for t in text]
    print(" ".join(stemmed_text))
from nltk.stem.wordnet import WordNetLemmatizer

# Note: use part of speech tag, we'll see this in machine learning! 
lemmatizer = WordNetLemmatizer()
lemmas = [lemmatizer.lemmatize(t) for t in text]
print(" ".join(lemmas))<jupyter_output>The woman running in the fog passed bunny working a computer scientist .<jupyter_text>Note that the lemmatizer has to load the WordNet corpus which takes a bit.Typical normalization of text for use as features in machine learning models looks something like this:<jupyter_code>import string
from nltk.corpus import wordnet as wn

## Module constants
lemmatizer  = WordNetLemmatizer()
stopwords   = set(nltk.corpus.stopwords.words('english'))
punctuation = string.punctuation

def tagwn(tag):
    """
    Returns the WordNet tag from the Penn Treebank tag.
    """

    return {
        'N': wn.NOUN,
        'V': wn.VERB,
        'R': wn.ADV,
        'J': wn.ADJ
    }.get(tag[0], wn.NOUN)


def normalize(text):
    for token, tag in nltk.pos_tag(nltk.wordpunct_tokenize(text)):
        #if you're going to do part of speech tagging, do it here
        token = token.lower()
        if token in stopwords and token in punctuation:
            continue
        token = lemmatizer.lemmatize(token, tagwn(tag))
        yield token

print(list(normalize("The eagle flies at midnight.")))<jupyter_output>['the', 'eagle', 'fly', 'at', 'midnight', '.']<jupyter_text>Named Entity RecognitionNLTK has an excellent MaxEnt backed Named Entity Recognizer that is trained on the Penn Treebank. You can also retrain the chunker if you'd like - the code is very readable to extend it with a Gazette or otherwise.<jupyter_code>print(nltk.ne_chunk(nltk.pos_tag(nltk.word_tokenize("<NAME> is from the United States of America and works at Microsoft Research Labs"))))<jupyter_output>(S
  (PERSON John/NNP)
  (PERSON Smith/NNP)
  is/VBZ
  from/IN
  the/DT
  (GPE United/NNP States/NNPS)
  of/IN
  (GPE America/NNP)
  and/CC
  works/VBZ
  at/IN
  (ORGANIZATION Microsoft/NNP Research/NNP Labs/NNP))<jupyter_text>You can also wrap the Stanford NER system, which many of you are also probably used to using.<jupyter_code>import os
from nltk.tag import StanfordNERTagger

# change the paths below to point to wherever you unzipped the Stanford NER download file
stanford_root = '/Users/benjamin/Development/stanford-ner-2014-01-04'
stanford_data = os.path.join(stanford_root, 'classifiers/english.all.3class.distsim.crf.ser.gz')
stanford_jar  = os.path.join(stanford_root, 'stanford-ner-2014-01-04.jar')

st = StanfordNERTagger(stanford_data, stanford_jar, 'utf-8')
for i in st.tag("<NAME> is from the United States of America and works at Microsoft Research Labs".split()):
    print('[' + i[1] + '] ' + i[0])<jupyter_output>[PERSON] John
[PERSON] Smith
[O] is
[O] from
[O] the
[LOCATION] United
[LOCATION] States
[LOCATION] of
[LOCATION] America
[O] and
[O] works
[O] at
[ORGANIZATION] Microsoft
[ORGANIZATION] Research
[ORGANIZATION] Labs<jupyter_text>Parsing Parsing is a difficult NLP task due to structural ambiguities in text. As the length of sentences increases, so does the number of possible trees.<jupyter_code>for name in dir(nltk.parse):
    if not name.startswith('_'): print(name)<jupyter_output>BllipParser
BottomUpChartParser
BottomUpLeftCornerChartParser
BottomUpProbabilisticChartParser
ChartParser
DependencyEvaluator
DependencyGraph
EarleyChartParser
FeatureBottomUpChartParser
FeatureBottomUpLeftCornerChartParser
FeatureChartParser
FeatureEarleyChartParser
FeatureIncrementalBottomUpChartParser
FeatureIncrementalBottomUpLeftCornerChartParser
FeatureIncrementalChartParser
FeatureIncrementalTopDownChartParser
FeatureTopDownChartParser
IncrementalBottomUpChartParser
IncrementalBottomUpLeftCornerChartParser
IncrementalChartParser
IncrementalLeftCornerChartParser
IncrementalTopDownChartParser
InsideChartParser
LeftCornerChartParser
LongestChartParser
MaltParser
NaiveBayesDependencyScorer
NonprojectiveDependencyParser
ParserI
ProbabilisticNonprojectiveParser
ProbabilisticProjectiveDependencyParser
ProjectiveDependencyParser
RandomChartParser
RecursiveDescentParser
ShiftReduceParser
SteppingChartParser
SteppingRecursiveDescentParser
SteppingShiftReduceParser
TestGrammar
TopDownChar[...]<jupyter_text>Similar to how you might write a compiler or an interpreter; parsing starts with a grammar that defines the construction of phrases and terminal entities.<jupyter_code>grammar = nltk.grammar.CFG.fromstring("""

S -> NP PUNCT | NP
NP -> N N | ADJP NP | DET N | DET ADJP
ADJP -> ADJ NP | ADJ N

DET -> 'an' | 'the' | 'a' | 'that'
N -> 'airplane' | 'runway' | 'lawn' | 'chair' | 'person' 
ADJ -> 'red' | 'slow' | 'tired' | 'long'
PUNCT -> '.'
""")
def parse(sent):
    sent = sent.lower()
    parser = nltk.parse.ChartParser(grammar)
    for p in parser.parse(nltk.word_tokenize(sent)):
        yield p 

        
for tree in parse("the long runway"): 
    tree.pprint()
    tree[0].draw()<jupyter_output>(S (NP (DET the) (ADJP (ADJ long) (N runway))))<jupyter_text>NLTK does come with some large grammars; but if constructing your own domain specific grammar isn't your thing; then you can use the Stanford parser (so long as you're willing to pay for it).<jupyter_code>from nltk.parse.stanford import StanfordParser

# change the paths below to point to wherever you unzipped the Stanford NER download file
stanford_root  = '/Users/benjamin/Development/stanford-parser-full-2014-10-31'
stanford_model = os.path.join(stanford_root, 'stanford-parser-3.5.0-models.jar')
stanford_jar   = os.path.join(stanford_root, 'stanford-parser.jar')

st = StanfordParser(stanford_model, stanford_jar)
sent = "The man hit the building with the baseball bat."
for tree in st.parse(nltk.wordpunct_tokenize(sent)):
    tree.pprint()
    tree.draw()<jupyter_output>(ROOT
  (S
    (NP (DT The) (NN man))
    (VP
      (VBD hit)
      (NP (DT the) (NN building))
      (PP (IN with) (NP (DT the) (NN baseball) (NN bat))))
    (. .)))
{'fraction_non_alphanumeric': 0.11704790377529027, 'fraction_numerical': 0.01668636584857123, 'mean_word_length': 5.230669265756985, 'pattern_counts': {'":': 0, '<': 105, '<?xml version=': 0, '>': 119, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 9, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '30', 'dir': 'jupyter-structured-clean-dedup', 'id': '202136', 'license': 'MIT', 'n_tokens_mistral': 9490, 'n_tokens_neox': 8732, 'n_words': 3311, 'path': 'notebooks/tutorial/Intro to NLTK.ipynb', 'repo_name': 'DistrictDataLabs/pycon2016', 'size': '28806'}