dtflow 0.5.7__tar.gz → 0.5.9__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 (59) hide show
  1. {dtflow-0.5.7 → dtflow-0.5.9}/.gitignore +3 -0
  2. {dtflow-0.5.7 → dtflow-0.5.9}/PKG-INFO +34 -2
  3. {dtflow-0.5.7 → dtflow-0.5.9}/README.md +28 -1
  4. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/SKILL.md +39 -5
  5. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/__init__.py +1 -1
  6. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/__main__.py +137 -8
  7. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/clean.py +294 -9
  8. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/commands.py +17 -1
  9. dtflow-0.5.9/dtflow/cli/eval.py +288 -0
  10. dtflow-0.5.9/dtflow/cli/export.py +81 -0
  11. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/sample.py +90 -3
  12. dtflow-0.5.9/dtflow/cli/split.py +138 -0
  13. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/stats.py +224 -30
  14. dtflow-0.5.9/dtflow/eval.py +276 -0
  15. dtflow-0.5.9/dtflow/utils/text_parser.py +124 -0
  16. {dtflow-0.5.7 → dtflow-0.5.9}/pyproject.toml +7 -0
  17. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_cli_clean.py +239 -1
  18. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_cli_stats.py +150 -0
  19. dtflow-0.5.9/tests/test_eval.py +445 -0
  20. {dtflow-0.5.7 → dtflow-0.5.9}/CHANGELOG.md +0 -0
  21. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/__init__.py +0 -0
  22. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/common.py +0 -0
  23. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/io_ops.py +0 -0
  24. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/lineage.py +0 -0
  25. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/pipeline.py +0 -0
  26. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/skill.py +0 -0
  27. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/transform.py +0 -0
  28. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/cli/validate.py +0 -0
  29. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/converters.py +0 -0
  30. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/core.py +0 -0
  31. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/framework.py +0 -0
  32. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/lineage.py +0 -0
  33. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/pipeline.py +0 -0
  34. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/presets.py +0 -0
  35. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/schema.py +0 -0
  36. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/storage/__init__.py +0 -0
  37. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/storage/io.py +0 -0
  38. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/streaming.py +0 -0
  39. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/tokenizers.py +0 -0
  40. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/utils/__init__.py +0 -0
  41. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/utils/display.py +0 -0
  42. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/utils/field_path.py +0 -0
  43. {dtflow-0.5.7 → dtflow-0.5.9}/dtflow/utils/helpers.py +0 -0
  44. {dtflow-0.5.7 → dtflow-0.5.9}/tests/README.md +0 -0
  45. {dtflow-0.5.7 → dtflow-0.5.9}/tests/benchmark_io.py +0 -0
  46. {dtflow-0.5.7 → dtflow-0.5.9}/tests/benchmark_sharegpt.py +0 -0
  47. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_cli_benchmark.py +0 -0
  48. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_cli_sample.py +0 -0
  49. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_cli_transform.py +0 -0
  50. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_converters.py +0 -0
  51. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_field_path.py +0 -0
  52. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_framework.py +0 -0
  53. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_io.py +0 -0
  54. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_lineage.py +0 -0
  55. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_pipeline.py +0 -0
  56. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_schema.py +0 -0
  57. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_streaming.py +0 -0
  58. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_tokenizers.py +0 -0
  59. {dtflow-0.5.7 → dtflow-0.5.9}/tests/test_transformer.py +0 -0
@@ -49,6 +49,9 @@ data/
49
49
  *.csv
50
50
  *.parquet
51
51
 
52
+ # Claude Code
53
+ .claude/settings.local.json
54
+
52
55
  # OS
53
56
  .DS_Store
54
57
  Thumbs.db
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dtflow
3
- Version: 0.5.7
3
+ Version: 0.5.9
4
4
  Summary: A flexible data transformation tool for ML training formats (SFT, RLHF, Pretrain)
5
5
  Project-URL: Homepage, https://github.com/yourusername/DataTransformer
6
6
  Project-URL: Documentation, https://github.com/yourusername/DataTransformer#readme
@@ -44,6 +44,7 @@ Requires-Dist: flake8>=3.9.0; extra == 'dev'
44
44
  Requires-Dist: huggingface-hub>=0.20.0; extra == 'dev'
45
45
  Requires-Dist: isort>=5.9.0; extra == 'dev'
46
46
  Requires-Dist: mypy>=0.910; extra == 'dev'
47
+ Requires-Dist: pandas>=1.3.0; extra == 'dev'
47
48
  Requires-Dist: pyarrow; extra == 'dev'
48
49
  Requires-Dist: pytest-cov>=2.12.0; extra == 'dev'
49
50
  Requires-Dist: pytest>=6.0.0; extra == 'dev'
@@ -57,10 +58,14 @@ Provides-Extra: docs
57
58
  Requires-Dist: myst-parser>=0.15.0; extra == 'docs'
58
59
  Requires-Dist: sphinx-rtd-theme>=0.5.0; extra == 'docs'
59
60
  Requires-Dist: sphinx>=4.0.0; extra == 'docs'
61
+ Provides-Extra: eval
62
+ Requires-Dist: pandas>=1.3.0; extra == 'eval'
63
+ Requires-Dist: scikit-learn>=0.24.0; extra == 'eval'
60
64
  Provides-Extra: full
61
65
  Requires-Dist: datasets>=2.0.0; extra == 'full'
62
66
  Requires-Dist: datasketch>=1.5.0; extra == 'full'
63
67
  Requires-Dist: huggingface-hub>=0.20.0; extra == 'full'
68
+ Requires-Dist: pandas>=1.3.0; extra == 'full'
64
69
  Requires-Dist: pyarrow; extra == 'full'
65
70
  Requires-Dist: rich>=10.0.0; extra == 'full'
66
71
  Requires-Dist: scikit-learn>=0.24.0; extra == 'full'
@@ -435,6 +440,13 @@ dt sample data.jsonl 1000 --by=messages.# # 按消息数量分层采样
435
440
  dt sample data.jsonl --where="category=tech" # 筛选后采样
436
441
  dt sample data.jsonl --where="messages.#>=2" # 多条件筛选
437
442
 
443
+ # 按行范围查看(Python 切片语法)
444
+ dt slice data.jsonl 10:20 # 第 10-19 行(0-based,左闭右开)
445
+ dt slice data.jsonl :100 # 前 100 行
446
+ dt slice data.jsonl 100: # 第 100 行到末尾
447
+ dt slice data.jsonl 10:20 -o sliced.jsonl # 保存到文件
448
+ dt slice data.jsonl 10:20 -f question,answer # 只显示指定字段
449
+
438
450
  # 数据转换 - 预设模式
439
451
  dt transform data.jsonl --preset=openai_chat
440
452
  dt transform data.jsonl --preset=alpaca
@@ -468,6 +480,9 @@ dt clean data.jsonl --max-len=messages[-1].content:500 # 最后一条消息最
468
480
  dt clean data.jsonl --keep=question,answer # 只保留这些字段
469
481
  dt clean data.jsonl --drop=metadata # 删除指定字段
470
482
  dt clean data.jsonl --strip # 去除字符串首尾空白
483
+ dt clean data.jsonl --min-tokens=content:10 # 最少 10 tokens
484
+ dt clean data.jsonl --max-tokens=content:1000 # 最多 1000 tokens
485
+ dt clean data.jsonl --min-tokens=text:50 -m gpt-4 # 指定分词器
471
486
 
472
487
  # 数据去重
473
488
  dt dedupe data.jsonl # 全量精确去重
@@ -476,11 +491,26 @@ dt dedupe data.jsonl --key=meta.id # 按嵌套字段去重
476
491
  dt dedupe data.jsonl --key=messages[0].content # 按第一条消息内容去重
477
492
  dt dedupe data.jsonl --key=text --similar=0.8 # 相似度去重
478
493
 
494
+ # 数据集切分
495
+ dt split data.jsonl --ratio=0.8 --seed=42 # 二分: train/test
496
+ dt split data.jsonl --ratio=0.7,0.15,0.15 # 三分: train/val/test
497
+ dt split data.jsonl --ratio=0.8 -o /tmp/output # 指定输出目录
498
+
499
+ # 训练框架导出
500
+ dt export data.jsonl --framework=llama-factory # 导出到 LLaMA-Factory
501
+ dt export data.jsonl -f swift -o ./swift_out # 导出到 ms-swift
502
+ dt export data.jsonl -f axolotl # 导出到 Axolotl
503
+ dt export data.jsonl -f llama-factory --check # 仅检查兼容性
504
+
479
505
  # 文件拼接
480
506
  dt concat a.jsonl b.jsonl -o merged.jsonl
481
507
 
482
508
  # 数据统计
483
- dt stats data.jsonl
509
+ dt stats data.jsonl # 快速模式
510
+ dt stats data.jsonl --full # 完整模式(含值分布)
511
+ dt stats data.jsonl --full --field=category # 指定字段统计
512
+ dt stats data.jsonl --full --expand=tags # 展开 list 字段统计元素分布
513
+ dt stats data.jsonl --full --expand='messages[*].role' # 展开嵌套 list 字段
484
514
 
485
515
  # Claude Code Skill 安装
486
516
  dt install-skill # 安装到 ~/.claude/skills/
@@ -516,6 +546,8 @@ CLI 命令中的字段参数支持嵌套路径语法,可访问深层嵌套的
516
546
  | `clean` | `--drop-empty=` | `--drop-empty=meta.source` |
517
547
  | `clean` | `--min-len=` | `--min-len=messages.#:2` |
518
548
  | `clean` | `--max-len=` | `--max-len=messages[-1].content:500` |
549
+ | `clean` | `--min-tokens=` | `--min-tokens=content:10` |
550
+ | `clean` | `--max-tokens=` | `--max-tokens=content:1000` |
519
551
  | `token-stats` | `--field=` | `--field=messages[-1].content` |
520
552
  | `diff` | `--key=` | `--key=meta.uuid` |
521
553
 
@@ -351,6 +351,13 @@ dt sample data.jsonl 1000 --by=messages.# # 按消息数量分层采样
351
351
  dt sample data.jsonl --where="category=tech" # 筛选后采样
352
352
  dt sample data.jsonl --where="messages.#>=2" # 多条件筛选
353
353
 
354
+ # 按行范围查看(Python 切片语法)
355
+ dt slice data.jsonl 10:20 # 第 10-19 行(0-based,左闭右开)
356
+ dt slice data.jsonl :100 # 前 100 行
357
+ dt slice data.jsonl 100: # 第 100 行到末尾
358
+ dt slice data.jsonl 10:20 -o sliced.jsonl # 保存到文件
359
+ dt slice data.jsonl 10:20 -f question,answer # 只显示指定字段
360
+
354
361
  # 数据转换 - 预设模式
355
362
  dt transform data.jsonl --preset=openai_chat
356
363
  dt transform data.jsonl --preset=alpaca
@@ -384,6 +391,9 @@ dt clean data.jsonl --max-len=messages[-1].content:500 # 最后一条消息最
384
391
  dt clean data.jsonl --keep=question,answer # 只保留这些字段
385
392
  dt clean data.jsonl --drop=metadata # 删除指定字段
386
393
  dt clean data.jsonl --strip # 去除字符串首尾空白
394
+ dt clean data.jsonl --min-tokens=content:10 # 最少 10 tokens
395
+ dt clean data.jsonl --max-tokens=content:1000 # 最多 1000 tokens
396
+ dt clean data.jsonl --min-tokens=text:50 -m gpt-4 # 指定分词器
387
397
 
388
398
  # 数据去重
389
399
  dt dedupe data.jsonl # 全量精确去重
@@ -392,11 +402,26 @@ dt dedupe data.jsonl --key=meta.id # 按嵌套字段去重
392
402
  dt dedupe data.jsonl --key=messages[0].content # 按第一条消息内容去重
393
403
  dt dedupe data.jsonl --key=text --similar=0.8 # 相似度去重
394
404
 
405
+ # 数据集切分
406
+ dt split data.jsonl --ratio=0.8 --seed=42 # 二分: train/test
407
+ dt split data.jsonl --ratio=0.7,0.15,0.15 # 三分: train/val/test
408
+ dt split data.jsonl --ratio=0.8 -o /tmp/output # 指定输出目录
409
+
410
+ # 训练框架导出
411
+ dt export data.jsonl --framework=llama-factory # 导出到 LLaMA-Factory
412
+ dt export data.jsonl -f swift -o ./swift_out # 导出到 ms-swift
413
+ dt export data.jsonl -f axolotl # 导出到 Axolotl
414
+ dt export data.jsonl -f llama-factory --check # 仅检查兼容性
415
+
395
416
  # 文件拼接
396
417
  dt concat a.jsonl b.jsonl -o merged.jsonl
397
418
 
398
419
  # 数据统计
399
- dt stats data.jsonl
420
+ dt stats data.jsonl # 快速模式
421
+ dt stats data.jsonl --full # 完整模式(含值分布)
422
+ dt stats data.jsonl --full --field=category # 指定字段统计
423
+ dt stats data.jsonl --full --expand=tags # 展开 list 字段统计元素分布
424
+ dt stats data.jsonl --full --expand='messages[*].role' # 展开嵌套 list 字段
400
425
 
401
426
  # Claude Code Skill 安装
402
427
  dt install-skill # 安装到 ~/.claude/skills/
@@ -432,6 +457,8 @@ CLI 命令中的字段参数支持嵌套路径语法,可访问深层嵌套的
432
457
  | `clean` | `--drop-empty=` | `--drop-empty=meta.source` |
433
458
  | `clean` | `--min-len=` | `--min-len=messages.#:2` |
434
459
  | `clean` | `--max-len=` | `--max-len=messages[-1].content:500` |
460
+ | `clean` | `--min-tokens=` | `--min-tokens=content:10` |
461
+ | `clean` | `--max-tokens=` | `--max-tokens=content:1000` |
435
462
  | `token-stats` | `--field=` | `--field=messages[-1].content` |
436
463
  | `diff` | `--key=` | `--key=meta.uuid` |
437
464
 
@@ -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 执行
@@ -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
@@ -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