- 数据集
【免费下载链接】quickdraw-dataset
Documentation on how to access and use the Quick, Draw! 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_id | 64 位无符号整数 | 全数据集中唯一的涂鸦标识符 |
| word | string | 游戏提示玩家绘制的类别词 |
| recognized | boolean | 该涂鸦是否被游戏判定为"识别成功" |
| timestamp | datetime | 涂鸦创建时间 |
| countrycode | string | 玩家所在国家/地区的两位字母代码(ISO 3166-1 alpha-2) |
| drawing | string | 以 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" 一节给出了简化流程的完整定义,共四步:
- 将涂鸦对齐到左上角,使坐标最小值归 0;
- 均匀缩放,使坐标最大值达到 255(即归一化到 256×256 区域);
- 所有笔画按 1 像素间距重新采样;
- 使用 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 5head -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 55.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 |
几条关键经验:
- 管道即探索流程:筛选、排序、截断、导出都可以自由组合,顺序通常为 filter → sort → head → reduce,每一步都即时可见;
- 字符串字段要加引号:
countrycode这类字符串的比较必须写成d.countrycode == "CA",而布尔字段直接比较即可; - 先 head 再 sort:
ndjson-sort需要全量读入内存,对超大文件建议先用 filter/head 缩小规模再排序; - 简化版足够日常使用:简化数据体积小约 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.
相关推荐
xyOps 备份格式(XYBK v1.0)完全指南:基于 NDJSON 的批量数据导出与导入机制
xyOps 备份格式(XYBK v1.0)完全指南:基于 NDJSON 的批量数据导出与导入机制 XYBK(xyOps Backup Format)是 xyOp
Flowbite Datatables 插件实战指南:基于 Tailwind CSS 构建可搜索、排序、筛选、分页与导出的数据表格
Flowbite Datatables 插件实战指南:基于 Tailwind CSS 构建可搜索、排序、筛选、分页与导出的数据表格 本指南以 Flowbite
UI组件前端Prisma 数据导出完全指南:CLI 命令与原生 Export API 实战(基于 NDF 格式)
Prisma 数据导出完全指南:CLI 命令与原生 Export API 实战(基于 NDF 格式) 本指南系统讲解 Prisma 服务数据导出的两种官方途径—
后端数据库GraphQL
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考