Abstractions csrmatrix normalizerows - CyrilB1531/lodestar GitHub Wiki

Home › Abstractions › The shared sparse primitive

CsrMatrix.NormalizeRows

Divide every row by its own norm, in place.


public void NormalizeRows(SparseNorm norm)

Parameters — norm is which norm to divide by: SparseNorm.L1 for a row that sums to 1, or SparseNorm.L2 for a row of unit Euclidean length.

Returns — nothing. The matrix is modified in place, which is what makes it cheap and what makes it a trap: a caller holding another reference to the same matrix sees the change.

Exceptions — ArgumentOutOfRangeException when norm is not a defined SparseNorm, as sklearn.preprocessing.normalize refuses an unknown norm. InvalidOperationException when a stored value is NaN or infinite, which scikit-learn's input check refuses too. Either way the matrix is left untouched.

Example — after normalizing, every row's norm is 1.

using Lodestar.Abstractions;
using Lodestar.Text.Vectorization;

string[] docs = ["the cat eats", "the dog eats", "the cat and the dog"];

CsrMatrix unit = new CountVectorizer().FitTransform(docs);
unit.NormalizeRows(SparseNorm.L2);
double length = unit.RowL2Norm(0);  // => 1

CsrMatrix shares = new CountVectorizer().FitTransform(docs);
shares.NormalizeRows(SparseNorm.L1);
double mass = shares.RowL1Norm(2);  // => 1

Remarks — in place rather than returning a copy, because the matrix is the large object in this namespace and copying it to change every value would double the memory for no benefit. Where a copy is wanted, transform the corpus again — vectorizing is cheaper than it looks, and the alternative is a copy nobody asked for on the common path.

A row that is entirely zero has no norm to divide by and is left alone rather than producing NaN. That is scikit-learn's choice too, and it is the reason an empty document does not poison a matrix.

A row of finite values whose sum of squares overflows to +∞ — [1e200], whose true norm is finite — is divided to zeros under SparseNorm.L2, as scikit-learn's inplace_csr_row_normalize_l2 divides it.

TfidfVectorizer already normalizes, by TfidfOptions.Norm; calling this on its output with the same norm changes nothing beyond rounding, and with the other norm rescales every row.

Applies to — net10.0, netstandard2.0.

See also — SparseNorm, CsrMatrix.RowL2Norm, TfidfOptions.