eda-explorer — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited eda-explorer (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 reproducible exploratory data analysis skill. Same dataset → same report structure → no surprises.
Activate this skill whenever the user wants to understand a tabular dataset before doing anything else with it. Typical signals:
.csv, .xlsx, .parquet, or .tsv fileDo NOT activate this skill for:
For every dataset, follow these steps in order:
## headers per sectionThe report must have exactly these 9 sections, in this order:
(n_rows, n_cols)df.memory_usage(deep=True).sum() in MB)head(5) previewn_unique == n_rows (likely IDs)n_unique == 1 (constant — useless)df.describe() plus skewness and kurtosisdf.select_dtypes(include='number').corr() (Pearson by default)imshow)[Q1 - 1.5*IQR, Q3 + 1.5*IQR]|z| > 3Score: XX/100 [GREEN | YELLOW | RED][CLEAN], [IMPUTE], [DROP], [ENGINEER], [INVESTIGATE]Use this template as the backbone. Adapt to the actual data, but keep the section structure identical.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
from scipy import stats
# Setup
OUTPUT_DIR = Path("outputs/eda")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
# 1. Load
df = pd.read_csv(filepath) # or read_excel, read_parquet
print(f"Loaded {df.shape[0]:,} rows × {df.shape[1]} cols")
# 2. Schema
schema = pd.DataFrame({
"dtype": df.dtypes,
"n_unique": df.nunique(),
"n_null": df.isna().sum(),
"sample": df.iloc[0],
})
# 3. Missing
missing = df.isna().sum().sort_values(ascending=False)
missing_pct = (missing / len(df) * 100).round(2)
# 4. Describe
numeric_describe = df.describe()
cat_cols = df.select_dtypes(include="object").columns
cat_describe = {col: df[col].value_counts().head(5) for col in cat_cols}
# 5. Distributions (one PNG per numeric col)
for col in df.select_dtypes(include="number").columns:
fig, ax = plt.subplots(figsize=(6, 3))
df[col].hist(bins=30, ax=ax)
ax.set_title(f"Distribution: {col}")
fig.tight_layout()
fig.savefig(OUTPUT_DIR / f"hist_{col}.png", dpi=80)
plt.close(fig)
# 6. Correlations
corr = df.select_dtypes(include="number").corr()
# 7. Outliers
def count_outliers(s):
q1, q3 = s.quantile([0.25, 0.75])
iqr = q3 - q1
iqr_count = ((s < q1 - 1.5 * iqr) | (s > q3 + 1.5 * iqr)).sum()
z_count = (np.abs(stats.zscore(s.dropna())) > 3).sum()
return iqr_count, z_count
# 8. Quality score (see formula in section 8 above)
# 9. Recommendations (generated from observations)The final response to the user must be a single markdown document with:
# EDA Report — <filename>
## 1. Dataset Overview
...
## 2. Schema & Types
...
## 3. Missing Values
...
## 4. Descriptive Statistics
...
## 5. Distributions
[]
...
## 6. Correlations
...
## 7. Outliers
...
## 8. Data Quality Score
**Score: 78/100 [YELLOW]**
Top red flags: ...
## 9. Recommendations
1. [CLEAN] Column `email` has 12 mixed-case duplicates — normalize to lowercase
2. [IMPUTE] Column `age` has 8% missing — consider median imputation grouped by `region`
3. ...~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.