ggsql:面向 SQL 的图形语法

Lobsters Hottest 工具

摘要

ggsql 是一款 Alpha 版本工具,它将图形语法的可视化能力引入 SQL,允许用户在 Quarto、Jupyter、Positron 和 VS Code 中利用 SQL 语法构建结构化、模块化的可视化图表。

<p><a href="https://lobste.rs/s/tf7ngv/ggsql_grammar_graphics_for_sql">评论</a></p>
查看原文 导出为 Word 导出为 PDF
查看缓存全文

缓存时间: 2026/04/21 02:58

# ggsql:SQL 的图形语法 Source: https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/ 今天,我们非常兴奋地宣布 `ggsql`(https://ggsql.org/)的 Alpha 版本正式发布。顾名思义,ggsql 是一种基于 SQL 语法的图形语法实现,为 SQL 带来了丰富且结构化的可视化支持。它已准备好在 Quarto、Jupyter notebook、Positron 和 VS Code 等环境中开箱即用。 在本文中,我们将探讨开发该工具的初衷,并提供大量使用示例;希望你能像我们一样对它充满期待。 ## 认识 ggsql (https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/#meet-ggsql) 在讨论“为什么”之前,让我们通过一些示例来看看 ggsql 到底是什么。 ### 第一个图表 (https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/#the-first-plot) 为了让大家快速入门,我们从可视化的“Hello World”开始:一个散点图,使用内置的 penguins 数据集: ```sql VISUALIZE bill_len AS x, bill_dep AS y FROM ggsql:penguins DRAW point ``` 效果还不错。虽然代码带有 SQL 的冗长感,但这也意味着你可以把绘图代码读出来并轻松理解其逻辑。我们来逐行拆解一下这里的操作: 1. 使用 `VISUALIZE` 启动可视化查询,并提供从内置 penguins 数据集到映射关系的设定:将 `x` 映射到 `bill_len` 列的数据,将 `y` 映射到 `bill_dep` 列。 2. 绘制一个点图层,默认情况下它会使用我们在顶层定义的映射关系。 在此基础上,我们可以继续扩展可视化内容: ```sql VISUALIZE bill_len AS x, bill_dep AS y, species AS color FROM ggsql:penguins DRAW point ``` 我们可以看到,只需在映射中添加一个属性,图表就自动变成了按颜色区分的类别图。这种代码的渐进式演进正是图形语法最大的优势之一。这里没有预定义的图表类型,只有可组合、可添加、可移除的模块化组件。为了进一步说明这一点,让我们给图表添加一条平滑回归线: ```sql VISUALIZE bill_len AS x, bill_dep AS y, species AS color FROM ggsql:penguins DRAW point DRAW smooth ``` 我们在点图层之上添加了新图层。该图层同样沿用了点图层的映射关系。由于按物种分组着色,平滑线也按物种分成了几条。 我们可以不断这样操作:添加更多映射、增加或替换图层、控制缩放方式等,直到得到我们需要的图表,无论简单还是复杂。在上述例子中,我们最后可能会决定更关注数据收集自三个岛屿的物种分布情况: ```sql VISUALIZE island AS x, species AS color FROM ggsql:penguins DRAW bar ``` 虽然这是一张完全不同的图表,但你可以看出大部分之前的代码都被复用过来了。 ### 完整示例 (https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/#a-complete-example) 掌握前几个图表后,我们来看一个完整的示例。它将包含一些此前未出现的元素,别担心,我们会在下面详细讲解,包括已经看过的部分。该示例改编自 Jack Davison(https://jack-davison.github.io/)为 TidyTuesday 创作的可视化作品。 ```sql WITH astronauts AS ( SELECT * FROM 'astronauts.parquet' QUALIFY ROW_NUMBER() OVER ( PARTITION BY name ORDER BY mission_number DESC ) = 1 ) SELECT *, year_of_selection - year_of_birth AS age, 'Age at selection' AS category FROM astronauts UNION ALL SELECT *, year_of_mission - year_of_birth AS age, 'Age at mission' AS category FROM astronauts VISUALIZE age AS x, category AS fill DRAW histogram SETTING binwidth => 1, position => 'identity' PLACE rule SETTING x => (34, 44), linetype => 'dotted' PLACE text SETTING x => (34, 44, 60), y => (66, 49, 20), label => ( 'Mean age at selection = 34', 'Mean age at mission = 44', 'John Glenn was 77\non his last mission -\nthe oldest person to\ntravel in space!' ), hjust => 'left', vjust => 'top', offset => (10, 0) SCALE fill TO accent LABEL title => 'How old are astronauts on their most recent mission?', subtitle => 'Age of astronauts when they were selected and when they were sent on their mission', x => 'Age of astronaut (years)', fill => null ``` 代码确实不少,但反过来说,通过这个单一示例,我们已经覆盖了语法中最重要的多数方面。 在最高层级上,这个查询分为两部分:SQL 查询和可视化查询。从开头到 `VISUALIZE` 子句之间的部分都是 SQL 查询。它是标准的 SQL 语法,接受后端支持的任何内容(本文中使用的是 DuckDB 后端)。查询结果不会像常规那样以表格形式返回,而是直接导入到可视化引擎中。 由于本文目的不是教授 SQL,我们不会过多讨论这部分。主要的要点是:`VISUALIZE` 子句之前的所有代码都是纯 SQL,生成的表会自动被可视化模块使用,其中创建的任何表或 CTE 都可以在后续的可视化查询中引用。 正如我们在第一个示例中所见,SQL 查询部分是可选的。如果你的数据已经处于适合绘图的状态,可以跳过它,直接在 `VISUALIZE` 子句中指定数据源: ```sql VISUALIZE year_of_selection AS x, year_of_mission AS y FROM 'astronauts.parquet' ``` 现在,让我们看看可视化查询——从 `VISUALIZE` 开始的所有内容。`VISUALIZE` 标志着 SQL 查询的结束和可视化查询的开始(如果你偏好英式拼写,也可以是 `VISUALISE`)。它可以独立存在,也可以像这里一样包含一个或多个映射关系,这些将成为后续每个图层的默认映射。映射仅用于将数据关联到抽象的视觉属性。映射类似于 SQL 中的 `SELECT`,你将列别名化为视觉属性(在图形语法中称为 *aesthetics*)。在上方的可视化中,我们规定 `age` 列的值用于 `x`(x 轴位置),`category` 列的值用于 `fill`(对象的填充色)。此时我们还没有指定如何绘制它们。 在 `VISUALIZE` 查询之后是 `DRAW` 子句。`DRAW` 用于向可视化中添加图层。ggsql 提供了丰富的图层类型。有些很直观:例如用 `point` 绘制散点图。有些则更复杂:`histogram`(此处使用)需要计算派生统计量,如分箱计数。可视化可以包含任意数量的图层,图层会按照定义的顺序渲染。`DRAW` 有一个兄弟子句叫 `PLACE`。它用于添加注释,工作方式类似 `DRAW`,但它不从表中获取数据,而是使用提供的字面量值。因此,我们的可视化示例实际上包含三个图层:展示表中数据的直方图图层、展示每类别预计算均值的结果线标注图层,以及为可视化添加上下文的文本标注图层。值得一提的是,一个图层并不对应单一的图形实体。就像上面的文本图层一样,每个图层都可以渲染多个同类型的独立实体,因此不需要为三个不同类别创建 3 个线条图层来绘制折线图。 在 `DRAW` 和 `PLACE` 子句之后是 `SCALE` 子句。该子句控制如何将数据值转换为对视觉属性有意义的值。在我们的例子中,`category` 列存储了字符串 “Age at mission” 和 “Age at selection”,它们本身无法直接映射为颜色值。`SCALE fill TO accent` 子句指示 ggsql 在使用与填充色映射的值时,采用 “accent” 调色板将其转换为实际颜色。Scale 的功能远不止于此,还可以用于连续数据的变换、定义断点以及设置特定的比例类型(如有序型或分箱型)。 可视化查询的最后一个子句是 `LABEL`,它允许我们添加或修改各种文本标签,如标题、副标题、坐标轴和图例名称。 ### 退一步看 (https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/#stepping-back) 内容确实有点多。不过有两个非常光明的前景: 1. 你现在已经了解了语法中最关键的方面(当然还有更多内容,你可以逐步掌握) 2. 许多可视化查询会比上面这个简单得多 上面我们已经看过较短的可视化查询示例,但让我们继续看一个按性别划分的宇航员出生年份箱线图: ```sql VISUALIZE sex AS x, year_of_birth AS y FROM 'astronauts.parquet' DRAW boxplot ``` 这段代码比上一个短了很多,但如果你来自其他绘图系统,甚至可能觉得它过于冗长(比如对比 `boxplot(astronauts.sex, astronauts.year_of_birth)` 这样的写法)。是的,它确实更长,但结构更清晰、更可组合、且具有自描述性。这些特性(直接源于图形语法的设计哲学)意味着,无论是你本人,还是你未来的 LLM 编程搭档,都能更轻松地表征和理解所有类型图表的运行逻辑。`ggplot2`(具有相同特性)在 R 生态系统中统治了 18 年,就是这一点的最佳证明。 举个例子,我们把上面的图表改为展示相同关系的抖动散点图: ```sql VISUALIZE sex AS x, year_of_birth AS y FROM 'astronauts.parquet' DRAW point SETTING position => 'jitter' ``` 又或者让抖动程度遵循数据分布,从而兼作小提琴图: ```sql VISUALIZE sex AS x, year_of_birth AS y FROM 'astronauts.parquet' DRAW point SETTING position => 'jitter', distribution => 'density' ``` 正如你所见,这种语法和可组合的特性使得可视化迭代非常顺手,这在探索性分析和可视化设计中都具有极高的价值。 ## 为什么是 ggsql? (https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/#why-ggsql) 从零开始编写一个新的可视化库是一项庞大的工作,你可能会好奇我们为什么要再次做这件事。原因如下: - 我们希望服务于主要使用 SQL 的数据分析师和科学家 - SQL 与图形语法搭配得天衣无缝 - 我们想打造一个极其强大、基于代码且不需要依赖完整编程语言(如 R 或 Python)的可视化工具 - LLM 非常擅长处理 SQL,同时也为数据可视化创作提供了全新的交互界面 - 我们从 `ggplot2`(https://ggplot2.tidyverse.org/)过去 18 年的发展中积累了大量经验,现迫不及待想在一片空白画布上应用它们 让我们逐一探讨。 ### Hello, SQL 用户 (https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/#hello-sql-user) SELECT 'Hello' AS greetings FROM SQL_users WHERE other_language NOT IN ('R', 'Python') 虽然 R 和随后 Python 席卷了数据科学革命的头条,SQL 却一直在底层作为可靠且强大的主力默默运行。有许多人处理数据时仅使用或主要使用 SQL。在我们看来,他们可视化数据的选择往往不够理想: - 导出数据然后使用 R 或 Python(可能超出他们的技术舒适区) - 使用 GUI 驱动的 BI 工具(可复现性支持较差) - 依赖少数几种能直接在查询中生成可视化的工具(我们认为它们功能不够强大或体验不够友好) 设计 ggsql 的目标是,让 SQL 用户能立刻看懂语法,契合他们对可组合、声明式子句的预期。 除了提供更好数据可视化方式外,ggsql 也是邀请 SQL 用户进入我们基于 Quarto(https://quarto.org/)构建的丰富代码报告生成与分享生态的一种方式。 ### 声明式数据处理,声明式可视化 (https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/#declarative-wrangling-declarative-visualization) 如果你不了解 SQL,这里做个简要回顾:SQL 是一种用于操作存储在一个或多个表中的关系型数据的领域特定语言。其语法基于关系代数的概念,这是一种结构化思考数据操作的方式。语义学定义了一组声明式而非函数式的模块化操作,允许用户使用一套明确定义的操作来组合出极其强大且高度定制的处理逻辑。 如果你对图形语法也不熟悉,这里做个简要回顾:图形语法是将数据可视化概念理论化拆解为模块化组件的框架。尽管纯属理论,但像 ggplot2 这样的工具已将其付诸实践。其语义同样定义了一组声明式而非函数式的模块化操作,让用户能通过明确定义的操作集组合出强大且自定义的可视化效果。 [表情包示意] SQL 与图形语法因同意采用声明式、可组合的操作而热情击掌 从上述略带夸张的概述中可以清楚看出,SQL 和图形语法在各自领域的处理思路上有诸多共通之处。结合起来,它们能为从原始数据到最终可视化的完整流程提供极其强大且自然的解决方案。 ### 无需运行时,问题不大 (https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/#no-runtime-no-problem) 为什么 `ggplot2`(https://ggplot2.tidyverse.org/)和 `plotnine`(https://plotnine.org/)分别依赖 R 和 Python 安装是个问题?用一个轻量级、专注的可执行文件来处理数据可视化有明显的优势: - 将小型可执行文件嵌入其他工具,比打包 R/Python(或要求用户安装它们)要容易得多 - 作用域更小,更容易进行沙盒隔离,防止恶意代码执行(无论是有意还是无意) 以上两点使得 ggsql 成为集成到诸如辅助数据分析的 AI 智能体,或在不同环境中执行代码的代码报告工具时的更有吸引力的选择。 脱离解释型语言或许让你觉得我们需要咽下一些苦果,但这确实给了我们巨大的收益。最重要的是,严谨的结构意味着我们可以在后端针对每个图层将整个数据流水线作为一个单独的 SQL 查询来执行。这意味着,如果你想绘制十亿条交易的柱状图,你只需要从数据仓库中获取每个柱子的计数值,而不是拉取整十亿行数据。箱线图、密度图等更复杂的图层也是如此。这与大多数可视化工具必须先物化完整数据、再进行必要计算、最后绘制的做法形成鲜明对比。 ### `SELECT pod_door FROM bay WHERE closed` (https://opensource.posit.co/blog/2026-04-20_ggsql_alpha_release/#select-pod_door-from-bay-where-closed) LLM 已被证明能非常高效地将自然语言转化为 SQL,我们对它们以同等效率处理 ggsql 也充满信心。在 `querychat`(https://posit-dev.github.io/querychat/)中我们已经看到了这一迹象,现在你可以借助 ggsql 通过自然语言以可视化方式探索数据。此外,由于 ggsql 是一个更安全、更轻量的运行环境 t

相似文章

SQLite查询结果格式化演示

Simon Willison's Blog

基于WebAssembly的交互式演示,允许用户以20多种样式格式化SQL查询结果,包括表格、CSV、JSON、HTML和Markdown,并支持实时调整。

SGOCR:一个空间定位的、以OCR为核心的流水线与V1数据集 [P]

Reddit r/MachineLearning

大家好!我一直在独立研究和开发小巧但强大的视觉语言模型(VLM),并注意到视觉数据集中的一个空白——没有一个数据集在教我的模型简单地将文本定位到图像中,而是试图让模型推理文本或场景本身。这促使我投入两周的副项目,创建了SGOCR,一个开源数据集流水线,用于生成空间定位的、以OCR为核心的VQA元组,包含大量丰富的元数据以支持多样化的VLM训练策