feature-engineer — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited feature-engineer (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A standardized feature engineering skill. Same logic, every dataset, fully traceable.
Activate when the user has a clean-ish tabular dataset and wants to make it model-ready. Typical signals:
Pre-condition: the data should already be reasonably clean. If you see > 30% missing in many columns or obvious quality issues, call `eda-explorer` first and tell the user to clean before feature engineering.
Do NOT activate this skill for:
eda-explorer)time-series-features skillFor every dataset, follow these 5 phases in order:
Auto-classify each column into one of:
numeric (int, float, bool)categorical_low_card (object, < 10 unique values)categorical_high_card (object, 10–50 unique values)categorical_very_high_card (object, > 50 unique values)datetimeid_or_constant (drop these — n_unique == n_rows or n_unique == 1)text (object with average string length > 30 chars — out of scope, drop with warning)Print the classification table to the user before proceeding.
For each column type, apply the default strategy:
| Type | Default transform | Justification |
|---|---|---|
numeric | StandardScaler if no outliers, else RobustScaler | Robust to outliers when present |
categorical_low_card | OneHotEncoder | Manageable dimension expansion |
categorical_high_card | OneHotEncoder with min_frequency=0.01 | Limit dimension |
categorical_very_high_card | TargetEncoder (if target supplied) or HashingEncoder | Avoid explosion |
datetime | Decompose to year, month, day, dayofweek, is_weekend, hour | Capture seasonality |
id_or_constant | Drop | No signal |
text | Drop with warning | Out of skill scope |
Override the default only if the user has specified a different strategy.
Use sklearn.compose.ColumnTransformer + Pipeline. Never hand-roll transformations.
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, RobustScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
numeric_pipe = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()), # or RobustScaler
])
cat_pipe = Pipeline([
("imputer", SimpleImputer(strategy="most_frequent")),
("encoder", OneHotEncoder(handle_unknown="ignore", min_frequency=0.01)),
])
preprocessor = ColumnTransformer([
("num", numeric_pipe, numeric_cols),
("cat", cat_pipe, categorical_cols),
("date", datetime_decomposer, datetime_cols),
], remainder="drop")y)get_feature_names_out()Save three files in outputs/features/:
pipeline.pkl — the fitted ColumnTransformer (joblib)feature_dict.json — a feature dictionary (see schema below)transformed_features.parquet — the transformed matrixThe JSON dictionary must follow this exact schema. It serves as auditable metadata.
{
"skill_version": "0.1.0",
"generated_at": "2026-04-27T14:32:11Z",
"source_shape": [2000, 12],
"output_shape": [2000, 47],
"features": [
{
"name": "age_scaled",
"source_column": "age",
"transform": "StandardScaler",
"params": {"mean": 42.3, "std": 14.1},
"type": "numeric",
"n_missing_before": 18,
"imputation": "median=42.0"
},
{
"name": "region_NORTH",
"source_column": "region",
"transform": "OneHotEncoder",
"params": {"category": "NORTH"},
"type": "categorical",
"n_missing_before": 0
}
],
"dropped_columns": [
{"name": "customer_id", "reason": "id_or_constant"},
{"name": "free_text_notes", "reason": "text_out_of_scope"}
]
}After execution, respond with:
# Feature Engineering Report
## Type Classification
| Column | Detected Type | Strategy |
|---|---|---|
...
## Pipeline Built
Steps: <list>
Input shape: (X, Y)
Output shape: (X, Z)
## Validation
- ✅ No NaN in output
- ✅ No infinite values
- ✅ Feature names extractable
- ⚠️ <any warnings>
## Artifacts Exported
- `outputs/features/pipeline.pkl` (Z KB)
- `outputs/features/feature_dict.json` (Z KB)
- `outputs/features/transformed_features.parquet` (Z KB)
## Next Steps
1. Train a baseline model with: `pd.read_parquet('outputs/features/transformed_features.parquet')`
2. Reload pipeline for inference: `joblib.load('outputs/features/pipeline.pkl')`
3. ...Pipeline + ColumnTransformer — never hand-rolly from the user (data leakage risk)~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.