diff --git a/docs/examples/action_timeline.html b/docs/examples/action_timeline.html index fb787a7d..3f4c8f6f 100644 --- a/docs/examples/action_timeline.html +++ b/docs/examples/action_timeline.html @@ -29,7 +29,7 @@ .fd-del-pos{color:#1a7f37}.fd-del-neg{color:#cf222e} .fd-mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8rem} -

freshdata clean report

backend=pandas · 0.015s
205 → 200
rows
5 → 5
columns
76 → 23
missing cells
5
duplicates removed
0
outliers handled
5
actions
-

Action timeline

fix_dtypes signup_date — converted to datetime64[ns] (205 affected) low automatic
confidence
100%
drop_duplicates — dropped 5 duplicate row(s) (2.4% of rows, keep='first') (5 affected) low automatic
confidence
100%
missing email — preserved 23 missing value(s) (0 affected) medium automatic
why: identifier-like column — imputing keys would fabricate identities
confidence
100%
missing tier — filled 51 missing value(s) with sentinel "Missing" ('Missing') (51 affected) medium automatic
why: medium missingness without a dominant category; an explicit sentinel keeps the gap visible
confidence
70%
outliers revenue — preserved 5 outlier(s), 2.5% of values (method=iqr, factor=1.5) (0 affected) low automatic
why: protected column role or domain
confidence
90%
-

Audit ledger

⬇ JSON⬇ CSV
columnactionriskconfidencecountdescription
signup_datefix_dtypeslow100%205converted to datetime64[ns]
drop_duplicateslow100%5dropped 5 duplicate row(s) (2.4% of rows, keep='first')
emailmissingmedium100%0preserved 23 missing value(s)
tiermissingmedium70%51filled 51 missing value(s) with sentinel "Missing" ('Missing')
revenueoutlierslow90%0preserved 5 outlier(s), 2.5% of values (method=iqr, factor=1.5)
-

Needs review

\ No newline at end of file +

freshdata clean report

backend=pandas · 0.021s
205 → 205
rows
5 → 6
columns
76 → 24
missing cells
0
duplicates removed
7
outliers handled
5
actions
+

Action timeline

fix_dtypes signup_date — converted to datetime64[ns] (205 affected) low automatic
confidence
100%
drop_duplicates — detected 5 duplicate row(s) (2.4%), none removed (0 affected) low automatic
why: drop_duplicates=False (default): duplicate rows are reported, never removed; pass drop_duplicates=True to remove them
confidence
100%
missing email — preserved 24 missing value(s) (0 affected) medium automatic
why: identifier-like column — imputing keys would fabricate identities
confidence
100%
missing tier — filled 52 missing value(s) with sentinel "Missing" ('Missing') (52 affected) medium automatic
why: medium missingness without a dominant category; an explicit sentinel keeps the gap visible
confidence
70%
outliers revenue — flagged 7 outlier(s), 3.4% of values (method=iqr, factor=1.5) in new column 'revenue_outlier' (7 affected) low automatic
why: flagging records the detection without altering any value
confidence
70%
+

Audit ledger

⬇ JSON⬇ CSV
columnactionriskconfidencecountdescription
signup_datefix_dtypeslow100%205converted to datetime64[ns]
drop_duplicateslow100%0detected 5 duplicate row(s) (2.4%), none removed
emailmissingmedium100%0preserved 24 missing value(s)
tiermissingmedium70%52filled 52 missing value(s) with sentinel "Missing" ('Missing')
revenueoutlierslow70%7flagged 7 outlier(s), 3.4% of values (method=iqr, factor=1.5) in new column 'revenue_outlier'
+

Needs review

\ No newline at end of file diff --git a/docs/examples/baseline_drift.html b/docs/examples/baseline_drift.html index 312a3f7c..ca575e34 100644 --- a/docs/examples/baseline_drift.html +++ b/docs/examples/baseline_drift.html @@ -32,4 +32,4 @@

freshdata drift report — baseline

FAIL · 1 error(s), 1 warning(s) · baseline v1.0.0
FAIL
verdict
1
errors
1
warnings

What likely matters

Findings

checkcolumnstatusmessage
drift.kscustomer_idmediumKS 0.244 >= warn threshold 0.1
drift.psicustomer_idhighPSI 0.877 >= fail threshold 0.25
-⬇ JSON
\ No newline at end of file +⬇ JSON \ No newline at end of file diff --git a/docs/examples/compare_plans_grid.html b/docs/examples/compare_plans_grid.html index c41e10b2..b871166a 100644 --- a/docs/examples/compare_plans_grid.html +++ b/docs/examples/compare_plans_grid.html @@ -29,4 +29,4 @@ .fd-del-pos{color:#1a7f37}.fd-del-neg{color:#cf222e} .fd-mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8rem} -

freshdata strategy comparison

strategy diff grid
columnstrategymissing_modeloutlier_actionn_outliers
emailbalancedpreserve0
revenuebalanced5
tierbalancedsentinel0
emailaggressivepreserve0
revenueaggressive5
tieraggressivesentinel0
\ No newline at end of file +

freshdata strategy comparison

strategy diff grid
columnstrategymissing_modeloutlier_actionn_outliers
emailbalancedpreserve0
revenuebalancedflag7
tierbalancedsentinel0
emailaggressivepreserve0
revenueaggressiveflag7
tieraggressivesentinel0
\ No newline at end of file diff --git a/docs/examples/explain_diff.html b/docs/examples/explain_diff.html index ed05fd9c..e5788493 100644 --- a/docs/examples/explain_diff.html +++ b/docs/examples/explain_diff.html @@ -29,8 +29,8 @@ .fd-del-pos{color:#1a7f37}.fd-del-neg{color:#cf222e} .fd-mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8rem} -

freshdata explain — balanced

before/after diff explorer
205×5 → 200×5
shape
76 → 23
missing
5
steps
1000
changed cells
-

Decisions

-

Safe changes

none
-

Risky changes (review)

columnbefore dtypeafter dtypechanged cells
customer_idint64int64200
emailobjectobject200
revenuefloat64float64200
signup_dateobjectdatetime64[ns]200
tierobjectobject200
-⬇ JSON
\ No newline at end of file +

freshdata explain — balanced

before/after diff explorer
205×5 → 205×6
shape
76 → 24
missing
5
steps
257
changed cells
+

Decisions

+

Safe changes

columnbefore dtypeafter dtypechanged cells
tierobjectobject52
customer_idint64int640
emailobjectobject0
revenuefloat64float640
+

Risky changes (review)

columnbefore dtypeafter dtypechanged cells
revenue_outlierbool205
signup_dateobjectdatetime64[ns]0
+⬇ JSON
\ No newline at end of file diff --git a/docs/examples/quality_debt.html b/docs/examples/quality_debt.html index 400abde9..491cefa2 100644 --- a/docs/examples/quality_debt.html +++ b/docs/examples/quality_debt.html @@ -29,7 +29,7 @@ .fd-del-pos{color:#1a7f37}.fd-del-neg{color:#cf222e} .fd-mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8rem} -

freshdata quality-debt gate

WARN · policy warn_then_fail · total debt 1.25
WARN
-
WARN
status
1.25
total debt
2
over threshold
97.6
trust
-
dimensionscorethresholdseveritytrenddetail
pii_risk1.000.00medium177 potential PII column(s)
human_review_backlog0.200.00medium2 item(s) awaiting review
duplicates0.020.10low5 duplicate row(s) removed
missingness0.020.10low23 missing cell(s) remain
schema_drift0.000.00lowno baseline supplied
type_instability0.000.10low0 column(s) with unstable types
outlier_spikes0.000.10low0 outlier(s) flagged
category_churn0.000.10lowno baseline supplied
failed_repairs0.000.00low0 high-risk action(s)
-⬇ JSON
\ No newline at end of file +

freshdata quality-debt gate

WARN · policy warn_then_fail · total debt 0.34
WARN
+
WARN
status
0.34
total debt
2
over threshold
97.5
trust
+
dimensionscorethresholdseveritytrenddetail
pii_risk0.170.00medium1 potential PII column(s)
human_review_backlog0.100.00medium1 item(s) awaiting review
outlier_spikes0.030.10low7 outlier(s) flagged
duplicates0.020.10low5 duplicate row(s) detected (0 removed)
missingness0.020.10low24 missing cell(s) remain
schema_driftn/a0.00unknownno baseline supplied
type_instability0.000.10low0 column(s) with unstable types
category_churnn/a0.10unknownno baseline supplied
failed_repairs0.000.00low0 high-risk action(s)
+⬇ JSON
\ No newline at end of file diff --git a/docs/examples/suggest_plan_cards.html b/docs/examples/suggest_plan_cards.html index c9117b20..d2b198a2 100644 --- a/docs/examples/suggest_plan_cards.html +++ b/docs/examples/suggest_plan_cards.html @@ -29,5 +29,5 @@ .fd-del-pos{color:#1a7f37}.fd-del-neg{color:#cf222e} .fd-mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8rem} -

freshdata clean plan — balanced

per-column decision cards
email
missing → preserve
confidence 95%
why: identifier column
revenue
no engine action
tier
missing → sentinel
confidence 80%
why: no dominant category — explicit sentinel
alternatives: mode
-⬇ JSON
\ No newline at end of file +

freshdata clean plan — balanced

per-column decision cards
email
missing → preserve
confidence 95%
why: identifier column
revenue
outliers → flag (7 flagged)
tier
missing → sentinel
confidence 80%
why: no dominant category — explicit sentinel
alternatives: mode
+⬇ JSON
\ No newline at end of file diff --git a/docs/installation.md b/docs/installation.md index 0d0494a7..07b827ee 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -59,6 +59,14 @@ Install only what you need: Pass a Polars DataFrame to `fd.clean` and get a Polars DataFrame back. +!!! note "Python 3.9 on Linux aarch64: the `privacy` extra builds from source" + `freshdata-cleaner[privacy]` (and `[all]`) pulls in spaCy through Presidio. + On Python 3.9 spaCy is capped at 3.8.7, which requires `thinc>=8.3.4,<8.4`, + and neither thinc 8.3.4 nor blis 1.2.0 publishes a cp39 Linux aarch64 wheel. + pip therefore compiles thinc and blis from source there, which needs a C/C++ + toolchain and takes several minutes. Python 3.10+ or x86-64 installs use + prebuilt wheels. + ## Verify the installation ```bash diff --git a/pyproject.toml b/pyproject.toml index 72e52101..bebadcd0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -168,6 +168,9 @@ excel = [ # Newer ones still declare Requires-Python >=3.9 but publish only an sdist for # 3.9, and spacy >=3.8.13 cannot even build there (it needs thinc>=8.3.12, which # is Python >=3.10). Keep these caps in sync with the `all` extra. +# Even at these caps, thinc 8.3.4 and blis 1.2.0 have no cp39 Linux aarch64 +# wheel, so that platform compiles them from source (see docs/installation.md). +# No pin avoids it: spacy 3.8.7 requires thinc>=8.3.4,<8.4. privacy = [ "presidio-analyzer>=2.2", "presidio-anonymizer>=2.2", diff --git a/tests/test_enterprise_entity_resolution_dedupe.py b/tests/test_enterprise_entity_resolution_dedupe.py index f36af65f..bd7601ca 100644 --- a/tests/test_enterprise_entity_resolution_dedupe.py +++ b/tests/test_enterprise_entity_resolution_dedupe.py @@ -494,9 +494,10 @@ def test_retail_template_and_overrides(): def test_benchmark_smoke(): root = pathlib.Path(__file__).resolve().parents[1] - spec = importlib.util.spec_from_file_location( - "bench_er", root / "benchmarks" / "bench_entity_resolution.py" - ) + bench_path = root / "benchmarks" / "bench_entity_resolution.py" + if not bench_path.is_file(): + pytest.skip("benchmarks/ directory not present (e.g. running from the sdist)") + spec = importlib.util.spec_from_file_location("bench_er", bench_path) assert spec and spec.loader mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) diff --git a/tests/test_streaming_cli.py b/tests/test_streaming_cli.py index 008d832a..b9826c88 100644 --- a/tests/test_streaming_cli.py +++ b/tests/test_streaming_cli.py @@ -120,9 +120,8 @@ def test_cli_stream_parquet_roundtrip(tmp_path): def bench_module(): sys.path.insert(0, str(BENCH_DIR)) try: - import bench_streaming # type: ignore[import-not-found] - - yield bench_streaming + # benchmarks/ is repo-only (not in the sdist): skip rather than error there. + yield pytest.importorskip("bench_streaming") finally: sys.path.remove(str(BENCH_DIR)) sys.modules.pop("bench_streaming", None) @@ -149,7 +148,12 @@ def test_benchmark_generator_emits_exact_row_count(bench_module): # -- benchmark-stream CLI command --------------------------------------------------- -def test_cli_benchmark_stream_writes_json(tmp_path): +def test_cli_benchmark_stream_writes_json(tmp_path, monkeypatch): + # The command imports bench_streaming from ./benchmarks, which only a git + # checkout has; the sdist ships tests/ without it. + if not (BENCH_DIR / "bench_streaming.py").is_file(): + pytest.skip("benchmarks/ directory not present (e.g. running from the sdist)") + monkeypatch.chdir(BENCH_DIR.parent) report = tmp_path / "bench.json" rc = main(["benchmark-stream", "--rows", "2000", "--batch-size", "1000", "--cols", "6", "--report", str(report)])