-
Notifications
You must be signed in to change notification settings - Fork 0
Text vectorizers
Development build. This page describes
main, not a released package. The latest published Lodestar.Text is 0.4.0 — read its documentation.
You have a corpus of documents and you need numbers. Lodestar.Text.Vectorization
reproduces sklearn.feature_extraction.text: it turns text into a sparse matrix
of features, one row per document and one column per term.
Three vectorizers do that, and what separates them is what they need to know about the corpus first.
flowchart TD
A["What do you need?"] --> B{"Do rare words<br/>deserve more weight<br/>than common ones?"}
B -->|"yes — this is search,<br/>or a classifier"| C["TfidfVectorizer"]
B -->|"no, raw occurrence counts"| D{"Can you afford to hold<br/>the vocabulary in memory?"}
D -->|yes| E["CountVectorizer"]
D -->|"no — the corpus is a stream,<br/>or the vocabulary is unbounded"| F["HashingVectorizer"]
A --> G{"Already have counts<br/>from somewhere else?"}
G -->|yes| H["TfidfTransformer"]
CountVectorizer counts. It learns the
vocabulary from the corpus during Fit, so column 7 means the same term in every row
and GetFeatureNames can tell you which.
TfidfVectorizer counts and then weights each
count by how rare the term is across the corpus. It is
CountVectorizer followed by
TfidfTransformer, and doing it in one step is
the only difference.
HashingVectorizer does not learn anything.
It hashes each term into one of a fixed number of columns, so it needs no Fit, holds
no vocabulary, and works on a stream — at the price that two terms can collide in one
column and no GetFeatureNames exists, because there is no vocabulary to name.
That trade is the whole reason to choose it.
Fit is where a vocabulary is learned, and it is why the order of calls matters:
| Call | What it does |
|---|---|
Fit(corpus) |
Learn the vocabulary and, for TF-IDF, the document frequencies. |
Transform(corpus) |
Use what was learned. Refuses if nothing was. |
FitTransform(corpus) |
Both, on the same corpus — and not the same as Fit then Transform on different ones. |
Transforming a document that holds a term the fit never saw drops that term silently: it has no column. That is scikit-learn's behaviour, and it is why fitting on the training corpus and transforming the test one is the correct order rather than a convenience.
HashingVectorizer has no Fit at all, and its
Transform and
FitTransform do the same thing —
the second exists so the three vectorizers can be swapped for one another.
Every one of them returns a CsrMatrix: compressed
sparse row, the same layout scipy.sparse.csr_matrix uses. A corpus of ten thousand
documents over fifty thousand terms is almost entirely zeros, and storing those zeros
is what this layout exists to avoid.
Read ToDense only when you mean it: it
allocates RowCount × ColumnCount doubles, which is exactly the array the sparse
layout was avoiding.
Most of what makes these match scikit-learn lives in the three options records rather
than in the vectorizers:
CountVectorizerOptions,
TfidfOptions and
HashingVectorizerOptions. Their defaults
are scikit-learn's defaults, and each property's page entry says which Python keyword
it answers to.
Two defaults surprise people, and both are scikit-learn's:
- the token pattern is
\b\w\w+\b, so single-letter words are dropped — "a" and "I" never become features; -
Lowercaseis on, soAppleandappleare one term.
| Type | What it is |
|---|---|
AnalyzerKind |
Whether features are words or character n-grams. |
CountVectorizer |
Term counts, over a vocabulary learned from the corpus. |
CountVectorizerOptions |
Everything that decides what counts as a term. |
CsrMatrix |
The compressed-sparse-row matrix every vectorizer returns. |
HashingVectorizer |
Counts into a fixed number of columns, learning nothing. |
HashingVectorizerOptions |
The column count, and what the hashing does with signs. |
SparseNorm |
Which norm CsrMatrix.NormalizeRows divides each row by. |
StopWords |
The six built-in stop-word lists. |
TfidfOptions |
The four switches that decide how the weighting is computed. |
TfidfTransformer |
Counts in, TF-IDF weights out. |
TfidfVectorizer |
CountVectorizer and TfidfTransformer in one pass. |
TfidfVectorizerOptions |
The two halves above, as one options object. |
- From string to vector — the guide, which walks a corpus through all three rather than describing them one at a time.
-
Python → C# equivalence — every
sklearn.feature_extraction.textcall and its counterpart here.
- 0001-target-framework
- 0002-unicode-comparison-unit
- 0003-provenance-and-licensing
- 0004-levenshtein-myers-backlog
- 0005-hamming-jellyfish-divergence
- 0006-ratcliff-autojunk
- 0007-metaphone-scope
- 0008-italian-enza-nltk-divergence
- 0009-sample-consumes-a-local-feed
- 0010-stop-word-list-provenance
- 0011-persistence-format
- 0012-per-package-versioning
- 0013-sentencepiece-parity-scope
- 0014-precompiled-normalizer
- 0015-sonar-rules-in-the-build
- 0016-metrics-package-placement
- 0017-bpe-parity-scope
- 0018-multiclass-roc-auc-parallelism-is-opt-in
- 0019-the-net-analysers-run-in-the-build-too
- 0020-normalize-is-a-projection-not-a-parameter
- 0021-multioutput-is-a-method-not-an-enum
- 0022-added-token-matching-flags
- 0023-byte-level-decode-substitutes
- 0024-weighted-median-averages-within-scikit-learns-epsilon
- 0025-quickselect-replaces-a-full-sort-for-the-median
- 0026-r2-and-explainedvariance-split-their-undefined-cases-differently
- 0027-r2-and-explainedvariance-vectorize-only-a-single-output
- 0028-log1p-is-kahans-identity-not-math-log-1-plus-x
- 0029-balanced-accuracy-adjusted-is-left-to-ieee-754-at-the-edge
- 0030-cohen-kappa-keeps-scikit-learns-expected-matrix-orientation
- 0031-nosamplecorrect-mirrors-numpys-float64-upcast
- 0032-fbeta-substitutes-tp-predicted-and-support-algebraically
- 0033-compensated-sum-is-neumaiers-variant
- 0034-dropout-is-refused-for-want-of-a-user
- 0035-a-null-pre-split-is-removed-with-invert-not-isolated
- 0036-a-member-may-ship-without-an-oracle-if-it-says-so
- 0037-the-guards-run-before-the-commit
- 0038-the-gate-confronts-an-exception-tag-with-the-page-that-documents-it
- 0039-mutual-information-returns-zero-on-an-empty-input
- 0040-a-curve-is-a-sealed-class-per-curve
- 0041-one-sample-file-per-public-class
- 0042-phonetic-encoders-refuse-a-null-word
- 0043-the-equality-table-is-sized-to-the-pattern
- 0044-compression-belongs-to-the-caller
- 0045-a-console-call-carries-its-reason-on-the-line
- 0046-check-adr-immutable-runs-in-ci-only
- 0047-one-gate-per-kernel-not-one-per-alphabet
- 0048-the-gate-depends-on-the-kernel-and-the-alphabet
- 0049-two-gates-per-kernel-tested-where-the-width-is-known
- 0050-the-sentencepiece-bpe-lineage-stays-a-bpe-model
- benchmark_latest
- decisions
- equivalence
- matplotlib
- migration
- nightly_run
- numpy
- pandas
- performance
- pytorch
- seaborn
- sklearn
- statsmodels