☰
Quick, Draw! 数据集 ndjson 格式探索指南:基于 ndjson-cli 的命令行筛选、排序与子集导出实战
2026/10/15 17:20:28 网站建设 项目流程
  • 数据集

【免费下载链接】quickdraw-dataset

Documentation on how to access and use the Quick, Draw! Dataset.

项目地址:https://gitcode.com/gh_mirrors/qu/quickdraw-dataset
点击查看免费下载

导读

Quick, Draw!(涂鸦快画)数据集以多种格式存放下百万量级的用户涂鸦,其中 ndjson(Newline Delimited JSON)是其最直观、最易用命令行处理的格式之一。本文以官方文档 examples/nodejs/ndjson.md 为主线,围绕"如何用 ndjson-cli 从海量涂鸦中快速筛选、排序并导出感兴趣的子集"这一核心场景,完整还原文档中的每一条命令,并结合仓库中的 README 与 Node.js 解析源码补充数据字段定义、简化流程与实现细节。读完本文,你将能够用cat、head、wc与ndjson-filter、ndjson-sort、ndjson-reduce等工具组合,像操作普通文本流一样对千万级涂鸦数据进行切片、过滤与导出,为后续的数据分析、可视化和模型训练打下基础。

1. ndjson:一行一个涂鸦对象

1.1 什么是 Quick, Draw! 数据集的 ndjson 格式

Quick, Draw! 数据集是 Google 发布的、由玩家在"你画我猜"游戏中贡献的涂鸦集合,包含 345 个类别(见仓库根目录的 categories.txt,face也在其中)、超过五千万张涂鸦。仓库 README.md 说明,原始数据按类别拆分为独立的 ndjson 文件,每个类别一个文件,每一行是一个完整的 JSON 对象,代表一张涂鸦及其元数据。

文档 examples/nodejs/ndjson.md 给出的单行示例结构如下:

{ "key_id":"5891796615823360", "word":"nose", "countrycode":"AE", "timestamp":"2017-03-01 20:41:36.70725 UTC", "recognized":true, "drawing":[[[129,128,129,129,130,130,131,132,132,133,133,133,133,...]]] }

配合 README.md 中的字段表,各字段含义如下:

字段类型说明
key_id64 位无符号整数全数据集中唯一的涂鸦标识符
wordstring游戏提示玩家绘制的类别词
recognizedboolean该涂鸦是否被游戏判定为"识别成功"
timestampdatetime涂鸦创建时间
countrycodestring玩家所在国家/地区的两位字母代码(ISO 3166-1 alpha-2)
drawingstring以 JSON 数组表示的矢量笔画数据

drawing数组的结构为"笔画"嵌套结构:

[ [ // 第一笔 stroke [x0, x1, x2, x3, ...], [y0, y1, y2, y3, ...], [t0, t1, t2, t3, ...] ], [ // 第二笔 stroke [x0, x1, x2, x3, ...], [y0, y1, y2, y3, ...], [t0, t1, t2, t3, ...] ], ... // 更多笔画 ]

其中x、y是像素坐标,t是自第一个点以来的毫秒时间戳;x、y为实数,t为整数。由于玩家使用的设备与输入方式不同,原始涂鸦的包围盒与点数差异很大。

1.2 原始数据 vs 简化数据(simplified)

文档明确指出,本指南聚焦于simplified drawings(简化涂鸦),理由是:文件体积约为原始数据的1/10,而涂鸦观感几乎一致。代价是简化版本丢失了原始数据中的时间信息——如果你后续想分析绘制时序,可以在熟悉数据结构后自行探索 raw 数据(其格式与简化版几乎完全一致,只是多了时间数组,且笔画数据点更多)。

README.md 的 "Preprocessed dataset" 一节给出了简化流程的完整定义,共四步:

  1. 将涂鸦对齐到左上角,使坐标最小值归 0;
  2. 均匀缩放,使坐标最大值达到 255(即归一化到 256×256 区域);
  3. 所有笔画按 1 像素间距重新采样;
  4. 使用 Ramer–Douglas–Peucker(RDP)算法、epsilon 取 2.0 进行笔画简化。

这也解释了为什么简化版每行示例中的坐标都是 0~255 范围内的整数——它们已经被统一到了 256×256 的画布上。

1.3 下载对应的类别文件

由于每个文件对应一个单词/类别的全部涂鸦,你可以按需只下载关心的那一个。以本文示例的face类别为例,其简化数据文件为face.ndjson,位于 Google Cloud Storage 的quickdraw_dataset/full/simplified目录。若想批量下载所有类别的简化文件,README 提供了gsutil方案:

gsutil -m cp 'gs://quickdraw_dataset/full/simplified/*.ndjson' .

说明:下载数据需要访问 Google Cloud Storage 公有数据集,具体访问方式可参考 Cloud Storage 的公开数据访问文档(见 README.md)。

2. 环境准备:安装 ndjson-cli 与 d3

2.1 ndjson-cli 是什么

文档推荐使用 ndjson-cli 这一命令行工具集来快速构造数据子集。它由 D3 作者 Mike Bostock 开发,提供ndjson-filter、ndjson-sort、ndjson-reduce等命令,核心设计是与标准 Unix 管道(pipe)无缝衔接——输入输出都是"一行一个 JSON"的文本流,因此可以自然地与cat、head、tail、wc等命令串联。

2.2 安装命令

仓库 examples/nodejs/package.json 的devDependencies中列出了相关依赖:

"devDependencies": { "d3": "^4.9.1", "ndjson-cli": "^0.3.0" }

对应安装命令如下:

npm install ndjson-cli npm install d3

注意文档中的关键提醒:排序功能需要引入 d3,因此npm install d3必须在你要执行这些命令的目录下进行。d3 在这里提供d3.ascending、d3.descending等比较函数,供ndjson-sort在 Node.js 运行时环境中调用(-r d3参数的含义见 4.2 节)。

3. 快速窥探数据:cat / head / tail

ndjson 格式的最大优势之一,就是它可以直接用最朴素的 Unix 命令进行快速预览,无需写任何解析代码。文档给出的两个示例:

# 查看前 5 行 cat face.ndjson | head -n 5 # 查看最后 5 行 cat face.ndjson | tail -n 5

head -n 5与tail -n 5分别输出文件的前 5 行与后 5 行。由于每一行就是一个完整的 JSON 对象,你可以立即看到face类别中涂鸦的key_id、countrycode、recognized等元数据以及drawing笔画数组。这就是"先看两眼、再决定下一步"的典型用法。

4. 筛选子集:ndjson-filter

4.1 基础筛选语法

ndjson-filter的作用与 Unix 的grep类似,但它的匹配对象是反序列化后的 JSON 对象而非纯文本行。它接收一个 JavaScript 表达式,表达式中d代表当前行的数据对象,表达式返回true的行会被保留。

文档给出了四个经典用例:

# 只保留被游戏识别成功的涂鸦 cat face.ndjson | ndjson-filter 'd.recognized == true' | head -n 5 # 统计识别成功的涂鸦数量 cat face.ndjson | ndjson-filter 'd.recognized == true' | wc -l # 统计未被识别的涂鸦数量 cat face.ndjson | ndjson-filter 'd.recognized == false' | wc -l # 组合条件:识别成功 且 玩家位于加拿大(countrycode 为 "CA") cat face.ndjson | ndjson-filter 'd.recognized == true && d.countrycode == "CA"' | wc -l

几个值得注意的细节:

  • d.recognized是布尔字段,直接与true/false比较即可,无需加引号;
  • d.countrycode是字符串字段,比较时必须加双引号:d.countrycode == "CA";
  • 多个条件用&&连接,实现"识别成功且来自加拿大"这类组合筛选;
  • wc -l统计行数,即筛选后剩余的涂鸦条数——这是快速评估数据子集规模的最直接手段。

由于recognized决定了一张涂鸦是否被游戏判定为"猜对",这类筛选对后续训练分类模型、过滤低质量数据非常实用。

4.2 关于字段路径的说明

从源码结构看,ndjson-filter的表达式在 Node.js 的 JavaScript 上下文中求值,因此你可以访问对象的所有字段,包括嵌套路径。例如d.word、d.key_id、d.timestamp都是合法的访问方式。文档聚焦于recognized与countrycode两个字段,其他字段(如按word过滤、按timestamp过滤)可举一反三。

5. 排序:ndjson-sort 与 d3

5.1 为什么需要 d3

ndjson-sort用于对整份数据按某个键排序。与filter不同,sort是全局操作,需要把整个数据流读入内存再排序输出,因此更适合在head -n N限定规模之后再使用(见 6.3 节)。

要让排序更方便,文档建议引入 d3,其比较函数可以直接作为排序依据。文档中的两个示例:

# 按涂鸦创建时间升序排列 cat face.ndjson | ndjson-sort -r d3 'd3.ascending(a.timestamp, b.timestamp)' | head -n 5 # 按笔画复杂度从高到低排列(用笔画数衡量) cat face.ndjson | ndjson-sort -r d3 'd3.descending(a.drawing.length, b.drawing.length)' | head -n 5

5.2 参数与表达式解读

  • -r d3:将d3模块注册为排序表达式可用的运行环境,这样表达式中才能调用d3.ascending/d3.descending;
  • 表达式中a、b分别代表被比较的两行数据对象;
  • d3.ascending(a.timestamp, b.timestamp):按timestamp字段升序——结合 1.1 节可知,timestamp是创建时间,因此第一行即最早的涂鸦;
  • d3.descending(a.drawing.length, b.drawing.length):按drawing数组长度(即笔画数)降序——第一行就是笔画最多、最复杂的涂鸦。

组合思路:如果你想知道"face 类别里谁画得最复杂",第二条命令的head -n 5直接给出答案;配合head -n 1则可拿到单个最复杂的样本。

6. 导出为 JSON:ndjson-reduce

6.1 为什么需要 reduce

ndjson-filter与ndjson-sort的输出仍然是"一行一个 JSON"的 ndjson 流,而有时我们想要的是标准的 JSON 数组文件(例如直接JSON.parse加载、或作为 API 响应体返回)。ndjson-reduce正是为此设计:它把输入的 ndjson 流累积(reduce)成一个 JSON 数组,输出到文件。

6.2 基础导出示例

文档中的第一个导出用例:

# 导出所有"识别成功且来自加拿大"的涂鸦为 canadian-faces.json cat face.ndjson | ndjson-filter 'd.recognized == true && d.countrycode == "CA"' | ndjson-reduce > canadian-faces.json

执行后,canadian-faces.json是一个标准的 JSON 数组,每个元素是一个完整的涂鸦对象,可以直接用JSON.parse()读取。

6.3 用 head 限制导出规模

海量数据下,全量导出往往不必要。文档展示了如何用head -n在导出前截断数据流:

# 只导出前 1000 张加拿大涂鸦 cat face.ndjson | ndjson-filter 'd.recognized == true && d.countrycode == "CA"' | head -n 1000 | ndjson-reduce > canadian-faces.json

这是"filter → head → reduce"三段式流水线:先过滤出目标子集,再用head取前 N 条,最后累积成数组落盘。head位于管道中游,意味着内存中只保留最终导出的 N 条对象,非常适合百万级文件。

6.4 组合排序与截断的完整流水线

文档给出的终极示例,把筛选、排序、截断、导出四个环节串成一条管道:

cat face.ndjson | ndjson-filter 'd.recognized == true && d.countrycode == "CA"' | ndjson-sort -r d3 'd3.descending(a.drawing.length, b.drawing.length)' | head -n 100 | ndjson-reduce > complex-faces.json

这条命令的语义是:从加拿大玩家、且被识别成功的涂鸦中,挑出笔画数最多的前 100 张,导出为complex-faces.json。它完整演示了本指南的全部能力——ndjson-filter负责条件筛选,ndjson-sort负责按复杂度排序,head -n 100截取 Top-100,ndjson-reduce将流累积为标准 JSON 数组。此类"复杂样本集"非常适合作为数据清洗、难度分层或可视化展示的输入。

7. 源码佐证:Node.js 中如何逐行解析 ndjson

命令行工具适合快速探索,而在 Node.js 程序中解析这些文件,则是 examples/nodejs/simplified-parser.js 演示的内容。它使用ndjsonnpm 包(该依赖已声明于 examples/nodejs/package.json),以流式方式逐行解析:

var fs = require('fs'); var ndjson = require('ndjson'); // npm install ndjson function parseSimplifiedDrawings(fileName, callback) { var drawings = []; var fileStream = fs.createReadStream(fileName) fileStream .pipe(ndjson.parse()) .on('data', function(obj) { drawings.push(obj) }) .on("error", callback) .on("end", function() { callback(null, drawings) }); }

这段代码揭示了 ndjson 格式的底层读取模型:fs.createReadStream创建文件读取流,.pipe(ndjson.parse())将文本流按行切分并反序列化为对象,data事件逐个收到涂鸦对象。这与ndjson-filter在命令行中的工作方式一致——每一行独立解析、独立处理,天然支持流式管道。示例脚本假设你把face-simple.ndjson放入同目录的data文件夹后运行,最终会逐条打印key_id与countrycode并输出总数。

作为对照,examples/nodejs/binary-parser.js 演示了同一数据集的.bin二进制格式解析,其recognized字段是 1 个 bit、timestamp是 uint32 秒级时间戳等细节,可以帮助你理解 ndjson 与二进制格式在信息密度上的差异。

8. 实战要点小结

将本文涉及的命令按用途归类,形成可直接复用的操作速查:

目标命令模式
预览数据cat face.ndjson \| head -n 5/tail -n 5
按条件筛选cat face.ndjson \| ndjson-filter 'd.recognized == true'
统计子集规模... \| wc -l
按字段排序... \| ndjson-sort -r d3 'd3.ascending(a.timestamp, b.timestamp)'
截断取前 N 条... \| head -n 1000
导出标准 JSON 数组... \| ndjson-reduce > out.json

几条关键经验:

  1. 管道即探索流程:筛选、排序、截断、导出都可以自由组合,顺序通常为 filter → sort → head → reduce,每一步都即时可见;
  2. 字符串字段要加引号:countrycode这类字符串的比较必须写成d.countrycode == "CA",而布尔字段直接比较即可;
  3. 先 head 再 sort:ndjson-sort需要全量读入内存,对超大文件建议先用 filter/head 缩小规模再排序;
  4. 简化版足够日常使用:简化数据体积小约 10 倍、坐标统一在 256×256 范围、不含时间信息,是快速实验的默认选择;需要时间序列分析时再转向 raw 数据。

9. 延伸阅读

  • 数据格式总览与全部预处理说明(含简化流程、二进制格式、numpy 位图):README.md
  • 全部 345 个类别清单:categories.txt
  • Node.js 流式解析 ndjson 完整示例:examples/nodejs/simplified-parser.js
  • Node.js 解析二进制格式对照示例:examples/nodejs/binary-parser.js
  • 依赖声明(ndjson、ndjson-cli、d3 等):examples/nodejs/package.json
  • Python 侧解析二进制文件的对照实现:examples/binary_file_parser.py

如果你打算基于这些涂鸦训练分类模型,README 还提供了 Sketch-RNN 相关的.npz预处理数据以及 TensorFlow 教程的指引,可与本文的 ndjson 子集导出流程衔接使用。

  • 数据集

【免费下载链接】quickdraw-dataset

Documentation on how to access and use the Quick, Draw! Dataset.

项目地址:https://gitcode.com/gh_mirrors/qu/quickdraw-dataset
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询