csvsmith 0.7.3__tar.gz → 0.9.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.
- {csvsmith-0.7.3/src/csvsmith.egg-info → csvsmith-0.9.0}/PKG-INFO +61 -4
- {csvsmith-0.7.3 → csvsmith-0.9.0}/README.rst +61 -4
- {csvsmith-0.7.3 → csvsmith-0.9.0}/pyproject.toml +10 -2
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/__init__.py +19 -1
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/cli.py +136 -1
- csvsmith-0.9.0/src/csvsmith/tools/dense_csv.py +348 -0
- csvsmith-0.9.0/src/csvsmith/tools/strict_concat.py +101 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/utils/clean_numeric.py +1 -1
- {csvsmith-0.7.3 → csvsmith-0.9.0/src/csvsmith.egg-info}/PKG-INFO +61 -4
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith.egg-info/SOURCES.txt +4 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_cli.py +64 -1
- csvsmith-0.9.0/tests/test_dense_csv.py +333 -0
- csvsmith-0.9.0/tests/test_strict_concat.py +102 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/LICENSE +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/setup.cfg +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/tools/__init__.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/tools/classify.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/tools/excel2csv.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/tools/filter_rows.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/tools/find_matches_in_csv.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/tools/move_files.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/tools/row_dedup.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/utils/__init__.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/utils/distance.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/utils/io.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith/utils/normalize.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith.egg-info/dependency_links.txt +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith.egg-info/entry_points.txt +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith.egg-info/requires.txt +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/src/csvsmith.egg-info/top_level.txt +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_classify.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_clean_numeric.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_excel2csv.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_filter_rows.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_find_matches_in_csv.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_move_files.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_normalize.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_row_dedup.py +0 -0
- {csvsmith-0.7.3 → csvsmith-0.9.0}/tests/test_string_distance.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csvsmith
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Small CSV utilities: row deduplication, classification, row filtering, and CLI helpers.
|
|
5
5
|
Author-email: Eiichi YAMAMOTO <info@yeiichi.com>
|
|
6
6
|
License: MIT License
|
|
@@ -87,6 +87,8 @@ Features
|
|
|
87
87
|
- Convert Excel workbooks to CSV
|
|
88
88
|
- Move files by suffix
|
|
89
89
|
- Find matching values inside CSV files
|
|
90
|
+
- Concatenate CSV files with identical headers
|
|
91
|
+
- Tokenize repeated CSV values and restore them with a versioned map
|
|
90
92
|
- Use the tools either from Python or from the command line
|
|
91
93
|
|
|
92
94
|
Installation
|
|
@@ -168,7 +170,7 @@ Convert Excel to CSV:
|
|
|
168
170
|
|
|
169
171
|
.. code-block:: console
|
|
170
172
|
|
|
171
|
-
csvsmith
|
|
173
|
+
csvsmith excel-to-csv input.xlsx
|
|
172
174
|
|
|
173
175
|
Move files by suffix:
|
|
174
176
|
|
|
@@ -182,6 +184,54 @@ Find matches in a CSV:
|
|
|
182
184
|
|
|
183
185
|
csvsmith find-matches input.csv target --ignore-case --ignore-whitespace
|
|
184
186
|
|
|
187
|
+
Concatenate CSV files:
|
|
188
|
+
|
|
189
|
+
.. code-block:: console
|
|
190
|
+
|
|
191
|
+
csvsmith strict-concat incoming/ -o combined.csv
|
|
192
|
+
|
|
193
|
+
Concentrate repeated values and restore them later:
|
|
194
|
+
|
|
195
|
+
.. code-block:: console
|
|
196
|
+
|
|
197
|
+
csvsmith concentrate input.csv
|
|
198
|
+
csvsmith rehydrate input.dense.csv -m input.dense-map.json -o restored.csv
|
|
199
|
+
|
|
200
|
+
Dense CSV scope and performance
|
|
201
|
+
-------------------------------
|
|
202
|
+
|
|
203
|
+
The dense CSV workflow is intended for spreadsheet-oriented and medium-sized
|
|
204
|
+
CSV files, roughly in the 100 MB class. Concentration makes two passes over the
|
|
205
|
+
input and keeps value counts in memory, so memory use depends on the number and
|
|
206
|
+
size of distinct values in the selected columns.
|
|
207
|
+
|
|
208
|
+
For substantially larger datasets, especially gigabyte-scale pipelines, a
|
|
209
|
+
flat CSV plus a separate JSON map is usually the wrong interchange format.
|
|
210
|
+
Consider a binary columnar format such as Apache Parquet instead.
|
|
211
|
+
|
|
212
|
+
Dense CSV replaces repeated values with tokens containing a 64-character
|
|
213
|
+
SHA-256 digest. The complete token includes the ``csvsmith:sha256:`` prefix and
|
|
214
|
+
is therefore 79 ASCII characters. The JSON map also stores each original value.
|
|
215
|
+
|
|
216
|
+
The workflow can still reduce expensive downstream work even when the files
|
|
217
|
+
become larger. A consumer can process each mapped value once, store the result
|
|
218
|
+
against its token, and apply that result to every repeated occurrence during
|
|
219
|
+
rehydration. The CLI reports this potential repeated-operation reduction among
|
|
220
|
+
mapped cells.
|
|
221
|
+
|
|
222
|
+
For example, 937 mapped cell occurrences backed by 82 unique map values imply
|
|
223
|
+
855 avoidable repeated operations, or about 91.2%. This is a deduplication
|
|
224
|
+
ratio for mapped work, not a file-compression ratio or a guarantee of total
|
|
225
|
+
pipeline savings.
|
|
226
|
+
|
|
227
|
+
.. warning::
|
|
228
|
+
|
|
229
|
+
Concentration does not guarantee a smaller combined output. Replacing short
|
|
230
|
+
values such as ``True``, ``M``, or ``NY`` with a 79-character token will
|
|
231
|
+
increase the CSV size. Select columns containing sufficiently long,
|
|
232
|
+
repeated values and consider both the concentrated CSV and its JSON map
|
|
233
|
+
when evaluating storage savings.
|
|
234
|
+
|
|
185
235
|
Find matches in a CSV
|
|
186
236
|
---------------------
|
|
187
237
|
|
|
@@ -235,13 +285,20 @@ CSV classification and filtering:
|
|
|
235
285
|
|
|
236
286
|
.. code-block:: python
|
|
237
287
|
|
|
238
|
-
from csvsmith import CSVClassifier, DropRowsBySubstring
|
|
288
|
+
from csvsmith import CSVClassifier, DropRowsBySubstring
|
|
239
289
|
|
|
240
290
|
File and conversion helpers:
|
|
241
291
|
|
|
242
292
|
.. code-block:: python
|
|
243
293
|
|
|
244
|
-
from csvsmith import
|
|
294
|
+
from csvsmith import (
|
|
295
|
+
concentrate_csv,
|
|
296
|
+
excel_to_csv,
|
|
297
|
+
move_by_suffix,
|
|
298
|
+
rehydrate_csv,
|
|
299
|
+
save_csv,
|
|
300
|
+
strict_concat_rows,
|
|
301
|
+
)
|
|
245
302
|
|
|
246
303
|
String comparison utilities:
|
|
247
304
|
|
|
@@ -41,6 +41,8 @@ Features
|
|
|
41
41
|
- Convert Excel workbooks to CSV
|
|
42
42
|
- Move files by suffix
|
|
43
43
|
- Find matching values inside CSV files
|
|
44
|
+
- Concatenate CSV files with identical headers
|
|
45
|
+
- Tokenize repeated CSV values and restore them with a versioned map
|
|
44
46
|
- Use the tools either from Python or from the command line
|
|
45
47
|
|
|
46
48
|
Installation
|
|
@@ -122,7 +124,7 @@ Convert Excel to CSV:
|
|
|
122
124
|
|
|
123
125
|
.. code-block:: console
|
|
124
126
|
|
|
125
|
-
csvsmith
|
|
127
|
+
csvsmith excel-to-csv input.xlsx
|
|
126
128
|
|
|
127
129
|
Move files by suffix:
|
|
128
130
|
|
|
@@ -136,6 +138,54 @@ Find matches in a CSV:
|
|
|
136
138
|
|
|
137
139
|
csvsmith find-matches input.csv target --ignore-case --ignore-whitespace
|
|
138
140
|
|
|
141
|
+
Concatenate CSV files:
|
|
142
|
+
|
|
143
|
+
.. code-block:: console
|
|
144
|
+
|
|
145
|
+
csvsmith strict-concat incoming/ -o combined.csv
|
|
146
|
+
|
|
147
|
+
Concentrate repeated values and restore them later:
|
|
148
|
+
|
|
149
|
+
.. code-block:: console
|
|
150
|
+
|
|
151
|
+
csvsmith concentrate input.csv
|
|
152
|
+
csvsmith rehydrate input.dense.csv -m input.dense-map.json -o restored.csv
|
|
153
|
+
|
|
154
|
+
Dense CSV scope and performance
|
|
155
|
+
-------------------------------
|
|
156
|
+
|
|
157
|
+
The dense CSV workflow is intended for spreadsheet-oriented and medium-sized
|
|
158
|
+
CSV files, roughly in the 100 MB class. Concentration makes two passes over the
|
|
159
|
+
input and keeps value counts in memory, so memory use depends on the number and
|
|
160
|
+
size of distinct values in the selected columns.
|
|
161
|
+
|
|
162
|
+
For substantially larger datasets, especially gigabyte-scale pipelines, a
|
|
163
|
+
flat CSV plus a separate JSON map is usually the wrong interchange format.
|
|
164
|
+
Consider a binary columnar format such as Apache Parquet instead.
|
|
165
|
+
|
|
166
|
+
Dense CSV replaces repeated values with tokens containing a 64-character
|
|
167
|
+
SHA-256 digest. The complete token includes the ``csvsmith:sha256:`` prefix and
|
|
168
|
+
is therefore 79 ASCII characters. The JSON map also stores each original value.
|
|
169
|
+
|
|
170
|
+
The workflow can still reduce expensive downstream work even when the files
|
|
171
|
+
become larger. A consumer can process each mapped value once, store the result
|
|
172
|
+
against its token, and apply that result to every repeated occurrence during
|
|
173
|
+
rehydration. The CLI reports this potential repeated-operation reduction among
|
|
174
|
+
mapped cells.
|
|
175
|
+
|
|
176
|
+
For example, 937 mapped cell occurrences backed by 82 unique map values imply
|
|
177
|
+
855 avoidable repeated operations, or about 91.2%. This is a deduplication
|
|
178
|
+
ratio for mapped work, not a file-compression ratio or a guarantee of total
|
|
179
|
+
pipeline savings.
|
|
180
|
+
|
|
181
|
+
.. warning::
|
|
182
|
+
|
|
183
|
+
Concentration does not guarantee a smaller combined output. Replacing short
|
|
184
|
+
values such as ``True``, ``M``, or ``NY`` with a 79-character token will
|
|
185
|
+
increase the CSV size. Select columns containing sufficiently long,
|
|
186
|
+
repeated values and consider both the concentrated CSV and its JSON map
|
|
187
|
+
when evaluating storage savings.
|
|
188
|
+
|
|
139
189
|
Find matches in a CSV
|
|
140
190
|
---------------------
|
|
141
191
|
|
|
@@ -189,13 +239,20 @@ CSV classification and filtering:
|
|
|
189
239
|
|
|
190
240
|
.. code-block:: python
|
|
191
241
|
|
|
192
|
-
from csvsmith import CSVClassifier, DropRowsBySubstring
|
|
242
|
+
from csvsmith import CSVClassifier, DropRowsBySubstring
|
|
193
243
|
|
|
194
244
|
File and conversion helpers:
|
|
195
245
|
|
|
196
246
|
.. code-block:: python
|
|
197
247
|
|
|
198
|
-
from csvsmith import
|
|
248
|
+
from csvsmith import (
|
|
249
|
+
concentrate_csv,
|
|
250
|
+
excel_to_csv,
|
|
251
|
+
move_by_suffix,
|
|
252
|
+
rehydrate_csv,
|
|
253
|
+
save_csv,
|
|
254
|
+
strict_concat_rows,
|
|
255
|
+
)
|
|
199
256
|
|
|
200
257
|
String comparison utilities:
|
|
201
258
|
|
|
@@ -225,4 +282,4 @@ Example:
|
|
|
225
282
|
License
|
|
226
283
|
-------
|
|
227
284
|
|
|
228
|
-
See the project license for details.
|
|
285
|
+
See the project license for details.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "csvsmith"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.9.0"
|
|
8
8
|
description = "Small CSV utilities: row deduplication, classification, row filtering, and CLI helpers."
|
|
9
9
|
readme = "README.rst"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -50,4 +50,12 @@ Documentation = "https://csvsmith.readthedocs.io/en/latest/"
|
|
|
50
50
|
csvsmith = "csvsmith.cli:main"
|
|
51
51
|
|
|
52
52
|
[tool.setuptools.packages.find]
|
|
53
|
-
where = ["src"]
|
|
53
|
+
where = ["src"]
|
|
54
|
+
|
|
55
|
+
[tool.semantic_release]
|
|
56
|
+
allow_zero_version = true
|
|
57
|
+
tag_format = "v{version}"
|
|
58
|
+
version_toml = ["pyproject.toml:project.version"]
|
|
59
|
+
|
|
60
|
+
[tool.semantic_release.changelog.default_templates]
|
|
61
|
+
changelog_file = "CHANGELOG.rst"
|
|
@@ -16,6 +16,10 @@ Public API:
|
|
|
16
16
|
- Relation
|
|
17
17
|
- Result
|
|
18
18
|
- analyze_pair
|
|
19
|
+
- strict_concat_rows
|
|
20
|
+
- save_csv
|
|
21
|
+
- concentrate_csv
|
|
22
|
+
- rehydrate_csv
|
|
19
23
|
|
|
20
24
|
Compatibility aliases:
|
|
21
25
|
- CSVCleaner
|
|
@@ -28,12 +32,19 @@ Submodules:
|
|
|
28
32
|
- csvsmith.filter_rows
|
|
29
33
|
- csvsmith.excel2csv
|
|
30
34
|
- csvsmith.move_files
|
|
35
|
+
- csvsmith.strict_concat
|
|
31
36
|
- csvsmith.cli (CLI entrypoint)
|
|
32
37
|
"""
|
|
33
38
|
|
|
34
|
-
__version__ = "0.
|
|
39
|
+
__version__ = "0.8.0"
|
|
35
40
|
|
|
36
41
|
from .tools.classify import CSVClassifier
|
|
42
|
+
from .tools.dense_csv import (
|
|
43
|
+
ConcentrateResult,
|
|
44
|
+
RehydrateResult,
|
|
45
|
+
concentrate_csv,
|
|
46
|
+
rehydrate_csv,
|
|
47
|
+
)
|
|
37
48
|
from .tools.excel2csv import excel_to_csv
|
|
38
49
|
from .tools.filter_rows import DropRowsBySubstring
|
|
39
50
|
from .tools.find_matches_in_csv import find_matches_in_csv
|
|
@@ -43,6 +54,7 @@ from .tools.row_dedup import (
|
|
|
43
54
|
find_duplicate_rows,
|
|
44
55
|
dedupe_with_report,
|
|
45
56
|
)
|
|
57
|
+
from .tools.strict_concat import save_csv, strict_concat_rows
|
|
46
58
|
from .utils.clean_numeric import clean_numeric
|
|
47
59
|
from .utils.distance import StringDistance, Relation, Result, analyze_pair
|
|
48
60
|
from .utils.io import (
|
|
@@ -68,4 +80,10 @@ __all__ = [
|
|
|
68
80
|
"Result",
|
|
69
81
|
"analyze_pair",
|
|
70
82
|
"clean_numeric",
|
|
83
|
+
"strict_concat_rows",
|
|
84
|
+
"save_csv",
|
|
85
|
+
"ConcentrateResult",
|
|
86
|
+
"RehydrateResult",
|
|
87
|
+
"concentrate_csv",
|
|
88
|
+
"rehydrate_csv",
|
|
71
89
|
]
|
|
@@ -8,6 +8,7 @@ from typing import Optional, Sequence
|
|
|
8
8
|
|
|
9
9
|
from . import __version__
|
|
10
10
|
from .tools.classify import CSVClassifier
|
|
11
|
+
from .tools.dense_csv import concentrate_csv, rehydrate_csv
|
|
11
12
|
from .tools.excel2csv import excel_to_csv
|
|
12
13
|
from .utils.clean_numeric import clean_currency_numeric, clean_numeric
|
|
13
14
|
from .tools.filter_rows import DropRowsBySubstring
|
|
@@ -16,6 +17,7 @@ from .tools.row_dedup import (
|
|
|
16
17
|
dedupe_with_report,
|
|
17
18
|
find_duplicate_rows,
|
|
18
19
|
)
|
|
20
|
+
from .tools.strict_concat import save_csv, strict_concat_rows
|
|
19
21
|
from .tools.find_matches_in_csv import find_matches_in_csv
|
|
20
22
|
from .utils.distance import analyze_pair
|
|
21
23
|
from .utils.io import read_csv_rows, write_csv_rows
|
|
@@ -180,6 +182,87 @@ def cmd_find_matches(args: argparse.Namespace) -> int:
|
|
|
180
182
|
return 0
|
|
181
183
|
|
|
182
184
|
|
|
185
|
+
def cmd_strict_concat(args: argparse.Namespace) -> int:
|
|
186
|
+
input_dir = Path(args.input_dir)
|
|
187
|
+
if not input_dir.is_dir():
|
|
188
|
+
print(f"Error: input directory not found: {input_dir}", file=sys.stderr)
|
|
189
|
+
return 1
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
rows = strict_concat_rows(input_dir)
|
|
193
|
+
save_csv(rows, args.output)
|
|
194
|
+
except Exception as e:
|
|
195
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
196
|
+
return 1
|
|
197
|
+
|
|
198
|
+
print(f"Wrote concatenated CSV to: {args.output}")
|
|
199
|
+
return 0
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def cmd_concentrate(args: argparse.Namespace) -> int:
|
|
203
|
+
input_path = Path(args.input)
|
|
204
|
+
output_path = (
|
|
205
|
+
Path(args.output)
|
|
206
|
+
if args.output
|
|
207
|
+
else input_path.with_name(f"{input_path.stem}.dense.csv")
|
|
208
|
+
)
|
|
209
|
+
map_path = (
|
|
210
|
+
Path(args.map)
|
|
211
|
+
if args.map
|
|
212
|
+
else input_path.with_name(f"{input_path.stem}.dense-map.json")
|
|
213
|
+
)
|
|
214
|
+
columns = (
|
|
215
|
+
[column.strip() for column in args.columns.split(",") if column.strip()]
|
|
216
|
+
if args.columns
|
|
217
|
+
else None
|
|
218
|
+
)
|
|
219
|
+
try:
|
|
220
|
+
result = concentrate_csv(
|
|
221
|
+
input_path,
|
|
222
|
+
output_path,
|
|
223
|
+
map_path,
|
|
224
|
+
columns=columns,
|
|
225
|
+
min_occurrences=args.min_occurrences,
|
|
226
|
+
)
|
|
227
|
+
except (OSError, ValueError, csv.Error, json.JSONDecodeError) as e:
|
|
228
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
229
|
+
return 1
|
|
230
|
+
|
|
231
|
+
print(
|
|
232
|
+
f"Wrote concentrated CSV to: {result.output_csv_path} "
|
|
233
|
+
f"({result.transformed_cell_count} cells, "
|
|
234
|
+
f"{result.mapped_value_count} mapped values)"
|
|
235
|
+
)
|
|
236
|
+
if result.transformed_cell_count:
|
|
237
|
+
avoided_operations = (
|
|
238
|
+
result.transformed_cell_count - result.mapped_value_count
|
|
239
|
+
)
|
|
240
|
+
reduction_percentage = (
|
|
241
|
+
avoided_operations / result.transformed_cell_count * 100
|
|
242
|
+
)
|
|
243
|
+
print(
|
|
244
|
+
"Potential repeated-operation reduction for mapped values: "
|
|
245
|
+
f"{avoided_operations}/{result.transformed_cell_count} "
|
|
246
|
+
f"({reduction_percentage:.1f}%)"
|
|
247
|
+
)
|
|
248
|
+
print(f"Wrote dense CSV map to: {result.output_map_path}")
|
|
249
|
+
return 0
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def cmd_rehydrate(args: argparse.Namespace) -> int:
|
|
253
|
+
try:
|
|
254
|
+
result = rehydrate_csv(args.input, args.map, args.output)
|
|
255
|
+
except (OSError, ValueError, csv.Error, json.JSONDecodeError) as e:
|
|
256
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
257
|
+
return 1
|
|
258
|
+
|
|
259
|
+
print(
|
|
260
|
+
f"Wrote rehydrated CSV to: {result.output_csv_path} "
|
|
261
|
+
f"({result.restored_cell_count} cells restored)"
|
|
262
|
+
)
|
|
263
|
+
return 0
|
|
264
|
+
|
|
265
|
+
|
|
183
266
|
def _add_find_matches_parser(subparsers) -> None:
|
|
184
267
|
parser = subparsers.add_parser("find-matches", help="Find matches in a CSV file.")
|
|
185
268
|
parser.add_argument("input", help="Input CSV file.")
|
|
@@ -190,6 +273,56 @@ def _add_find_matches_parser(subparsers) -> None:
|
|
|
190
273
|
parser.set_defaults(func=cmd_find_matches)
|
|
191
274
|
|
|
192
275
|
|
|
276
|
+
def _add_strict_concat_parser(subparsers) -> None:
|
|
277
|
+
parser = subparsers.add_parser(
|
|
278
|
+
"strict-concat",
|
|
279
|
+
help="Concatenate CSVs in a directory only when all headers match exactly.",
|
|
280
|
+
)
|
|
281
|
+
parser.add_argument("input_dir", help="Directory containing CSV files to concatenate.")
|
|
282
|
+
parser.add_argument("-o", "--output", required=True, help="Output CSV file path.")
|
|
283
|
+
parser.set_defaults(func=cmd_strict_concat)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _add_concentrate_parser(subparsers) -> None:
|
|
287
|
+
parser = subparsers.add_parser(
|
|
288
|
+
"concentrate",
|
|
289
|
+
help="Replace repeated CSV values with deterministic tokens.",
|
|
290
|
+
)
|
|
291
|
+
parser.add_argument("input", help="Input CSV file.")
|
|
292
|
+
parser.add_argument(
|
|
293
|
+
"-o",
|
|
294
|
+
"--output",
|
|
295
|
+
help="Output CSV file (default: <input-stem>.dense.csv).",
|
|
296
|
+
)
|
|
297
|
+
parser.add_argument(
|
|
298
|
+
"-m",
|
|
299
|
+
"--map",
|
|
300
|
+
help="Output JSON map file (default: <input-stem>.dense-map.json).",
|
|
301
|
+
)
|
|
302
|
+
parser.add_argument(
|
|
303
|
+
"--columns",
|
|
304
|
+
help="Comma-separated column names to concentrate (default: all columns).",
|
|
305
|
+
)
|
|
306
|
+
parser.add_argument(
|
|
307
|
+
"--min-occurrences",
|
|
308
|
+
type=int,
|
|
309
|
+
default=2,
|
|
310
|
+
help="Minimum repetitions required before replacing a value (default: 2).",
|
|
311
|
+
)
|
|
312
|
+
parser.set_defaults(func=cmd_concentrate)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _add_rehydrate_parser(subparsers) -> None:
|
|
316
|
+
parser = subparsers.add_parser(
|
|
317
|
+
"rehydrate",
|
|
318
|
+
help="Restore a concentrated CSV using its JSON map.",
|
|
319
|
+
)
|
|
320
|
+
parser.add_argument("input", help="Concentrated CSV file.")
|
|
321
|
+
parser.add_argument("-m", "--map", required=True, help="Dense CSV JSON map file.")
|
|
322
|
+
parser.add_argument("-o", "--output", required=True, help="Output CSV file.")
|
|
323
|
+
parser.set_defaults(func=cmd_rehydrate)
|
|
324
|
+
|
|
325
|
+
|
|
193
326
|
def _add_row_duplicates_parser(subparsers) -> None:
|
|
194
327
|
parser = subparsers.add_parser("row-duplicates", help="Find duplicate rows in a CSV.")
|
|
195
328
|
parser.add_argument("input", help="Input CSV file.")
|
|
@@ -317,6 +450,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
317
450
|
|
|
318
451
|
_add_row_duplicates_parser(subparsers)
|
|
319
452
|
_add_find_matches_parser(subparsers)
|
|
453
|
+
_add_strict_concat_parser(subparsers)
|
|
454
|
+
_add_concentrate_parser(subparsers)
|
|
455
|
+
_add_rehydrate_parser(subparsers)
|
|
320
456
|
_add_dedupe_parser(subparsers)
|
|
321
457
|
_add_classify_parser(subparsers)
|
|
322
458
|
_add_move_files_parser(subparsers)
|
|
@@ -342,4 +478,3 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
342
478
|
|
|
343
479
|
if __name__ == "__main__":
|
|
344
480
|
sys.exit(main())
|
|
345
|
-
|