dtflow 0.5.7__py3-none-any.whl → 0.5.9__py3-none-any.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.
dtflow/SKILL.md CHANGED
@@ -1,6 +1,17 @@
1
1
  ---
2
2
  name: dtflow
3
- description: 数据文件处理(JSONL/CSV/Parquet)- 去重/采样/统计/过滤/转换/Schema验证/训练框架导出
3
+ description: >
4
+ 当用户需要处理 JSONL/CSV/Parquet/JSON/Arrow 数据文件时使用此 skill。
5
+ 提供 CLI 工具 `dt` 和 Python API `DataTransformer`。
6
+ 适用场景:(1) 查看数据:dt sample/head/tail 采样预览,dt slice 按行范围查看,dt stats 统计字段分布;
7
+ (2) 数据清洗:dt clean 支持 --drop-empty/--min-len/--max-len 过滤行,--keep/--drop/--rename/--promote/--add-field/--fill/--reorder 操作字段;
8
+ (3) 去重:dt dedupe 精确去重或 --similar 相似度去重;
9
+ (4) 格式转换:dt transform 预设模板(openai_chat/alpaca/sharegpt/dpo)或自定义配置;
10
+ (5) Schema 验证:dt validate --preset 验证数据格式;
11
+ (6) 数据集切分:dt split 按比例切分 train/test/val;
12
+ (7) 训练框架导出:dt export / export_for() 一键导出到 llama-factory/swift/axolotl;
13
+ (8) 大文件流式处理:load_stream() O(1) 内存处理 100GB+ 文件。
14
+ 注意:此工具专注数据文件的结构化处理,不涉及 LLM 调用(LLM 调用请用 flexllm)。
4
15
  ---
5
16
 
6
17
  # dtflow - 机器学习训练数据格式转换工具
@@ -135,6 +146,8 @@ dt.stats() # 统计
135
146
  dt stats data.jsonl # 基本统计(文件大小、条数、字段)
136
147
  dt stats data.jsonl --full # 完整模式:值分布、唯一值、非空率
137
148
  dt stats data.jsonl --full -n 20 # 显示 Top 20 值分布
149
+ dt stats data.jsonl --field=meta.source # 只统计指定字段(支持嵌套路径,可多次使用)
150
+ dt stats data.jsonl --expand=tags # 展开 list 字段统计(可多次使用)
138
151
 
139
152
  # Token 统计
140
153
  dt token-stats data.jsonl # 默认统计 messages 字段
@@ -149,7 +162,7 @@ dt sample data.jsonl 1000 --by=category # 分层采样
149
162
  dt sample data.jsonl 1000 --by=category --uniform # 均匀分层采样
150
163
  dt sample data.jsonl --where="messages.#>=2" # 条件筛选
151
164
  dt sample data.jsonl 10 -f input,output # 只显示指定字段
152
- dt sample data.jsonl 10 --raw # 输出原始 JSON(不截断)
165
+ dt sample data.jsonl 10 --pretty # 表格预览模式(默认原始 JSON
153
166
  dt sample data.jsonl 100 --seed=42 -o out.jsonl # 固定随机种子并保存
154
167
 
155
168
  # 去重
@@ -165,8 +178,26 @@ dt clean data.jsonl --max-len=text:2000 # 最大长度过滤
165
178
  dt clean data.jsonl --min-len=messages.#:2 # 最少 2 条消息
166
179
  dt clean data.jsonl --keep=question,answer # 只保留指定字段
167
180
  dt clean data.jsonl --drop=metadata # 删除指定字段
181
+ dt clean data.jsonl --rename=question:instruction,answer:output # 重命名字段
182
+ dt clean data.jsonl --promote=meta.label # 提升嵌套字段到顶层
183
+ dt clean data.jsonl --promote=meta.label:tag # 提升并自定义名称
184
+ dt clean data.jsonl --add-field=source:web # 添加常量字段
185
+ dt clean data.jsonl --fill=label:unknown # 填充空值/缺失字段
186
+ dt clean data.jsonl --reorder=id,text,label # 控制字段输出顺序
168
187
  dt clean data.jsonl --strip # 去除字符串首尾空白
169
- dt clean data.jsonl --strip --drop-empty=input -o cleaned.jsonl # 组合使用
188
+ dt clean data.jsonl --min-tokens=content:10 # 最少 10 tokens
189
+ dt clean data.jsonl --max-tokens=content:1000 -m gpt-4 # 最多 1000 tokens(指定分词器)
190
+ dt clean data.jsonl --promote=meta.label --drop=meta --fill=label:unknown # 组合使用
191
+
192
+ # 数据集切分
193
+ dt split data.jsonl --ratio=0.8 --seed=42 # 二分: train/test
194
+ dt split data.jsonl --ratio=0.7,0.15,0.15 # 三分: train/val/test
195
+ dt split data.jsonl --ratio=0.8 -o /tmp/output # 指定输出目录
196
+
197
+ # 训练框架导出
198
+ dt export data.jsonl --framework=llama-factory # 导出到 LLaMA-Factory
199
+ dt export data.jsonl -f swift -o ./swift_out # 导出到 ms-swift
200
+ dt export data.jsonl -f llama-factory --check # 仅检查兼容性
170
201
 
171
202
  # 验证
172
203
  dt validate data.jsonl --preset=openai_chat # 预设: openai_chat/alpaca/dpo/sharegpt
@@ -185,10 +216,13 @@ dt diff a.jsonl b.jsonl --key=id # 对比差异
185
216
  dt diff a.jsonl b.jsonl --key=id -o report.md # 输出对比报告
186
217
 
187
218
  # 查看数据
188
- dt head data.jsonl 10 # 前 10
219
+ dt head data.jsonl 10 # 前 10 条(默认原始 JSON)
189
220
  dt head data.jsonl 10 -f input,output # 只显示指定字段
190
- dt head data.jsonl 10 --raw # 输出完整 JSON(不截断)
221
+ dt head data.jsonl 10 --pretty # 表格预览模式
191
222
  dt tail data.jsonl 10 # 后 10 条
223
+ dt slice data.jsonl 10:20 # 第 10-19 行(Python 切片语法)
224
+ dt slice data.jsonl :100 # 前 100 行
225
+ dt slice data.jsonl 100: # 第 100 行到末尾
192
226
 
193
227
  # 其他
194
228
  dt run pipeline.yaml # Pipeline 执行
dtflow/__init__.py CHANGED
@@ -60,7 +60,7 @@ from .tokenizers import (
60
60
  token_stats,
61
61
  )
62
62
 
63
- __version__ = "0.5.7"
63
+ __version__ = "0.5.9"
64
64
 
65
65
  __all__ = [
66
66
  # core
dtflow/__main__.py CHANGED
@@ -18,6 +18,8 @@ Commands:
18
18
  clean 数据清洗
19
19
  run 执行 Pipeline 配置文件
20
20
  history 显示数据血缘历史
21
+ split 分割数据集
22
+ export 导出到训练框架
21
23
  validate 使用 Schema 验证数据格式
22
24
  logs 日志查看工具使用说明
23
25
  install-skill 安装 dtflow skill 到 Claude Code
@@ -33,12 +35,16 @@ from .cli.commands import clean as _clean
33
35
  from .cli.commands import concat as _concat
34
36
  from .cli.commands import dedupe as _dedupe
35
37
  from .cli.commands import diff as _diff
38
+ from .cli.commands import eval as _eval
39
+ from .cli.commands import export as _export
36
40
  from .cli.commands import head as _head
37
41
  from .cli.commands import history as _history
38
42
  from .cli.commands import install_skill as _install_skill
39
43
  from .cli.commands import run as _run
40
44
  from .cli.commands import sample as _sample
41
45
  from .cli.commands import skill_status as _skill_status
46
+ from .cli.commands import slice_data as _slice_data
47
+ from .cli.commands import split as _split
42
48
  from .cli.commands import stats as _stats
43
49
  from .cli.commands import tail as _tail
44
50
  from .cli.commands import token_stats as _token_stats
@@ -69,12 +75,12 @@ def sample(
69
75
  by: Optional[str] = typer.Option(None, "--by", help="分层采样字段"),
70
76
  uniform: bool = typer.Option(False, "--uniform", help="均匀采样模式"),
71
77
  fields: Optional[str] = typer.Option(None, "--fields", "-f", help="只显示指定字段(逗号分隔)"),
72
- raw: bool = typer.Option(False, "--raw", "-r", help="输出原始 JSON(不截断)"),
78
+ pretty: bool = typer.Option(False, "--pretty", "-R", help="使用表格预览(默认原始 JSON"),
73
79
  where: Optional[List[str]] = typer.Option(None, "--where", "-w", help="筛选条件 (可多次使用)"),
74
80
  ):
75
81
  """从数据文件中采样指定数量的数据"""
76
82
  actual_num = num_arg if num_arg is not None else num
77
- _sample(filename, actual_num, type, output, seed, by, uniform, fields, raw, where)
83
+ _sample(filename, actual_num, type, output, seed, by, uniform, fields, not pretty, where)
78
84
 
79
85
 
80
86
  @app.command()
@@ -84,12 +90,12 @@ def head(
84
90
  num: int = typer.Option(10, "--num", "-n", help="显示数量", show_default=True),
85
91
  output: Optional[str] = typer.Option(None, "--output", "-o", help="输出文件路径"),
86
92
  fields: Optional[str] = typer.Option(None, "--fields", "-f", help="只显示指定字段"),
87
- raw: bool = typer.Option(False, "--raw", "-r", help="输出原始 JSON(不截断)"),
93
+ pretty: bool = typer.Option(False, "--pretty", "-R", help="使用表格预览(默认原始 JSON"),
88
94
  ):
89
95
  """显示文件的前 N 条数据"""
90
96
  # 位置参数优先于选项参数
91
97
  actual_num = num_arg if num_arg is not None else num
92
- _head(filename, actual_num, output, fields, raw)
98
+ _head(filename, actual_num, output, fields, not pretty)
93
99
 
94
100
 
95
101
  @app.command()
@@ -99,12 +105,31 @@ def tail(
99
105
  num: int = typer.Option(10, "--num", "-n", help="显示数量", show_default=True),
100
106
  output: Optional[str] = typer.Option(None, "--output", "-o", help="输出文件路径"),
101
107
  fields: Optional[str] = typer.Option(None, "--fields", "-f", help="只显示指定字段"),
102
- raw: bool = typer.Option(False, "--raw", "-r", help="输出原始 JSON(不截断)"),
108
+ pretty: bool = typer.Option(False, "--pretty", "-R", help="使用表格预览(默认原始 JSON"),
103
109
  ):
104
110
  """显示文件的后 N 条数据"""
105
111
  # 位置参数优先于选项参数
106
112
  actual_num = num_arg if num_arg is not None else num
107
- _tail(filename, actual_num, output, fields, raw)
113
+ _tail(filename, actual_num, output, fields, not pretty)
114
+
115
+
116
+ @app.command("slice")
117
+ def slice_cmd(
118
+ filename: str = typer.Argument(..., help="输入文件路径"),
119
+ range_str: str = typer.Argument(..., help="行号范围 (start:end),如 10:20、:100、100:、-10:"),
120
+ output: Optional[str] = typer.Option(None, "--output", "-o", help="输出文件路径"),
121
+ fields: Optional[str] = typer.Option(None, "--fields", "-f", help="只显示指定字段"),
122
+ pretty: bool = typer.Option(False, "--pretty", "-R", help="使用表格预览(默认原始 JSON)"),
123
+ ):
124
+ """按行号范围查看数据(Python 切片语法)
125
+
126
+ 示例:
127
+ dt slice data.jsonl 10:20 第 10-19 行
128
+ dt slice data.jsonl :100 前 100 行
129
+ dt slice data.jsonl 100: 第 100 行到末尾
130
+ dt slice data.jsonl -10: 最后 10 行
131
+ """
132
+ _slice_data(filename, range_str, output, fields, not pretty)
108
133
 
109
134
 
110
135
  # ============ 数据转换命令 ============
@@ -164,11 +189,44 @@ def clean(
164
189
  max_len: Optional[str] = typer.Option(None, "--max-len", help="最大长度过滤 (字段:长度)"),
165
190
  keep: Optional[str] = typer.Option(None, "--keep", help="只保留指定字段"),
166
191
  drop: Optional[str] = typer.Option(None, "--drop", help="删除指定字段"),
192
+ rename: Optional[str] = typer.Option(None, "--rename", help="重命名字段 (old:new,old2:new2)"),
193
+ promote: Optional[str] = typer.Option(
194
+ None, "--promote", help="提升嵌套字段到顶层 (meta.label 或 meta.label:tag)"
195
+ ),
196
+ add_field: Optional[str] = typer.Option(None, "--add-field", help="添加常量字段 (key:value)"),
197
+ fill: Optional[str] = typer.Option(None, "--fill", help="填充空值 (field:default_value)"),
198
+ reorder: Optional[str] = typer.Option(
199
+ None, "--reorder", help="控制字段顺序 (field1,field2,...)"
200
+ ),
167
201
  strip: bool = typer.Option(False, "--strip", help="去除字符串首尾空白"),
202
+ min_tokens: Optional[str] = typer.Option(
203
+ None, "--min-tokens", help="最小 token 数过滤 (字段:数量)"
204
+ ),
205
+ max_tokens: Optional[str] = typer.Option(
206
+ None, "--max-tokens", help="最大 token 数过滤 (字段:数量)"
207
+ ),
208
+ model: str = typer.Option("cl100k_base", "--model", "-m", help="分词器模型 (默认 cl100k_base)"),
168
209
  output: Optional[str] = typer.Option(None, "--output", "-o", help="输出文件路径"),
169
210
  ):
170
211
  """数据清洗"""
171
- _clean(filename, drop_empty, min_len, max_len, keep, drop, strip, output)
212
+ _clean(
213
+ filename,
214
+ drop_empty,
215
+ min_len,
216
+ max_len,
217
+ keep,
218
+ drop,
219
+ rename,
220
+ promote,
221
+ add_field,
222
+ fill,
223
+ reorder,
224
+ strip,
225
+ min_tokens,
226
+ max_tokens,
227
+ model,
228
+ output,
229
+ )
172
230
 
173
231
 
174
232
  # ============ 数据统计命令 ============
@@ -179,9 +237,15 @@ def stats(
179
237
  filename: str = typer.Argument(..., help="输入文件路径"),
180
238
  top: int = typer.Option(10, "--top", "-n", help="显示 Top N 值"),
181
239
  full: bool = typer.Option(False, "--full", "-f", help="完整模式:统计值分布、唯一值等详细信息"),
240
+ field: Optional[List[str]] = typer.Option(
241
+ None, "--field", help="指定统计字段(可多次使用),支持嵌套路径"
242
+ ),
243
+ expand: Optional[List[str]] = typer.Option(
244
+ None, "--expand", help="展开 list 字段统计(可多次使用)"
245
+ ),
182
246
  ):
183
247
  """显示数据文件的统计信息"""
184
- _stats(filename, top, full)
248
+ _stats(filename, top, full, field, expand)
185
249
 
186
250
 
187
251
  @app.command("token-stats")
@@ -217,6 +281,71 @@ def history(
217
281
  _history(filename, json)
218
282
 
219
283
 
284
+ # ============ 切分与导出命令 ============
285
+
286
+
287
+ @app.command()
288
+ def split(
289
+ filename: str = typer.Argument(..., help="输入文件路径"),
290
+ ratio: str = typer.Option("0.8", "--ratio", "-r", help="分割比例,如 0.8 或 0.7,0.15,0.15"),
291
+ seed: Optional[int] = typer.Option(None, "--seed", help="随机种子"),
292
+ output: Optional[str] = typer.Option(None, "--output", "-o", help="输出目录(默认同目录)"),
293
+ ):
294
+ """分割数据集为 train/test (或 train/val/test)"""
295
+ _split(filename, ratio, seed, output)
296
+
297
+
298
+ @app.command()
299
+ def export(
300
+ filename: str = typer.Argument(..., help="输入文件路径"),
301
+ framework: str = typer.Option(
302
+ ..., "--framework", "-f", help="目标框架: llama-factory, swift, axolotl"
303
+ ),
304
+ output: Optional[str] = typer.Option(None, "--output", "-o", help="输出目录"),
305
+ name: Optional[str] = typer.Option(None, "--name", "-n", help="数据集名称"),
306
+ check: bool = typer.Option(False, "--check", help="仅检查兼容性,不导出"),
307
+ ):
308
+ """导出数据到训练框架 (LLaMA-Factory, ms-swift, Axolotl)"""
309
+ _export(filename, framework, output, name, check)
310
+
311
+
312
+ # ============ 评估命令 ============
313
+
314
+
315
+ @app.command()
316
+ def eval(
317
+ result_file: str = typer.Argument(..., help="模型输出的 .jsonl 文件路径"),
318
+ source: Optional[str] = typer.Option(
319
+ None, "--source", "-s", help="原始输入文件,按行号对齐合并"
320
+ ),
321
+ response_col: str = typer.Option("content", "--response-col", "-r", help="模型响应字段名"),
322
+ label_col: Optional[str] = typer.Option(
323
+ None, "--label-col", "-l", help="标签字段名(不指定时自动检测)"
324
+ ),
325
+ extract: str = typer.Option(
326
+ "direct",
327
+ "--extract",
328
+ "-e",
329
+ help="管道式提取规则,算子: direct/tag:X/json_key:X/index:N/line:N/lines/regex:X",
330
+ ),
331
+ sep: Optional[str] = typer.Option(None, "--sep", help="配合 index 算子使用的分隔符"),
332
+ mapping: Optional[str] = typer.Option(None, "--mapping", "-m", help="值映射 (k1:v1,k2:v2)"),
333
+ output_dir: str = typer.Option("record", "--output-dir", "-o", help="指标报告输出目录"),
334
+ ):
335
+ """对模型输出进行解析和指标评估
336
+
337
+ 两阶段解析:自动清洗(去 think 标签、提取代码块)+ 管道式提取。
338
+
339
+ 示例:
340
+ dt eval result.jsonl --label-col=label
341
+ dt eval result.jsonl --extract="tag:标签" --mapping="是:1,否:0"
342
+ dt eval result.jsonl --source=input.jsonl --response-col=api_output.content
343
+ dt eval result.jsonl --extract="json_key:result | index:0" --sep=","
344
+ dt eval result.jsonl --extract="lines | index:1" --sep="|"
345
+ """
346
+ _eval(result_file, source, response_col, label_col, extract, sep, mapping, output_dir)
347
+
348
+
220
349
  # ============ 验证命令 ============
221
350
 
222
351