Demystifying TF-IDF: How sklearn‘s TfidfVectorizer Calculates Term Importance

If you‘ve worked with text data before, you‘ve likely come across the concept of TF-IDF. Short for term frequency–inverse document frequency, TF-IDF is a numerical statistic that reflects how important a word is to a document in a collection or corpus of documents. It is often used as a weighting factor in text mining applications like search engines, text summarization, document clustering, and more.

While the concept of TF-IDF seems straightforward on the surface, there are some intricacies and variations in how it can be calculated, especially when using popular machine learning libraries like scikit-learn. In this in-depth guide, we‘ll walk through exactly how sklearn‘s TfidfVectorizer class computes TF-IDF step-by-step. By the end, you‘ll have a crystal clear understanding of what‘s happening under the hood.

A Quick Refresher on TF-IDF

Before we dive into the details of TfidfVectorizer, let‘s make sure we‘re on the same page about what TF-IDF is measuring conceptually. The intuition behind it is:

  • If a word appears frequently in a particular document, it‘s probably important to the meaning and topic of that document (term frequency or TF)
  • But if a word appears in many documents in the corpus, it‘s less unique to any particular document (inverse document frequency or IDF)

So TF-IDF aims to find the words that are common in a document but rare across documents. These are likely to be the most informative and meaningful words.

Mathematically, the TF-IDF value for a word t in a document d from a corpus D is calculated as:

TF-IDF(t,d) = TF(t,d) * IDF(t,D)

Where:

  • TF(t,d) is the term frequency of t in document d
  • IDF(t,D) is the inverse document frequency of t across all documents D

We‘ll look at the exact formulas sklearn uses for TF and IDF shortly. But first, let‘s see an overview of how we can generate a matrix of TF-IDF features using sklearn.

Calculating TF-IDF with TfidfVectorizer

sklearn provides the convenient TfidfVectorizer class to calculate a TF-IDF matrix from a corpus of text documents. Here‘s a simple example:

from sklearn.feature_extraction.text import TfidfVectorizer

corpus = [
    ‘This is the first document.‘,
    ‘This document is the second document.‘,
    ‘And this is the third one.‘,
    ‘Is this the first document?‘,
]

vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(corpus)

print(tfidf_matrix.shape)
(4, 9)

In this example, we:

  1. Import the TfidfVectorizer class
  2. Define our corpus as a list of 4 text documents
  3. Initialize the vectorizer with default parameters
  4. Fit the vectorizer to the corpus and transform the text to a TF-IDF matrix
  5. Print the shape of the resulting matrix

The output tells us that our TF-IDF matrix has 4 rows (one for each document) and 9 columns (one for each unique word). To see what those words are, we can inspect the vectorizer‘s vocabulary:

print(vectorizer.vocabulary_)
{‘this‘: 8, ‘is‘: 3, ‘the‘: 6, ‘first‘: 2, ‘document‘: 1, ‘second‘: 5, ‘and‘: 0, ‘third‘: 7, ‘one‘: 4}

This prints out a dictionary mapping each unique word to its column index in the matrix.

We can also view the actual TF-IDF values:

print(tfidf_matrix.toarray())
[[0.         0.46979139 0.58028582 0.38408524 0.         0.
  0.38408524 0.         0.38408524]
 [0.         0.6876236  0.         0.28108867 0.         0.53864762
  0.28108867 0.         0.28108867]
 [0.51184851 0.         0.         0.26710379 0.51184851 0.
  0.26710379 0.51184851 0.26710379]
 [0.         0.46979139 0.58028582 0.38408524 0.         0.
  0.38408524 0.         0.38408524]]

This is the core of what TfidfVectorizer does – taking in raw text documents and transforming them into a numeric matrix of TF-IDF features. However, there are quite a few details going on behind the scenes here. Let‘s unpack the exact calculations.

Calculating Term Frequency (TF)

The first component of TF-IDF is term frequency, which measures how frequently a word occurs in a document. There are a few different ways this can be calculated. Some common formulas include:

  1. Raw count: TF(t,d) = f(t,d)
    • The number of times term t appears in document d
  2. Boolean frequency: TF(t,d) = 1 if t occurs in d, 0 otherwise
  3. Log normalization: TF(t,d) = 1 + log(f(t,d))
  4. Double normalization: TF(t,d) = 0.5 + 0.5 * (f(t,d) / max(f(w,d)))

By default, sklearn‘s TfidfVectorizer uses the raw count method. However, this can be changed by setting the use_idf parameter to False and using the norm parameter to specify an alternate normalization scheme.

Calculating Inverse Document Frequency (IDF)

The second component, inverse document frequency, measures how common or rare a word is across the entire corpus. The intuition is that words which occur in many documents are less informative than words that occur in few.

Again, there are a few different formulas that can be used:

  1. IDF(t) = log(N / df(t))
    • N is the total number of documents
    • df(t) is the number of documents containing term t
  2. IDF(t) = log((1 + N) / (1 + df(t)))
  3. IDF(t,D) = log((1 + N) / (1 + df(t))) + 1

sklearn uses a smoothed variant of the last formula by default. The smoothing (adding 1 to the numerator and denominator) is used to avoid divide-by-zero errors and the +1 at the end is used to ensure all IDF values are greater than or equal to 1. This behavior can be controlled with the smooth_idf parameter.

Normalizing TF-IDF Vectors

One last detail is that the resulting TF-IDF vectors for each document are usually normalized to be unit vectors (Euclidean norm). This is so that documents of different lengths can be compared more fairly. sklearn does this normalization by default, but it can be disabled by setting norm=None.

Putting it All Together

To summarize, here are the key steps sklearn‘s TfidfVectorizer takes to calculate the TF-IDF matrix:

  1. Build the vocabulary by tokenizing the text and counting unique words
  2. Calculate the term frequency (TF) for each word in each document
  3. Calculate the inverse document frequency (IDF) for each word across all documents
  4. Multiply TF and IDF to get TF-IDF values
  5. Normalize the TF-IDF vectors to unit vectors

By default, it uses raw counts for TF, a smoothed IDF formula, and normalizes vectors to Euclidean unit length. But all of these behaviors can be customized.

Let‘s put our understanding to the test by replicating sklearn‘s calculations manually. We‘ll use the same corpus as before:

corpus = [
    ‘This is the first document.‘,
    ‘This document is the second document.‘,
    ‘And this is the third one.‘,
    ‘Is this the first document?‘,
]

Step 1: Vocabulary and word-counts

import collections

def build_vocabulary(corpus):
    vocabulary = collections.Counter()
    for doc in corpus:
        vocabulary.update(doc.lower().split())
    return vocabulary

vocabulary = build_vocabulary(corpus)
print(vocabulary)
Counter({‘document‘: 4, ‘this‘: 4, ‘is‘: 3, ‘the‘: 3, ‘first‘: 2, ‘and‘: 1, ‘second‘: 1, ‘third‘: 1, ‘one‘: 1})

Step 2: Term frequencies

tf_matrix = []
for doc in corpus:
    tf_counter = collections.Counter(doc.lower().split())
    tf_matrix.append([tf_counter[word] for word in vocabulary])

print(tf_matrix)
[[1, 1, 1, 1, 0, 0, 1, 0, 1], 
 [1, 2, 0, 1, 0, 1, 1, 0, 1], 
 [1, 0, 0, 1, 1, 0, 1, 1, 1], 
 [1, 1, 1, 1, 0, 0, 1, 0, 1]]

Step 3: Inverse document frequencies

import math

def calculate_idf(corpus, unique_words):
    idf_dict = {}
    N = len(corpus)
    for word in unique_words:
        count = sum(1 for doc in corpus if word in doc.lower().split())
        idf_dict[word] = math.log((N+1) / (count+1)) + 1
    return idf_dict

idf_dict = calculate_idf(corpus, vocabulary)
print(idf_dict)
{‘this‘: 1.2231435513142097, ‘is‘: 1.5108256237659907, ‘the‘: 1.5108256237659907, ‘first‘: 1.916290731874155, ‘document‘: 1.2231435513142097, ‘and‘: 2.2174839442139063, ‘second‘: 2.2174839442139063, ‘third‘: 2.2174839442139063, ‘one‘: 2.2174839442139063}

Step 4: TF-IDF scores

tfidf_matrix = []
for tf_vector in tf_matrix:
    tfidf_vector = [a*b for a,b in zip(tf_vector, idf_dict.values())]
    tfidf_matrix.append(tfidf_vector)

print(tfidf_matrix)    
[[1.2231435513142097, 1.2231435513142097, 1.916290731874155, 1.5108256237659907, 0.0, 0.0, 1.5108256237659907, 0.0, 1.2231435513142097], 
 [1.2231435513142097, 2.4462871026284194, 0.0, 1.5108256237659907, 0.0, 2.2174839442139063, 1.5108256237659907, 0.0, 1.2231435513142097], 
 [1.2231435513142097, 0.0, 0.0, 1.5108256237659907, 2.2174839442139063, 0.0, 1.5108256237659907, 2.2174839442139063, 1.2231435513142097], 
 [1.2231435513142097, 1.2231435513142097, 1.916290731874155, 1.5108256237659907, 0.0, 0.0, 1.5108256237659907, 0.0, 1.2231435513142097]]

Step 5: Normalized vectors

import numpy as np

def l2_normalize(tfidf_matrix):
    norms = np.linalg.norm(tfidf_matrix, axis=1, keepdims=True)
    return tfidf_matrix / norms

normalized_tfidf = l2_normalize(np.array(tfidf_matrix))
print(normalized_tfidf)
[[0.35593673 0.35593673 0.55775063 0.43980119 0.         0.
  0.43980119 0.         0.35593673]
 [0.30300549 0.60601099 0.         0.37419887 0.         0.5498576
  0.37419887 0.         0.30300549]
 [0.29206679 0.         0.         0.36082631 0.52987422 0.
  0.36082631 0.52987422 0.29206679]
 [0.35593673 0.35593673 0.55775063 0.43980119 0.         0.
  0.43980119 0.         0.35593673]]

And there you have it! Our manual calculations match the output of sklearn‘s TfidfVectorizer. Hopefully walking through this example step-by-step has clarified exactly how the TF-IDF values are derived.

When to Use TF-IDF

TF-IDF is used extensively in information retrieval and text mining. Some common applications include:

  • Search engines: TF-IDF can be used to rank a set of documents based on how relevant they are to a user‘s search query. Documents with high TF-IDF scores for the query words would be considered more relevant.

  • Text summarization: When generating summaries, TF-IDF can help identify the most important sentences in a document by finding the sentences that contain words with high TF-IDF scores.

  • Document clustering and similarity: TF-IDF vectors can be used to represent documents in a high-dimensional space. This allows for measuring document similarity (e.g. with cosine similarity) and clustering similar documents together.

  • Keyword extraction: Words with high TF-IDF scores in a document are often the most characteristic and meaningful words, and thus make good candidates for keywords or tags.

However, TF-IDF also has some limitations. It doesn‘t capture the meaning or semantic relationships between words (e.g. synonyms would be treated as totally different). It also struggles with very common words that appear in almost every document (which is why stop word removal is often used in conjunction with TF-IDF).

Conclusion

In this guide, we took a deep dive into how sklearn‘s TfidfVectorizer converts a corpus of raw text documents into a matrix of TF-IDF features. We explored the intuition behind TF-IDF, the exact formulas used to calculate term frequencies and inverse document frequencies, and how the vectors are normalized.

By replicating the calculations manually in Python, we saw that despite the many steps involved, the core concept of TF-IDF is quite understandable. With a solid grasp of how it works, you‘re now well-equipped to utilize this foundational NLP technique in your own projects with sklearn.

The complete code used in this article is available as a Jupyter notebook on GitHub. Feel free to use it as a starting point for further explorations, such as comparing the effect of different normalization schemes or smoothing parameters. Happy coding!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Similar Posts