Applying Bag-of-Words to Text Data
CountVectorizer from scikit-learn converts a collection of text documents into a matrix of token counts, which the source calls a bag-of-words representation.
Why Text Must Become Numbers
Machine learning algorithms work with numbers, not raw sentences. A sentence such as a line of poetry cannot be fed directly into a learning algorithm, so something has to translate it into a numeric form first. This article follows one practical bridge between the two: the CountVectorizer tool from the scikit-learn library. It converts a collection of text documents into a matrix of token counts, and the source calls that matrix a bag-of-words representation. Each document becomes a row of counts, so the text can be processed numerically.
The method here is to run the code rather than read about it first. Before you look at the final printed line of each program, try to say in your own words what each line is responsible for. Then check your guess against what the line actually does.
Tracing the Four-Line Workflow
The program works on a variable named bards_words, which holds the collection of text documents used in the source. The source imports CountVectorizer from sklearn.feature_extraction.text, creates an instance named vect, fits it to the documents, and then transforms the same documents into a bag-of-words result. The four lines below are the whole workflow, so each one is worth naming as you trace it.
- Line 1 imports the class that does the counting
- Line 2 creates an unfitted vectorizer named vect; it has learned nothing yet
- Line 3 fit examines the documents and learns which words the vectorizer will count
- Line 4 transform uses what was learned to turn the documents into the count matrix, which is stored in
bag_of_words
- 1Line 1: bring CountVectorizer into the program. Nothing is counted yet.
- 2Line 2: create vect. The object exists, but it has no vocabulary.
- 3Line 3: call fit on
bards_words. The vectorizer reads the documents and records the words it will count. - 4Line 4: call transform on
bards_words. The vectorizer uses its recorded vocabulary to produce the bag-of-words result and assigns it tobag_of_words. - 5Displaying
bag_of_wordsshows its repr, a one-line description of the matrix.
Fit Learns, Transform Produces
The program has two distinct stages. The fit call examines the documents so the vectorizer knows the words it will count. The transform call then uses that knowledge to turn the documents into the matrix of counts. The fit call is not the call that produces the result. It only prepares the vectorizer. Keeping the two stages separate in your head makes the program much easier to debug, because a problem usually belongs to one stage or the other.
Notice that transform receives the documents and the vocabulary learned by fit. This explains where problems tend to hide. If the vocabulary was learned from the wrong documents, the columns are wrong. If the counts were produced from the wrong documents, the rows are wrong. Those are two different questions, and asking them separately narrows the search.
Reading the Shape of the Result
The repr of the bag-of-words result reports a sparse matrix with shape $2 \times 13$. Read this as two rows and thirteen columns. The two rows correspond to the two documents in the collection. The thirteen columns correspond to the thirteen distinct words the vectorizer found across the whole corpus. Each column is a word, and each row is a document, so a single cell answers the question: how many times does this word appear in this document?
The source describes this result as sparse. For typical text, most cells are zero, because any one document uses only a small fraction of all the words in the corpus. A sparse object therefore stores only its nonzero entries rather than every cell. The repr line signals this storage choice, but the learner's mental model should still be the full grid of documents by terms.
Predicting the Shape Before Running
What do you think happens?
You pass three sentences to the same four-line workflow. Before running it, what should the shape of the result be?
Reveal answer
Three rows, one per sentence, and as many columns as there are distinct words across all three sentences.
Each document becomes a row, so the row count equals the number of sentences supplied. The column count reflects distinct words across the collection, not the total number of words.
From Sentence to Columns
Before any counting happens, each document must be split into tokens, the individual words that the vectorizer will count. The vectorizer's recorded vocabulary is simply the set of distinct tokens found across the collection, and each distinct token becomes one column. This is why the column count depends on the whole corpus and not on any single document.
Counting a two-sentence collection
Two sentences: Sentence 1 is "The cat sat on the mat" and Sentence 2 is "The dog sat on the log". How many rows and columns does the bag-of-words result have, and what is the count of the word the in each row?
- 1Split into tokens
Sentence 1 yields the, cat, sat, on, the, mat. Sentence 2 yields the, dog, sat, on, the, log.
- 2Collect distinct words
Across both sentences the distinct words are cat, dog, log, mat, on, sat, and the. That is seven columns.
- 3Count rows
There are two documents, so there are two rows.
- 4Read the count for the
The word the appears twice in each sentence, so its column holds 2 in row 1 and 2 in row 2.
The shape is 2 by 7. Rows are the two sentences, columns are the seven distinct words, and the cell for the in each row is 2.
Finding Where Results Diverge
Suppose you run the same program and the printed line reports a different shape from the one you predicted. The source gives you enough structure to find the failure point without guessing. Start at the top of the program and ask, at each line, what that line was supposed to produce. Then compare that expectation with what the next line receives. The first line where the expectation and the reality disagree is the place to look.
- 1Line 1, the import: expected CountVectorizer to be available. If this fails, nothing downstream can run.
- 2Line 2, creating vect: expected an unfitted vectorizer with no vocabulary. It holds no data yet, so it cannot be the cause of a wrong shape.
- 3Line 3, fit: expected the vocabulary to come from the intended documents. If the columns look wrong, check which collection was passed here.
- 4Line 4, transform: expected one row per document, using the vocabulary learned at line 3. If the rows look wrong, check which collection was passed here and whether it matches the one used for fit.
Common mistake Treating fit as the step that produces the result
What it looks likeAssuming the bag-of-words matrix exists after the fit line runs.
Why it failsFit only examines the documents and learns the vocabulary. The matrix is produced by transform.
Do this insteadLook for the transform call when you want the count matrix.
Common mistake Reading the repr as a list of the counts
What it looks likeExpecting the printed line to show each word and how often it appears.
Why it failsThe repr reports the sparse matrix's shape, $2 \times 13$, and does not show the individual counts.
Do this insteadUse the shape to check size, and inspect the matrix's contents separately to see the counts.
Common mistake Guessing at the cause of a wrong shape
What it looks likeChanging random lines until the printed shape looks right.
Why it failsA wrong shape has a specific origin, and guessing hides it.
Do this insteadWalk the program top to bottom and find the first line whose output departs from your expectation, then check the input passed to that line.
Common mistake Assuming columns count total words
What it looks likeExpecting a column for every occurrence of a word.
Why it failsColumns correspond to distinct words across the corpus, so repeated words share one column.
Do this insteadCount the distinct words, not the total word count, when predicting the column number.
Checking Your Reading on a New Collection
Build a short list of two or three sentences of your own and pass it to the same four-line workflow. Before running it, predict the number of rows and the number of columns. Then compare your prediction with the printed shape. If they disagree, go back to the divergence check and find the line responsible.
Hints
- The number of rows should equal the number of sentences you supplied.
- Count distinct words across all sentences together. Repeated words share a single column.
- If your prediction is off by one column, check whether a word appears in only one sentence or is repeated across them.
Key Takeaways
- CountVectorizer converts a collection of text documents into a matrix of token counts, called a bag-of-words representation, with one row per document.
- fit learns the vocabulary from the documents, and transform produces the count matrix; a problem usually belongs to one of these two stages.
- The repr of the result reports a sparse matrix of shape $2 \times 13$: two rows for documents and thirteen columns for distinct corpus words, without showing the counts.
- Columns depend on distinct words across the whole collection, and word order is not kept in the counts.
- When a result is unexpected, walk the program top to bottom and find the first line where expectation and output diverge, then check its input.