Thanks to visit codestin.com
Credit goes to github.com

Skip to content

PolynomialCountSketch.transform is much slower on sparse input than on the equivalent dense array #34920

Description

@regarmukesh3g

Warning

This issue is not yet ready for a PR. If you are interested in contributing to scikit-learn, please have a look at our contributing guidelines, and in particular the sections for new contributors and the "Needs triage" label.

Introduce yourself

I use scikit-learn for general ML work. I found this while looking for a
small contribution to make: the open issue backlog was heavily claimed
already, so I read through some less-trafficked modules directly and found
this in kernel_approximation.py.

Describe the bug and give evidence about its user-facing impact

PolynomialCountSketch.transform's sparse-input branch loops over every
feature and every degree, slicing a single sparse column and densifying it
with .toarray() on each iteration:

for j in range(X_gamma.shape[1]):
    for d in range(self.degree):
        iHashIndex = self.indexHash_[d, j]
        iHashBit = self.bitHash_[d, j]
        count_sketches[:, d, iHashIndex] += (
            (iHashBit * X_gamma[:, [j]]).toarray().ravel()
        )

This means the cost scales with n_features * degree, not with the number of
actual nonzeros, so a sparse matrix with a large number of columns but very
few nonzeros per column is transformed much slower than an equivalent dense
array of the same shape — defeating the purpose of the sparse code path. This
affects anyone using PolynomialCountSketch on sparse, wide, high-dimensional
data (e.g. bag-of-words or one-hot encoded features), where sparse support is
the main reason to reach for this transformer in the first place.

Steps/Code to Reproduce

import time
import numpy as np
import scipy.sparse as sp
from sklearn.kernel_approximation import PolynomialCountSketch

n_samples, n_components = 2000, 300
for n_features in [1000, 4000, 8000]:
    X_sparse = sp.random(n_samples, n_features, density=0.01, format="csc", random_state=0)
    X_dense = X_sparse.toarray()

    ps_sparse = PolynomialCountSketch(degree=2, n_components=n_components, random_state=0).fit(X_sparse)
    t0 = time.time()
    ps_sparse.transform(X_sparse)
    t1 = time.time()

    ps_dense = PolynomialCountSketch(degree=2, n_components=n_components, random_state=0).fit(X_dense)
    t2 = time.time()
    ps_dense.transform(X_dense)
    t3 = time.time()

    print(f"n_features={n_features} nnz={X_sparse.nnz} sparse={t1 - t0:.4f}s dense={t3 - t2:.4f}s")

Expected Results

Transforming genuinely sparse input (1% density here) should be at least as
fast as transforming the equivalent densified array, since scipy sparse
operations scale with the number of nonzeros rather than the matrix shape.

Actual Results

n_features=1000 nnz=20000  sparse=0.0970s dense=0.0151s
n_features=4000 nnz=80000  sparse=0.3228s dense=0.0380s
n_features=8000 nnz=160000 sparse=0.6310s dense=0.0690s

The sparse path is roughly 9-10x slower than the dense path on the same data
at every size tested, and the gap grows with n_features since the loop
count (not the nonzero count) drives the cost.

Versions

System:
    python: 3.14.6 (v3.14.6:c63aec69bd5, Jun 10 2026, 08:07:54) [Clang 21.0.0 (clang-2100.1.1.101)]
executable: /Library/Frameworks/Python.framework/Versions/3.14/bin/python3
   machine: macOS-26.5.2-arm64-arm-64bit-Mach-O

Python dependencies:
      sklearn: 1.10.dev0
          pip: 26.1.2
   setuptools: 83.0.0
        numpy: 2.5.1
        scipy: 1.18.1
       Cython: 3.3.0
       pandas: None
   matplotlib: None
       joblib: 1.6.0
threadpoolctl: 3.6.0
     narwhals: 2.25.0

Built with OpenMP: True

I'd like to open a PR to fix this — I already have a working, tested fix
(replacing the per-column densification with one sparse matrix
multiplication per degree) and will link the PR here once triage clears.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions