Skip to content

Warn at fit when X contains likely-text columns - #1159

Open
vahidblm wants to merge 4 commits into
mainfrom
warn-on-likely-text-columns
Open

Warn at fit when X contains likely-text columns#1159
vahidblm wants to merge 4 commits into
mainfrom
warn-on-likely-text-columns

Conversation

@vahidblm

@vahidblm vahidblm commented Aug 5, 2026

Copy link
Copy Markdown

Issue

No GitHub issue. This was driven by repeated reports of users fitting on free-text columns, which leads to silently worse results.

Motivation

High-cardinality string columns that cannot be parsed as numbers are labelled FeatureModality.TEXT by modality detection, but nothing used this label: they were silently swept into the same OrdinalEncoder as real categoricals (which selects columns by dtype), turning near-unique text into near-unique integer codes, without any warnings.

fit() now emits a UserWarning naming the affected columns and pointing at the fixes: convert to a numeric dtype (numbers stored as strings), or use tabpfn-client if it's a genuine text, or pass the column in categorical_features_indices if it's a real high-cardinality category (in this case, there won't be warnings).

The check runs inside detect_feature_modalities (the single place the TEXT labels are produced, before clean_data rebuilds the schema). Detection logic itself is not changed (purely diagnostic).


Public API Changes

  • No Public API changes (new warning only; no signatures changed)

How Has This Been Tested?

  • Unit tests in tests/test_preprocessing/test_modality_detection.py: free text warns; low-cardinality strings and fully numeric strings do not warn; a numeric column with one stray "N/A" token warns; columns declared categorical (both plain string and category dtype) do not warn.
  • One parametrized estimator-level test (classifier and regressor) in tests/test_preprocessing/test_modality_detection.py: fit warns and names the column, predict stays quiet, and categorical_features_indices silences it.
  • stacklevel=6 so the warning points at the caller's fit() line (the @config_context(...) decorator on fit adds a contextlib wrapper frame, on top of the detect_feature_modalities frame); pinned by warning.filename asserts in the tests.

Checklist

  • The changes have been tested locally.
  • Documentation has been updated (if the public API or usage changes) [no update is need, as this is just a warning].
  • A changelog entry has been added (see changelog/README.md).
  • The code follows the project's style guidelines.
  • I have considered the impact of these changes on the public API.

High-cardinality string columns that cannot be parsed as numbers are
labelled TEXT by modality detection, but nothing consumed the label:
they were silently swept into the OrdinalEncoder alongside real
categoricals, turning near-unique text into integer noise. fit() now
emits a UserWarning naming the affected columns and pointing at the
fixes (numeric dtype, tabpfn-client for genuine text, or
categorical_features_indices to declare a real high-cardinality
category, which also silences the warning).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@CLAassistant

CLAassistant commented Aug 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Comment thread src/tabpfn/validation.py Outdated
The @config_context(...) instance decorating fit() wraps it via
ContextDecorator.__call__, whose wrapper is a real frame in
contextlib.py, so stacklevel=4 blamed contextlib instead of the
caller's fit() line. Bump to 5 and pin it with warning.filename
asserts in the interface tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@bejaeger bejaeger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @vahidblm ! Looks mostly good to me, please see my 2 comments.

Comment thread changelog/1159.added.md Outdated
Comment thread src/tabpfn/validation.py Outdated
Comment thread src/tabpfn/classifier.py Outdated
warn_if_text_features(
feature_schema,
declared_categorical_indices=self.categorical_features_indices,
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about making this part of detect_feature_modalities itself?
Advantages: 1) a single place where we call this function. 2) we can add a single test to the detect_feature_modalities tests and don't need two separate tests for the classifier and regressor?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I added everything into detect_feature_modalities and simplified the tests too (so now all the tests are in test_modality_detection

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Per review feedback, the likely-text warning now fires from inside
detect_feature_modalities instead of being called separately by each
estimator. This gives a single call site (the one place TEXT labels are
produced) and a single place to test, so the classifier/regressor tests
collapse into one parametrized test under the modality-detection suite.

warn_if_text_features moves from validation to modality_detection;
stacklevel goes 5 -> 6 for the extra detect_feature_modalities frame.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vahidblm
vahidblm force-pushed the warn-on-likely-text-columns branch from d87662a to 0dbad7e Compare August 7, 2026 13:57
@vahidblm
vahidblm requested a review from bejaeger August 7, 2026 14:15
Refactor import statements for cleaner code.

@bejaeger bejaeger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the two changes!
Two more nits, but otherwise LGTM! approving already

return feature_schema


def warn_if_text_features(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can be made private

feature_schema = FeatureSchema(features=features)
# Warn here rather than at each call site: this is the single place the TEXT
# labels are produced, and they do not survive the first preprocessing step
# that rebuilds the schema.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I guess this comment refers to the previous state of the code base and is not really needed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants