dataframe-toolkit 0.11.2__tar.gz → 0.12.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.
Files changed (54) hide show
  1. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/PKG-INFO +113 -39
  2. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/README.md +111 -38
  3. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/pyproject.toml +2 -1
  4. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/pyproject.toml.orig +2 -1
  5. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/__init__.py +1 -0
  6. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/annotate_cmd.py +1 -1
  7. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/binx_cmd.py +4 -4
  8. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/chi_cmd.py +4 -4
  9. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/cleannames_cmd.py +3 -3
  10. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/concat_cmd.py +3 -3
  11. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/corr_cmd.py +6 -6
  12. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/describe_cmd.py +4 -4
  13. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/eval_cmd.py +25 -7
  14. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/explode_cmd.py +4 -4
  15. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/fit_cmd.py +6 -6
  16. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/func_cmd.py +5 -5
  17. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/hist_cmd.py +10 -10
  18. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/implode_cmd.py +6 -6
  19. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/info_cmd.py +2 -2
  20. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/interp_cmd.py +6 -6
  21. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/line_cmd.py +14 -14
  22. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/melt_cmd.py +1 -1
  23. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/merge_cmd.py +32 -35
  24. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/pivot_cmd.py +8 -8
  25. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/query_cmd.py +33 -11
  26. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/randvar_cmd.py +3 -3
  27. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/sample_cmd.py +9 -9
  28. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/scale_cmd.py +11 -11
  29. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/scat_cmd.py +12 -12
  30. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/segid_cmd.py +2 -2
  31. dataframe_toolkit-0.12.0/src/dftk/commands/skill_cmd.py +146 -0
  32. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/split_cmd.py +10 -7
  33. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/stat_cmd.py +4 -4
  34. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/test_cmd.py +3 -3
  35. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/transpose_cmd.py +2 -2
  36. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/wstat_cmd.py +4 -4
  37. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/common/io.py +61 -29
  38. dataframe_toolkit-0.12.0/src/dftk/skills/dftk/SKILL.md +196 -0
  39. dataframe_toolkit-0.12.0/src/dftk/skills/dftk/references/snakemake.md +167 -0
  40. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/__init__.py +0 -0
  41. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/cli.py +0 -0
  42. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/base.py +0 -0
  43. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/clean_cmd.py +0 -0
  44. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/dataset_cmd.py +0 -0
  45. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/help.py +0 -0
  46. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/commands/print_cmd.py +0 -0
  47. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/common/__init__.py +0 -0
  48. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/common/copula.py +0 -0
  49. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/common/evalfuncs.py +0 -0
  50. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/common/plot.py +0 -0
  51. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/common/seed.py +0 -0
  52. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/common/seq.py +0 -0
  53. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/common/stats.py +0 -0
  54. {dataframe_toolkit-0.11.2 → dataframe_toolkit-0.12.0}/src/dftk/io.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dataframe-toolkit
3
- Version: 0.11.2
3
+ Version: 0.12.0
4
4
  Summary: A command-line toolkit for DataFrame analysis and manipulation
5
5
  Keywords: dataframe,statistics,data analysis,CLI,TSV,pandas
6
6
  Author: Gunnar Pálsson
@@ -20,6 +20,7 @@ Requires-Dist: statsmodels>=0.14
20
20
  Requires-Dist: patsy>=0.5.6
21
21
  Requires-Dist: duckdb>=0.8
22
22
  Requires-Dist: matplotlib>=3.7
23
+ Requires-Dist: pillow>=13.0
23
24
  Requires-Dist: seaborn>=0.12
24
25
  Requires-Dist: tqdm>=4.65
25
26
  Requires-Python: >=3.12
@@ -76,17 +77,20 @@ uv tool install .
76
77
 
77
78
  ### Skill for coding agents
78
79
 
79
- `skills/dftk/` is an agent skill: a short guide that coding agents such as
80
- Codex and Claude Code load when a task involves dftk. It covers the piping
80
+ dftk ships an agent skill: a short guide that coding agents such as Codex
81
+ and Claude Code load when a task involves dftk. It covers the piping
81
82
  conventions, which command to use for what, and patterns for Snakemake and
82
- Make. Copy it into your agent's skills directory, for example:
83
+ Make. Install it with:
83
84
 
84
85
  ```bash
85
- cp -r skills/dftk ~/.codex/skills/ # Codex
86
- cp -r skills/dftk ~/.claude/skills/ # Claude Code
86
+ dftk skill install # every agent found: ~/.claude and/or ~/.codex
87
+ dftk skill install --codex # only one (--claude or --codex; creates the folder)
88
+ dftk skill install --dir DIR # another agent's skills folder
87
89
  ```
88
90
 
89
- Copy it again after upgrading dftk so it matches the installed version.
91
+ It prints where it installed the skill. Run it again after upgrading dftk
92
+ so the skill matches the installed version; it replaces only the earlier
93
+ dftk skill. The source is in `src/dftk/skills/dftk/`.
90
94
 
91
95
  ## Quick example
92
96
 
@@ -94,15 +98,15 @@ Copy it again after upgrading dftk so it matches the installed version.
94
98
  dftk dataset tips | dftk stat - -c tip total_bill -g day
95
99
  ```
96
100
 
97
- Chain commands with `-o` (write to a temp parquet, print its path) and `...`
98
- (read a parquet path from stdin) instead of piping TSV text directly — faster
99
- for large data, and the recommended pattern once a pipeline has more than one
100
- step:
101
+ Chain commands with a bare `-o` (write a temp parquet, print its path)
102
+ instead of piping TSV text: the next command sees the path on stdin and reads
103
+ that file. It's faster for large data, keeps column types exact, and is the
104
+ recommended pattern once a pipeline has more than one step:
101
105
 
102
106
  ```bash
103
107
  dftk dataset tips -o \
104
- | dftk pivot ... -i day -v tip -f mean -o \
105
- | dftk print ...
108
+ | dftk pivot -i day -v tip -f mean -o \
109
+ | dftk print
106
110
  ```
107
111
 
108
112
  ## Subcommands
@@ -162,7 +166,7 @@ publication-quality output.
162
166
  | `sample` | Random row sampling (with or without replacement, by count or fraction) |
163
167
  | `split` | Split a dataframe into one file per group |
164
168
  | `annotate` | Read and write provenance metadata (genome, source, …) in parquet files |
165
- | `print` | Read any dftk input (TSV, stdin, `...` parquet pipe) and write TSV |
169
+ | `print` | Read any dftk input (TSV, parquet, stdin) and write TSV |
166
170
  | `clean` | Remove leftover temp parquet pipe files from interrupted pipelines |
167
171
  | `help` | List all subcommands or show full help for one: `dftk help stat` |
168
172
 
@@ -173,8 +177,8 @@ publication-quality output.
173
177
  ```bash
174
178
  # z-score sepal_length within species, then fit against petal_length
175
179
  dftk dataset iris -o \
176
- | dftk scale ... -c sepal_length -g species -o \
177
- | dftk fit ... -f "sepal_length_scaled ~ petal_length" -g species
180
+ | dftk scale -c sepal_length -g species -o \
181
+ | dftk fit -f "sepal_length_scaled ~ petal_length" -g species
178
182
  ```
179
183
 
180
184
  ### Clean up spreadsheet headers
@@ -183,8 +187,8 @@ Headers such as `Country Name` or `1960` need quoting in SQL. `cleannames` turns
183
187
 
184
188
  ```bash
185
189
  dftk dataset fertility -o \
186
- | dftk cleannames ... -o \
187
- | dftk query ... --sql "SELECT country_name, x1960, x2000 FROM data LIMIT 3"
190
+ | dftk cleannames -o \
191
+ | dftk query --sql "SELECT country_name, x1960, x2000 FROM data LIMIT 3"
188
192
  ```
189
193
 
190
194
  For your own export, `dftk cleannames data.tsv --backend duckdb -o data.parquet` also keeps integer columns with empty cells as integers.
@@ -195,7 +199,7 @@ With tens of thousands of points a scatter plot is a solid blob. `--hexbin` coun
195
199
 
196
200
  ```bash
197
201
  dftk dataset diamonds -o \
198
- | dftk scat ... -x carat -y price --hexbin --hexlog --logx --logy -f diamonds.png
202
+ | dftk scat -x carat -y price --hexbin --hexlog --logx --logy -f diamonds.png
199
203
  ```
200
204
 
201
205
  `--colorcol COL` colours each hexagon by the mean of `COL` instead of the count; with `--subgraphcol` all panels share one colour scale.
@@ -204,8 +208,8 @@ dftk dataset diamonds -o \
204
208
 
205
209
  ```bash
206
210
  dftk dataset tips -o \
207
- | dftk stat ... -c tip -g day -o \
208
- | dftk line ... -x day -y mean --yerr sem -f fig.png
211
+ | dftk stat -c tip -g day -o \
212
+ | dftk line -x day -y mean --yerr sem -f fig.png
209
213
  ```
210
214
 
211
215
  ### Wide-to-long then plot overlaid histograms
@@ -249,18 +253,18 @@ suffix `_r` from `merge`; fill from it, then drop it. Here the defaults are
249
253
  each species' median body mass:
250
254
 
251
255
  ```bash
252
- dftk dataset penguins -o | dftk pivot ... -i species -v body_mass_g \
256
+ dftk dataset penguins -o | dftk pivot -i species -v body_mass_g \
253
257
  -f median --move median_body_mass_g:body_mass_g -o defaults.parquet
254
258
  dftk dataset penguins -o \
255
- | dftk merge ... -r defaults.parquet -k species -t left -o \
256
- | dftk eval ... -f "body_mass_g = body_mass_g.fillna(body_mass_g_r)" \
259
+ | dftk merge -r defaults.parquet -k species -t left -o \
260
+ | dftk eval -f "body_mass_g = body_mass_g.fillna(body_mass_g_r)" \
257
261
  --drop body_mass_g_r
258
262
  ```
259
263
 
260
264
  **From neighbouring rows** (`co2` is a weekly series with gaps):
261
265
 
262
266
  ```bash
263
- dftk dataset co2 -o | dftk eval ... -f "ffill = co2.ffill()" \
267
+ dftk dataset co2 -o | dftk eval -f "ffill = co2.ffill()" \
264
268
  -f "bfill = co2.bfill()" -f "linear = co2.interpolate()"
265
269
  ```
266
270
 
@@ -278,9 +282,9 @@ to be next to each other, but within each group they must be in order:
278
282
 
279
283
  ```bash
280
284
  dftk dataset healthexp -o \
281
- | dftk eval ... -f "le = Life_Expectancy.where(Year % 10 != 5)" \
285
+ | dftk eval -f "le = Life_Expectancy.where(Year % 10 != 5)" \
282
286
  --sortasc Country Year -o \
283
- | dftk eval ... -f "le_filled = le.groupby(Country).ffill()"
287
+ | dftk eval -f "le_filled = le.groupby(Country).ffill()"
284
288
  ```
285
289
 
286
290
  `interpolate()` has no grouped form. Once each group's rows are together,
@@ -290,14 +294,78 @@ with something before and after them in the same group:
290
294
 
291
295
  ```bash
292
296
  dftk dataset healthexp -o \
293
- | dftk eval ... -f "le = Life_Expectancy.where(Year % 10 != 5)" \
297
+ | dftk eval -f "le = Life_Expectancy.where(Year % 10 != 5)" \
294
298
  --sortasc Country Year -o \
295
- | dftk eval ... -f "before = le.groupby(Country).ffill().notna()" \
299
+ | dftk eval -f "before = le.groupby(Country).ffill().notna()" \
296
300
  -f "after = le.groupby(Country).bfill().notna()" \
297
301
  -f "le_linear = le.interpolate().where(before & after)" \
298
302
  --drop before after
299
303
  ```
300
304
 
305
+ ### Differences between rows
306
+
307
+ For the change in a column from one row to the next, `eval -f` takes
308
+ pandas `diff()` and `pct_change()`, also within groups. They use the rows
309
+ in their order, so sort first, in a step of its own:
310
+
311
+ ```bash
312
+ dftk dataset healthexp -o | dftk eval --sortasc Country Year -o \
313
+ | dftk eval -f "change = Spending_USD.groupby(Country).diff().round(1)" \
314
+ -f "pct = (Spending_USD.groupby(Country).pct_change() * 100).round(1)" \
315
+ -f "years = Year.groupby(Country).diff()"
316
+ ```
317
+
318
+ ```text
319
+ Year Country Spending_USD Life_Expectancy change pct years
320
+ 1971 Canada 313.391 72.8
321
+ 1976 Canada 543.337 73.8 229.9 73.4 5.0
322
+ 1979 Canada 692.269 75.1 148.9 27.4 3.0
323
+ 1980 Canada 791.812 75.2 99.5 14.4 1.0
324
+ ```
325
+
326
+ The first row of each country gets an empty cell. Without
327
+ `groupby(Country)`, it would be compared with the last row of the
328
+ previous country.
329
+
330
+ To compare a value with one in another column of the previous or next row, for example the
331
+ gap between segments given by `Chr`, `Begin`, `End`, use SQL's `LAG()` /
332
+ `LEAD()` in `query --sql`:
333
+
334
+ ```bash
335
+ dftk query segments.tsv --sql \
336
+ 'SELECT *, "Begin" - LAG("End") OVER (PARTITION BY Chr ORDER BY "Begin")
337
+ AS gap
338
+ FROM data ORDER BY Chr, "Begin"'
339
+ ```
340
+
341
+ ```text
342
+ Chr Begin End Id gap
343
+ chr1 100 200 a
344
+ chr1 250 400 b 50
345
+ chr1 390 500 c -10
346
+ chr2 50 80 d
347
+ chr2 100 300 e 20
348
+ ```
349
+
350
+ A negative gap is an overlap, and the first segment on each chromosome
351
+ gets an empty cell. `lead("Begin") OVER (...) - "End"` gives the gap to
352
+ the next segment instead.
353
+
354
+ - `Begin` and `End` are SQL keywords: quote such column names with double
355
+ quotes and the whole statement with single quotes.
356
+ - No sorting is needed first; `ORDER BY` inside `OVER` sets the order.
357
+ - Integers stay integers.
358
+
359
+ `eval -f` can do the same with pandas `shift`, but it goes by row order, so
360
+ sort by `Chr` and `Begin` first (results are floats):
361
+
362
+ ```bash
363
+ dftk eval segments.tsv -f 'gap = Begin - End.groupby(Chr).shift()'
364
+ ```
365
+
366
+ Without `groupby(Chr)` the first segment of a chromosome is compared with
367
+ the last one of the previous chromosome.
368
+
301
369
  ### Bootstrap confidence intervals
302
370
 
303
371
  Bootstrap mode repeats the full stat computation N times on resampled data
@@ -307,9 +375,9 @@ confidence interval:
307
375
 
308
376
  ```bash
309
377
  dftk dataset tips -o \
310
- | dftk stat ... -c tip -g day --bootstrap 1000 --randomseed 42 -o \
311
- | dftk pivot ... -i day -v mean -f mean cilo cihi -o \
312
- | dftk print ...
378
+ | dftk stat -c tip -g day --bootstrap 1000 --randomseed 42 -o \
379
+ | dftk pivot -i day -v mean -f mean cilo cihi -o \
380
+ | dftk print
313
381
  ```
314
382
 
315
383
  ## Input/output
@@ -317,15 +385,21 @@ dftk dataset tips -o \
317
385
  All commands accept:
318
386
 
319
387
  - A TSV filename as a positional argument
320
- - `-` to read TSV from stdin
321
- - `...` to receive a parquet path from stdin (written by a previous `-o` command)
322
388
  - A `.parquet` filename to read a named parquet file directly
389
+ - Nothing, or `-`, to read stdin: either TSV, or the parquet path printed by
390
+ a previous command's bare `-o` (that temp file is then read and deleted)
391
+ - A pipe as the filename, e.g. `<(grep -v '^#' data.tsv)` or `<(dftk … -o)`,
392
+ read the same way as stdin
393
+
394
+ ```bash
395
+ dftk dataset tips -o | dftk stat -c tip -g day # parquet path on stdin
396
+ dftk dataset tips | dftk stat -c tip -g day # TSV on stdin
397
+ dftk merge -l tips.tsv -r <(dftk pivot tips.tsv -i day -v tip -f mean -o) -k day
398
+ ```
323
399
 
324
- > **`-` vs `...`** — these are not interchangeable. Use `-` when the previous
325
- > command in the pipe wrote plain TSV to stdout (the default). Use `...` when
326
- > the previous command used bare `-o` (pipe mode), which writes a temp
327
- > parquet and prints *its path* to stdout — `-` would try to parse that path
328
- > as TSV and fail.
400
+ Older scripts give `...` as the file to read a parquet path from stdin; that
401
+ still works. In zsh with Oh My Zsh an unquoted `...` becomes `../..`, so
402
+ leave it out (or use `-`) in scripts you share.
329
403
 
330
404
  ### Comment lines and preambles
331
405
 
@@ -47,17 +47,20 @@ uv tool install .
47
47
 
48
48
  ### Skill for coding agents
49
49
 
50
- `skills/dftk/` is an agent skill: a short guide that coding agents such as
51
- Codex and Claude Code load when a task involves dftk. It covers the piping
50
+ dftk ships an agent skill: a short guide that coding agents such as Codex
51
+ and Claude Code load when a task involves dftk. It covers the piping
52
52
  conventions, which command to use for what, and patterns for Snakemake and
53
- Make. Copy it into your agent's skills directory, for example:
53
+ Make. Install it with:
54
54
 
55
55
  ```bash
56
- cp -r skills/dftk ~/.codex/skills/ # Codex
57
- cp -r skills/dftk ~/.claude/skills/ # Claude Code
56
+ dftk skill install # every agent found: ~/.claude and/or ~/.codex
57
+ dftk skill install --codex # only one (--claude or --codex; creates the folder)
58
+ dftk skill install --dir DIR # another agent's skills folder
58
59
  ```
59
60
 
60
- Copy it again after upgrading dftk so it matches the installed version.
61
+ It prints where it installed the skill. Run it again after upgrading dftk
62
+ so the skill matches the installed version; it replaces only the earlier
63
+ dftk skill. The source is in `src/dftk/skills/dftk/`.
61
64
 
62
65
  ## Quick example
63
66
 
@@ -65,15 +68,15 @@ Copy it again after upgrading dftk so it matches the installed version.
65
68
  dftk dataset tips | dftk stat - -c tip total_bill -g day
66
69
  ```
67
70
 
68
- Chain commands with `-o` (write to a temp parquet, print its path) and `...`
69
- (read a parquet path from stdin) instead of piping TSV text directly — faster
70
- for large data, and the recommended pattern once a pipeline has more than one
71
- step:
71
+ Chain commands with a bare `-o` (write a temp parquet, print its path)
72
+ instead of piping TSV text: the next command sees the path on stdin and reads
73
+ that file. It's faster for large data, keeps column types exact, and is the
74
+ recommended pattern once a pipeline has more than one step:
72
75
 
73
76
  ```bash
74
77
  dftk dataset tips -o \
75
- | dftk pivot ... -i day -v tip -f mean -o \
76
- | dftk print ...
78
+ | dftk pivot -i day -v tip -f mean -o \
79
+ | dftk print
77
80
  ```
78
81
 
79
82
  ## Subcommands
@@ -133,7 +136,7 @@ publication-quality output.
133
136
  | `sample` | Random row sampling (with or without replacement, by count or fraction) |
134
137
  | `split` | Split a dataframe into one file per group |
135
138
  | `annotate` | Read and write provenance metadata (genome, source, …) in parquet files |
136
- | `print` | Read any dftk input (TSV, stdin, `...` parquet pipe) and write TSV |
139
+ | `print` | Read any dftk input (TSV, parquet, stdin) and write TSV |
137
140
  | `clean` | Remove leftover temp parquet pipe files from interrupted pipelines |
138
141
  | `help` | List all subcommands or show full help for one: `dftk help stat` |
139
142
 
@@ -144,8 +147,8 @@ publication-quality output.
144
147
  ```bash
145
148
  # z-score sepal_length within species, then fit against petal_length
146
149
  dftk dataset iris -o \
147
- | dftk scale ... -c sepal_length -g species -o \
148
- | dftk fit ... -f "sepal_length_scaled ~ petal_length" -g species
150
+ | dftk scale -c sepal_length -g species -o \
151
+ | dftk fit -f "sepal_length_scaled ~ petal_length" -g species
149
152
  ```
150
153
 
151
154
  ### Clean up spreadsheet headers
@@ -154,8 +157,8 @@ Headers such as `Country Name` or `1960` need quoting in SQL. `cleannames` turns
154
157
 
155
158
  ```bash
156
159
  dftk dataset fertility -o \
157
- | dftk cleannames ... -o \
158
- | dftk query ... --sql "SELECT country_name, x1960, x2000 FROM data LIMIT 3"
160
+ | dftk cleannames -o \
161
+ | dftk query --sql "SELECT country_name, x1960, x2000 FROM data LIMIT 3"
159
162
  ```
160
163
 
161
164
  For your own export, `dftk cleannames data.tsv --backend duckdb -o data.parquet` also keeps integer columns with empty cells as integers.
@@ -166,7 +169,7 @@ With tens of thousands of points a scatter plot is a solid blob. `--hexbin` coun
166
169
 
167
170
  ```bash
168
171
  dftk dataset diamonds -o \
169
- | dftk scat ... -x carat -y price --hexbin --hexlog --logx --logy -f diamonds.png
172
+ | dftk scat -x carat -y price --hexbin --hexlog --logx --logy -f diamonds.png
170
173
  ```
171
174
 
172
175
  `--colorcol COL` colours each hexagon by the mean of `COL` instead of the count; with `--subgraphcol` all panels share one colour scale.
@@ -175,8 +178,8 @@ dftk dataset diamonds -o \
175
178
 
176
179
  ```bash
177
180
  dftk dataset tips -o \
178
- | dftk stat ... -c tip -g day -o \
179
- | dftk line ... -x day -y mean --yerr sem -f fig.png
181
+ | dftk stat -c tip -g day -o \
182
+ | dftk line -x day -y mean --yerr sem -f fig.png
180
183
  ```
181
184
 
182
185
  ### Wide-to-long then plot overlaid histograms
@@ -220,18 +223,18 @@ suffix `_r` from `merge`; fill from it, then drop it. Here the defaults are
220
223
  each species' median body mass:
221
224
 
222
225
  ```bash
223
- dftk dataset penguins -o | dftk pivot ... -i species -v body_mass_g \
226
+ dftk dataset penguins -o | dftk pivot -i species -v body_mass_g \
224
227
  -f median --move median_body_mass_g:body_mass_g -o defaults.parquet
225
228
  dftk dataset penguins -o \
226
- | dftk merge ... -r defaults.parquet -k species -t left -o \
227
- | dftk eval ... -f "body_mass_g = body_mass_g.fillna(body_mass_g_r)" \
229
+ | dftk merge -r defaults.parquet -k species -t left -o \
230
+ | dftk eval -f "body_mass_g = body_mass_g.fillna(body_mass_g_r)" \
228
231
  --drop body_mass_g_r
229
232
  ```
230
233
 
231
234
  **From neighbouring rows** (`co2` is a weekly series with gaps):
232
235
 
233
236
  ```bash
234
- dftk dataset co2 -o | dftk eval ... -f "ffill = co2.ffill()" \
237
+ dftk dataset co2 -o | dftk eval -f "ffill = co2.ffill()" \
235
238
  -f "bfill = co2.bfill()" -f "linear = co2.interpolate()"
236
239
  ```
237
240
 
@@ -249,9 +252,9 @@ to be next to each other, but within each group they must be in order:
249
252
 
250
253
  ```bash
251
254
  dftk dataset healthexp -o \
252
- | dftk eval ... -f "le = Life_Expectancy.where(Year % 10 != 5)" \
255
+ | dftk eval -f "le = Life_Expectancy.where(Year % 10 != 5)" \
253
256
  --sortasc Country Year -o \
254
- | dftk eval ... -f "le_filled = le.groupby(Country).ffill()"
257
+ | dftk eval -f "le_filled = le.groupby(Country).ffill()"
255
258
  ```
256
259
 
257
260
  `interpolate()` has no grouped form. Once each group's rows are together,
@@ -261,14 +264,78 @@ with something before and after them in the same group:
261
264
 
262
265
  ```bash
263
266
  dftk dataset healthexp -o \
264
- | dftk eval ... -f "le = Life_Expectancy.where(Year % 10 != 5)" \
267
+ | dftk eval -f "le = Life_Expectancy.where(Year % 10 != 5)" \
265
268
  --sortasc Country Year -o \
266
- | dftk eval ... -f "before = le.groupby(Country).ffill().notna()" \
269
+ | dftk eval -f "before = le.groupby(Country).ffill().notna()" \
267
270
  -f "after = le.groupby(Country).bfill().notna()" \
268
271
  -f "le_linear = le.interpolate().where(before & after)" \
269
272
  --drop before after
270
273
  ```
271
274
 
275
+ ### Differences between rows
276
+
277
+ For the change in a column from one row to the next, `eval -f` takes
278
+ pandas `diff()` and `pct_change()`, also within groups. They use the rows
279
+ in their order, so sort first, in a step of its own:
280
+
281
+ ```bash
282
+ dftk dataset healthexp -o | dftk eval --sortasc Country Year -o \
283
+ | dftk eval -f "change = Spending_USD.groupby(Country).diff().round(1)" \
284
+ -f "pct = (Spending_USD.groupby(Country).pct_change() * 100).round(1)" \
285
+ -f "years = Year.groupby(Country).diff()"
286
+ ```
287
+
288
+ ```text
289
+ Year Country Spending_USD Life_Expectancy change pct years
290
+ 1971 Canada 313.391 72.8
291
+ 1976 Canada 543.337 73.8 229.9 73.4 5.0
292
+ 1979 Canada 692.269 75.1 148.9 27.4 3.0
293
+ 1980 Canada 791.812 75.2 99.5 14.4 1.0
294
+ ```
295
+
296
+ The first row of each country gets an empty cell. Without
297
+ `groupby(Country)`, it would be compared with the last row of the
298
+ previous country.
299
+
300
+ To compare a value with one in another column of the previous or next row, for example the
301
+ gap between segments given by `Chr`, `Begin`, `End`, use SQL's `LAG()` /
302
+ `LEAD()` in `query --sql`:
303
+
304
+ ```bash
305
+ dftk query segments.tsv --sql \
306
+ 'SELECT *, "Begin" - LAG("End") OVER (PARTITION BY Chr ORDER BY "Begin")
307
+ AS gap
308
+ FROM data ORDER BY Chr, "Begin"'
309
+ ```
310
+
311
+ ```text
312
+ Chr Begin End Id gap
313
+ chr1 100 200 a
314
+ chr1 250 400 b 50
315
+ chr1 390 500 c -10
316
+ chr2 50 80 d
317
+ chr2 100 300 e 20
318
+ ```
319
+
320
+ A negative gap is an overlap, and the first segment on each chromosome
321
+ gets an empty cell. `lead("Begin") OVER (...) - "End"` gives the gap to
322
+ the next segment instead.
323
+
324
+ - `Begin` and `End` are SQL keywords: quote such column names with double
325
+ quotes and the whole statement with single quotes.
326
+ - No sorting is needed first; `ORDER BY` inside `OVER` sets the order.
327
+ - Integers stay integers.
328
+
329
+ `eval -f` can do the same with pandas `shift`, but it goes by row order, so
330
+ sort by `Chr` and `Begin` first (results are floats):
331
+
332
+ ```bash
333
+ dftk eval segments.tsv -f 'gap = Begin - End.groupby(Chr).shift()'
334
+ ```
335
+
336
+ Without `groupby(Chr)` the first segment of a chromosome is compared with
337
+ the last one of the previous chromosome.
338
+
272
339
  ### Bootstrap confidence intervals
273
340
 
274
341
  Bootstrap mode repeats the full stat computation N times on resampled data
@@ -278,9 +345,9 @@ confidence interval:
278
345
 
279
346
  ```bash
280
347
  dftk dataset tips -o \
281
- | dftk stat ... -c tip -g day --bootstrap 1000 --randomseed 42 -o \
282
- | dftk pivot ... -i day -v mean -f mean cilo cihi -o \
283
- | dftk print ...
348
+ | dftk stat -c tip -g day --bootstrap 1000 --randomseed 42 -o \
349
+ | dftk pivot -i day -v mean -f mean cilo cihi -o \
350
+ | dftk print
284
351
  ```
285
352
 
286
353
  ## Input/output
@@ -288,15 +355,21 @@ dftk dataset tips -o \
288
355
  All commands accept:
289
356
 
290
357
  - A TSV filename as a positional argument
291
- - `-` to read TSV from stdin
292
- - `...` to receive a parquet path from stdin (written by a previous `-o` command)
293
358
  - A `.parquet` filename to read a named parquet file directly
359
+ - Nothing, or `-`, to read stdin: either TSV, or the parquet path printed by
360
+ a previous command's bare `-o` (that temp file is then read and deleted)
361
+ - A pipe as the filename, e.g. `<(grep -v '^#' data.tsv)` or `<(dftk … -o)`,
362
+ read the same way as stdin
363
+
364
+ ```bash
365
+ dftk dataset tips -o | dftk stat -c tip -g day # parquet path on stdin
366
+ dftk dataset tips | dftk stat -c tip -g day # TSV on stdin
367
+ dftk merge -l tips.tsv -r <(dftk pivot tips.tsv -i day -v tip -f mean -o) -k day
368
+ ```
294
369
 
295
- > **`-` vs `...`** — these are not interchangeable. Use `-` when the previous
296
- > command in the pipe wrote plain TSV to stdout (the default). Use `...` when
297
- > the previous command used bare `-o` (pipe mode), which writes a temp
298
- > parquet and prints *its path* to stdout — `-` would try to parse that path
299
- > as TSV and fail.
370
+ Older scripts give `...` as the file to read a parquet path from stdin; that
371
+ still works. In zsh with Oh My Zsh an unquoted `...` becomes `../..`, so
372
+ leave it out (or use `-`) in scripts you share.
300
373
 
301
374
  ### Comment lines and preambles
302
375
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dataframe-toolkit"
3
- version = "0.11.2"
3
+ version = "0.12.0"
4
4
  description = "A command-line toolkit for DataFrame analysis and manipulation"
5
5
  readme = "README.md"
6
6
  keywords = [
@@ -29,6 +29,7 @@ dependencies = [
29
29
  "patsy>=0.5.6",
30
30
  "duckdb>=0.8",
31
31
  "matplotlib>=3.7",
32
+ "pillow>=13.0",
32
33
  "seaborn>=0.12",
33
34
  "tqdm>=4.65",
34
35
  ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dataframe-toolkit"
3
- version = "0.11.2"
3
+ version = "0.12.0"
4
4
  description = "A command-line toolkit for DataFrame analysis and manipulation"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -26,6 +26,7 @@ dependencies = [
26
26
  "patsy>=0.5.6",
27
27
  "duckdb>=0.8",
28
28
  "matplotlib>=3.7",
29
+ "pillow>=13.0", # not used directly (matplotlib needs it); floor for security fixes
29
30
  "seaborn>=0.12",
30
31
  "tqdm>=4.65",
31
32
  ]
@@ -73,6 +73,7 @@ COMMAND_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
73
73
  ("annotate", "annotate_cmd:AnnotateCommand"),
74
74
  ("print", "print_cmd:PrintCommand"),
75
75
  ("clean", "clean_cmd:CleanCommand"),
76
+ ("skill", "skill_cmd:SkillCommand"),
76
77
  ],
77
78
  ),
78
79
  ]
@@ -26,7 +26,7 @@ Usage examples:
26
26
  --meta genome=hg38 --meta source=my_pipeline
27
27
 
28
28
  # Same, built from a bundled dataset:
29
- dftk dataset iris -o | dftk eval ... -f "z = sepal_length + petal_length" \\
29
+ dftk dataset iris -o | dftk eval -f "z = sepal_length + petal_length" \\
30
30
  -o result.parquet --meta genome=hg38 --meta source=my_pipeline
31
31
 
32
32
  Annotations propagate through the parquet pipe: a file annotated at
@@ -74,10 +74,10 @@ USEVALUE (--usevalue)
74
74
  EXAMPLES
75
75
  --------
76
76
  dftk binx data.tsv -c age -b 0:100:10
77
- dftk dataset tips -o | dftk binx ... -c total_bill -b 0:60:10
78
- dftk dataset tips -o | dftk binx ... -c tip -b 0,2,4,6,10 -d tip_bracket --usevalue m
79
- dftk dataset tips -o | dftk binx ... -c total_bill -b 0:60:10 -d bill_bin -o \\
80
- | dftk stat ... -c tip -g bill_bin
77
+ dftk dataset tips -o | dftk binx -c total_bill -b 0:60:10
78
+ dftk dataset tips -o | dftk binx -c tip -b 0,2,4,6,10 -d tip_bracket --usevalue m
79
+ dftk dataset tips -o | dftk binx -c total_bill -b 0:60:10 -d bill_bin -o \\
80
+ | dftk stat -c tip -g bill_bin
81
81
  """
82
82
 
83
83
 
@@ -116,21 +116,21 @@ EXAMPLES
116
116
  Basic chi-plot:
117
117
 
118
118
  dftk chi data.tsv -x recomb_rate -y gc_content
119
- dftk dataset tips -o | dftk chi ... -x total_bill -y tip
119
+ dftk dataset tips -o | dftk chi -x total_bill -y tip
120
120
 
121
121
  Split into a subplot grid by category, to check whether tail dependence is
122
122
  consistent across the whole dataset or driven by a few subgroups (e.g.
123
123
  chromosome, sector, region — here, diamond cut quality):
124
124
 
125
- dftk dataset diamonds -o | dftk chi ... -x carat -y price --subgraphcol cut
125
+ dftk dataset diamonds -o | dftk chi -x carat -y price --subgraphcol cut
126
126
 
127
127
  Colour-code by group instead of splitting into subplots:
128
128
 
129
- dftk dataset diamonds -o | dftk chi ... -x carat -y price -g cut
129
+ dftk dataset diamonds -o | dftk chi -x carat -y price -g cut
130
130
 
131
131
  Publication figure (Nature single column, PDF):
132
132
 
133
- dftk dataset tips -o | dftk chi ... -x total_bill -y tip \\
133
+ dftk dataset tips -o | dftk chi -x total_bill -y tip \\
134
134
  --size single --fontsize publication -f fig.pdf
135
135
  """
136
136
 
@@ -57,13 +57,13 @@ column per year (1960, 1961, ...); after cleaning they can be used
57
57
  unquoted in SQL:
58
58
 
59
59
  dftk dataset fertility -o \\
60
- | dftk cleannames ... -o \\
61
- | dftk query ... --sql "SELECT country_name, x1960, x2000 FROM data LIMIT 3"
60
+ | dftk cleannames -o \\
61
+ | dftk query --sql "SELECT country_name, x1960, x2000 FROM data LIMIT 3"
62
62
 
63
63
  Write the old and new names to a TSV file, e.g. for a data dictionary:
64
64
 
65
65
  dftk dataset fertility -o \\
66
- | dftk cleannames ... --mapping names.tsv -o fertility.parquet
66
+ | dftk cleannames --mapping names.tsv -o fertility.parquet
67
67
  """
68
68
 
69
69
  # Letters that Unicode does not decompose into ASCII letter + accent
@@ -12,7 +12,7 @@ Examples
12
12
  dftk concat penguins.tsv penguins.tsv --sourcecol source
13
13
  dftk dataset penguins -o penguins.parquet
14
14
  dftk concat penguins.parquet penguins.parquet -o
15
- dftk dataset penguins -o | dftk eval ... -f "x=1" -o | dftk concat ... penguins.tsv
15
+ dftk dataset penguins -o | dftk eval -f "x=1" -o | dftk concat - penguins.tsv
16
16
  """
17
17
 
18
18
  import argparse
@@ -51,7 +51,7 @@ EXAMPLES
51
51
  dftk concat penguins.tsv penguins.tsv --sourcecol source
52
52
  dftk dataset penguins -o penguins.parquet
53
53
  dftk concat penguins.parquet penguins.parquet -o result.parquet
54
- dftk dataset penguins -o | dftk eval ... -f "x=1" -o | dftk concat ... penguins.tsv
54
+ dftk dataset penguins -o | dftk eval -f "x=1" -o | dftk concat - penguins.tsv
55
55
 
56
56
  Mismatched columns are outer-joined and filled (NaN by default, or --fill):
57
57
 
@@ -65,7 +65,7 @@ Mismatched columns are outer-joined and filled (NaN by default, or --fill):
65
65
  metavar="FILE",
66
66
  help=(
67
67
  "Input files to concatenate (TSV or .parquet). "
68
- "Use - for TSV stdin or ... for a parquet pipe path on stdin. "
68
+ "Use - for stdin (TSV, or a parquet path from -o). "
69
69
  "At most one stdin source is allowed."
70
70
  ),
71
71
  )