MODELCATALOG

Text and dicts into matrices.

sklearn.feature_extraction

feature_extraction

Text and dicts into matrices.

1 Get data Something to practise on
2 Split & tune An honest test set
3 Prepare Into numbers a model accepts
4 Compose One object, no leakage
5 Model The algorithm itself
6 Unsupervised No labels available
7 Measure Is it any good, and why
8 Utilities Glue and extension

What it is for

Turning things that are not already numbers into numbers. The `.text` submodule is the one you will use — it is what makes the TF-IDF baseline possible, and it remains competitive on narrow, jargon-heavy text where a transformer's contextual understanding has nothing to work with.

sklearn.feature_extraction.text

TfidfVectorizer common Counts weighted down by how common a term is across documents. The default for text
CountVectorizer Raw counts. Pairs well with MultinomialNB
TfidfTransformer Applies the weighting to counts you already have
HashingVectorizer Fixed-size output, no stored vocabulary. Streaming or huge corpora. Not invertible

Other

DictVectorizer A list of dicts into a matrix
FeatureHasher The hashing trick on arbitrary features

Worked examples 3

Runnable, with the arguments that change the outcome called out. Output shapes are shown as comments where they help.

sklearn.feature_extraction

from sklearn.feature_extraction.text import TfidfVectorizer

vec = TfidfVectorizer(
    ngram_range=(1, 2),    # unigrams + bigrams; catches "not fraudulent"
    min_df=3,              # drop terms in fewer than 3 docs (noise)
    max_df=0.9,            # drop terms in >90% of docs (uninformative)
    sublinear_tf=True,     # log-scale counts; usually helps
    strip_accents="unicode",
    stop_words=None,       # careful: "not" and "no" are stop words
)
X = vec.fit_transform(train_texts)      # sparse matrix
print(X.shape, vec.get_feature_names_out()[:10])

TF-IDF, and what the arguments actually do

Four arguments change results more than the choice of classifier downstream.

from sklearn.feature_extraction.text import TfidfVectorizer

docs = ["I was charged an overdraft fee incorrectly",
        "This was not a fraudulent transaction",
        "My card was declined at the supermarket",
        "Charged twice for the same overdraft"]

vec = TfidfVectorizer(
    ngram_range=(1, 2),      # THE important one: bigrams let a bag of
                             # words see "not fraudulent" as one feature
    min_df=1,                # raise to 3-5 on real data to drop noise
    max_df=0.9,              # drop terms in >90% of docs
    sublinear_tf=True,       # log-scale counts
    strip_accents="unicode",
    lowercase=True,
)

X = vec.fit_transform(docs)
print(X.shape)                            # (4, 42) - sparse
print([t for t in vec.get_feature_names_out() if " " in t][:6])
# ['an overdraft', 'charged an', 'charged twice', 'fee incorrectly', ...]

# Inspect what a single document became
import numpy as np
row = X[1].toarray().ravel()
terms = vec.get_feature_names_out()
for i in np.argsort(row)[-5:][::-1]:
    print(f"  {terms[i]:22} {row[i]:.3f}")

Character n-grams for messy short text

Payment narratives, merchant names and SWIFT fields are abbreviated and inconsistently spelled. Character n-grams survive that where word tokens do not.

from sklearn.feature_extraction.text import TfidfVectorizer

names = ["NORTHWOOD CAP LTD", "Northwood Capital Limited",
         "NRTHWD CAPITAL", "Southwood Capital Ltd"]

# Word-level: "NRTHWD" shares no tokens with "Northwood"
word = TfidfVectorizer(analyzer="word")

# Character-level: they share many 3-grams, so similarity survives
char = TfidfVectorizer(analyzer="char_wb", ngram_range=(3, 5))

from sklearn.metrics.pairwise import cosine_similarity
for name, v in [("word", word), ("char", char)]:
    M = v.fit_transform(names)
    print(name, cosine_similarity(M[0:1], M).round(2))
# word [[1.   0.   0.   0.35]]      <- misses the abbreviation entirely
# char [[1.   0.62 0.41 0.55]]      <- catches it

# analyzer="char_wb" keeps n-grams inside word boundaries, which works
# better than plain "char" for name matching.

Worth knowing

  1. ngram_range=(1,2) is the single most valuable argument here — it is what lets a bag of words see negation.

  2. Removing stop words deletes 'not' and 'no'. On complaint text that frequently inverts the meaning.