Concepts / Bag-of-Words Features

Bag-of-Words Features

An n-gram feature is a sequence of n consecutive items, and trigrams are the case where n is three.

  • Programming

Why Single Words Fall Short

A bag-of-words model counts individual words and treats each count as a separate piece of evidence. Consider the words 'not' and 'good'. A model that counts only single words treats them as two unrelated signals, even though the phrase 'not good' means something very different from either word alone. The meaning lives in the order and the neighborhood of the words, and single-word counts throw that information away.

An n-gram feature offers one way to keep some of that local order. Instead of counting only individual items, the model also counts short runs of neighboring items, so the feature itself carries a small amount of context. This article follows one kind of n-gram, the trigram, through three steps: how the sequences are formed, how Python selects them, and how to read a visualization of the trigram features a trained model relied on most.

Sliding a Window Across a Sentence

Definition

An n-gram feature is a sequence of n consecutive items. A trigram is the case where n is three, so each trigram is a run of three consecutive words.

The easiest way to see how these sequences are formed is to slide a window of fixed length across a sentence. For a window of three words, the first trigram starts at the first word, the next trigram starts at the second word, and so on until the window reaches the end. Each position produces exactly one trigram. Changing n changes the length of the window and therefore the sequences it captures: a window of two produces bigrams, a window of three produces trigrams, and a larger window captures longer runs.

the food wasPosition 1food was notPosition 2was not goodPosition 3slide one word rightslide one word right
When n is 3, which consecutive word sequences does the window capture as it moves across the sentence 'the food was not good'?
Rule

Worked example

Counting the trigrams in a five-word sentence

How many trigrams does the sentence 'the food was not good' produce, and what are they?

  1. 1
    Count the words

    The words are the, food, was, not, good. So $w = 5$.

  2. 2
    Apply the formula

    The number of overlapping trigrams is $w - 2 = 5 - 2 = 3$.

  3. 3
    Slide the window

    Starting at word 1, the window captures 'the food was'. Starting at word 2, it captures 'food was not'. Starting at word 3, it captures 'was not good'. The window then reaches the end of the sentence.

Result

Three overlapping trigrams: 'the food was', 'food was not', and 'was not good'. Notice that 'not' appears inside two of them, which is how the window preserves its neighbors.

Choosing Trigrams from a Vocabulary

In a real model, the feature names come from a vectorizer that has already built a vocabulary of unigrams, bigrams, and trigrams. Selecting trigrams is then a filter: keep only the names whose word count is exactly three and discard the rest. The filter does not change the names themselves; it only decides which ones move forward.

All feature namesunigrams, bigrams, trigramsWord count is exactly3?split each name on spacesTrigram subsetonly three-word nameseach nameyes, keep it
How does the full list of feature names flow through the word-count filter to produce only the trigram subset?

What do you think happens?

Given the feature names in the code below, which ones survive a filter that keeps only names with exactly three words? Try to predict the result before you read the answer.

  • Only 'not very good' and 'none of the'

  • 'not very good', 'none of the', 'the only good', 'of the most', and 'on and on'

  • Every name that contains the word 'good'

Reveal answer
Answer:

['not very good', 'none of the', 'the only good', 'of the most', 'on and on']

The filter counts words, not characters, by splitting each name on spaces. Single words and two-word names are dropped. Every name with exactly three words is kept, in its original order.

python
Output (expected)
['not very good', 'none of the', 'the only good', 'of the most', 'on and on']
Notes on the code
  • feature_names is a plain list of strings, standing in for the names a vectorizer would produce
  • name.split() breaks each string into its words on spaces, so len(name.split()) counts words
  • The comprehension keeps a name only when its word count is exactly three

The same logic can be written with a boolean mask over a NumPy array of names, which is how many pipelines express it. The list comprehension above is easier to follow because each step is visible: split, count, compare, keep. Either version returns the same subset.

Reading the Trigram Coefficient Plot

After a model is trained, one useful view is a plot of only its trigram features, each paired with the coefficient the model assigned to it. The plot keeps the 40 most influential trigram features. Each bar or label pairs a three-word pattern with its coefficient, so you can see which patterns pushed the model's decision one way or the other. Visualizing trigrams separately gives a direct view of how the model uses features that carry local context.

The trigrams highlighted in this kind of plot include phrases such as 'none of the', 'the only good', 'on and on', 'this is one', and 'of the most'. Each phrase carries a small, meaningful pattern. Reading the plot means asking, for each pattern, whether its coefficient pushes the prediction toward one outcome or the other, and how strongly.

Why Trigrams Add Context

The difference between a single word and a trigram is easiest to see side by side. A single word such as 'not' is ambiguous on its own, while the trigram it sits inside says much more about how it is used.

FeatureWhat it recordsWhat is lost or gained
not (unigram)The single word 'not' appears in the textSays nothing about which words come next to it
good (unigram)The single word 'good' appears in the textCannot tell whether 'good' was negated
not very good (trigram)The run 'not', 'very', 'good' appears in orderKeeps the local order, so the negation sits next to the word it modifies
none of the (trigram)The run 'none', 'of', 'the' appears in orderCaptures a phrase whose meaning is not carried by any single word

Single-word features compared with trigram features for the same local text

Where the Length Check Goes Wrong

A trigram filter depends on counting words correctly. A wrong comparison value silently changes which features appear, and the code still runs without any error. The example below uses the same feature names as before. The wrong version measures length in characters with len(name), while the correct version measures words with len(name.split()).

Before
trigram_names = [name for name in feature_names if len(name) == 3]

Counts characters, so the filter keeps names with three characters, such as the single word 'not'. None of the real trigrams survive.

After
trigram_names = [name for name in feature_names if len(name.split()) == 3]

Counts words, so the filter keeps names with exactly three words: 'not very good', 'none of the', 'the only good', 'of the most', and 'on and on'.

not3 characters, passesnot1 word, rejectednot very good13 characters, rejectednot very good3 words, kept
Which feature names pass a length check that counts characters instead of words, and how does that differ from the correct word-count filter?
  • Common mistake

    Measuring length in characters instead of words

    What it looks like

    len(name) == 3 keeps the single word 'not' and drops every real trigram

    Why it fails

    A character count has nothing to do with how many words a name contains, so the filter silently selects the wrong features.

    Do this instead

    Split the name on spaces first and count the pieces: len(name.split()) == 3

  • Common mistake

    Assuming an error will flag a wrong filter

    What it looks like

    The wrong comprehension runs cleanly and prints ['not']

    Why it fails

    The code is valid Python, so the mistake shows up only in the output, and only if you check it.

    Do this instead

    Print the selected names and confirm that each one really has three words before trusting the subset

  • Common mistake

    Believing trigrams replace single words

    What it looks like

    Dropping all unigram features and keeping only trigrams

    Why it fails

    Trigram impact is limited compared with unigram features, and many trigrams are made of common words, so they add context rather than standing alone.

    Do this instead

    Use trigrams alongside single-word features so the model keeps both the words and their local order

  • Common mistake

    Confusing the value of n with the number of features: n sets the length of each sequence, not how many sequences a model keeps

Practice Filtering Trigrams

MEDIUM

Using the feature_names list from the earlier code example, write a list comprehension that keeps only the names with exactly two words (bigrams). Predict which names it will return before you run it, then check your prediction.

Hints
  • Reuse the pattern len(name.split()) == ... and change the number
  • 'good' has one word and 'not very good' has three, so neither belongs in the bigram list
  • The bigrams in the list are 'not good' and 'on and on' is not one of them, because it has three words
EASY

A colleague says their trigram selection is correct because it returned five items. Explain what you would check to confirm that every item is a trigram, and describe the single line you would change if the check were counting characters instead of words.

Hints
  • Print the selected names and count the words in each one
  • The fix is in the condition that compares a length to 3

Key Takeaways

  • An n-gram feature is a sequence of n consecutive items, and a trigram is the case where n is three.
  • Sliding a window of length three across a sentence of w words yields w minus 2 overlapping trigrams, each one capturing a run of neighboring words.
  • Selecting trigrams is a filter on the vectorizer's feature names: keep only the names whose word count is exactly three, measured by splitting on spaces rather than counting characters.
  • Trigrams add local context that single words cannot carry, such as the difference between 'not good' and 'good' alone, but they supplement unigram features rather than replacing them.

Related concepts

More concepts starting with B