data-sampler 3.4.0__tar.gz → 3.4.1__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.
Files changed (42) hide show
  1. {data_sampler-3.4.0 → data_sampler-3.4.1}/CHANGELOG.md +27 -0
  2. {data_sampler-3.4.0 → data_sampler-3.4.1}/PKG-INFO +25 -14
  3. {data_sampler-3.4.0 → data_sampler-3.4.1}/README.md +24 -13
  4. {data_sampler-3.4.0 → data_sampler-3.4.1}/pyproject.toml +1 -1
  5. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/__init__.py +1 -1
  6. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/stats.py +7 -0
  7. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/tui/app.py +382 -44
  8. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_stats.py +19 -0
  9. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_tui.py +299 -0
  10. {data_sampler-3.4.0 → data_sampler-3.4.1}/.gitattributes +0 -0
  11. {data_sampler-3.4.0 → data_sampler-3.4.1}/.github/workflows/release.yml +0 -0
  12. {data_sampler-3.4.0 → data_sampler-3.4.1}/.gitignore +0 -0
  13. {data_sampler-3.4.0 → data_sampler-3.4.1}/LICENSE +0 -0
  14. {data_sampler-3.4.0 → data_sampler-3.4.1}/RELEASING.md +0 -0
  15. {data_sampler-3.4.0 → data_sampler-3.4.1}/ROADMAP.md +0 -0
  16. {data_sampler-3.4.0 → data_sampler-3.4.1}/TROUBLESHOOTING.md +0 -0
  17. {data_sampler-3.4.0 → data_sampler-3.4.1}/examples/employees.csv +0 -0
  18. {data_sampler-3.4.0 → data_sampler-3.4.1}/examples/using_data_sampler.ipynb +0 -0
  19. {data_sampler-3.4.0 → data_sampler-3.4.1}/requirements.txt +0 -0
  20. {data_sampler-3.4.0 → data_sampler-3.4.1}/scripts/run-tui.bat +0 -0
  21. {data_sampler-3.4.0 → data_sampler-3.4.1}/scripts/run-tui.sh +0 -0
  22. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/__main__.py +0 -0
  23. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/_logging.py +0 -0
  24. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/_names.py +0 -0
  25. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/anonymize.py +0 -0
  26. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/cli.py +0 -0
  27. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/engine.py +0 -0
  28. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/io.py +0 -0
  29. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/reduce.py +0 -0
  30. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/report.py +0 -0
  31. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/sampling.py +0 -0
  32. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/tui/__init__.py +0 -0
  33. {data_sampler-3.4.0 → data_sampler-3.4.1}/src/data_sampler/workflow.py +0 -0
  34. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/conftest.py +0 -0
  35. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_anonymize.py +0 -0
  36. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_cli.py +0 -0
  37. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_engine.py +0 -0
  38. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_io.py +0 -0
  39. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_reduce.py +0 -0
  40. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_report.py +0 -0
  41. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_sampling.py +0 -0
  42. {data_sampler-3.4.0 → data_sampler-3.4.1}/tests/test_workflow.py +0 -0
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.4.1 — 2026-07-24
4
+
5
+ - **TUI columns table now breaks the single summary cell into separate
6
+ `mean` / `median` / `mode` / `sd` columns.** Numeric columns fill all four;
7
+ non-numeric columns show `mode` (their most frequent value) with dashes for
8
+ the numeric-only stats. `ColumnStats` gained a `mode` field, populated for
9
+ every column kind by `compute_column_stats`.
10
+ - **Bulk configuration via multi-select on the columns screen.** Ctrl-click
11
+ toggles individual rows, shift-click selects a contiguous range (keyboard:
12
+ `space` toggles the cursor row, `x` clears the selection). Any anonymizer
13
+ choice, stratification-skip, or reduction-skip then applies to every
14
+ selected column at once; the panel title shows the selection count.
15
+ - **Columns can now be excluded from the PCA reduction from the TUI.** A new
16
+ “skip from reduction (PCA)” switch (and the `d` key) sets a per-column
17
+ `skip_reduce` flag that is passed to `reduce_columns(exclude=…)`, so chosen
18
+ numeric columns pass through the reduction unchanged. The columns table
19
+ gained a `reduce` column showing each column's candidacy / skip state, and
20
+ excluded columns are listed in the run report.
21
+ - **Undo / redo for column configuration** (`ctrl+z` / `ctrl+y`), keeping well
22
+ over the last ten steps. Bursts of edits to one option field coalesce into a
23
+ single undo step.
24
+ - **File screen: refresh the directory browser** with `ctrl+r` or the new
25
+ ⟳ refresh button, so files created after launch appear without restarting.
26
+ - **Report screen: save the report + column histograms to a text file** with
27
+ `ctrl+s` or the 💾 save .txt button; it writes `<sample-name>_report.txt`
28
+ next to the sample output.
29
+
3
30
  ## v3.4.0 — 2026-07-24
4
31
 
5
32
  - **Added optional PCA column reduction** (`data_sampler.reduce`): after
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-sampler
3
- Version: 3.4.0
3
+ Version: 3.4.1
4
4
  Summary: Hand someone data that looks and behaves like your production data, isn't your production data, and provably kept its statistical variety — stratified sampling + consistent anonymization, with a terminal UI.
5
5
  Project-URL: Homepage, https://github.com/aaronified/data-sampler
6
6
  Project-URL: Changelog, https://github.com/aaronified/data-sampler/blob/main/CHANGELOG.md
@@ -120,24 +120,35 @@ data_sampler.run_tui("data.csv") # pre-load a file
120
120
 
121
121
  The TUI is a panel-based dashboard (think btop / lazydocker):
122
122
 
123
- 1. **File screen** — type a path or pick a file from the directory browser;
124
- Excel files take an optional sheet name.
123
+ 1. **File screen** — type a path or pick a file from the directory browser
124
+ (**`ctrl+r`** or the **⟳ refresh** button re-scans the folder so files
125
+ created since launch show up); Excel files take an optional sheet name.
125
126
  2. **Columns screen** — every column with its type, missing %, unique count,
126
- distribution sparkline, and summary (modelled after the Data Wrangler
127
- VS Code extension). Select a column to see full stats and distribution
128
- bars, choose an anonymizer for it, and toggle whether it should be
129
- skipped when preserving statistical variety (stratification). Set the
130
- sample size, output folder, optional seed, an optional PCA column
131
- reduction (`reduce`: N components or a variance target), and run.
127
+ distribution sparkline, and per-stat **mean / median / mode / sd** columns
128
+ (modelled after the Data Wrangler VS Code extension). Select a column to
129
+ see full stats and distribution bars, choose an anonymizer for it, and
130
+ toggle whether it should be skipped when preserving statistical variety
131
+ (stratification) or excluded from the PCA reduction. **Multi-select** rows
132
+ to configure them in bulk: **ctrl-click** toggles individual rows,
133
+ **shift-click** selects a range (**space** toggles the cursor row from the
134
+ keyboard, **`x`** clears) — then any anonymizer / skip / reduce choice
135
+ applies to every selected column at once. **`ctrl+z` / `ctrl+y`** undo and
136
+ redo column-config changes (at least the last ten steps). Set the sample
137
+ size, output folder, optional seed, an optional PCA column reduction
138
+ (`reduce`: N components or a variance target), and run.
132
139
  3. **Report screen** — the stratification comparison and anonymization summary
133
140
  on the left, and a **column histograms** panel on the right showing every
134
141
  column's source-vs-sample distribution (numeric columns share bin edges;
135
142
  others use the source's top categories) so you can see at a glance how well
136
- the sample preserved each column. The output path is shown too.
137
-
138
- Key bindings: `ctrl+r` run sample, `a` auto-suggest anonymizer types,
139
- `s` toggle stratification skip,
140
- `escape` back, `ctrl+q` quit.
143
+ the sample preserved each column. The output path is shown too, and
144
+ **`ctrl+s`** (or **💾 save .txt**) writes the report and histograms to a
145
+ `*_report.txt` beside the sample.
146
+
147
+ Key bindings — columns screen: `ctrl+r` run sample, `a` auto-suggest
148
+ anonymizer types, `s` toggle stratification skip, `d` toggle reduction skip,
149
+ `space` select row, `x` clear selection, `ctrl+z`/`ctrl+y` undo/redo,
150
+ `escape` back. Report screen: `ctrl+s` save report to text, `n` new file.
151
+ Everywhere: `ctrl+q` quit.
141
152
 
142
153
  ## CLI (headless)
143
154
 
@@ -65,24 +65,35 @@ data_sampler.run_tui("data.csv") # pre-load a file
65
65
 
66
66
  The TUI is a panel-based dashboard (think btop / lazydocker):
67
67
 
68
- 1. **File screen** — type a path or pick a file from the directory browser;
69
- Excel files take an optional sheet name.
68
+ 1. **File screen** — type a path or pick a file from the directory browser
69
+ (**`ctrl+r`** or the **⟳ refresh** button re-scans the folder so files
70
+ created since launch show up); Excel files take an optional sheet name.
70
71
  2. **Columns screen** — every column with its type, missing %, unique count,
71
- distribution sparkline, and summary (modelled after the Data Wrangler
72
- VS Code extension). Select a column to see full stats and distribution
73
- bars, choose an anonymizer for it, and toggle whether it should be
74
- skipped when preserving statistical variety (stratification). Set the
75
- sample size, output folder, optional seed, an optional PCA column
76
- reduction (`reduce`: N components or a variance target), and run.
72
+ distribution sparkline, and per-stat **mean / median / mode / sd** columns
73
+ (modelled after the Data Wrangler VS Code extension). Select a column to
74
+ see full stats and distribution bars, choose an anonymizer for it, and
75
+ toggle whether it should be skipped when preserving statistical variety
76
+ (stratification) or excluded from the PCA reduction. **Multi-select** rows
77
+ to configure them in bulk: **ctrl-click** toggles individual rows,
78
+ **shift-click** selects a range (**space** toggles the cursor row from the
79
+ keyboard, **`x`** clears) — then any anonymizer / skip / reduce choice
80
+ applies to every selected column at once. **`ctrl+z` / `ctrl+y`** undo and
81
+ redo column-config changes (at least the last ten steps). Set the sample
82
+ size, output folder, optional seed, an optional PCA column reduction
83
+ (`reduce`: N components or a variance target), and run.
77
84
  3. **Report screen** — the stratification comparison and anonymization summary
78
85
  on the left, and a **column histograms** panel on the right showing every
79
86
  column's source-vs-sample distribution (numeric columns share bin edges;
80
87
  others use the source's top categories) so you can see at a glance how well
81
- the sample preserved each column. The output path is shown too.
82
-
83
- Key bindings: `ctrl+r` run sample, `a` auto-suggest anonymizer types,
84
- `s` toggle stratification skip,
85
- `escape` back, `ctrl+q` quit.
88
+ the sample preserved each column. The output path is shown too, and
89
+ **`ctrl+s`** (or **💾 save .txt**) writes the report and histograms to a
90
+ `*_report.txt` beside the sample.
91
+
92
+ Key bindings — columns screen: `ctrl+r` run sample, `a` auto-suggest
93
+ anonymizer types, `s` toggle stratification skip, `d` toggle reduction skip,
94
+ `space` select row, `x` clear selection, `ctrl+z`/`ctrl+y` undo/redo,
95
+ `escape` back. Report screen: `ctrl+s` save report to text, `n` new file.
96
+ Everywhere: `ctrl+q` quit.
86
97
 
87
98
  ## CLI (headless)
88
99
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "data-sampler"
7
- version = "3.4.0"
7
+ version = "3.4.1"
8
8
  description = "Hand someone data that looks and behaves like your production data, isn't your production data, and provably kept its statistical variety — stratified sampling + consistent anonymization, with a terminal UI."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -18,7 +18,7 @@ Public API::
18
18
 
19
19
  from __future__ import annotations
20
20
 
21
- __version__ = "3.4.0"
21
+ __version__ = "3.4.1"
22
22
 
23
23
  from .io import SUPPORTED_EXTENSIONS, list_sheets, load_file, save_output
24
24
  from .reduce import ReductionResult, reduce_columns
@@ -28,6 +28,9 @@ class ColumnStats:
28
28
  mean: float | None = None
29
29
  std: float | None = None
30
30
  median: float | None = None
31
+ # most frequent value: a float for numeric columns, a string otherwise
32
+ # (None when the column is empty). Populated for every kind.
33
+ mode: object | None = None
31
34
  # most frequent values as (value, count), up to 8
32
35
  top_values: list[tuple[str, int]] = field(default_factory=list)
33
36
  # distribution: 10-bin histogram counts for numeric, top-8 category counts otherwise
@@ -146,6 +149,8 @@ def compute_column_stats(series: pd.Series, n_rows: int | None = None) -> Column
146
149
  stats.mean = float(non_null.mean())
147
150
  stats.std = float(non_null.std()) if count > 1 else 0.0
148
151
  stats.median = float(non_null.median())
152
+ m = non_null.mode()
153
+ stats.mode = float(m.iloc[0]) if len(m) else None
149
154
  finite = non_null.astype(float)
150
155
  finite = finite[np.isfinite(finite)]
151
156
  if len(finite) > 0:
@@ -164,6 +169,8 @@ def compute_column_stats(series: pd.Series, n_rows: int | None = None) -> Column
164
169
  stats.top_values = [(str(v), int(c)) for v, c in vc.items()]
165
170
  stats.histogram = [int(c) for c in vc.values]
166
171
  stats.histogram_labels = [str(v) for v in vc.index]
172
+ # the most frequent category is the first entry of value_counts
173
+ stats.mode = stats.top_values[0][0] if stats.top_values else None
167
174
 
168
175
  return stats
169
176