Token attributes: lemma, POS, stop words

spaCy · Essentials · 10 min

Most text preprocessing and feature extraction is a filter or map over these token attributes.

Each token exposes lexical and predicted attributes:

  • lemma_: the base form ("running" to "run"), from the lemmatizer
  • pos_: coarse Universal POS tag (NOUN, VERB, ADJ, PROPN...); tag_: the fine-grained, language-specific tag (Penn Treebank VBG for English)
  • morph: morphological features such as Tense=Past|VerbForm=Fin
  • is_stop, is_punct, is_alpha, is_digit, like_num, like_email, like_url
  • shape_: an abstract shape like Xxxxx or dd, lower_, prefix_, suffix_

The trailing underscore matters. token.pos_ is the readable string; token.pos (no underscore) is an integer id or hash. spaCy stores strings as 64-bit hashes internally for speed and memory, so the int form is what you'd use in arrays, and the string form is for humans and most code.

spacy.explain("VBG") describes a tag or label.

Lexical flags like is_stop and like_num come from language data and don't depend on the model; pos_, tag_ and lemma_ are predictions (or rule-based lookups) and can be wrong on unusual text.

A typical cleanup for bag-of-words features is a comprehension: keep tokens that are alphabetic and not stopwords, map them to lowercase lemmas. Unlike NLTK, the context-aware tagger already handles the POS needed for correct lemmas.

Example

import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("The striped bats were hanging upside down, eating 12 mangoes.")

for t in doc[:5]:
    print(f"{t.text:8} {t.lemma_:6} {t.pos_:5} {t.tag_:4} {t.is_stop}")
# The      the    DET   DT   True
# striped  striped ADJ  JJ   False
# bats     bat    NOUN  NNS  False
# were     be     AUX   VBD  True
# hanging  hang   VERB  VBG  False

print(doc[9].text, doc[9].like_num, doc[9].shape_)    # 12 True dd
print(doc[4].morph)                                   # Aspect=Prog|Tense=Pres|VerbForm=Part
print(doc[2].pos, doc[2].pos_)                        # 92 NOUN  (id vs string)
print(spacy.explain("VBG"))                           # verb, gerund or present participle

clean = [t.lemma_.lower() for t in doc if t.is_alpha and not t.is_stop]
print(clean)   # ['striped', 'bat', 'hang', 'upside', 'eat', 'mango']

Remember

  • lemma_, pos_, tag_ and morph are pipeline predictions
  • is_stop, is_alpha, like_num, shape_ come from lexical rules
  • Underscore = readable string; no underscore = integer id/hash
  • spacy.explain decodes tags and labels

Watch out for

  • Using token.lemma (an integer hash) when you meant the string token.lemma_.

Check yourself

  1. What is the difference between `token.pos_` and `token.tag_`?
  2. `print(token.lemma)` shows a large integer. Why?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: spacy.io/usage/linguistic-features#pos-tagging