diff --git a/CHANGELOG.md b/CHANGELOG.md index fa6daf2..156e84e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) ## [Unreleased] +### Fixed +- Removed dead `hasattr(X, "columns")` branch in `WealthPercentileTransformer.fit` since `validate_data` returns a NumPy array and sets `feature_names_in_` for DataFrame inputs (issue #168). + ### Changed - README coverage badge now links to `pyproject.toml` and reads "≥92% floor" rather than a bare "≥92%". It was a static shields.io string with no tie to diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6ebc1aa..fd9e4e5 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -45,6 +45,8 @@ contribution. Code, docs, tests, and review all count. single-class fallback coverage for `PlannedGivingIntentScorer.predict_intent_score` ([#149](https://github.com/PhilanthroPy-Project/PhilanthroPy/pull/149)). +- [@HeaTTap](https://github.com/HeaTTap): removed dead `hasattr(X, "columns")` branch in `WealthPercentileTransformer.fit` and pinned feature-name handling for DataFrame and array inputs ([#168](https://github.com/PhilanthroPy-Project/PhilanthroPy/issues/168)). + ## Getting listed diff --git a/philanthropy/preprocessing/_wealth_percentile.py b/philanthropy/preprocessing/_wealth_percentile.py index 666a1df..3f7f9dd 100755 --- a/philanthropy/preprocessing/_wealth_percentile.py +++ b/philanthropy/preprocessing/_wealth_percentile.py @@ -42,11 +42,9 @@ def fit(self, X, y=None): training distribution, not against the batch being transformed. """ X = validate_data(self, X, ensure_all_finite="allow-nan", reset=True) - - if hasattr(X, "columns"): - self.feature_names_in_ = np.array(X.columns.tolist(), dtype=object) - elif not hasattr(self, "feature_names_in_"): - self.feature_names_in_ = np.array([f"x{i}" for i in range(X.shape[1])], dtype=object) + # validate_data sets feature_names_in_ when input is a DataFrame + if not hasattr(self, "feature_names_in_"): + self.feature_names_in_ = np.array([f"x{i}" for i in range(X.shape[1])], dtype=object) # Use feature_names_in_ to resolve columns if self.wealth_cols is not None: diff --git a/tests/test_preprocessing.py b/tests/test_preprocessing.py index 67acc77..80670d3 100755 --- a/tests/test_preprocessing.py +++ b/tests/test_preprocessing.py @@ -31,6 +31,7 @@ CRMCleaner, EncounterTransformer, FiscalYearTransformer, + WealthPercentileTransformer, WealthScreeningImputer, ) @@ -723,3 +724,12 @@ def test_crm_cleaner_on_unnamed_ndarrays_skips_named_columns(self): X = np.array([["2023-01-01", "1250.50"]], dtype=object) out = np.asarray(CRMCleaner().fit(X).transform(X)) assert out.shape == (1, 2) + + +def test_wealth_percentile_feature_names_match_for_frame_and_array_input(): + df = pd.DataFrame({"net_worth": [1.0, 2.0, 3.0], "other": [1.0, 2.0, 3.0]}) + a = WealthPercentileTransformer().fit(df) + b = WealthPercentileTransformer().fit(df.to_numpy()) + assert list(a.get_feature_names_out()) == ["net_worth", "other", "net_worth_pct_rank"] + assert list(b.get_feature_names_out()) == ["x0", "x1"] +