Computation for Linguists

spaCy

Dr. Andrew M. Byrd

2025-11-03

Review

  • What did you learn last time?

Recap from Last Time

list = ["eeny", "meeny", "miney", "mo"]
list[0][0]

Recap from Last Time

sentences = [
    ["the", "cat", "sleeps"],
    ["the", "dog", "runs"],
    ["the", "bird", "flies"]
]
sentences[0][0]

Recap from Last Time

consonants = {
    "p": {"voice": False, "place": "bilabial", "manner": "stop"},
    "t": {"voice": False, "place": "alveolar", "manner": "stop"},
    "k": {"voice": False, "place": "velar", "manner": "stop"}
}

Recap from Last Time

students = {
    "Alice": {"quiz": 9, "homework": 10, "final": 8},
    "Ben": {"quiz": 7, "homework": 9, "final": 10}
}

students["Alice"]["quiz"]

Recap from Last Time

import pandas as pd
consonants = {
    "p": {"voice": False, "place": "bilabial", "manner": "stop"},
    "t": {"voice": False, "place": "alveolar", "manner": "stop"},
    "k": {"voice": False, "place": "velar", "manner": "stop"}
}

df = pd.DataFrame(consonants).T.reset_index(names="phoneme")
df

Review Activity

  • Copy the list below. Using a for loop and the zip() function, create a dictionary with the words as keys. Then, using the .T method, convert it into a pd.DataFrame.
words   = ["students", "run", "ran", "roots", "languages"]
pos_tags = ["NOUN", "VERB", "VERB", "NOUN", "NOUN"]
lemmas  = ["student", "run", "run", "root", "language"]
freqs   = [5, 3, 2, 4, 6]

spaCy

Install & Load spaCy

# In a terminal, once per machine:
# pip install spacy
# python -m spacy download en_core_web_sm
import spacy
nlp = spacy.load("en_core_web_sm")

What is spaCy?

spaCy is an industrial-strength NLP library for Python that provides:

  • Tokenization
  • Part-of-speech tagging
  • Lemmatization

What is spaCy?

spaCy provides:

  • Dependency parsing
  • Named Entity Recognition (NER)
  • Built-in stopword list and rule-based matchers

Thus far…

  • We have used tokenization
    • breaking text into meaningful units (tokens).

What is Tokenization?

  • Tokens can be words, punctuation, numbers, or even subwords.
  • How words are tokenized is important, as boundaries aren’t always obvious:
    • “Dr. Byrd’s” → ?
    • “can’t” → ?
    • “U.S.” → ?

Simple Tokenization (Strings Only)

  • We’ve learned about .split()
text = "Dr. Byrd's students can't wait to analyze PIE roots!"
tokens_basic = text.split()
tokens_basic

Slightly better: use regex to remove punctuation

  • And we’ve learned about re.split()
import re

text = "Dr. Byrd's students can't wait to analyze PIE roots!"

tokens_clean = re.split(r"[\s\W]+", text)
tokens_clean

Even better: using spaCy

  • You can also split things up using spaCy, which “knows” some linguistic structure already

Tokenization: using spaCy

import spacy
nlp = spacy.load("en_core_web_sm")

text = "Dr. Byrd's students can't wait to analyze PIE roots!"

doc = nlp(text)
[t.text for t in doc]

Token Attributes in spaCy

  • text – raw token string
  • lemma_ – base form (great for type–token counts, lexeme stats).
  • pos_ – Universal POS tag (quick, language-agnostic part of speech).
  • tag_ – Fine-grained POS (useful for English-specific distinctions).

Token Attributes in spaCy

  • is_stop – filter function words for clearer frequency analyses.
  • is_punct / is_space – cleaning before counts.
  • like_num / like_url / like_email – quick heuristics for data cleaning.
  • is_alpha – keep only alphabetic tokens for many corpus tasks.

Token Attributes in spaCy

import spacy
import pandas as pd

# Load English model
nlp = spacy.load("en_core_web_sm")

text = "Dr. Byrd's students can't wait to analyze PIE roots!"
doc = nlp(text)

# Create list of dicts, one per token
data = []
for t in doc:
    data.append({
        "text": t.text,
        "lemma": t.lemma_,
        "POS": t.pos_,
        "tag": t.tag_,
        "stop": t.is_stop,
        "is_punct": t.is_punct
    })

# Make DataFrame
df = pd.DataFrame(data)
print(df)

Penn Treebank POS Tags

Penn Treebank Project Tags

spaCy Activity

  • Copy the below jfk string, and then analyze its attributes using spaCy.
jfk = "This year’s space budget is three times what it was in January 1961, and it is greater than the space budget of the previous eight years combined. That budget now stands at $5,400,000 a year — a staggering sum, though somewhat less than we pay for cigarettes and cigars every year. Space expenditures will soon rise some more, from 40 cents per person per week to more than 50 cents a week for every man, woman and child in the United States, for we have given this program a high national priority — even though I realize that this is in some measure an act of faith and vision, for we do not now know what benefits await us. But if I were to say, my fellow citizens, that we shall send to the moon, 240,000 miles away from the control station in Houston, a giant rocket more than 300 feet tall, the length of this football field, made of new metal alloys, some of which have not yet been invented, capable of standing heat and stresses several times more than have ever been experienced, fitted together with a precision better than the finest watch, carrying all the equipment needed for propulsion, guidance, control, communications, food and survival, on an untried mission, to an unknown celestial body, and then return it safely to Earth, re-entering the atmosphere at speeds of over 25,000 miles per hour, causing heat about half that of the temperature of the sun — almost as hot as it is here today — and do all this, and do it right, and do it first before this decade is out — then we must be bold. I’m the one who is doing all the work, so we just want you to stay cool for a minute. [laughter] However, I think we’re going to do it, and I think that we must pay what needs to be paid. I don’t think we ought to waste any money, but I think we ought to do the job. And this will be done in the decade of the sixties. It may be done while some of you are still here at school at this college and university. It will be done during the term of office of some of the people who sit here on this platform. But it will be done. And it will be done before the end of this decade. I am delighted that this university is playing a part in putting a man on the moon as part of a great national effort of the United States of America. Many years ago, the great British explorer George Mallory, who was to die on Mount Everest, was asked why did he want to climb it? He said, “Because it is there.” Well, space is there, and we’re going to climb it, and the moon and the planets are there, and new hopes for knowledge and peace are there. And, therefore, as we set sail we ask God’s blessing on the most hazardous and dangerous and greatest adventure on which man has ever embarked. Thank you."

Lemmas

Lemmas

  • lemmas are the base form of a word (the form you’d find as a dictionary headword)
Word Lemma POS
running run VERB
ran run VERB
mice mouse NOUN
better good ADJ

Why Lemmatize?

  • To treat morphological variants as the same word
  • To improve search accuracy (e.g., run, ran, runningrun)
  • To normalize data for counting, clustering, or semantic analysis
  • To conduct cross-linguistic comparison

Lemmatization with spaCy

import spacy
nlp = spacy.load("en_core_web_sm")

doc = nlp("The children ran quickly to their houses.")

for token in doc:
    print(token.text, "→", token.lemma_, token.pos_)

Tokenization vs. Lemmatization

text = "Students were running, reading, and writing all day."
doc = nlp(text)

tokens = [t.text for t in doc]
lemmas = [t.lemma_ for t in doc]

list(zip(tokens, lemmas))

Filtering by Part of Speech

  • You can filter by POS in your analyses
nouns = [t.lemma_ for t in doc if t.pos_ == "NOUN"]
verbs = [t.lemma_ for t in doc if t.pos_ == "VERB"]

print("Nouns:", nouns)
print("Verbs:", verbs)

Creating a Lemma Frequency Table

  • We can import the “Counter” class from the module “collections” to easily identify counts
    • it creates a dictionary from the inputted list
import pandas as pd
from collections import Counter

text = "Students were running, reading, and writing all day."
sentence = nlp(text)

lemmas = [t.lemma_.lower() for t in sentence if t.is_alpha]
freq = Counter(lemmas)

sentence_df = pd.DataFrame(freq.items(), columns=["lemma", "count"]).sort_values("count", ascending=False)
sentence_df.head()

Visualizing Lemma Counts

import matplotlib.pyplot as plt

top10 = sentence_df.head(10)
plt.bar(top10["lemma"], top10["count"])
plt.title("Top 10 Lemmas in the Text")
plt.xlabel("Lemma")
plt.ylabel("Count")
plt.show()

Stopwords

Stopwords

  • Stopwords are function words that you can filter out of your analysis
    • Why would we want to get rid of stopwords when we do research?
  • As we saw earlier, we can do so using .is_stop

Filtering Out Items

import spacy

content_lemmas = [t.lemma_.lower() for t in doc
                  if not (t.is_stop or t.is_punct or t.is_space or t.like_num)]

Stopwords & Frequency Table

from collections import Counter
import pandas as pd

freq = Counter(content_lemmas)
df_freq = (pd.DataFrame(freq.items(), columns=["lemma", "count"])
           .sort_values("count", ascending=False))
df_freq.head(10)

Visualize Top Words

import matplotlib.pyplot as plt

top10 = df_freq.head(10)
plt.figure()
plt.bar(top10["lemma"], top10["count"])
plt.title("Top 10 Content Lemmas")
plt.xlabel("Lemma")
plt.ylabel("Count")
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

Activity: Alice in Wonderland

  1. For this activity, you’ll be using alice.txt. Make sure it’s in the same folder as this .qmd file.
  2. Filter out stopwords, punctuation, whitespace, and “like numbers”.
  3. Build a pd.DataFrame, and count how many times each word occurs in the story.
  4. Plot the top 20.