tabalyst 0.2.1__tar.gz → 0.4.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 (109) hide show
  1. {tabalyst-0.2.1/src/tabalyst.egg-info → tabalyst-0.4.0}/PKG-INFO +121 -17
  2. {tabalyst-0.2.1 → tabalyst-0.4.0}/README.md +118 -14
  3. {tabalyst-0.2.1 → tabalyst-0.4.0}/examples/README.md +12 -4
  4. tabalyst-0.4.0/examples/config.json +60 -0
  5. {tabalyst-0.2.1 → tabalyst-0.4.0}/pyproject.toml +6 -3
  6. tabalyst-0.4.0/src/tabalyst/__init__.py +34 -0
  7. tabalyst-0.4.0/src/tabalyst/batch.py +157 -0
  8. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/cli/app.py +4 -0
  9. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/cli/report.py +48 -63
  10. tabalyst-0.4.0/src/tabalyst/cli/sample.py +132 -0
  11. tabalyst-0.4.0/src/tabalyst/cli/scan.py +180 -0
  12. tabalyst-0.4.0/src/tabalyst/cli/terminal.py +56 -0
  13. tabalyst-0.4.0/src/tabalyst/config.py +262 -0
  14. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/execution_log.py +6 -4
  15. tabalyst-0.4.0/src/tabalyst/models.py +386 -0
  16. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/progress.py +7 -0
  17. tabalyst-0.4.0/src/tabalyst/report_config.py +72 -0
  18. tabalyst-0.4.0/src/tabalyst/report_profile.py +1084 -0
  19. tabalyst-0.4.0/src/tabalyst/report_service.py +268 -0
  20. tabalyst-0.4.0/src/tabalyst/reporting.py +130 -0
  21. tabalyst-0.4.0/src/tabalyst/sampling.py +344 -0
  22. tabalyst-0.4.0/src/tabalyst/sampling_service.py +133 -0
  23. tabalyst-0.4.0/src/tabalyst/scan_reuse.py +147 -0
  24. tabalyst-0.4.0/src/tabalyst/scan_service.py +184 -0
  25. tabalyst-0.4.0/src/tabalyst/scanner/__init__.py +12 -0
  26. tabalyst-0.4.0/src/tabalyst/scanner/api.py +175 -0
  27. tabalyst-0.4.0/src/tabalyst/scanner/config.py +519 -0
  28. tabalyst-0.4.0/src/tabalyst/scanner/detectors/__init__.py +16 -0
  29. tabalyst-0.4.0/src/tabalyst/scanner/detectors/amount.py +77 -0
  30. tabalyst-0.4.0/src/tabalyst/scanner/detectors/base.py +172 -0
  31. tabalyst-0.4.0/src/tabalyst/scanner/detectors/boolean.py +69 -0
  32. tabalyst-0.4.0/src/tabalyst/scanner/detectors/currency.py +134 -0
  33. tabalyst-0.4.0/src/tabalyst/scanner/detectors/email.py +86 -0
  34. tabalyst-0.4.0/src/tabalyst/scanner/detectors/enumeration.py +76 -0
  35. tabalyst-0.4.0/src/tabalyst/scanner/detectors/ip.py +128 -0
  36. tabalyst-0.4.0/src/tabalyst/scanner/detectors/names.py +87 -0
  37. tabalyst-0.4.0/src/tabalyst/scanner/detectors/number.py +161 -0
  38. tabalyst-0.4.0/src/tabalyst/scanner/detectors/pattern.py +48 -0
  39. tabalyst-0.4.0/src/tabalyst/scanner/detectors/percentage.py +43 -0
  40. tabalyst-0.4.0/src/tabalyst/scanner/detectors/phone.py +140 -0
  41. tabalyst-0.4.0/src/tabalyst/scanner/detectors/postal.py +99 -0
  42. tabalyst-0.4.0/src/tabalyst/scanner/detectors/quantity.py +100 -0
  43. tabalyst-0.4.0/src/tabalyst/scanner/detectors/registry.py +445 -0
  44. tabalyst-0.4.0/src/tabalyst/scanner/detectors/shape.py +36 -0
  45. tabalyst-0.4.0/src/tabalyst/scanner/detectors/temporal.py +399 -0
  46. tabalyst-0.4.0/src/tabalyst/scanner/detectors/url.py +144 -0
  47. tabalyst-0.4.0/src/tabalyst/scanner/detectors/uuid.py +97 -0
  48. tabalyst-0.4.0/src/tabalyst/scanner/diagnostics.py +82 -0
  49. tabalyst-0.4.0/src/tabalyst/scanner/engine.py +172 -0
  50. tabalyst-0.4.0/src/tabalyst/scanner/exposure.py +102 -0
  51. tabalyst-0.4.0/src/tabalyst/scanner/field.py +144 -0
  52. tabalyst-0.4.0/src/tabalyst/scanner/measures.py +452 -0
  53. tabalyst-0.4.0/src/tabalyst/scanner/models.py +525 -0
  54. tabalyst-0.4.0/src/tabalyst/scanner/normalization.py +99 -0
  55. tabalyst-0.4.0/src/tabalyst/scanner/observations.py +117 -0
  56. tabalyst-0.4.0/src/tabalyst/scanner/paths.py +142 -0
  57. tabalyst-0.4.0/src/tabalyst/scanner/readers/__init__.py +1 -0
  58. tabalyst-0.4.0/src/tabalyst/scanner/readers/base.py +78 -0
  59. tabalyst-0.4.0/src/tabalyst/scanner/readers/csv_reader.py +158 -0
  60. tabalyst-0.4.0/src/tabalyst/scanner/readers/json_reader.py +532 -0
  61. tabalyst-0.4.0/src/tabalyst/scanner/records.py +227 -0
  62. tabalyst-0.4.0/src/tabalyst/scanner/structure.py +320 -0
  63. tabalyst-0.4.0/src/tabalyst/scanner/technical.py +77 -0
  64. tabalyst-0.4.0/src/tabalyst/scanner/values.py +558 -0
  65. tabalyst-0.4.0/src/tabalyst/service.py +325 -0
  66. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/static/report.js +17 -3
  67. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/static/theme.css +24 -0
  68. tabalyst-0.4.0/src/tabalyst/templates/report.html +108 -0
  69. {tabalyst-0.2.1 → tabalyst-0.4.0/src/tabalyst.egg-info}/PKG-INFO +121 -17
  70. tabalyst-0.4.0/src/tabalyst.egg-info/SOURCES.txt +95 -0
  71. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst.egg-info/requires.txt +2 -2
  72. {tabalyst-0.2.1 → tabalyst-0.4.0}/tests/test_cli.py +17 -13
  73. {tabalyst-0.2.1 → tabalyst-0.4.0}/tests/test_public_api.py +2 -2
  74. {tabalyst-0.2.1 → tabalyst-0.4.0}/tests/test_reporting.py +4 -4
  75. tabalyst-0.4.0/tests/test_sampling.py +362 -0
  76. tabalyst-0.4.0/tests/test_scan_command.py +416 -0
  77. tabalyst-0.4.0/tests/test_scanner_amounts.py +618 -0
  78. tabalyst-0.4.0/tests/test_scanner_config.py +85 -0
  79. tabalyst-0.4.0/tests/test_scanner_csv.py +170 -0
  80. tabalyst-0.4.0/tests/test_scanner_email_url.py +432 -0
  81. tabalyst-0.4.0/tests/test_scanner_exposure.py +263 -0
  82. tabalyst-0.4.0/tests/test_scanner_paths.py +72 -0
  83. tabalyst-0.4.0/tests/test_scanner_phone.py +308 -0
  84. tabalyst-0.4.0/tests/test_scanner_postal.py +270 -0
  85. tabalyst-0.4.0/tests/test_scanner_uuid_ip.py +518 -0
  86. tabalyst-0.2.1/examples/config.json +0 -48
  87. tabalyst-0.2.1/src/tabalyst/__init__.py +0 -22
  88. tabalyst-0.2.1/src/tabalyst/analysis.py +0 -883
  89. tabalyst-0.2.1/src/tabalyst/config.py +0 -214
  90. tabalyst-0.2.1/src/tabalyst/ingestion.py +0 -86
  91. tabalyst-0.2.1/src/tabalyst/models.py +0 -211
  92. tabalyst-0.2.1/src/tabalyst/report_service.py +0 -244
  93. tabalyst-0.2.1/src/tabalyst/reporting.py +0 -73
  94. tabalyst-0.2.1/src/tabalyst/service.py +0 -182
  95. tabalyst-0.2.1/src/tabalyst/templates/report.html +0 -83
  96. tabalyst-0.2.1/src/tabalyst.egg-info/SOURCES.txt +0 -37
  97. tabalyst-0.2.1/tests/test_analysis.py +0 -621
  98. {tabalyst-0.2.1 → tabalyst-0.4.0}/LICENSE +0 -0
  99. {tabalyst-0.2.1 → tabalyst-0.4.0}/MANIFEST.in +0 -0
  100. {tabalyst-0.2.1 → tabalyst-0.4.0}/examples/input/basic.csv +0 -0
  101. {tabalyst-0.2.1 → tabalyst-0.4.0}/examples/input/insurance-customers.csv +0 -0
  102. {tabalyst-0.2.1 → tabalyst-0.4.0}/setup.cfg +0 -0
  103. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/__main__.py +0 -0
  104. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/_version.py +0 -0
  105. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/cli/__init__.py +0 -0
  106. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/errors.py +0 -0
  107. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst.egg-info/dependency_links.txt +0 -0
  108. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst.egg-info/entry_points.txt +0 -0
  109. {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tabalyst
3
- Version: 0.2.1
3
+ Version: 0.4.0
4
4
  Summary: Open-source toolkit for understanding and working with unfamiliar data.
5
5
  Author: Gregory Borelli
6
6
  License-Expression: MIT
@@ -21,9 +21,9 @@ Classifier: Topic :: Scientific/Engineering :: Information Analysis
21
21
  Requires-Python: >=3.11
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
+ Requires-Dist: ijson>=3.3
24
25
  Requires-Dist: jinja2>=3.1
25
- Requires-Dist: pandas>=2.2
26
- Requires-Dist: pydantic>=2.10
26
+ Requires-Dist: pydantic>=2.11
27
27
  Requires-Dist: typer>=0.12
28
28
  Provides-Extra: dev
29
29
  Requires-Dist: build>=1.2; extra == "dev"
@@ -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 |
@@ -62,6 +65,8 @@ pip install tabalyst
62
65
  | One file, custom name | `tabalyst report data.csv -o report.html` | The file `report.html` |
63
66
  | Several files, automatic names | `tabalyst report *.csv` | Beside each source |
64
67
  | Several files, one directory | `tabalyst report *.csv -d reports/` | The directory `reports/` |
68
+ | One JSON file | `tabalyst report data.json` | `data.report.html` beside the source |
69
+ | From a scan document | `tabalyst report --scan data.scan.json` | `data.html` beside the scan |
65
70
 
66
71
  The simplest command keeps the source filename:
67
72
 
@@ -84,6 +89,18 @@ Use `-o` to choose a different HTML filename for one source:
84
89
  tabalyst report customers.csv -o customer-analysis.html
85
90
  ```
86
91
 
92
+ ### JSON files
93
+
94
+ A JSON file gets a report too, named `<stem>.report.html` so its profile
95
+ `<stem>.report.json` never replaces the source. Each collection of records,
96
+ such as the `customers` array of `{"customers": [...]}`, is a dataset of the
97
+ report, and nested fields are columns named by their path, such as
98
+ `address.city`:
99
+
100
+ ```console
101
+ tabalyst report orders.json
102
+ ```
103
+
87
104
  ### Multiple files
88
105
 
89
106
  Report several CSV files at once:
@@ -170,16 +187,81 @@ tabalyst --version
170
187
 
171
188
  `python -m tabalyst` accepts the same commands.
172
189
 
190
+ ## Tabalyst Sample
191
+
192
+ Create a smaller CSV without modifying the source:
193
+
194
+ ```console
195
+ tabalyst sample customers.csv --sample-method random --rows 1000 --seed 42
196
+ ```
197
+
198
+ The default output is `customers.sample.csv` beside the source. Use `-o` to
199
+ name the output for one input, or `-d` to sample several files into one
200
+ directory:
201
+
202
+ ```console
203
+ tabalyst sample customers.csv --sample-method first --rows 100 -o test.csv
204
+ tabalyst sample *.csv --sample-method random --percent 5 -d samples/
205
+ ```
206
+
207
+ Available methods are `first`, `last`, `random` and `stratified`. Stratified
208
+ sampling approximately preserves the distribution of a selected field:
209
+
210
+ ```console
211
+ tabalyst sample customers.csv --sample-method stratified --field province --rows 1000 --seed 42
212
+ ```
213
+
214
+ Sampling reads CSV records as a stream. Random and stratified sampling keep
215
+ only the requested sample, plus stratum counts, in memory. Existing outputs
216
+ require `--force`, and an input file is never overwritten.
217
+
218
+ ## Tabalyst Scan
219
+
220
+ Describe every field of a CSV or JSON file in one JSON document:
221
+
222
+ ```console
223
+ tabalyst scan customers.csv
224
+ tabalyst scan orders.json --collection "$.customers[]"
225
+ ```
226
+
227
+ The default output is `customers.scan.json` beside the source; `-o`, `-d` and
228
+ `--force` work as for the other commands. Tabalyst Scan reads the file once as
229
+ a stream, with memory bounded by configurable limits, and records for each
230
+ field its presence, native types, missing values, frequencies, exact
231
+ statistics, normalization variants, technical type and the result of every
232
+ detector: numbers with decimal commas, dates, booleans, enumerations, email
233
+ addresses, URLs, phone numbers, postal codes, currency amounts, percentages,
234
+ quantities, UUIDs and IP addresses. Values of sensitive fields, such as email
235
+ addresses, are masked by default. Results are written atomically: an
236
+ interrupted scan never leaves a partial file.
237
+
238
+ Build the report from a scan document without reading the source again:
239
+
240
+ ```console
241
+ tabalyst report --scan customers.scan.json
242
+ ```
243
+
244
+ Tabalyst refuses a scan whose source changed since it was written, or whose
245
+ settings differ from the `scan` settings of `--config`.
246
+
247
+ See [Scan CSV and JSON files](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/how-to/scan-files.md)
248
+ and the [scan format](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/scan-format.md).
249
+
173
250
  ## What the report analyzes
174
251
 
175
252
  - Dataset dimensions, missing cells, duplicates, and quality observations.
176
253
  - Physical and semantic types with confidence and error rates.
177
254
  - Numeric, date, string-length, normalization, and value distributions.
178
- - Distinct values, representative examples, enum candidates, and date formats.
255
+ - Distinct values, representative examples, date formats, and semantic types
256
+ such as enumerations, email addresses, phone numbers, and postal codes.
179
257
  - CSV record widths, quoting, encoding, and delimiter configuration.
180
258
  - A bounded raw-data preview while every record is analyzed.
181
259
 
182
- The HTML report is self-contained and works without a CDN or network connection.
260
+ The report is built on Tabalyst Scan: it reads the CSV once as a stream and
261
+ masks values of sensitive columns, such as email addresses, by default.
262
+ Ambiguous dates stay ambiguous; the report shows the evidence of the column
263
+ without applying it. The HTML report is self-contained and works without a CDN
264
+ or network connection.
183
265
  The JSON profile contains the same canonical analysis result for scripts and
184
266
  future Tabalyst tools.
185
267
 
@@ -200,10 +282,24 @@ batch = tabalyst.generate_reports(
200
282
  ["*.csv"],
201
283
  output_dir="reports",
202
284
  )
285
+
286
+ sample = tabalyst.sample_csv(
287
+ "customers.csv",
288
+ method="random",
289
+ rows=1000,
290
+ seed=42,
291
+ )
292
+
293
+ scan = tabalyst.scan("orders.json")
294
+ scans = tabalyst.generate_scans(["data/*.json"], output_dir="scans")
295
+ reports = tabalyst.generate_reports(["scans/*.scan.json"], from_scan=True)
203
296
  ```
204
297
 
205
298
  `analyze()` returns the JSON-serializable profile for one report.
206
299
  `generate_reports()` returns the complete batch plan, successes, and failures.
300
+ `scan()` returns a scan result without writing anything; `generate_scans()`
301
+ writes one `.scan.json` document per source; `from_scan=True` builds reports
302
+ from such documents.
207
303
  Expected failures derive from `tabalyst.TabalystError`.
208
304
 
209
305
  Existing artifacts are never replaced silently. Pass `force=True` when
@@ -215,16 +311,22 @@ Configuration files are strict JSON. A minimal file is:
215
311
 
216
312
  ```json
217
313
  {
218
- "csv": {
219
- "delimiter": ";",
220
- "encoding": "cp1252"
314
+ "scan": {
315
+ "csv": {
316
+ "delimiter": ";",
317
+ "encoding": "cp1252"
318
+ },
319
+ "values": {"null_markers": ["N/A"]}
221
320
  }
222
321
  }
223
322
  ```
224
323
 
225
- Explicit CLI or Python arguments override the configuration file, which overrides
226
- Tabalyst defaults. See the
227
- [configuration reference](https://github.com/loribel-labs/tabalyst/blob/main/docs/configuration.md)
324
+ Analysis settings, CSV reading included, go in the `scan` object, shared by
325
+ `tabalyst report` and `tabalyst scan`. Top-level settings shape the report
326
+ presentation, and `csv` configures `tabalyst sample`. Explicit CLI or Python
327
+ arguments override the configuration file, which overrides Tabalyst defaults.
328
+ No configuration file is loaded unless it is passed with `--config`. See the
329
+ [configuration reference](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
228
330
  for all analysis settings.
229
331
 
230
332
  ## Local-first behavior and current limits
@@ -233,9 +335,11 @@ Tabalyst performs analysis locally and adds no telemetry or remote processing.
233
335
  Generated JSON and HTML may contain source values and should be shared
234
336
  accordingly.
235
337
 
236
- The complete CSV is currently loaded into memory. File and phase progress is
237
- available now; row percentages, throughput estimates, recursive directory input,
238
- and parallel batch execution will require later ingestion work.
338
+ Reports, samples, and scans read files as a stream, with memory bounded by
339
+ configurable limits, including duplicate-row detection
340
+ (`scan.limits.max_tracked_records`). Reports and scans show the share of the file read;
341
+ throughput estimates, recursive directory input, and parallel batch execution
342
+ will require later work.
239
343
 
240
344
  ## Examples and development
241
345
 
@@ -251,8 +355,8 @@ python -m build
251
355
 
252
356
  Additional documentation:
253
357
 
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)
358
+ - [Architecture](https://github.com/loribel-labs/tabalyst/blob/main/docs/dev/architecture.md)
359
+ - [Configuration](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
256
360
  - [Release procedure](https://github.com/loribel-labs/tabalyst/blob/main/RELEASING.md)
257
361
 
258
362
  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 |
@@ -28,6 +31,8 @@ pip install tabalyst
28
31
  | One file, custom name | `tabalyst report data.csv -o report.html` | The file `report.html` |
29
32
  | Several files, automatic names | `tabalyst report *.csv` | Beside each source |
30
33
  | Several files, one directory | `tabalyst report *.csv -d reports/` | The directory `reports/` |
34
+ | One JSON file | `tabalyst report data.json` | `data.report.html` beside the source |
35
+ | From a scan document | `tabalyst report --scan data.scan.json` | `data.html` beside the scan |
31
36
 
32
37
  The simplest command keeps the source filename:
33
38
 
@@ -50,6 +55,18 @@ Use `-o` to choose a different HTML filename for one source:
50
55
  tabalyst report customers.csv -o customer-analysis.html
51
56
  ```
52
57
 
58
+ ### JSON files
59
+
60
+ A JSON file gets a report too, named `<stem>.report.html` so its profile
61
+ `<stem>.report.json` never replaces the source. Each collection of records,
62
+ such as the `customers` array of `{"customers": [...]}`, is a dataset of the
63
+ report, and nested fields are columns named by their path, such as
64
+ `address.city`:
65
+
66
+ ```console
67
+ tabalyst report orders.json
68
+ ```
69
+
53
70
  ### Multiple files
54
71
 
55
72
  Report several CSV files at once:
@@ -136,16 +153,81 @@ tabalyst --version
136
153
 
137
154
  `python -m tabalyst` accepts the same commands.
138
155
 
156
+ ## Tabalyst Sample
157
+
158
+ Create a smaller CSV without modifying the source:
159
+
160
+ ```console
161
+ tabalyst sample customers.csv --sample-method random --rows 1000 --seed 42
162
+ ```
163
+
164
+ The default output is `customers.sample.csv` beside the source. Use `-o` to
165
+ name the output for one input, or `-d` to sample several files into one
166
+ directory:
167
+
168
+ ```console
169
+ tabalyst sample customers.csv --sample-method first --rows 100 -o test.csv
170
+ tabalyst sample *.csv --sample-method random --percent 5 -d samples/
171
+ ```
172
+
173
+ Available methods are `first`, `last`, `random` and `stratified`. Stratified
174
+ sampling approximately preserves the distribution of a selected field:
175
+
176
+ ```console
177
+ tabalyst sample customers.csv --sample-method stratified --field province --rows 1000 --seed 42
178
+ ```
179
+
180
+ Sampling reads CSV records as a stream. Random and stratified sampling keep
181
+ only the requested sample, plus stratum counts, in memory. Existing outputs
182
+ require `--force`, and an input file is never overwritten.
183
+
184
+ ## Tabalyst Scan
185
+
186
+ Describe every field of a CSV or JSON file in one JSON document:
187
+
188
+ ```console
189
+ tabalyst scan customers.csv
190
+ tabalyst scan orders.json --collection "$.customers[]"
191
+ ```
192
+
193
+ The default output is `customers.scan.json` beside the source; `-o`, `-d` and
194
+ `--force` work as for the other commands. Tabalyst Scan reads the file once as
195
+ a stream, with memory bounded by configurable limits, and records for each
196
+ field its presence, native types, missing values, frequencies, exact
197
+ statistics, normalization variants, technical type and the result of every
198
+ detector: numbers with decimal commas, dates, booleans, enumerations, email
199
+ addresses, URLs, phone numbers, postal codes, currency amounts, percentages,
200
+ quantities, UUIDs and IP addresses. Values of sensitive fields, such as email
201
+ addresses, are masked by default. Results are written atomically: an
202
+ interrupted scan never leaves a partial file.
203
+
204
+ Build the report from a scan document without reading the source again:
205
+
206
+ ```console
207
+ tabalyst report --scan customers.scan.json
208
+ ```
209
+
210
+ Tabalyst refuses a scan whose source changed since it was written, or whose
211
+ settings differ from the `scan` settings of `--config`.
212
+
213
+ See [Scan CSV and JSON files](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/how-to/scan-files.md)
214
+ and the [scan format](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/scan-format.md).
215
+
139
216
  ## What the report analyzes
140
217
 
141
218
  - Dataset dimensions, missing cells, duplicates, and quality observations.
142
219
  - Physical and semantic types with confidence and error rates.
143
220
  - Numeric, date, string-length, normalization, and value distributions.
144
- - Distinct values, representative examples, enum candidates, and date formats.
221
+ - Distinct values, representative examples, date formats, and semantic types
222
+ such as enumerations, email addresses, phone numbers, and postal codes.
145
223
  - CSV record widths, quoting, encoding, and delimiter configuration.
146
224
  - A bounded raw-data preview while every record is analyzed.
147
225
 
148
- The HTML report is self-contained and works without a CDN or network connection.
226
+ The report is built on Tabalyst Scan: it reads the CSV once as a stream and
227
+ masks values of sensitive columns, such as email addresses, by default.
228
+ Ambiguous dates stay ambiguous; the report shows the evidence of the column
229
+ without applying it. The HTML report is self-contained and works without a CDN
230
+ or network connection.
149
231
  The JSON profile contains the same canonical analysis result for scripts and
150
232
  future Tabalyst tools.
151
233
 
@@ -166,10 +248,24 @@ batch = tabalyst.generate_reports(
166
248
  ["*.csv"],
167
249
  output_dir="reports",
168
250
  )
251
+
252
+ sample = tabalyst.sample_csv(
253
+ "customers.csv",
254
+ method="random",
255
+ rows=1000,
256
+ seed=42,
257
+ )
258
+
259
+ scan = tabalyst.scan("orders.json")
260
+ scans = tabalyst.generate_scans(["data/*.json"], output_dir="scans")
261
+ reports = tabalyst.generate_reports(["scans/*.scan.json"], from_scan=True)
169
262
  ```
170
263
 
171
264
  `analyze()` returns the JSON-serializable profile for one report.
172
265
  `generate_reports()` returns the complete batch plan, successes, and failures.
266
+ `scan()` returns a scan result without writing anything; `generate_scans()`
267
+ writes one `.scan.json` document per source; `from_scan=True` builds reports
268
+ from such documents.
173
269
  Expected failures derive from `tabalyst.TabalystError`.
174
270
 
175
271
  Existing artifacts are never replaced silently. Pass `force=True` when
@@ -181,16 +277,22 @@ Configuration files are strict JSON. A minimal file is:
181
277
 
182
278
  ```json
183
279
  {
184
- "csv": {
185
- "delimiter": ";",
186
- "encoding": "cp1252"
280
+ "scan": {
281
+ "csv": {
282
+ "delimiter": ";",
283
+ "encoding": "cp1252"
284
+ },
285
+ "values": {"null_markers": ["N/A"]}
187
286
  }
188
287
  }
189
288
  ```
190
289
 
191
- Explicit CLI or Python arguments override the configuration file, which overrides
192
- Tabalyst defaults. See the
193
- [configuration reference](https://github.com/loribel-labs/tabalyst/blob/main/docs/configuration.md)
290
+ Analysis settings, CSV reading included, go in the `scan` object, shared by
291
+ `tabalyst report` and `tabalyst scan`. Top-level settings shape the report
292
+ presentation, and `csv` configures `tabalyst sample`. Explicit CLI or Python
293
+ arguments override the configuration file, which overrides Tabalyst defaults.
294
+ No configuration file is loaded unless it is passed with `--config`. See the
295
+ [configuration reference](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
194
296
  for all analysis settings.
195
297
 
196
298
  ## Local-first behavior and current limits
@@ -199,9 +301,11 @@ Tabalyst performs analysis locally and adds no telemetry or remote processing.
199
301
  Generated JSON and HTML may contain source values and should be shared
200
302
  accordingly.
201
303
 
202
- The complete CSV is currently loaded into memory. File and phase progress is
203
- available now; row percentages, throughput estimates, recursive directory input,
204
- and parallel batch execution will require later ingestion work.
304
+ Reports, samples, and scans read files as a stream, with memory bounded by
305
+ configurable limits, including duplicate-row detection
306
+ (`scan.limits.max_tracked_records`). Reports and scans show the share of the file read;
307
+ throughput estimates, recursive directory input, and parallel batch execution
308
+ will require later work.
205
309
 
206
310
  ## Examples and development
207
311
 
@@ -217,8 +321,8 @@ python -m build
217
321
 
218
322
  Additional documentation:
219
323
 
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)
324
+ - [Architecture](https://github.com/loribel-labs/tabalyst/blob/main/docs/dev/architecture.md)
325
+ - [Configuration](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
222
326
  - [Release procedure](https://github.com/loribel-labs/tabalyst/blob/main/RELEASING.md)
223
327
 
224
328
  Please report defects and feature requests through the
@@ -8,13 +8,18 @@ examples/
8
8
  |-- config.json
9
9
  |-- input/
10
10
  | |-- basic.csv
11
- | `-- insurance-customers.csv
11
+ | |-- insurance-customers.csv
12
+ | `-- orders.json
12
13
  `-- output/
13
14
  |-- basic/
14
15
  | |-- report.html
15
16
  | |-- report.json
16
17
  | `-- executions.json
17
- `-- insurance-customers/
18
+ |-- insurance-customers/
19
+ | |-- report.html
20
+ | |-- report.json
21
+ | `-- executions.json
22
+ `-- orders/
18
23
  |-- report.html
19
24
  |-- report.json
20
25
  `-- executions.json
@@ -23,13 +28,16 @@ examples/
23
28
  `basic.csv` is a five-row smoke example. `insurance-customers.csv` contains 3,000
24
29
  synthetic customer and contract records across 34 columns. Its people, contact
25
30
  details, and contracts are fictional; email addresses use reserved `.example`
26
- domains.
31
+ domains. `orders.json` is a synthetic shop export: 60 customers with nested
32
+ addresses, tags and orders, whose report has two datasets, `$` and
33
+ `$.customers[]`.
27
34
 
28
- From the repository root, regenerate both outputs with:
35
+ From the repository root, regenerate the outputs with:
29
36
 
30
37
  ```console
31
38
  tabalyst report examples/input/basic.csv -o examples/output/basic/report.html --config examples/config.json --force
32
39
  tabalyst report examples/input/insurance-customers.csv -o examples/output/insurance-customers/report.html --config examples/config.json --force
40
+ tabalyst report examples/input/orders.json -o examples/output/orders/report.html --config examples/config.json --force
33
41
  ```
34
42
 
35
43
  Every output folder is independent and contains its own HTML report, canonical
@@ -0,0 +1,60 @@
1
+ {
2
+ "string_analysis": {
3
+ "very_short_max_length": 5,
4
+ "short_max_length": 20,
5
+ "medium_max_length": 50,
6
+ "long_max_length": 255,
7
+ "length_distribution_max_length": 50,
8
+ "examples_per_length": 10
9
+ },
10
+ "value_examples": {
11
+ "full_distribution_max_distinct": 50,
12
+ "short_text_max_length": 20,
13
+ "short_text_percentile": 0.95,
14
+ "short_text_result_size": 20,
15
+ "long_text_result_size": 20,
16
+ "long_text_truncate_at": 30,
17
+ "truncation_suffix": "...",
18
+ "inline_display_size": 3
19
+ },
20
+ "scan": {
21
+ "csv": {
22
+ "encoding": "utf-8-sig",
23
+ "delimiter": ","
24
+ },
25
+ "values": {
26
+ "null_markers": ["N/A", "NULL"],
27
+ "missing": ["absent", "null", "empty", "blank", "marker"]
28
+ },
29
+ "normalization": {
30
+ "trim": true,
31
+ "collapse_whitespace": true
32
+ },
33
+ "types": {
34
+ "minimum_confidence": 0.95
35
+ },
36
+ "detectors": {
37
+ "date": {
38
+ "orders": ["YMD", "MDY", "DMY"],
39
+ "separators": ["-", "/", "."],
40
+ "ambiguous_order": null
41
+ },
42
+ "enumeration": {
43
+ "minimum_values": 500,
44
+ "maximum_distinct": 49,
45
+ "case_sensitive": true
46
+ }
47
+ },
48
+ "limits": {
49
+ "max_samples": 100
50
+ },
51
+ "records": {
52
+ "preview": 20,
53
+ "duplicates": true
54
+ },
55
+ "exposure": {
56
+ "sensitive_values": "mask"
57
+ },
58
+ "random_seed": 42
59
+ }
60
+ }
@@ -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.4.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"
@@ -34,9 +34,9 @@ classifiers = [
34
34
  "Topic :: Scientific/Engineering :: Information Analysis",
35
35
  ]
36
36
  dependencies = [
37
+ "ijson>=3.3",
37
38
  "jinja2>=3.1",
38
- "pandas>=2.2",
39
- "pydantic>=2.10",
39
+ "pydantic>=2.11",
40
40
  "typer>=0.12",
41
41
  ]
42
42
 
@@ -65,6 +65,9 @@ Issues = "https://github.com/loribel-labs/tabalyst/issues"
65
65
  [tool.pytest.ini_options]
66
66
  testpaths = ["tests"]
67
67
  pythonpath = ["src"]
68
+ markers = [
69
+ "lot(name): Tabalyst Scan implementation lot that provides the tested behavior",
70
+ ]
68
71
 
69
72
  [tool.ruff]
70
73
  line-length = 88
@@ -0,0 +1,34 @@
1
+ """Public Python interface for Tabalyst."""
2
+
3
+ from tabalyst._version import __version__
4
+ from tabalyst.errors import ConfigurationError, InputError, ReportError, TabalystError
5
+ from tabalyst.report_config import ReportConfig as ReportConfig
6
+ from tabalyst.report_service import generate_reports as generate_reports
7
+ from tabalyst.reporting import render_report as render_report
8
+ from tabalyst.sampling import SampleMethod as SampleMethod
9
+ from tabalyst.sampling import SampleResult as SampleResult
10
+ from tabalyst.sampling import sample_csv as sample_csv
11
+ from tabalyst.sampling_service import generate_samples as generate_samples
12
+ from tabalyst.scan_service import generate_scans as generate_scans
13
+ from tabalyst.scanner import ScanConfig as ScanConfig
14
+ from tabalyst.scanner import ScanResult as ScanResult
15
+ from tabalyst.scanner import scan as scan
16
+ from tabalyst.service import analyze
17
+ from tabalyst.service import analyze_csv as analyze_csv
18
+ from tabalyst.service import analyze_scan as analyze_scan
19
+
20
+ __all__ = [
21
+ "ConfigurationError",
22
+ "InputError",
23
+ "ReportError",
24
+ "ScanConfig",
25
+ "ScanResult",
26
+ "TabalystError",
27
+ "__version__",
28
+ "analyze",
29
+ "generate_reports",
30
+ "generate_samples",
31
+ "generate_scans",
32
+ "sample_csv",
33
+ "scan",
34
+ ]