Bag-of-Words Features
An n-gram feature is a sequence of n consecutive items, and trigrams are the case where n is three.
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
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.
Counting the trigrams in a five-word sentence
How many trigrams does the sentence 'the food was not good' produce, and what are they?
- 1Count the words
The words are the, food, was, not, good. So $w = 5$.
- 2Apply the formula
The number of overlapping trigrams is $w - 2 = 5 - 2 = 3$.
- 3Slide 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.
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.
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.
Reveal 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.
['not very good', 'none of the', 'the only good', 'of the most', 'on and on']feature_namesis a plain list of strings, standing in for the names a vectorizer would producename.split()breaks each string into its words on spaces, solen(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.
| Feature | What it records | What is lost or gained |
|---|---|---|
| not (unigram) | The single word 'not' appears in the text | Says nothing about which words come next to it |
| good (unigram) | The single word 'good' appears in the text | Cannot tell whether 'good' was negated |
| not very good (trigram) | The run 'not', 'very', 'good' appears in order | Keeps the local order, so the negation sits next to the word it modifies |
| none of the (trigram) | The run 'none', 'of', 'the' appears in order | Captures 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()).
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.
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'.
Common mistake Measuring length in characters instead of words
What it looks likelen(name) == 3 keeps the single word 'not' and drops every real trigram
Why it failsA character count has nothing to do with how many words a name contains, so the filter silently selects the wrong features.
Do this insteadSplit 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 likeThe wrong comprehension runs cleanly and prints ['not']
Why it failsThe code is valid Python, so the mistake shows up only in the output, and only if you check it.
Do this insteadPrint 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 likeDropping all unigram features and keeping only trigrams
Why it failsTrigram 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 insteadUse 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
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
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.