Skip to main content

profile-correspondence-encoder

profile-correspondence-encoder provides a scikit-learn compatible PCEEncoder for high-cardinality categorical data.

It is inspired by MCA/correspondence analysis, but fits on a sparse category co-occurrence graph and outputs dense latent vectors.

Install

pip install profile-correspondence-encoder

Why Use It

  • Unsupervised (no target leakage when fit on train only)
  • pandas-friendly
  • No one-hot explosion in output dimensionality
  • Scalable sparse fit over category graph
  • Canonical normalization + optional aliases
  • Frequency-based shrinkage for rare categories
  • Streaming/chunked fit and customizable graph construction

Quick Start

import pandas as pd
from profile_correspondence_encoder import PCEEncoder

df = pd.DataFrame({
    "city": ["New York", "NY", "new-york", "Los Angeles", "LA", None],
    "state": ["New York", "New York", "New York", "California", "California", None],
    "segment": ["A", "A", "A", "B", "B", "B"],
})

encoder = PCEEncoder(
    columns=["city", "state", "segment"],
    n_components=3,
    min_frequency=2,
    aliases={"city": {"ny": "new york", "la": "los angeles"}},
    output_format="pandas",
)

X_enc = encoder.fit_transform(df)
print(X_enc.head())
print(encoder.get_metadata().head())
print(encoder.fit_stats_)

Full Parameter Reference

PCEEncoder signature:

PCEEncoder(
    columns=None,
    n_components=4,
    min_frequency=5,
    normalize_text=True,
    lowercase=True,
    strip_accents=True,
    separator=" ",
    rare_token="__RARE__",
    unknown_token="__UNK__",
    missing_token="__MISSING__",
    aliases=None,
    dtype=np.float32,
    svd_n_iter=7,
    random_state=42,
    output_format="numpy",
    enable_rare_shrinkage=True,
    shrinkage_lambda=5.0,
    pair_count_method="lexsort",
    chunk_size=None,
    max_categories_per_column=None,
    pairing_strategy="all",
    anchor_columns=None,
    pair_sample_ratio=1.0,
    n_jobs=1,
    edge_weighting="count",
    svd_algorithm="randomized",
)

Core Representation

  • columns: list[str] | None Columns to encode. If None, uses all input columns.
  • n_components: int (default 4) Latent dimensions per categorical column.
  • min_frequency: int (default 5) Minimum count for a category to keep a dedicated code.

Text Canonicalization

  • normalize_text: bool (default True) Enables text normalization pipeline.
  • lowercase: bool (default True) Lowercases category strings.
  • strip_accents: bool (default True) Removes accents/diacritics.
  • separator: str (default " ") Separator used after punctuation/whitespace normalization.
  • aliases: dict[str, dict[str, str]] | None Manual synonym mapping per column after normalization.

Special Tokens

  • rare_token: str (default "__RARE__")
  • unknown_token: str (default "__UNK__")
  • missing_token: str (default "__MISSING__")

Rare-Category Shrinkage

  • enable_rare_shrinkage: bool (default True) Applies smooth interpolation for seen-rare categories.
  • shrinkage_lambda: float (default 5.0) Controls shrinkage strength with alpha = n / (n + lambda).

Graph Construction Performance

  • pair_count_method: {"lexsort", "unique"} (default "lexsort") Integer pair-count implementation. lexsort is usually faster.
  • chunk_size: int | None (default None) If set, fit is processed in row chunks (streaming-like behavior).
  • max_categories_per_column: int | None (default None) Optional top-K cap (after min_frequency) per column.
  • pairing_strategy: {"all", "anchor", "sample"} (default "all") How column pairs are connected in the graph.
  • anchor_columns: list[str] | None Required when pairing_strategy="anchor".
  • pair_sample_ratio: float in (0, 1] (default 1.0) Used when pairing_strategy="sample".
  • n_jobs: int (default 1) Parallelism for per-pair counting.

Edge Weighting / Decomposition

  • edge_weighting: {"count", "pmi", "ppmi"} (default "count") Edge transformation before spectral embedding.
  • svd_algorithm: {"randomized", "arpack"} (default "randomized") Truncated SVD backend.
  • svd_n_iter: int (default 7) Power iterations for SVD.
  • random_state: int | None (default 42) Seed for reproducibility.

Output

  • output_format: {"numpy", "pandas"} (default "numpy") Output type for transform.
  • dtype: numpy dtype (default np.float32) Embedding/output numeric dtype.

Customization Recipes

Baseline (Recommended)

enc = PCEEncoder(
    columns=cat_cols,
    n_components=4,
    min_frequency=10,
    output_format="numpy",
)

Large-Scale / Memory-Aware

enc = PCEEncoder(
    columns=cat_cols,
    n_components=6,
    min_frequency=20,
    chunk_size=250_000,
    max_categories_per_column=100_000,
    pair_count_method="lexsort",
    n_jobs=4,
    output_format="numpy",
)

Very Wide Tables (Reduce Pair Explosion)

enc = PCEEncoder(
    columns=cat_cols,
    pairing_strategy="anchor",
    anchor_columns=["country", "segment", "device_type"],
    n_components=4,
)

Faster Approximate Pair Graph

enc = PCEEncoder(
    columns=cat_cols,
    pairing_strategy="sample",
    pair_sample_ratio=0.35,
    n_components=4,
    random_state=42,
)

Quality-Oriented Weighting

enc = PCEEncoder(
    columns=cat_cols,
    edge_weighting="ppmi",
    n_components=8,
    svd_n_iter=10,
)

Rare Handling Control

enc = PCEEncoder(
    columns=cat_cols,
    min_frequency=15,
    enable_rare_shrinkage=True,
    shrinkage_lambda=8.0,
)

Scikit-learn Pipeline Example (Titanic)

from sklearn.datasets import fetch_openml
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

from profile_correspondence_encoder import PCEEncoder

# Public Titanic dataset via OpenML
data = fetch_openml(name="titanic", version=1, as_frame=True)
X = data.data[["sex", "embarked", "pclass"]].copy()
y = (data.target == "1").astype(int)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y
)

pipe = Pipeline(
    steps=[
        (
            "pce",
            PCEEncoder(
                columns=["sex", "embarked", "pclass"],
                n_components=4,
                min_frequency=10,
                output_format="numpy",
            ),
        ),
        ("clf", LogisticRegression(max_iter=2000)),
    ]
)

pipe.fit(X_train, y_train)
proba = pipe.predict_proba(X_test)[:, 1]
print("ROC-AUC:", roc_auc_score(y_test, proba))

Useful Methods and Attributes

After fitting:

  • transform(X): transform new data
  • get_feature_names_out(): output feature names
  • get_metadata(): raw/canonical/count/code metadata
  • get_column_embedding(column): embedding table for one column
  • fit_stats_: fit timing metrics (for monitoring regressions)

Development

pip install -e .[dev]
pytest
python -m build

Metadata

Release files for profile-correspondence-encoder 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for profile-correspondence-encoder 0.2.0
File Size Uploaded Provenance
profile_correspondence_encoder-0.2.0.tar.gz 12.4 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for profile-correspondence-encoder 0.2.0
File Interpreter ABI Platform
profile_correspondence_encoder-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.3 kB

Release files / profile_correspondence_encoder-0.2.0.tar.gz

Download URL profile_correspondence_encoder-0.2.0.tar.gz
Size 12.4 kB
Tags Source
SHA-256 checksum
How to use checksums
d958356edd015d04c823af4953ad99c153a457b7e59d44782bf45607fe2a30ba
BLAKE2b-256 checksum
How to use checksums
0759ad18758fb162dd2bdba492ac24390676adf6bcaf519ee28176c6083ce8fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.11

Release files / profile_correspondence_encoder-0.2.0-py3-none-any.whl

Download URL profile_correspondence_encoder-0.2.0-py3-none-any.whl
Size 9.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
990518a80fba43eb977099770c97334ea8d49729d21a74342b67c3420e0881a5
BLAKE2b-256 checksum
How to use checksums
bce03749da5651c8f6197bad06dcd1af793f32f8e9ad1f5832d4354480d0b610
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.11

Security for profile-correspondence-encoder 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page