ml-feature-check 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pranay Mahendrakar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.5
2
+ Name: ml-feature-check
3
+ Version: 0.1.0
4
+ Summary: Catch useless, redundant, leaking and suspicious features before you train on them
5
+ Project-URL: Homepage, https://pypi.org/project/ml-feature-check/
6
+ Project-URL: Author, https://pypi.org/user/pranaymahendrakar/
7
+ Author: Pranay Mahendrakar
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: data-leakage,data-quality,feature-selection,machine-learning,pandas,preprocessing,tabular
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Requires-Python: >=3.9
19
+ Requires-Dist: numpy>=1.23
20
+ Requires-Dist: pandas>=1.5
21
+ Requires-Dist: scikit-learn>=1.1
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=7; extra == 'dev'
24
+ Provides-Extra: parquet
25
+ Requires-Dist: pyarrow>=12; extra == 'parquet'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # ml-feature-check
29
+
30
+ One call tells you which columns should not go into the model: the ones that
31
+ carry no signal, the ones that repeat another column, the ones that already know
32
+ the answer, and the ones that are really row identifiers - each explained, ranked
33
+ by severity, and removable with a single `report.apply(df)`.
34
+
35
+ ## Install
36
+
37
+ ```
38
+ pip install ml-feature-check
39
+ ```
40
+
41
+ Parquet input needs the optional extra: `pip install "ml-feature-check[parquet]"`.
42
+
43
+ ## Quickstart
44
+
45
+ ```python
46
+ import pandas as pd
47
+ from ml_feature_check import check
48
+
49
+ df = pd.DataFrame({"row_id": range(60),
50
+ "age": [23, 34, 45, 31, 29] * 12,
51
+ "age_in_years": [23, 34, 45, 31, 29] * 12,
52
+ "country": ["IN"] * 60,
53
+ "signed_up": ["2024-01-05", "2024-02-11", "2024-03-18"] * 20,
54
+ "churn_flag": [0, 1] * 30,
55
+ "churned": [0, 1] * 30})
56
+ report = check(df, target="churned")
57
+ print(report.summary())
58
+ clean = report.apply(df) # the same frame minus report.drop_recommended
59
+ ```
60
+
61
+ ```
62
+ ml-feature-check: 6 feature(s), 60 rows, target 'churned'
63
+ Drop recommended (4):
64
+ row_id id_like [high] 100.0% of the 60 values are distinct and they run consecutively: looks like a row identifier, not a feature
65
+ suspicious_name [low] the name contains 'row', which usually marks a row identifier or bookkeeping column rather than a feature
66
+ age_in_years duplicate_of [high] identical to column 'age': keep one of the two
67
+ country constant [high] only one distinct value ('IN')
68
+ churn_flag leakage_suspect [high] a 1-feature decision tree reaches 1.000 accuracy on held-out rows for target 'churned' (baseline 0.500)
69
+ Worth a look (2):
70
+ age zero_importance [low] a small random forest gives it no measurable importance (+0.0000 against a 0.0000 noise level)
71
+ signed_up date_as_string [low] text that parses as dates (for example '2024-01-05'): convert with pandas.to_datetime and feed the parts (year, month, weekday) to the model
72
+ zero_importance [low] a small random forest gives it no measurable importance (+0.0000 against a 0.0000 noise level)
73
+ Keep (2): age, signed_up
74
+ Notes:
75
+ - target 'churned' was treated as classification
76
+ ```
77
+
78
+ `report.features` maps every feature column to its findings, `report.drop_recommended`
79
+ is the ordered hit list, `report.keep` is the rest, and `report.to_dict()` /
80
+ `report.to_markdown()` give you the same findings as JSON or as a report page.
81
+
82
+ ## What it checks
83
+
84
+ Every finding is a `Finding` with a `kind`. Passing `target=` switches on the two
85
+ checks that need a label; everything else runs either way.
86
+
87
+ | kind | severity | fires when |
88
+ |---|---|---|
89
+ | `constant` | high | the column has one distinct value, or is entirely missing |
90
+ | `near_constant` | medium | more than 99% of the non-missing values are the same one |
91
+ | `high_missing` | medium (high above 95%) | the missing share is above `missing_threshold` |
92
+ | `id_like` | high (medium for timestamps) | more than `cardinality_threshold` of the values are distinct, and the column is text, integer or datetime rather than a continuous measurement |
93
+ | `duplicate_of` | high | the column is value-for-value identical to an earlier column |
94
+ | `highly_correlated_with` | medium | numeric: `abs(pearson)` above `corr_threshold` with a column kept earlier; categorical: bias-corrected Cramer's V above the same threshold |
95
+ | `leakage_suspect` | high | with a target: `abs(corr)` above 0.95 for a numeric target, a cross-fitted 1-feature decision tree above 0.99 accuracy / R2, or a categorical column whose values map onto the target almost one-to-one |
96
+ | `date_as_string` | low | at least 90% of a text column parses as a date |
97
+ | `suspicious_name` | low | the name contains `id`, `uuid`, `index`, `key`, `timestamp`, `row` or `unnamed` |
98
+ | `zero_importance` | low | with a target: a small random forest gives the column no more holdout permutation importance than a shuffled copy of a real column |
99
+
100
+ **What "drop" means.** A `high` or `medium` finding puts the column on
101
+ `drop_recommended`, ordered worst first; `low` findings leave it on `keep` and in
102
+ `report.review`, the "worth a look" list. Nothing is ever removed for you until
103
+ you call `apply()`.
104
+
105
+ **Checks that cannot fire twice.** A constant column is not then tested for
106
+ missingness or cardinality, a column already reported as a `duplicate_of` another
107
+ is not also reported as correlated with it, and the column a duplicate points at
108
+ is kept. So one problem produces one finding, on one column.
109
+
110
+ **The survivor of a redundant group is a column worth keeping.** When several
111
+ columns carry the same signal, the one left on `keep` is the one with no
112
+ drop-level finding of its own and the fewest missing values, not whichever the
113
+ frame happened to list first - so a 90%-missing copy never displaces the complete
114
+ column beside it. Columns that tie on both criteria fall back to frame order, so
115
+ two equally good copies keep whichever the frame lists first; reordering the
116
+ frame can swap which of that pair is dropped, and the report stays correct
117
+ either way.
118
+
119
+ **Leakage without false alarms.** The single-feature tree is cross-fitted on two
120
+ halves and scored only on rows it did not train on, and the categorical
121
+ value-to-target mapping is scored leave-one-out - each row predicted from the
122
+ *other* rows sharing its value. An identifier column therefore scores at chance
123
+ instead of scoring perfectly, which is what a plain `groupby` accuracy would do.
124
+
125
+ **Zero variance is not a warning.** Correlation on a constant column yields no
126
+ finding rather than a NaN one, and no numpy warning is emitted anywhere. Two
127
+ columns are compared only when they share at least 20 non-missing rows (5% of the
128
+ frame once that is larger), and when the overlap is partial the finding says how
129
+ many rows it rests on - `correlation +1.000 with column 'a' over 46 shared
130
+ non-missing rows` - with the count in `detail["n_shared"]`.
131
+
132
+ **Sampling.** Frames longer than `sample` rows (200,000 by default) are checked on
133
+ a seeded random sample; the forest behind `zero_importance` fits on at most 20,000
134
+ of those rows. `report.n_rows`, `report.n_rows_checked` and `report.notes` say
135
+ exactly what happened. Smaller frames are read whole, so their counts are exact.
136
+
137
+ ## API
138
+
139
+ ```python
140
+ check(df, target=None, *, corr_threshold=0.95, missing_threshold=0.6,
141
+ cardinality_threshold=0.98, sample=200_000, random_state=0) -> FeatureReport
142
+ ```
143
+
144
+ `df` is a `pandas.DataFrame` or a path to a `.csv`, `.tsv` or `.parquet` file.
145
+ `target` names the label column: it is never reported as a feature, and it
146
+ unlocks `leakage_suspect` and `zero_importance`. A target that is not a column
147
+ raises `ValueError` listing the columns that do exist, and so do duplicate column
148
+ names. The input frame is never modified.
149
+
150
+ `FeatureReport`
151
+
152
+ - `.features` - `dict[column -> list[Finding]]`, every feature column, worst finding first; an empty list means the column is clean
153
+ - `.drop_recommended` - columns with a high or medium finding, most severe first
154
+ - `.keep` - the remaining columns, in the original order
155
+ - `.review` - kept columns that still have a low-severity finding
156
+ - `.flagged` - every column with any finding; `.columns_with(kind)` - filtered by kind
157
+ - `.apply(df)` - a copy of `df` without the `drop_recommended` columns; a frame
158
+ that is missing those columns is logged as a warning on the `ml_feature_check`
159
+ logger rather than passed over in silence
160
+ - `.iter_findings()` - `(column, finding)` pairs, most severe first
161
+ - `.target`, `.n_features`, `.n_rows`, `.n_rows_checked`, `.params`
162
+ - `.notes` - what was skipped or sampled and why
163
+ - `.summary()` - human text; `.to_dict()` - JSON-safe dict; `.to_markdown()` - a report page
164
+
165
+ `Finding(kind, severity, message, detail)`
166
+
167
+ `kind` is one of `ml_feature_check.KINDS`, `severity` is `"high"`, `"medium"` or
168
+ `"low"`, and `detail` is a JSON-safe dict with the numbers behind the message -
169
+ the other column, the correlation, the score, the threshold that was crossed.
170
+
171
+ ```python
172
+ FeatureChecker(*, corr_threshold=0.95, missing_threshold=0.6, cardinality_threshold=0.98,
173
+ sample=200_000, random_state=0, near_constant_threshold=0.99,
174
+ leakage_score_threshold=0.99, leakage_corr_threshold=0.95, min_id_rows=20,
175
+ max_model_rows=20_000, max_model_categories=100, max_pair_columns=400)
176
+ FeatureChecker.check(df, target=None) -> FeatureReport
177
+ ```
178
+
179
+ The same run with every threshold exposed, for reuse across many frames.
180
+
181
+ Without a target the two label-dependent checks are skipped and a note says so;
182
+ everything else still runs. The library never prints - it logs through
183
+ `logging.getLogger("ml_feature_check")`.
184
+
185
+ ## CLI
186
+
187
+ ```
188
+ ml-feature-check train.csv # print the summary
189
+ ml-feature-check train.csv --target churned # add the leakage and importance checks
190
+ ml-feature-check train.csv --json # print to_dict() as JSON
191
+ ml-feature-check train.csv --markdown # print to_markdown()
192
+ ml-feature-check train.csv --output report.md # also write it (.json/.md/.txt by suffix)
193
+ ml-feature-check train.csv --apply clean.csv # write the frame without the dropped columns
194
+ ml-feature-check train.csv --sample 50000 --corr-threshold 0.9
195
+ ml-feature-check --help
196
+ ```
197
+
198
+ `python -m ml_feature_check train.csv` works the same way. The command exits 1
199
+ with a one-line message on a bad file, a missing target or a bad threshold.
200
+
201
+ ## License
202
+
203
+ MIT
@@ -0,0 +1,176 @@
1
+ # ml-feature-check
2
+
3
+ One call tells you which columns should not go into the model: the ones that
4
+ carry no signal, the ones that repeat another column, the ones that already know
5
+ the answer, and the ones that are really row identifiers - each explained, ranked
6
+ by severity, and removable with a single `report.apply(df)`.
7
+
8
+ ## Install
9
+
10
+ ```
11
+ pip install ml-feature-check
12
+ ```
13
+
14
+ Parquet input needs the optional extra: `pip install "ml-feature-check[parquet]"`.
15
+
16
+ ## Quickstart
17
+
18
+ ```python
19
+ import pandas as pd
20
+ from ml_feature_check import check
21
+
22
+ df = pd.DataFrame({"row_id": range(60),
23
+ "age": [23, 34, 45, 31, 29] * 12,
24
+ "age_in_years": [23, 34, 45, 31, 29] * 12,
25
+ "country": ["IN"] * 60,
26
+ "signed_up": ["2024-01-05", "2024-02-11", "2024-03-18"] * 20,
27
+ "churn_flag": [0, 1] * 30,
28
+ "churned": [0, 1] * 30})
29
+ report = check(df, target="churned")
30
+ print(report.summary())
31
+ clean = report.apply(df) # the same frame minus report.drop_recommended
32
+ ```
33
+
34
+ ```
35
+ ml-feature-check: 6 feature(s), 60 rows, target 'churned'
36
+ Drop recommended (4):
37
+ row_id id_like [high] 100.0% of the 60 values are distinct and they run consecutively: looks like a row identifier, not a feature
38
+ suspicious_name [low] the name contains 'row', which usually marks a row identifier or bookkeeping column rather than a feature
39
+ age_in_years duplicate_of [high] identical to column 'age': keep one of the two
40
+ country constant [high] only one distinct value ('IN')
41
+ churn_flag leakage_suspect [high] a 1-feature decision tree reaches 1.000 accuracy on held-out rows for target 'churned' (baseline 0.500)
42
+ Worth a look (2):
43
+ age zero_importance [low] a small random forest gives it no measurable importance (+0.0000 against a 0.0000 noise level)
44
+ signed_up date_as_string [low] text that parses as dates (for example '2024-01-05'): convert with pandas.to_datetime and feed the parts (year, month, weekday) to the model
45
+ zero_importance [low] a small random forest gives it no measurable importance (+0.0000 against a 0.0000 noise level)
46
+ Keep (2): age, signed_up
47
+ Notes:
48
+ - target 'churned' was treated as classification
49
+ ```
50
+
51
+ `report.features` maps every feature column to its findings, `report.drop_recommended`
52
+ is the ordered hit list, `report.keep` is the rest, and `report.to_dict()` /
53
+ `report.to_markdown()` give you the same findings as JSON or as a report page.
54
+
55
+ ## What it checks
56
+
57
+ Every finding is a `Finding` with a `kind`. Passing `target=` switches on the two
58
+ checks that need a label; everything else runs either way.
59
+
60
+ | kind | severity | fires when |
61
+ |---|---|---|
62
+ | `constant` | high | the column has one distinct value, or is entirely missing |
63
+ | `near_constant` | medium | more than 99% of the non-missing values are the same one |
64
+ | `high_missing` | medium (high above 95%) | the missing share is above `missing_threshold` |
65
+ | `id_like` | high (medium for timestamps) | more than `cardinality_threshold` of the values are distinct, and the column is text, integer or datetime rather than a continuous measurement |
66
+ | `duplicate_of` | high | the column is value-for-value identical to an earlier column |
67
+ | `highly_correlated_with` | medium | numeric: `abs(pearson)` above `corr_threshold` with a column kept earlier; categorical: bias-corrected Cramer's V above the same threshold |
68
+ | `leakage_suspect` | high | with a target: `abs(corr)` above 0.95 for a numeric target, a cross-fitted 1-feature decision tree above 0.99 accuracy / R2, or a categorical column whose values map onto the target almost one-to-one |
69
+ | `date_as_string` | low | at least 90% of a text column parses as a date |
70
+ | `suspicious_name` | low | the name contains `id`, `uuid`, `index`, `key`, `timestamp`, `row` or `unnamed` |
71
+ | `zero_importance` | low | with a target: a small random forest gives the column no more holdout permutation importance than a shuffled copy of a real column |
72
+
73
+ **What "drop" means.** A `high` or `medium` finding puts the column on
74
+ `drop_recommended`, ordered worst first; `low` findings leave it on `keep` and in
75
+ `report.review`, the "worth a look" list. Nothing is ever removed for you until
76
+ you call `apply()`.
77
+
78
+ **Checks that cannot fire twice.** A constant column is not then tested for
79
+ missingness or cardinality, a column already reported as a `duplicate_of` another
80
+ is not also reported as correlated with it, and the column a duplicate points at
81
+ is kept. So one problem produces one finding, on one column.
82
+
83
+ **The survivor of a redundant group is a column worth keeping.** When several
84
+ columns carry the same signal, the one left on `keep` is the one with no
85
+ drop-level finding of its own and the fewest missing values, not whichever the
86
+ frame happened to list first - so a 90%-missing copy never displaces the complete
87
+ column beside it. Columns that tie on both criteria fall back to frame order, so
88
+ two equally good copies keep whichever the frame lists first; reordering the
89
+ frame can swap which of that pair is dropped, and the report stays correct
90
+ either way.
91
+
92
+ **Leakage without false alarms.** The single-feature tree is cross-fitted on two
93
+ halves and scored only on rows it did not train on, and the categorical
94
+ value-to-target mapping is scored leave-one-out - each row predicted from the
95
+ *other* rows sharing its value. An identifier column therefore scores at chance
96
+ instead of scoring perfectly, which is what a plain `groupby` accuracy would do.
97
+
98
+ **Zero variance is not a warning.** Correlation on a constant column yields no
99
+ finding rather than a NaN one, and no numpy warning is emitted anywhere. Two
100
+ columns are compared only when they share at least 20 non-missing rows (5% of the
101
+ frame once that is larger), and when the overlap is partial the finding says how
102
+ many rows it rests on - `correlation +1.000 with column 'a' over 46 shared
103
+ non-missing rows` - with the count in `detail["n_shared"]`.
104
+
105
+ **Sampling.** Frames longer than `sample` rows (200,000 by default) are checked on
106
+ a seeded random sample; the forest behind `zero_importance` fits on at most 20,000
107
+ of those rows. `report.n_rows`, `report.n_rows_checked` and `report.notes` say
108
+ exactly what happened. Smaller frames are read whole, so their counts are exact.
109
+
110
+ ## API
111
+
112
+ ```python
113
+ check(df, target=None, *, corr_threshold=0.95, missing_threshold=0.6,
114
+ cardinality_threshold=0.98, sample=200_000, random_state=0) -> FeatureReport
115
+ ```
116
+
117
+ `df` is a `pandas.DataFrame` or a path to a `.csv`, `.tsv` or `.parquet` file.
118
+ `target` names the label column: it is never reported as a feature, and it
119
+ unlocks `leakage_suspect` and `zero_importance`. A target that is not a column
120
+ raises `ValueError` listing the columns that do exist, and so do duplicate column
121
+ names. The input frame is never modified.
122
+
123
+ `FeatureReport`
124
+
125
+ - `.features` - `dict[column -> list[Finding]]`, every feature column, worst finding first; an empty list means the column is clean
126
+ - `.drop_recommended` - columns with a high or medium finding, most severe first
127
+ - `.keep` - the remaining columns, in the original order
128
+ - `.review` - kept columns that still have a low-severity finding
129
+ - `.flagged` - every column with any finding; `.columns_with(kind)` - filtered by kind
130
+ - `.apply(df)` - a copy of `df` without the `drop_recommended` columns; a frame
131
+ that is missing those columns is logged as a warning on the `ml_feature_check`
132
+ logger rather than passed over in silence
133
+ - `.iter_findings()` - `(column, finding)` pairs, most severe first
134
+ - `.target`, `.n_features`, `.n_rows`, `.n_rows_checked`, `.params`
135
+ - `.notes` - what was skipped or sampled and why
136
+ - `.summary()` - human text; `.to_dict()` - JSON-safe dict; `.to_markdown()` - a report page
137
+
138
+ `Finding(kind, severity, message, detail)`
139
+
140
+ `kind` is one of `ml_feature_check.KINDS`, `severity` is `"high"`, `"medium"` or
141
+ `"low"`, and `detail` is a JSON-safe dict with the numbers behind the message -
142
+ the other column, the correlation, the score, the threshold that was crossed.
143
+
144
+ ```python
145
+ FeatureChecker(*, corr_threshold=0.95, missing_threshold=0.6, cardinality_threshold=0.98,
146
+ sample=200_000, random_state=0, near_constant_threshold=0.99,
147
+ leakage_score_threshold=0.99, leakage_corr_threshold=0.95, min_id_rows=20,
148
+ max_model_rows=20_000, max_model_categories=100, max_pair_columns=400)
149
+ FeatureChecker.check(df, target=None) -> FeatureReport
150
+ ```
151
+
152
+ The same run with every threshold exposed, for reuse across many frames.
153
+
154
+ Without a target the two label-dependent checks are skipped and a note says so;
155
+ everything else still runs. The library never prints - it logs through
156
+ `logging.getLogger("ml_feature_check")`.
157
+
158
+ ## CLI
159
+
160
+ ```
161
+ ml-feature-check train.csv # print the summary
162
+ ml-feature-check train.csv --target churned # add the leakage and importance checks
163
+ ml-feature-check train.csv --json # print to_dict() as JSON
164
+ ml-feature-check train.csv --markdown # print to_markdown()
165
+ ml-feature-check train.csv --output report.md # also write it (.json/.md/.txt by suffix)
166
+ ml-feature-check train.csv --apply clean.csv # write the frame without the dropped columns
167
+ ml-feature-check train.csv --sample 50000 --corr-threshold 0.9
168
+ ml-feature-check --help
169
+ ```
170
+
171
+ `python -m ml_feature_check train.csv` works the same way. The command exits 1
172
+ with a one-line message on a bad file, a missing target or a bad threshold.
173
+
174
+ ## License
175
+
176
+ MIT
@@ -0,0 +1,60 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "ml-feature-check"
7
+ version = "0.1.0"
8
+ description = "Catch useless, redundant, leaking and suspicious features before you train on them"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Pranay Mahendrakar" }]
14
+ keywords = [
15
+ "machine-learning",
16
+ "feature-selection",
17
+ "data-leakage",
18
+ "data-quality",
19
+ "pandas",
20
+ "preprocessing",
21
+ "tabular",
22
+ ]
23
+ classifiers = [
24
+ "Development Status :: 4 - Beta",
25
+ "Intended Audience :: Developers",
26
+ "Intended Audience :: Science/Research",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3 :: Only",
29
+ "Operating System :: OS Independent",
30
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
31
+ ]
32
+ dependencies = [
33
+ "pandas>=1.5",
34
+ "numpy>=1.23",
35
+ "scikit-learn>=1.1",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ parquet = ["pyarrow>=12"]
40
+ dev = ["pytest>=7"]
41
+
42
+ [project.scripts]
43
+ ml-feature-check = "ml_feature_check.cli:main"
44
+
45
+ [project.urls]
46
+ Homepage = "https://pypi.org/project/ml-feature-check/"
47
+ Author = "https://pypi.org/user/pranaymahendrakar/"
48
+
49
+ [tool.hatch.build.targets.wheel]
50
+ packages = ["src/ml_feature_check"]
51
+
52
+ [tool.pytest.ini_options]
53
+ testpaths = ["tests"]
54
+ filterwarnings = [
55
+ "error::RuntimeWarning",
56
+ "error::FutureWarning",
57
+ "error::DeprecationWarning",
58
+ "error::PendingDeprecationWarning",
59
+ "error::UserWarning",
60
+ ]
@@ -0,0 +1,21 @@
1
+ """Catch useless, redundant, leaking and suspicious features before you train on them.
2
+
3
+ >>> from ml_feature_check import check
4
+ >>> report = check(df, target="label")
5
+ >>> print(report.summary())
6
+ >>> clean = report.apply(df)
7
+ """
8
+
9
+ from .checker import KINDS, FeatureChecker, check
10
+ from .report import FeatureReport, Finding
11
+
12
+ __version__ = "0.1.0"
13
+
14
+ __all__ = [
15
+ "check",
16
+ "FeatureChecker",
17
+ "FeatureReport",
18
+ "Finding",
19
+ "KINDS",
20
+ "__version__",
21
+ ]
@@ -0,0 +1,6 @@
1
+ """``python -m ml_feature_check`` runs the command line interface."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__": # pragma: no cover
6
+ raise SystemExit(main())