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.
- {tabalyst-0.2.1/src/tabalyst.egg-info → tabalyst-0.4.0}/PKG-INFO +121 -17
- {tabalyst-0.2.1 → tabalyst-0.4.0}/README.md +118 -14
- {tabalyst-0.2.1 → tabalyst-0.4.0}/examples/README.md +12 -4
- tabalyst-0.4.0/examples/config.json +60 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/pyproject.toml +6 -3
- tabalyst-0.4.0/src/tabalyst/__init__.py +34 -0
- tabalyst-0.4.0/src/tabalyst/batch.py +157 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/cli/app.py +4 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/cli/report.py +48 -63
- tabalyst-0.4.0/src/tabalyst/cli/sample.py +132 -0
- tabalyst-0.4.0/src/tabalyst/cli/scan.py +180 -0
- tabalyst-0.4.0/src/tabalyst/cli/terminal.py +56 -0
- tabalyst-0.4.0/src/tabalyst/config.py +262 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/execution_log.py +6 -4
- tabalyst-0.4.0/src/tabalyst/models.py +386 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/progress.py +7 -0
- tabalyst-0.4.0/src/tabalyst/report_config.py +72 -0
- tabalyst-0.4.0/src/tabalyst/report_profile.py +1084 -0
- tabalyst-0.4.0/src/tabalyst/report_service.py +268 -0
- tabalyst-0.4.0/src/tabalyst/reporting.py +130 -0
- tabalyst-0.4.0/src/tabalyst/sampling.py +344 -0
- tabalyst-0.4.0/src/tabalyst/sampling_service.py +133 -0
- tabalyst-0.4.0/src/tabalyst/scan_reuse.py +147 -0
- tabalyst-0.4.0/src/tabalyst/scan_service.py +184 -0
- tabalyst-0.4.0/src/tabalyst/scanner/__init__.py +12 -0
- tabalyst-0.4.0/src/tabalyst/scanner/api.py +175 -0
- tabalyst-0.4.0/src/tabalyst/scanner/config.py +519 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/__init__.py +16 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/amount.py +77 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/base.py +172 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/boolean.py +69 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/currency.py +134 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/email.py +86 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/enumeration.py +76 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/ip.py +128 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/names.py +87 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/number.py +161 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/pattern.py +48 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/percentage.py +43 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/phone.py +140 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/postal.py +99 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/quantity.py +100 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/registry.py +445 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/shape.py +36 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/temporal.py +399 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/url.py +144 -0
- tabalyst-0.4.0/src/tabalyst/scanner/detectors/uuid.py +97 -0
- tabalyst-0.4.0/src/tabalyst/scanner/diagnostics.py +82 -0
- tabalyst-0.4.0/src/tabalyst/scanner/engine.py +172 -0
- tabalyst-0.4.0/src/tabalyst/scanner/exposure.py +102 -0
- tabalyst-0.4.0/src/tabalyst/scanner/field.py +144 -0
- tabalyst-0.4.0/src/tabalyst/scanner/measures.py +452 -0
- tabalyst-0.4.0/src/tabalyst/scanner/models.py +525 -0
- tabalyst-0.4.0/src/tabalyst/scanner/normalization.py +99 -0
- tabalyst-0.4.0/src/tabalyst/scanner/observations.py +117 -0
- tabalyst-0.4.0/src/tabalyst/scanner/paths.py +142 -0
- tabalyst-0.4.0/src/tabalyst/scanner/readers/__init__.py +1 -0
- tabalyst-0.4.0/src/tabalyst/scanner/readers/base.py +78 -0
- tabalyst-0.4.0/src/tabalyst/scanner/readers/csv_reader.py +158 -0
- tabalyst-0.4.0/src/tabalyst/scanner/readers/json_reader.py +532 -0
- tabalyst-0.4.0/src/tabalyst/scanner/records.py +227 -0
- tabalyst-0.4.0/src/tabalyst/scanner/structure.py +320 -0
- tabalyst-0.4.0/src/tabalyst/scanner/technical.py +77 -0
- tabalyst-0.4.0/src/tabalyst/scanner/values.py +558 -0
- tabalyst-0.4.0/src/tabalyst/service.py +325 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/static/report.js +17 -3
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/static/theme.css +24 -0
- tabalyst-0.4.0/src/tabalyst/templates/report.html +108 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0/src/tabalyst.egg-info}/PKG-INFO +121 -17
- tabalyst-0.4.0/src/tabalyst.egg-info/SOURCES.txt +95 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst.egg-info/requires.txt +2 -2
- {tabalyst-0.2.1 → tabalyst-0.4.0}/tests/test_cli.py +17 -13
- {tabalyst-0.2.1 → tabalyst-0.4.0}/tests/test_public_api.py +2 -2
- {tabalyst-0.2.1 → tabalyst-0.4.0}/tests/test_reporting.py +4 -4
- tabalyst-0.4.0/tests/test_sampling.py +362 -0
- tabalyst-0.4.0/tests/test_scan_command.py +416 -0
- tabalyst-0.4.0/tests/test_scanner_amounts.py +618 -0
- tabalyst-0.4.0/tests/test_scanner_config.py +85 -0
- tabalyst-0.4.0/tests/test_scanner_csv.py +170 -0
- tabalyst-0.4.0/tests/test_scanner_email_url.py +432 -0
- tabalyst-0.4.0/tests/test_scanner_exposure.py +263 -0
- tabalyst-0.4.0/tests/test_scanner_paths.py +72 -0
- tabalyst-0.4.0/tests/test_scanner_phone.py +308 -0
- tabalyst-0.4.0/tests/test_scanner_postal.py +270 -0
- tabalyst-0.4.0/tests/test_scanner_uuid_ip.py +518 -0
- tabalyst-0.2.1/examples/config.json +0 -48
- tabalyst-0.2.1/src/tabalyst/__init__.py +0 -22
- tabalyst-0.2.1/src/tabalyst/analysis.py +0 -883
- tabalyst-0.2.1/src/tabalyst/config.py +0 -214
- tabalyst-0.2.1/src/tabalyst/ingestion.py +0 -86
- tabalyst-0.2.1/src/tabalyst/models.py +0 -211
- tabalyst-0.2.1/src/tabalyst/report_service.py +0 -244
- tabalyst-0.2.1/src/tabalyst/reporting.py +0 -73
- tabalyst-0.2.1/src/tabalyst/service.py +0 -182
- tabalyst-0.2.1/src/tabalyst/templates/report.html +0 -83
- tabalyst-0.2.1/src/tabalyst.egg-info/SOURCES.txt +0 -37
- tabalyst-0.2.1/tests/test_analysis.py +0 -621
- {tabalyst-0.2.1 → tabalyst-0.4.0}/LICENSE +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/MANIFEST.in +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/examples/input/basic.csv +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/examples/input/insurance-customers.csv +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/setup.cfg +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/__main__.py +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/_version.py +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/cli/__init__.py +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst/errors.py +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst.egg-info/dependency_links.txt +0 -0
- {tabalyst-0.2.1 → tabalyst-0.4.0}/src/tabalyst.egg-info/entry_points.txt +0 -0
- {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.
|
|
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:
|
|
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,
|
|
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
|
|
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
|
-
"
|
|
219
|
-
"
|
|
220
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
and
|
|
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,
|
|
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
|
|
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
|
-
"
|
|
185
|
-
"
|
|
186
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
and
|
|
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
|
-
|
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
"
|
|
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
|
+
]
|