tabalyst 0.2.1__tar.gz → 0.3.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.
Files changed (43) hide show
  1. {tabalyst-0.2.1/src/tabalyst.egg-info → tabalyst-0.3.0}/PKG-INFO +46 -7
  2. {tabalyst-0.2.1 → tabalyst-0.3.0}/README.md +45 -6
  3. {tabalyst-0.2.1 → tabalyst-0.3.0}/pyproject.toml +1 -1
  4. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/__init__.py +6 -0
  5. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/cli/app.py +2 -0
  6. tabalyst-0.3.0/src/tabalyst/cli/sample.py +132 -0
  7. tabalyst-0.3.0/src/tabalyst/sampling.py +342 -0
  8. tabalyst-0.3.0/src/tabalyst/sampling_service.py +172 -0
  9. {tabalyst-0.2.1 → tabalyst-0.3.0/src/tabalyst.egg-info}/PKG-INFO +46 -7
  10. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst.egg-info/SOURCES.txt +5 -1
  11. {tabalyst-0.2.1 → tabalyst-0.3.0}/tests/test_cli.py +2 -2
  12. tabalyst-0.3.0/tests/test_sampling.py +346 -0
  13. {tabalyst-0.2.1 → tabalyst-0.3.0}/LICENSE +0 -0
  14. {tabalyst-0.2.1 → tabalyst-0.3.0}/MANIFEST.in +0 -0
  15. {tabalyst-0.2.1 → tabalyst-0.3.0}/examples/README.md +0 -0
  16. {tabalyst-0.2.1 → tabalyst-0.3.0}/examples/config.json +0 -0
  17. {tabalyst-0.2.1 → tabalyst-0.3.0}/examples/input/basic.csv +0 -0
  18. {tabalyst-0.2.1 → tabalyst-0.3.0}/examples/input/insurance-customers.csv +0 -0
  19. {tabalyst-0.2.1 → tabalyst-0.3.0}/setup.cfg +0 -0
  20. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/__main__.py +0 -0
  21. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/_version.py +0 -0
  22. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/analysis.py +0 -0
  23. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/cli/__init__.py +0 -0
  24. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/cli/report.py +0 -0
  25. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/config.py +0 -0
  26. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/errors.py +0 -0
  27. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/execution_log.py +0 -0
  28. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/ingestion.py +0 -0
  29. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/models.py +0 -0
  30. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/progress.py +0 -0
  31. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/report_service.py +0 -0
  32. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/reporting.py +0 -0
  33. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/service.py +0 -0
  34. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/static/report.js +0 -0
  35. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/static/theme.css +0 -0
  36. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst/templates/report.html +0 -0
  37. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst.egg-info/dependency_links.txt +0 -0
  38. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst.egg-info/entry_points.txt +0 -0
  39. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst.egg-info/requires.txt +0 -0
  40. {tabalyst-0.2.1 → tabalyst-0.3.0}/src/tabalyst.egg-info/top_level.txt +0 -0
  41. {tabalyst-0.2.1 → tabalyst-0.3.0}/tests/test_analysis.py +0 -0
  42. {tabalyst-0.2.1 → tabalyst-0.3.0}/tests/test_public_api.py +0 -0
  43. {tabalyst-0.2.1 → tabalyst-0.3.0}/tests/test_reporting.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tabalyst
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Open-source toolkit for understanding and working with unfamiliar data.
5
5
  Author: Gregory Borelli
6
6
  License-Expression: MIT
@@ -51,9 +51,12 @@ the shared toolkit architecture is being established.
51
51
  Tabalyst supports Python 3.11, 3.12, 3.13, and 3.14.
52
52
 
53
53
  ```console
54
- pip install tabalyst
54
+ pip install --upgrade tabalyst
55
55
  ```
56
56
 
57
+ The same command installs Tabalyst or updates it. Tabalyst is in alpha and
58
+ changes often: update it before each new test.
59
+
57
60
  ## Tabalyst Report
58
61
 
59
62
  | Use case | Command | Destination |
@@ -170,6 +173,34 @@ tabalyst --version
170
173
 
171
174
  `python -m tabalyst` accepts the same commands.
172
175
 
176
+ ## Tabalyst Sample
177
+
178
+ Create a smaller CSV without modifying the source:
179
+
180
+ ```console
181
+ tabalyst sample customers.csv --sample-method random --rows 1000 --seed 42
182
+ ```
183
+
184
+ The default output is `customers.sample.csv` beside the source. Use `-o` to
185
+ name the output for one input, or `-d` to sample several files into one
186
+ directory:
187
+
188
+ ```console
189
+ tabalyst sample customers.csv --sample-method first --rows 100 -o test.csv
190
+ tabalyst sample *.csv --sample-method random --percent 5 -d samples/
191
+ ```
192
+
193
+ Available methods are `first`, `last`, `random` and `stratified`. Stratified
194
+ sampling approximately preserves the distribution of a selected field:
195
+
196
+ ```console
197
+ tabalyst sample customers.csv --sample-method stratified --field province --rows 1000 --seed 42
198
+ ```
199
+
200
+ Sampling reads CSV records as a stream. Random and stratified sampling keep
201
+ only the requested sample, plus stratum counts, in memory. Existing outputs
202
+ require `--force`, and an input file is never overwritten.
203
+
173
204
  ## What the report analyzes
174
205
 
175
206
  - Dataset dimensions, missing cells, duplicates, and quality observations.
@@ -200,6 +231,13 @@ batch = tabalyst.generate_reports(
200
231
  ["*.csv"],
201
232
  output_dir="reports",
202
233
  )
234
+
235
+ sample = tabalyst.sample_csv(
236
+ "customers.csv",
237
+ method="random",
238
+ rows=1000,
239
+ seed=42,
240
+ )
203
241
  ```
204
242
 
205
243
  `analyze()` returns the JSON-serializable profile for one report.
@@ -224,7 +262,7 @@ Configuration files are strict JSON. A minimal file is:
224
262
 
225
263
  Explicit CLI or Python arguments override the configuration file, which overrides
226
264
  Tabalyst defaults. See the
227
- [configuration reference](https://github.com/loribel-labs/tabalyst/blob/main/docs/configuration.md)
265
+ [configuration reference](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
228
266
  for all analysis settings.
229
267
 
230
268
  ## Local-first behavior and current limits
@@ -233,8 +271,9 @@ Tabalyst performs analysis locally and adds no telemetry or remote processing.
233
271
  Generated JSON and HTML may contain source values and should be shared
234
272
  accordingly.
235
273
 
236
- The complete CSV is currently loaded into memory. File and phase progress is
237
- available now; row percentages, throughput estimates, recursive directory input,
274
+ Report analysis currently loads the complete CSV into memory. Sampling uses a
275
+ streaming reader with bounded row storage. File and phase progress is available
276
+ for reports; row percentages, throughput estimates, recursive directory input,
238
277
  and parallel batch execution will require later ingestion work.
239
278
 
240
279
  ## Examples and development
@@ -251,8 +290,8 @@ python -m build
251
290
 
252
291
  Additional documentation:
253
292
 
254
- - [Architecture](https://github.com/loribel-labs/tabalyst/blob/main/docs/architecture.md)
255
- - [Configuration](https://github.com/loribel-labs/tabalyst/blob/main/docs/configuration.md)
293
+ - [Architecture](https://github.com/loribel-labs/tabalyst/blob/main/docs/dev/architecture.md)
294
+ - [Configuration](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
256
295
  - [Release procedure](https://github.com/loribel-labs/tabalyst/blob/main/RELEASING.md)
257
296
 
258
297
  Please report defects and feature requests through the
@@ -17,9 +17,12 @@ the shared toolkit architecture is being established.
17
17
  Tabalyst supports Python 3.11, 3.12, 3.13, and 3.14.
18
18
 
19
19
  ```console
20
- pip install tabalyst
20
+ pip install --upgrade tabalyst
21
21
  ```
22
22
 
23
+ The same command installs Tabalyst or updates it. Tabalyst is in alpha and
24
+ changes often: update it before each new test.
25
+
23
26
  ## Tabalyst Report
24
27
 
25
28
  | Use case | Command | Destination |
@@ -136,6 +139,34 @@ tabalyst --version
136
139
 
137
140
  `python -m tabalyst` accepts the same commands.
138
141
 
142
+ ## Tabalyst Sample
143
+
144
+ Create a smaller CSV without modifying the source:
145
+
146
+ ```console
147
+ tabalyst sample customers.csv --sample-method random --rows 1000 --seed 42
148
+ ```
149
+
150
+ The default output is `customers.sample.csv` beside the source. Use `-o` to
151
+ name the output for one input, or `-d` to sample several files into one
152
+ directory:
153
+
154
+ ```console
155
+ tabalyst sample customers.csv --sample-method first --rows 100 -o test.csv
156
+ tabalyst sample *.csv --sample-method random --percent 5 -d samples/
157
+ ```
158
+
159
+ Available methods are `first`, `last`, `random` and `stratified`. Stratified
160
+ sampling approximately preserves the distribution of a selected field:
161
+
162
+ ```console
163
+ tabalyst sample customers.csv --sample-method stratified --field province --rows 1000 --seed 42
164
+ ```
165
+
166
+ Sampling reads CSV records as a stream. Random and stratified sampling keep
167
+ only the requested sample, plus stratum counts, in memory. Existing outputs
168
+ require `--force`, and an input file is never overwritten.
169
+
139
170
  ## What the report analyzes
140
171
 
141
172
  - Dataset dimensions, missing cells, duplicates, and quality observations.
@@ -166,6 +197,13 @@ batch = tabalyst.generate_reports(
166
197
  ["*.csv"],
167
198
  output_dir="reports",
168
199
  )
200
+
201
+ sample = tabalyst.sample_csv(
202
+ "customers.csv",
203
+ method="random",
204
+ rows=1000,
205
+ seed=42,
206
+ )
169
207
  ```
170
208
 
171
209
  `analyze()` returns the JSON-serializable profile for one report.
@@ -190,7 +228,7 @@ Configuration files are strict JSON. A minimal file is:
190
228
 
191
229
  Explicit CLI or Python arguments override the configuration file, which overrides
192
230
  Tabalyst defaults. See the
193
- [configuration reference](https://github.com/loribel-labs/tabalyst/blob/main/docs/configuration.md)
231
+ [configuration reference](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
194
232
  for all analysis settings.
195
233
 
196
234
  ## Local-first behavior and current limits
@@ -199,8 +237,9 @@ Tabalyst performs analysis locally and adds no telemetry or remote processing.
199
237
  Generated JSON and HTML may contain source values and should be shared
200
238
  accordingly.
201
239
 
202
- The complete CSV is currently loaded into memory. File and phase progress is
203
- available now; row percentages, throughput estimates, recursive directory input,
240
+ Report analysis currently loads the complete CSV into memory. Sampling uses a
241
+ streaming reader with bounded row storage. File and phase progress is available
242
+ for reports; row percentages, throughput estimates, recursive directory input,
204
243
  and parallel batch execution will require later ingestion work.
205
244
 
206
245
  ## Examples and development
@@ -217,8 +256,8 @@ python -m build
217
256
 
218
257
  Additional documentation:
219
258
 
220
- - [Architecture](https://github.com/loribel-labs/tabalyst/blob/main/docs/architecture.md)
221
- - [Configuration](https://github.com/loribel-labs/tabalyst/blob/main/docs/configuration.md)
259
+ - [Architecture](https://github.com/loribel-labs/tabalyst/blob/main/docs/dev/architecture.md)
260
+ - [Configuration](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
222
261
  - [Release procedure](https://github.com/loribel-labs/tabalyst/blob/main/RELEASING.md)
223
262
 
224
263
  Please report defects and feature requests through the
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tabalyst"
7
- version = "0.2.1"
7
+ version = "0.3.0"
8
8
  description = "Open-source toolkit for understanding and working with unfamiliar data."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -6,6 +6,10 @@ from tabalyst.config import AnalysisConfig as AnalysisConfig
6
6
  from tabalyst.errors import ConfigurationError, InputError, ReportError, TabalystError
7
7
  from tabalyst.report_service import generate_reports as generate_reports
8
8
  from tabalyst.reporting import render_report as render_report
9
+ from tabalyst.sampling import SampleMethod as SampleMethod
10
+ from tabalyst.sampling import SampleResult as SampleResult
11
+ from tabalyst.sampling import sample_csv as sample_csv
12
+ from tabalyst.sampling_service import generate_samples as generate_samples
9
13
  from tabalyst.service import analyze
10
14
  from tabalyst.service import analyze_csv as analyze_csv
11
15
 
@@ -17,6 +21,8 @@ __all__ = [
17
21
  "__version__",
18
22
  "analyze",
19
23
  "generate_reports",
24
+ "generate_samples",
25
+ "sample_csv",
20
26
  ]
21
27
 
22
28
  # Alpha compatibility imports remain available, but are not part of the 0.1.0 API.
@@ -6,6 +6,7 @@ import typer
6
6
 
7
7
  from tabalyst import __version__
8
8
  from tabalyst.cli.report import report_command
9
+ from tabalyst.cli.sample import sample_command
9
10
 
10
11
  app = typer.Typer(
11
12
  add_completion=False,
@@ -37,6 +38,7 @@ def root(
37
38
 
38
39
 
39
40
  app.command("report")(report_command)
41
+ app.command("sample")(sample_command)
40
42
 
41
43
 
42
44
  def main() -> None:
@@ -0,0 +1,132 @@
1
+ """CLI adapter for CSV sampling."""
2
+
3
+ from pathlib import Path
4
+ from typing import Annotated
5
+
6
+ import typer
7
+
8
+ from tabalyst.errors import ConfigurationError, InputError, TabalystError
9
+ from tabalyst.sampling import SampleMethod, SampleResult
10
+ from tabalyst.sampling_service import generate_samples
11
+
12
+
13
+ def _print_result(result: SampleResult) -> None:
14
+ typer.echo("Sample created successfully.", err=True)
15
+ typer.echo("", err=True)
16
+ typer.echo(f"Source: {result.source}", err=True)
17
+ typer.echo(f"Method: {result.method.value}", err=True)
18
+ if result.field is not None:
19
+ typer.echo(f"Field: {result.field}", err=True)
20
+ typer.echo(f"Source rows: {result.source_rows:,}", err=True)
21
+ typer.echo(f"Sample rows: {result.sample_rows:,}", err=True)
22
+ typer.echo(f"Sample rate: {result.sample_rate:.2f}%", err=True)
23
+ if result.seed is not None and result.method in {
24
+ SampleMethod.RANDOM,
25
+ SampleMethod.STRATIFIED,
26
+ }:
27
+ typer.echo(f"Seed: {result.seed}", err=True)
28
+ typer.echo(f"Output: {result.output.resolve()}", err=True)
29
+
30
+
31
+ def sample_command(
32
+ inputs: Annotated[
33
+ list[str],
34
+ typer.Argument(
35
+ metavar="INPUT...",
36
+ help="One or more CSV files or non-recursive glob patterns.",
37
+ ),
38
+ ],
39
+ method: Annotated[
40
+ SampleMethod,
41
+ typer.Option("--sample-method", help="Sampling strategy."),
42
+ ],
43
+ rows: Annotated[
44
+ int | None,
45
+ typer.Option("--rows", help="Number of data rows to select."),
46
+ ] = None,
47
+ percent: Annotated[
48
+ float | None,
49
+ typer.Option("--percent", help="Percentage of data rows to select."),
50
+ ] = None,
51
+ field: Annotated[
52
+ str | None,
53
+ typer.Option("--field", help="Field used for stratified sampling."),
54
+ ] = None,
55
+ seed: Annotated[
56
+ int | None,
57
+ typer.Option("--seed", help="Seed for reproducible random selection."),
58
+ ] = None,
59
+ output: Annotated[
60
+ Path | None,
61
+ typer.Option("--output", "-o", help="Output CSV filename."),
62
+ ] = None,
63
+ output_dir: Annotated[
64
+ Path | None,
65
+ typer.Option(
66
+ "--output-dir",
67
+ "-d",
68
+ help="Directory for samples named after their source files.",
69
+ ),
70
+ ] = None,
71
+ config: Annotated[
72
+ Path | None,
73
+ typer.Option("--config", "-c", help="JSON configuration file."),
74
+ ] = None,
75
+ delimiter: Annotated[
76
+ str | None,
77
+ typer.Option("--delimiter", help="One-character CSV delimiter."),
78
+ ] = None,
79
+ encoding: Annotated[
80
+ str | None,
81
+ typer.Option("--encoding", help="CSV text encoding."),
82
+ ] = None,
83
+ force: Annotated[
84
+ bool,
85
+ typer.Option("--force", "-f", help="Replace an existing sample file."),
86
+ ] = False,
87
+ ) -> None:
88
+ """Create a CSV sample without modifying the source file."""
89
+
90
+ try:
91
+ batch = generate_samples(
92
+ inputs,
93
+ method=method,
94
+ rows=rows,
95
+ percent=percent,
96
+ field=field,
97
+ seed=seed,
98
+ output=output,
99
+ output_dir=output_dir,
100
+ config_path=config,
101
+ delimiter=delimiter,
102
+ encoding=encoding,
103
+ force=force,
104
+ )
105
+ except TabalystError as exc:
106
+ typer.echo(f"Error: {exc}", err=True)
107
+ if isinstance(exc, ConfigurationError):
108
+ code = 2
109
+ elif isinstance(exc, InputError):
110
+ code = 4
111
+ else:
112
+ code = 1
113
+ raise typer.Exit(code) from exc
114
+
115
+ for index, success in enumerate(batch.successes):
116
+ if index:
117
+ typer.echo("", err=True)
118
+ _print_result(success.result)
119
+ for failure in batch.failures:
120
+ typer.echo(f"Error [{failure.job.source}]: {failure.error}", err=True)
121
+ if len(batch.plan.jobs) > 1:
122
+ typer.echo(
123
+ f"{len(batch.successes)} succeeded, {len(batch.failures)} failed",
124
+ err=True,
125
+ )
126
+ if batch.failures:
127
+ code = (
128
+ 4
129
+ if all(isinstance(item.error, InputError) for item in batch.failures)
130
+ else 1
131
+ )
132
+ raise typer.Exit(code)