12 个版本 (6 个破坏性更新)

0.7.0 2023 年 6 月 29 日
0.6.1 2023 年 1 月 18 日
0.5.4 2022 年 12 月 26 日
0.4.2 2022 年 12 月 20 日
0.1.0 2022 年 11 月 20 日

#1104 in 文本处理

自定义许可

33KB
431

dr.rs

状态徽章 | 下载 | 源代码 | 错误报告

一个使用命令行处理数据文件(csv 和 parquet)的工具包,灵感来自 csvkit,速度极快,由 Rust 驱动。

你可能想知道为什么我要实现这个,因为已经有了 xsv。有两个原因:

  1. 这是我为了学习 Rust 而实现的功能。
  2. Rust 数据生态系统自 xsv 开始以来已经发生了巨大变化。现在我们可以添加诸如 SQL 命令来过滤 csv 文件,或将结果转换为 parquet 文件等功能。

TL;DR

你可以使用 cargo install dr 以 Rust 方式安装 dr,但下载此处的二进制文件可能就足够了。

$ dr --help
dr is a handy command line tool to handle csv and parquet files.
It is designed to integrate nicely with other command line tools
like cat, sed, awk and database clients cli. You can find more
information an a short tutorial https://git.guillemborrell.es/guillem/dr
            

Usage: dr [COMMAND]

Commands:
  csv
          Read csv, output arrow stream
  schema
          Several table schema related utilities
  sql
          Runs a sql statement on the file
  print
          Pretty prints the table
  rpq
          Read parquet file
  wpq
          Write to a paquet file
  help
          Print this message or the help of the given subcommand(s)

Options:
  -h, --help
          Print help information (use `-h` for a summary)

  -V, --version
          Print version information

如何使用

dr 是一个便利的命令,用于探索、转换和分析 csv 和 parquet 文件,以节省你编写临时 Python 脚本或为非常简单的任务创建自定义容器映像的时间。它旨在让数据工程师的生活更加轻松。

假设你有一个非常大的 csv 文件,而你只想将其转换为 parquet 格式,同时进行一些类型推断和合理的默认设置。使用 dr,这就像这样:

$ dr csv wine.csv -P wine.pq

Parquet 文件是二进制的,你可能想要检查你是否没有写出无意义的文件,可以在终端上打印出头部。

$ dr rpq wine.pq -a
shape: (5, 14)
┌──────┬─────────┬────────────┬──────┬─────┬───────────┬──────┬──────┬─────────┐
 Wine ┆ Alcohol ┆ Malic.acid ┆ Ash  ┆ ... ┆ Color.int ┆ Hue  ┆ OD   ┆ Proline │
 ---  ┆ ---     ┆ ---        ┆ ---  ┆     ┆ ---       ┆ ---  ┆ ---  ┆ ---     │
 i64  ┆ f64     ┆ f64        ┆ f64  ┆     ┆ f64       ┆ f64  ┆ f64  ┆ i64     │
╞══════╪═════════╪════════════╪══════╪═════╪═══════════╪══════╪══════╪═════════╡
 1    ┆ 14.23   ┆ 1.71       ┆ 2.43 ┆ ... ┆ 5.64      ┆ 1.04 ┆ 3.92 ┆ 1065    │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
 1    ┆ 13.2    ┆ 1.78       ┆ 2.14 ┆ ... ┆ 4.38      ┆ 1.05 ┆ 3.4  ┆ 1050    │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
 1    ┆ 13.16   ┆ 2.36       ┆ 2.67 ┆ ... ┆ 5.68      ┆ 1.03 ┆ 3.17 ┆ 1185    │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
 1    ┆ 14.37   ┆ 1.95       ┆ 2.5  ┆ ... ┆ 7.8       ┆ 0.86 ┆ 3.45 ┆ 1480    │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
 1    ┆ 13.24   ┆ 2.59       ┆ 2.87 ┆ ... ┆ 4.32      ┆ 1.04 ┆ 2.93 ┆ 735     │
└──────┴─────────┴────────────┴──────┴─────┴───────────┴──────┴──────┴─────────┘

《dr》最有趣的功能可能是能够使用SQL处理csv和parquet文件,而像《xsv》和《csvkit》这样的解决方案则依赖于一组丰富的子命令和选项。如果您已经熟悉SQL,就不需要再阅读更多文档来选择、过滤或分组数据。您需要记住的唯一一点是,表将被称为《this》。以下命令输出的是在流行的葡萄酒数据集中酒精浓度最高的葡萄酒的csv文件。

 dr csv wine.csv -q "select * from this where Alcohol = max(Alcohol)" | dr print
shape: (1, 14)
┌──────┬─────────┬────────────┬──────┬─────┬───────────┬──────┬──────┬─────────┐
│ Wine ┆ Alcohol ┆ Malic.acid ┆ Ash  ┆ ... ┆ Color.int ┆ Hue  ┆ OD   ┆ Proline │
│ ------------  ┆     ┆ ------------     │
│ i64f64f64f64  ┆     ┆ f64f64f64i64     │
╞══════╪═════════╪════════════╪══════╪═════╪═══════════╪══════╪══════╪═════════╡
│ 114.831.642.17...5.21.082.851045    │
└──────┴─────────┴────────────┴──────┴─────┴───────────┴──────┴──────┴─────────┘

如果您没有使用任何格式化结果的选项,那么《dr》会输出Arrow的IPC格式,这意味着多个《dr》调用可以有效地链式连接,且开销非常低。以下脚本加载了一个月的纽约出租车数据,并在此数据上执行了两个SQL查询。

$ dr rpq data/yellow_tripdata_2014-01.parquet \
    -q "select count(1) as cnt, passenger_count from this group by passenger_count" \
    | dr sql "select * from this order by cnt desc" \
    | dr print
┌─────────┬─────────────────┐
 cnt     ┆ passenger_count │
 ---     ┆ ---             │
 u32     ┆ i64             │
╞═════════╪═════════════════╡
 9727321 ┆ 1               │
├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
 1891588 ┆ 2               │
├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
 789070  ┆ 5               │
├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
 566248  ┆ 3               │
├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
 ...     ┆ ...             │
├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
 19      ┆ 208             │
├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
 16      ┆ 9               │
├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
 7       ┆ 7               │
├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
 5       ┆ 8               │
└─────────┴─────────────────┘

使用SQL数据库操作

您有多少次需要将csv文件(有时比内存还要大)插入数据库?十几次?几百次?您可能已经使用了Pandas,因为它可以推断表的类型。因此,简单的数据操作变成了带有Pandas和PostgreSQL驱动的Python脚本。

现在,《dr》可以提供包含少量列的表创建语句

$ head wine.csv | dr schema -i -p -n wine
CREATE TABLE IF NOT EXISTS "wine" (  );
ALTER TABLE "wine" ADD COLUMN "Wine" integer;
ALTER TABLE "wine" ADD COLUMN "Alcohol" real;
ALTER TABLE "wine" ADD COLUMN "Malic.acid" real;
ALTER TABLE "wine" ADD COLUMN "Ash" real;
ALTER TABLE "wine" ADD COLUMN "Acl" real;
ALTER TABLE "wine" ADD COLUMN "Mg" integer;
ALTER TABLE "wine" ADD COLUMN "Phenols" real;
ALTER TABLE "wine" ADD COLUMN "Flavanoids" real;
ALTER TABLE "wine" ADD COLUMN "Nonflavanoid.phenols" real;
ALTER TABLE "wine" ADD COLUMN "Proanth" real;
ALTER TABLE "wine" ADD COLUMN "Color.int" real;
ALTER TABLE "wine" ADD COLUMN "Hue" real;
ALTER TABLE "wine" ADD COLUMN "OD" real;
ALTER TABLE "wine" ADD COLUMN "Proline" integer;

更多内容请参阅示例部分

由于大多数数据库都可以摄入和输出CSV文件,因此一些简单操作可以通过《dr》得到增强,例如将查询结果存储为parquet文件。

$ psql -c "copy (select * from wine) to stdout with (FORMAT 'csv', HEADER)" | dr csv -i -P wine.pq

参考

一些生成ipc格式原始输出的命令。

  • 读取csv或parquet文件并打印标题: dr {csv, rpq} [file] -a
  • 读取csv或parquet文件,执行SQL语句,并以Arrow的ipc格式输出结果到stdout: dr {csv, rpq} [file] -q "statement"
  • 读取csv或parquet文件并打印每个列的摘要: dr {csv, rpq} [file] -s "[query]"
  • 读取csv或parquet文件,执行查询,并以csv格式输出结果到stdout: dr {csv, rpq} [file] -s "[query]" -t
  • 读取csv并将内容相同的文件写入parquet: dr csv [file.csv] -P [file.pq]

一些将原始输入转换为ipc格式的命令

  • 从stdin以ipc格式读取并格式化打印表: dr print
  • 从stdin以csv格式读取并格式化打印表: dr print -t
  • 从stdin以ipc格式读取并写入parquet: dr wpq [file.pq]

一些从stdin读取csv数据的命令

  • 从stdin读取csv并打印出作为插入到postgresql数据库的schema:dr schema -i -p -n tablename
  • 从stdin读取csv并保存为parquet,自动推断类型:dr csv -i -P filename.pq

示例

将CSV插入到postgresql中

假设你得到了一个大的(几个GiB)文件,它具有奇怪的(latin1)编码,你想要将其插入到postgresql中。这个数据集可能太大,无法一次性存储在内存中,所以你希望将其流式传输到数据库中。你需要

  • 读取csv文件
  • 推断schema,并创建一个表
  • 将文件的编码改为与数据库相同的编码

你可以使用dr将此过程转换为两步,并一次性进行编码转换。第一步是推断结果的表schema并创建表

$ head large_csv_file.csv | iconv -f latin1 -t utf-8 | dr schema -i -p -n tablename | pgsql -U username -h hostname database

第二步是利用pgsql命令将文件的 内容写入数据库

$ cat large_csv_file.csv | iconv -f latin1 -t UTF-8 | psql -U username -h hostname -c "\copy tablename from stdin with (FORMAT 'csv', HEADER)" database

数据导入过程是原子的,这意味着如果pgsql在插入任何记录时失败,则不会进行任何插入。如果插入失败,可能是由于某些类型为varchar的列无法适应推断的类型,你可以使用以下方法更改类型

$ psql -U username -h hostname -c 'alter table tablename alter column "LongDescription" type varchar(1024);' database

然后再次尝试插入

性能

此命令运行两个dr进程。第一个进程对144MB大小的压缩parquet文件进行聚合,第二个进程只是对结果进行排序

$ dr rpq data/yellow_tripdata_2014-01.parquet \
    -q "select count(1) as cnt, passenger_count from this group by passenger_count" \
    | dr sql "select * from this order by cnt desc" \
    > /dev/null

在非常旧的机器(Intel(R) Core(TM) i5-6500T CPU @ 2.50GHz)上,这需要大约半秒钟的时间,这大约是读取和解压缩parquet文件所需的时间。Polar的csv和parquet读取器有一些不错的性能,所以你可以相信dr是其中最快的之一。

注意事项

  1. dr使用Polars在Rust中构建和转换数据框,整个表可能被加载到内存中。在dr创建时,流式传输支持与SQL上下文不太兼容。

  2. dr使用Polars的SQLContext来执行查询,该查询支持SQL语言的子集。

站在巨人的肩膀上

没有这些,这一切都是不可能的。Polars

依赖关系

~33–46MB
~796K SLoC