dataframe-toolkit 0.13.0__tar.gz → 0.14.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.13.0 → dataframe_toolkit-0.14.0}/PKG-INFO +32 -1
  2. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/README.md +31 -0
  3. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/pyproject.toml +1 -1
  4. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/pyproject.toml.orig +1 -1
  5. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/query_cmd.py +61 -0
  6. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/skills/dftk/SKILL.md +1 -0
  7. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/__init__.py +0 -0
  8. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/cli.py +0 -0
  9. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/__init__.py +0 -0
  10. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/annotate_cmd.py +0 -0
  11. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/base.py +0 -0
  12. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/binx_cmd.py +0 -0
  13. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/chi_cmd.py +0 -0
  14. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/clean_cmd.py +0 -0
  15. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/cleannames_cmd.py +0 -0
  16. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/concat_cmd.py +0 -0
  17. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/corr_cmd.py +0 -0
  18. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/dataset_cmd.py +0 -0
  19. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/describe_cmd.py +0 -0
  20. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/eval_cmd.py +0 -0
  21. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/explode_cmd.py +0 -0
  22. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/fit_cmd.py +0 -0
  23. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/func_cmd.py +0 -0
  24. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/help.py +0 -0
  25. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/hist_cmd.py +0 -0
  26. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/implode_cmd.py +0 -0
  27. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/info_cmd.py +0 -0
  28. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/interp_cmd.py +0 -0
  29. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/line_cmd.py +0 -0
  30. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/melt_cmd.py +0 -0
  31. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/merge_cmd.py +0 -0
  32. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/pivot_cmd.py +0 -0
  33. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/print_cmd.py +0 -0
  34. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/randvar_cmd.py +0 -0
  35. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/sample_cmd.py +0 -0
  36. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/scale_cmd.py +0 -0
  37. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/scat_cmd.py +0 -0
  38. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/segid_cmd.py +0 -0
  39. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/skill_cmd.py +0 -0
  40. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/split_cmd.py +0 -0
  41. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/stat_cmd.py +0 -0
  42. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/test_cmd.py +0 -0
  43. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/transpose_cmd.py +0 -0
  44. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/commands/wstat_cmd.py +0 -0
  45. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/common/__init__.py +0 -0
  46. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/common/copula.py +0 -0
  47. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/common/evalfuncs.py +0 -0
  48. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/common/io.py +0 -0
  49. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/common/plot.py +0 -0
  50. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/common/seed.py +0 -0
  51. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/common/seq.py +0 -0
  52. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/common/stats.py +0 -0
  53. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/io.py +0 -0
  54. {dataframe_toolkit-0.13.0 → dataframe_toolkit-0.14.0}/src/dftk/skills/dftk/references/snakemake.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dataframe-toolkit
3
- Version: 0.13.0
3
+ Version: 0.14.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
@@ -193,6 +193,23 @@ dftk dataset fertility -o \
193
193
 
194
194
  For your own export, `dftk cleannames data.tsv --backend duckdb -o data.parquet` also keeps integer columns with empty cells as integers.
195
195
 
196
+ ### JSON files
197
+
198
+ DuckDB reads JSON itself, so `query --sql` takes a JSON file in `FROM` (a file of records, `[{...}, {...}]`, or JSON Lines, one object per line; `'logs/*.jsonl'` reads several). When the SQL doesn't use the `data` table, dftk reads no input. Nested objects and lists would end up as one text cell each, so flatten them in the same query: `pos.*` makes a column of each field, `len()` counts a list and `unnest()` gives one row per item:
199
+
200
+ ```bash
201
+ printf '%s\n' '{"id": 1, "pos": {"chr": "1", "start": 100}, "tags": ["x", "y"]}' \
202
+ '{"id": 2, "pos": {"chr": "2", "start": 200}, "tags": []}' \
203
+ > records.jsonl
204
+ dftk query --sql \
205
+ "SELECT * EXCLUDE (pos, tags), pos.*, len(tags) AS ntags FROM 'records.jsonl'"
206
+ # id chr start ntags
207
+ # 1 1 100 2
208
+ # 2 2 200 0
209
+ dftk query --sql "SELECT id, unnest(tags) AS tag FROM 'records.jsonl'" -o \
210
+ | dftk pivot -i tag -v id -f count
211
+ ```
212
+
196
213
  ### Large tables: hexbin instead of points
197
214
 
198
215
  With tens of thousands of points a scatter plot is a solid blob. `--hexbin` counts points per hexagon instead (`--hexlog` for a log colour scale), and `--fit lowess` adds a smooth curve:
@@ -390,6 +407,8 @@ All commands accept:
390
407
  a previous command's bare `-o` (that temp file is then read and deleted)
391
408
  - A pipe as the filename, e.g. `<(grep -v '^#' data.tsv)` or `<(dftk … -o)`,
392
409
  read the same way as stdin
410
+ - JSON through `query --sql "SELECT … FROM 'f.jsonl'"`, which needs no
411
+ other input (see [JSON files](#json-files))
393
412
 
394
413
  ```bash
395
414
  dftk dataset tips -o | dftk stat -c tip -g day # parquet path on stdin
@@ -427,6 +446,18 @@ dftk print data.txt --noheader --move V1:chr V2:begin V3:end -o named.tsv
427
446
  dftk eval data.txt --noheader -f "len = V3 - V2" --removeheader # no header out
428
447
  ```
429
448
 
449
+ In `merge`, `--noheader` applies to both files, so both sides are named
450
+ `V1 V2 …`. The key appears once and the right side's other columns get the
451
+ `_r` suffix. `--move` runs after the merge, so it renames the merged names:
452
+
453
+ ```bash
454
+ dftk merge -l left.txt -r right.txt -k V1 --noheader
455
+ # V1 V2 V3 V2_r V3_r
456
+ dftk merge -l left.txt -r right.txt -k V1 --noheader \
457
+ --move V1:id V2:n V3:s V2_r:n2 V3_r:s2
458
+ # id n s n2 s2
459
+ ```
460
+
430
461
  Parquet always has column names, so `--removeheader` with `-o` and
431
462
  `--noheader` on parquet input do nothing (dftk warns).
432
463
 
@@ -163,6 +163,23 @@ dftk dataset fertility -o \
163
163
 
164
164
  For your own export, `dftk cleannames data.tsv --backend duckdb -o data.parquet` also keeps integer columns with empty cells as integers.
165
165
 
166
+ ### JSON files
167
+
168
+ DuckDB reads JSON itself, so `query --sql` takes a JSON file in `FROM` (a file of records, `[{...}, {...}]`, or JSON Lines, one object per line; `'logs/*.jsonl'` reads several). When the SQL doesn't use the `data` table, dftk reads no input. Nested objects and lists would end up as one text cell each, so flatten them in the same query: `pos.*` makes a column of each field, `len()` counts a list and `unnest()` gives one row per item:
169
+
170
+ ```bash
171
+ printf '%s\n' '{"id": 1, "pos": {"chr": "1", "start": 100}, "tags": ["x", "y"]}' \
172
+ '{"id": 2, "pos": {"chr": "2", "start": 200}, "tags": []}' \
173
+ > records.jsonl
174
+ dftk query --sql \
175
+ "SELECT * EXCLUDE (pos, tags), pos.*, len(tags) AS ntags FROM 'records.jsonl'"
176
+ # id chr start ntags
177
+ # 1 1 100 2
178
+ # 2 2 200 0
179
+ dftk query --sql "SELECT id, unnest(tags) AS tag FROM 'records.jsonl'" -o \
180
+ | dftk pivot -i tag -v id -f count
181
+ ```
182
+
166
183
  ### Large tables: hexbin instead of points
167
184
 
168
185
  With tens of thousands of points a scatter plot is a solid blob. `--hexbin` counts points per hexagon instead (`--hexlog` for a log colour scale), and `--fit lowess` adds a smooth curve:
@@ -360,6 +377,8 @@ All commands accept:
360
377
  a previous command's bare `-o` (that temp file is then read and deleted)
361
378
  - A pipe as the filename, e.g. `<(grep -v '^#' data.tsv)` or `<(dftk … -o)`,
362
379
  read the same way as stdin
380
+ - JSON through `query --sql "SELECT … FROM 'f.jsonl'"`, which needs no
381
+ other input (see [JSON files](#json-files))
363
382
 
364
383
  ```bash
365
384
  dftk dataset tips -o | dftk stat -c tip -g day # parquet path on stdin
@@ -397,6 +416,18 @@ dftk print data.txt --noheader --move V1:chr V2:begin V3:end -o named.tsv
397
416
  dftk eval data.txt --noheader -f "len = V3 - V2" --removeheader # no header out
398
417
  ```
399
418
 
419
+ In `merge`, `--noheader` applies to both files, so both sides are named
420
+ `V1 V2 …`. The key appears once and the right side's other columns get the
421
+ `_r` suffix. `--move` runs after the merge, so it renames the merged names:
422
+
423
+ ```bash
424
+ dftk merge -l left.txt -r right.txt -k V1 --noheader
425
+ # V1 V2 V3 V2_r V3_r
426
+ dftk merge -l left.txt -r right.txt -k V1 --noheader \
427
+ --move V1:id V2:n V3:s V2_r:n2 V3_r:s2
428
+ # id n s n2 s2
429
+ ```
430
+
400
431
  Parquet always has column names, so `--removeheader` with `-o` and
401
432
  `--noheader` on parquet input do nothing (dftk warns).
402
433
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dataframe-toolkit"
3
- version = "0.13.0"
3
+ version = "0.14.0"
4
4
  description = "A command-line toolkit for DataFrame analysis and manipulation"
5
5
  readme = "README.md"
6
6
  keywords = [
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dataframe-toolkit"
3
- version = "0.13.0"
3
+ version = "0.14.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 @@ sql mode (--sql)
26
26
  """
27
27
 
28
28
  import argparse
29
+ import json
29
30
  import logging
30
31
  import os
31
32
 
@@ -85,6 +86,9 @@ def _sql_query(args: argparse.Namespace, sql: str, table: str) -> pd.DataFrame:
85
86
  if direct_parquet:
86
87
  rel = conn.read_parquet(filename)
87
88
  conn.register(table, rel)
89
+ elif filename is None and not _uses_table(sql, table):
90
+ # SQL reads its own files (FROM 'f.jsonl'): don't wait for stdin.
91
+ pass
88
92
  else:
89
93
  # stdin, TSV files, or parquet-pipe: read into pandas first.
90
94
  df = io.read(args)
@@ -99,6 +103,37 @@ def _sql_query(args: argparse.Namespace, sql: str, table: str) -> pd.DataFrame:
99
103
  return _hugeint_to_int64(df, description)
100
104
 
101
105
 
106
+ def _uses_table(sql: str, table: str) -> bool:
107
+ """Return True unless DuckDB's parser says *sql* never names *table*.
108
+
109
+ Uses json_serialize_sql, which only parses (duckdb.get_table_names also
110
+ binds, and fails on valid SQL such as SELECT pos.*). On a parse error
111
+ the input is read as before and the query itself reports the error.
112
+ """
113
+ try:
114
+ tree = json.loads(
115
+ duckdb.execute("SELECT json_serialize_sql(?)", [sql]).fetchone()[0]
116
+ )
117
+ except Exception: # an old duckdb without json_serialize_sql
118
+ return True
119
+ if tree.get("error"):
120
+ return True
121
+ names: set[str] = set()
122
+
123
+ def walk(node) -> None:
124
+ if isinstance(node, dict):
125
+ if node.get("type") == "BASE_TABLE":
126
+ names.add(node.get("table_name", "").lower())
127
+ for value in node.values():
128
+ walk(value)
129
+ elif isinstance(node, list):
130
+ for value in node:
131
+ walk(value)
132
+
133
+ walk(tree)
134
+ return table.lower() in names
135
+
136
+
102
137
  def _hugeint_to_int64(df: pd.DataFrame, description) -> pd.DataFrame:
103
138
  """Return 128-bit integer result columns as Int64 instead of float64.
104
139
 
@@ -197,6 +232,30 @@ CTEs:
197
232
  )
198
233
  SELECT * FROM ranked WHERE rank <= 3"
199
234
 
235
+ JSON FILES
236
+ ----------
237
+ DuckDB reads JSON itself: name the file in FROM, in single quotes. A file
238
+ of records ([{...}, {...}]) and JSON Lines (one object per line) both
239
+ work, and 'logs/*.jsonl' reads several files as one table. When the SQL
240
+ doesn't use the data table, dftk reads no input, so nothing is piped in:
241
+
242
+ printf '%s\\n' '{"id": 1, "pos": {"chr": "1", "start": 100}, "tags": ["x", "y"]}' \\
243
+ '{"id": 2, "pos": {"chr": "2", "start": 200}, "tags": []}' \\
244
+ > records.jsonl
245
+ dftk query --sql "SELECT * FROM 'records.jsonl'"
246
+
247
+ Nested objects and lists come out as one text cell each, which later
248
+ commands can't compute on. Flatten them in the same query: pos.* makes a
249
+ column of each field, len() counts a list, unnest() gives a row per item:
250
+
251
+ dftk query --sql \\
252
+ "SELECT * EXCLUDE (pos, tags), pos.*, len(tags) AS ntags
253
+ FROM 'records.jsonl'"
254
+ dftk query --sql "SELECT id, unnest(tags) AS tag FROM 'records.jsonl'"
255
+ dftk query --sql \\
256
+ "SELECT * EXCLUDE (pos, tags), pos.* FROM 'records.jsonl'" -o \\
257
+ | dftk stat -c start
258
+
200
259
  OUT-OF-CORE LARGE FILES
201
260
  ------------------------
202
261
  When the input is a named .parquet file, DuckDB queries it natively without
@@ -254,6 +313,8 @@ class QueryCommand(BaseCommand):
254
313
  "Full DuckDB SQL query. The input is registered as a virtual "
255
314
  "table named 'data' (see --table). Named .parquet inputs are "
256
315
  "queried natively without full load into memory. "
316
+ "SQL that only reads its own files (FROM 'f.jsonl') "
317
+ "needs no input. "
257
318
  "Cannot be combined with -q. Requires duckdb."
258
319
  ),
259
320
  )
@@ -104,6 +104,7 @@ Transform:
104
104
  | New column from a formula: `z = x / y`, `where(...)`, string, DNA and statistical functions | `eval -f`, `eval -c` (constant), `eval -s` (string) |
105
105
  | Fill empty cells: from another column, a group mean, the row above (`ffill`, per group with `groupby`) or by interpolation; sort first, fills follow row order | `eval -f "x = x.fillna(y)"`, `"x = x.groupby(g).ffill()"` (see `eval -h`) |
106
106
  | Filter rows (pandas expression) or run any DuckDB SQL (`FROM data`) | `query -q` / `query --sql` |
107
+ | Read JSON or JSON Lines (no input needed); flatten nested fields with `pos.*`, lists with `unnest()` | `query --sql "SELECT * EXCLUDE (pos), pos.* FROM 'f.jsonl'"` |
107
108
  | Join two tables, including anti-joins | `merge DATAFILE -r OTHER -k KEY [-t left] [--only left]` |
108
109
  | Stack tables row-wise | `concat A B C [--sourcecol src]` |
109
110
  | Wide to long / long to wide plus aggregation | `melt` / `pivot -i ROWS -g COLS -v VALS -f FUNCS` |