datacom 0.2.0__py3-none-win_arm64.whl
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.
- datacom/__init__.py +131 -0
- datacom/__main__.py +16 -0
- datacom/bin/datacom.exe +0 -0
- datacom-0.2.0.dist-info/LICENSE +21 -0
- datacom-0.2.0.dist-info/METADATA +69 -0
- datacom-0.2.0.dist-info/RECORD +8 -0
- datacom-0.2.0.dist-info/WHEEL +4 -0
- datacom-0.2.0.dist-info/entry_points.txt +2 -0
datacom/__init__.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Clean, profile and query messy data files from Python.
|
|
2
|
+
|
|
3
|
+
A thin wrapper around the datacom binary that ships inside this package:
|
|
4
|
+
every function runs the same engine as the command line, so results match.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
import tempfile
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
__all__ = ["DatacomError", "binary", "run", "profile", "clean", "query", "convert", "clean_frame", "__version__"]
|
|
14
|
+
__version__ = "0.2.0"
|
|
15
|
+
|
|
16
|
+
_REPEATABLE = {"where", "derive"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DatacomError(RuntimeError):
|
|
20
|
+
"""datacom exited with an error; stderr holds its message."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, returncode, stderr):
|
|
23
|
+
super().__init__(stderr or f"datacom exited with status {returncode}")
|
|
24
|
+
self.returncode = returncode
|
|
25
|
+
self.stderr = stderr
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def binary():
|
|
29
|
+
"""Path of the datacom executable: DATACOM_BINARY, else the bundled one."""
|
|
30
|
+
env = os.environ.get("DATACOM_BINARY")
|
|
31
|
+
if env:
|
|
32
|
+
return env
|
|
33
|
+
exe = Path(__file__).parent / "bin" / ("datacom.exe" if os.name == "nt" else "datacom")
|
|
34
|
+
if not exe.exists():
|
|
35
|
+
raise FileNotFoundError(f"no datacom binary at {exe}; install a platform wheel or set DATACOM_BINARY")
|
|
36
|
+
return str(exe)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _flags(options):
|
|
40
|
+
args = []
|
|
41
|
+
for key, value in options.items():
|
|
42
|
+
if value is None or value is False:
|
|
43
|
+
continue
|
|
44
|
+
flag = "-" + key.replace("_", "-")
|
|
45
|
+
if value is True:
|
|
46
|
+
args.append(flag)
|
|
47
|
+
elif isinstance(value, (list, tuple)):
|
|
48
|
+
if key in _REPEATABLE:
|
|
49
|
+
for v in value:
|
|
50
|
+
args += [flag, str(v)]
|
|
51
|
+
else:
|
|
52
|
+
args += [flag, ",".join(str(v) for v in value)]
|
|
53
|
+
else:
|
|
54
|
+
args += [flag, str(value)]
|
|
55
|
+
return args
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _sources(source):
|
|
59
|
+
if isinstance(source, (str, os.PathLike)):
|
|
60
|
+
return [os.fspath(source)]
|
|
61
|
+
return [os.fspath(s) for s in source]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def run(command, *sources, **options):
|
|
65
|
+
"""Run any datacom command and return the CompletedProcess.
|
|
66
|
+
|
|
67
|
+
Keyword options become flags: snake=True is -snake, near_dupes=0.8 is
|
|
68
|
+
-near-dupes 0.8, text=["title", "body"] is -text title,body, and lists
|
|
69
|
+
for where and derive repeat the flag.
|
|
70
|
+
"""
|
|
71
|
+
args = [binary(), command, *(os.fspath(s) for s in sources), *_flags(options)]
|
|
72
|
+
p = subprocess.run(args, capture_output=True, text=True)
|
|
73
|
+
if p.returncode != 0:
|
|
74
|
+
raise DatacomError(p.returncode, p.stderr.strip())
|
|
75
|
+
return p
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def profile(source, **options):
|
|
79
|
+
"""Summarize a file and return the report as a dict."""
|
|
80
|
+
return json.loads(run("profile", *_sources(source), json=True, **options).stdout)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _write(command, source, output, options):
|
|
84
|
+
with tempfile.TemporaryDirectory() as d:
|
|
85
|
+
manifest = options.pop("manifest", None) or os.path.join(d, "run.json")
|
|
86
|
+
run(command, *_sources(source), o=os.fspath(output), manifest=manifest, q=True, **options)
|
|
87
|
+
with open(manifest, encoding="utf-8") as f:
|
|
88
|
+
return json.load(f)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def clean(source, output, **options):
|
|
92
|
+
"""Clean source into output and return the run manifest as a dict.
|
|
93
|
+
|
|
94
|
+
The manifest lists input and output SHA-256, every rule applied and how
|
|
95
|
+
many rows or values each one dropped or changed.
|
|
96
|
+
"""
|
|
97
|
+
return _write("clean", source, output, options)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def query(source, output, **options):
|
|
101
|
+
"""Filter, derive, join, group or sort, and return the run manifest."""
|
|
102
|
+
return _write("query", source, output, options)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def convert(source, output, **options):
|
|
106
|
+
"""Write source in the format output's extension names; returns the manifest."""
|
|
107
|
+
return _write("convert", source, output, options)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def clean_frame(df, **options):
|
|
111
|
+
"""Clean a pandas DataFrame and return a new one.
|
|
112
|
+
|
|
113
|
+
The frame goes through a temporary Parquet file when pyarrow is
|
|
114
|
+
installed, and CSV otherwise.
|
|
115
|
+
"""
|
|
116
|
+
import pandas as pd
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
import pyarrow # noqa: F401
|
|
120
|
+
|
|
121
|
+
ext = ".parquet"
|
|
122
|
+
except ImportError:
|
|
123
|
+
ext = ".csv"
|
|
124
|
+
with tempfile.TemporaryDirectory() as d:
|
|
125
|
+
src, dst = os.path.join(d, "in" + ext), os.path.join(d, "out" + ext)
|
|
126
|
+
if ext == ".parquet":
|
|
127
|
+
df.to_parquet(src, index=False)
|
|
128
|
+
else:
|
|
129
|
+
df.to_csv(src, index=False)
|
|
130
|
+
clean(src, dst, **options)
|
|
131
|
+
return pd.read_parquet(dst) if ext == ".parquet" else pd.read_csv(dst, dtype=str, keep_default_na=False)
|
datacom/__main__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from . import binary
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
exe = binary()
|
|
10
|
+
if os.name == "nt":
|
|
11
|
+
sys.exit(subprocess.call([exe, *sys.argv[1:]]))
|
|
12
|
+
os.execv(exe, [exe, *sys.argv[1:]])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
main()
|
datacom/bin/datacom.exe
ADDED
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 datacom authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: datacom
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Clean, profile and query messy CSV, JSON, Excel and Parquet files; mask personal data and dedupe text for AI training
|
|
5
|
+
Home-page: https://github.com/moodykhalif23/datacom
|
|
6
|
+
Project-URL: Source, https://github.com/moodykhalif23/datacom
|
|
7
|
+
License: MIT
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Provides-Extra: pandas
|
|
10
|
+
Requires-Dist: pandas; extra == 'pandas'
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# datacom for Python
|
|
17
|
+
|
|
18
|
+
Clean, profile and query messy data files, and prepare text for AI training, from Python. The package bundles the datacom binary for your platform, so there is nothing else to install and the results match the command line exactly.
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pip install datacom
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
import datacom
|
|
28
|
+
|
|
29
|
+
report = datacom.profile("sales.xlsx")
|
|
30
|
+
print(report["rows"], report["issues"])
|
|
31
|
+
|
|
32
|
+
manifest = datacom.clean("raw.csv.gz", "clean.parquet", snake=True, dedupe=True, redact="all")
|
|
33
|
+
print(manifest["rows_out"], manifest["outputs"][0]["sha256"])
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`clean`, `query` and `convert` return the run manifest: input and output SHA-256, every rule applied, and how many rows or values each one dropped or changed.
|
|
37
|
+
|
|
38
|
+
## Training data
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
manifest = datacom.clean(
|
|
42
|
+
"s3://lake/crawl/2026-11/*.jsonl.gz",
|
|
43
|
+
"clean/part-*.parquet",
|
|
44
|
+
text=["body"],
|
|
45
|
+
quality="default",
|
|
46
|
+
decontam="evals/mmlu.jsonl",
|
|
47
|
+
near_dupes=0.8,
|
|
48
|
+
redact="all",
|
|
49
|
+
workers=16,
|
|
50
|
+
)
|
|
51
|
+
for rule in manifest["rules"]:
|
|
52
|
+
print(rule)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `text` cleans those columns as documents: repairs garbled characters, strips HTML, normalizes Unicode and removes control characters.
|
|
56
|
+
- `quality` applies the Gopher rules and counts what each rule dropped.
|
|
57
|
+
- `decontam` drops documents that share a 13-word sequence with a test set.
|
|
58
|
+
- `near_dupes` removes near-duplicates with MinHash in a fixed-size index.
|
|
59
|
+
- `redact` masks emails, phone numbers, ID numbers and card numbers.
|
|
60
|
+
|
|
61
|
+
Sources can be local paths, URLs, `s3://`, `gs://` or `hf://datasets/...`, with globs. Credentials come from the usual places: `AWS_*` variables or `~/.aws/credentials`, `GOOGLE_OAUTH_ACCESS_TOKEN`, and `HF_TOKEN`.
|
|
62
|
+
|
|
63
|
+
## Options
|
|
64
|
+
|
|
65
|
+
Keyword arguments become flags: `snake=True` is `-snake`, `near_dupes=0.8` is `-near-dupes 0.8`, `text=["title", "body"]` is `-text title,body`, and lists for `where` and `derive` repeat the flag. `datacom.run(command, *sources, **options)` runs any command and returns the `subprocess.CompletedProcess`; failures raise `datacom.DatacomError` with datacom's message.
|
|
66
|
+
|
|
67
|
+
With pandas installed, `datacom.clean_frame(df, snake=True, dedupe=True)` returns a cleaned copy of a DataFrame.
|
|
68
|
+
|
|
69
|
+
The `datacom` command is installed too, so everything in the [main documentation](https://github.com/moodykhalif23/datacom) works from the shell.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
datacom/__init__.py,sha256=p9JuVjkYJGZ74ZdBmRVaU721Ty2nn9XgehjxFhAzzfA,4343
|
|
2
|
+
datacom/__main__.py,sha256=WhCuPfMyoVb33x6lbmw3nJ0pPgE6AjC-z68wMbGlxVA,254
|
|
3
|
+
datacom/bin/datacom.exe,sha256=ymEbKFvWBz_e72ZBSfky1_7pEOwmeP2YyjsjW2fReLQ,13243904
|
|
4
|
+
datacom-0.2.0.dist-info/METADATA,sha256=7n6cAxvwqHScEyZjDoBS2CzuinCuNMObKfwvi1YwZXE,2922
|
|
5
|
+
datacom-0.2.0.dist-info/WHEEL,sha256=WSv1FTpHVEz5pEOm_F7Jn0CggS7qvoB_Mp_8uTeH84U,101
|
|
6
|
+
datacom-0.2.0.dist-info/entry_points.txt,sha256=jE44ePB7nafDdB9oPRlP-vvk1xVSImhYf_HzE5MCx9c,50
|
|
7
|
+
datacom-0.2.0.dist-info/LICENSE,sha256=hiacSLWN2WAlnrLY6Z9unO8_NJveZy-8mSRf0BkrPqw,1072
|
|
8
|
+
datacom-0.2.0.dist-info/RECORD,,
|