Show HN: XY – 一个快速、可组合、GPU加速的交互式绘图库

Hacker News Top 工具

摘要

XY 是一个快速、可组合、GPU加速的交互式绘图库,专为Web和笔记本设计,可处理多达100亿个点,支持平移和缩放。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/07/28 18:28

reflex-dev/xy

来源:https://github.com/reflex-dev/xy

XY 是一个极快、交互式、可定制的 Python 图表库,适用于网页、笔记本和静态导出。图表通过声明式或 matplotlib 惯例构建。你可以使用 Python、CSS 或 Tailwind 完全自定义。对于小图表,每个点都会发送到浏览器。对于大图表,Rust 内核仅根据屏幕分辨率计算屏幕需要显示的内容。平移、缩放、悬停和选择可以通过对新范围执行相同进程来显示完整细节,并且选择会返回原始行。使用 XY,我们渲染了整个 OpenStreetMap——一个 10,000,000,000 点 的数据集。查看示例 → (https://github.com/reflex-dev/xy/tree/main/examples/osm)

XY 处于 alpha 阶段,正在频繁增强。
⭐️ 给该仓库加星以跟踪进展。

XY 适合我吗?

XY 适用于希望在从日常绘图到自定义应用可视化以及大型数据集的各类场景中,使用一个灵活图表库的 Python 用户。一次构建图表,然后在笔记本和 Web 应用中使用,或导出为 HTML、PNG、SVG 或 PDF。

安装

pip install xy
# 或使用 uv
uv add xy

快速入门

一个图表是一个容器及其内部的标记。任何序列均可使用;NumPy 是可选的。

import xy
chart = xy.line_chart(xy.line([1, 2, 3, 4, 5], [120, 180, 165, 240, 310]))
# chart.to_html("chart.html")
# chart.to_png("chart.png")
# chart.to_svg("chart.svg")
chart  # 笔记本中会渲染

同样的 API 可扩展至一亿个点,以密度曲面形式呈现:

import numpy as np
import xy

rng = np.random.default_rng(7)
n = 100_000_000
r = 6.0 * rng.beta(1.2, 3.0, n)
theta = 2.9 * np.log1p(r) + rng.integers(0, 4, n) * (np.pi / 2) + rng.normal(0, 0.045 + 0.016 * r, n)

chart = xy.scatter_chart(
    xy.scatter(
        r * np.cos(theta),
        r * np.sin(theta),
        color=np.exp(-r / 2.2),
        colormap="magma_r",
        density=True,
        opacity=0.85,
        # 一旦视图深入至实际行,标记会增长并固化。
        size=2.5,
        zoom_size_factor=2.6,
        zoom_opacity=0.95,
    ),
    xy.theme(
        background="#ffffff",
        plot_background="#ffffff",
        grid_color="#e6e6e1",
        axis_color="#c3c2b7",
        text_color="#0b0b0b",
    ),
    title="1亿个点",
)
chart

从 matplotlib 迁移

对于常见的 pyplot 工作流,修改导入并保留绘图代码:

import numpy as np
import xy.pyplot as plt

x = np.linspace(0, 10, 200)
fig, ax = plt.subplots()
ax.plot(x, np.sin(x), "r--", label="信号")
ax.legend()
plt.show()

查看 兼容性指南;目前并非所有图表和功能都支持。

自定义每一层

使用 Python 控制图表,包括标记、坐标轴、交互和布局。

  • 标记: 控制颜色、大小、透明度、符号、渐变、描边、曲线和色图。
  • 指引: 自定义坐标轴、刻度、网格、注释、图例、色标和工具提示。
  • 交互: 添加平移、缩放、悬停、选择、十字准线、回调和联动图表。
  • 布局: 创建图层和分面,设置响应式尺寸,应用主题。
chart = xy.line_chart(
    xy.line(x, y, color="#7c3aed", width=3),
    class_name="rounded-xl bg-white",
    class_names={"tooltip": "rounded-lg bg-zinc-900 text-white"},
)

查看样式指南 (https://github.com/reflex-dev/xy/blob/main/docs/styling/index.md) 中的示例。如需详细了解可自定义的内容,请参见能力矩阵 (https://github.com/reflex-dev/xy/blob/main/spec/api/capability-matrix.md)。

基准测试

实时交互图表,10k 到 100M 个点。每个库都会获取所有行,并通过其自身的输入路径在真实浏览器中驱动。计时仅在画布既正确(放置的哨兵点已验证点亮)又稳定(10 帧字节相同)时停止,因此渐进式渲染器会一直计时直到最后一块到达。XY 在 10k 时耗时 0.071 秒,100M 时耗时 0.081 秒,在四个数量级上保持平坦,因为超过 200k 行时,它绘制的是屏幕边界内的密度曲面,而非每行一个标记,并且缩放时会钻取回精确行。每个精确标记路径则随 N 线性增长:Matplotlib 在大约 3M 时超过一秒,在 50M 时达到 13.4 秒;Plotly 在大约 2.5M 时超过一秒,在 25M 时达到 9.8 秒。浅色线是 XY 设置 density=False:相同的引擎每行绘制一个标记,不享受聚合优势。它在 5.26 GiB 内存下,用 1.34 秒渲染了 100M 个精确标记。

图中展示的是直到每个点都显示在屏幕上所花费的时间(秒)。✕ 表示该库未渲染该尺寸:Plotly 在 50M 时从未完成图形构建,Matplotlib 在 100M 时绘制了但从未解析后续的缩放。

点数10k100k500k1M2.5M5M10M25M50M100M
XY 加速比1×2×3×4×9×16×34×89×177×—
XY0.0710.0720.0750.0840.0830.0890.0830.0770.0760.081
XY (density=False)0.0850.0740.0870.0980.1110.1440.2060.4240.6451.343
Matplotlib (WebAgg)0.0860.1150.2240.3570.7581.4242.8046.83813.385✕
Plotly (scattergl)0.3410.3730.4770.6141.0331.7853.3679.794✕✕

Python 端峰值常驻内存(GiB)。浏览器内存单独跟踪,此处未计入,因为无头 Chrome 在绘制任何内容前已占用约 1 GiB。

点数10k100k500k1M2.5M5M10M25M50M100M
XY 优势1.8×1.7×1.9×2.1×2.1×2.4×2.6×2.9×2.8×—
XY0.050.050.060.070.130.190.320.701.362.58
XY (density=False)0.050.050.070.100.180.310.571.352.665.26
Matplotlib (WebAgg)0.090.090.120.150.280.460.842.063.85✕
Plotly (scattergl)0.210.180.280.360.601.051.864.70✕✕

同一台机器(Apple M5 Pro),每个单元格运行一次;在小规模下,计时存在大约 ±10 ms 的运行间波动。关于环境、方法、各尺寸视频和原始结果,请参阅 基准测试运行手册 和 竞争性基准测试规范。

在 Reflex 应用中嵌入 XY

reflex-xy 适配器可将任何 XY 图表转换为常规 Reflex 组件,无需 JavaScript、iframe 或单独的图表服务。它作为一个独立的包提供,并引入 xy 和 reflex:

pip install reflex-xy
# 或使用 uv
uv add reflex-xy

只需注册一次适配器:

# rxconfig.py
import reflex as rx
import reflex_xy

config = rx.Config(
    app_name="dashboard",
    plugins=[reflex_xy.XYPlugin()],
)

然后在组件树的任何位置添加图表:

import reflex as rx
import reflex_xy
import xy

signups = xy.line_chart(
    xy.line([1, 2, 3, 4, 5], [120, 180, 165, 240, 310]),
    title="每周注册量",
)

def index() -> rx.Component:
    return rx.card(
        rx.heading("增长"),
        reflex_xy.chart(signups, height="320px"),
        width="100%",
    )

app = rx.App()
app.add_page(index)

悬停、平移和缩放仍然有效。对于由 Reflex 状态、事件或实时流驱动的图表,请参阅 Reflex 集成指南 (https://reflex.dev/docs/xy/integrations/reflex/) 和 可运行示例应用。

示例

每个笔记本从链接的公共源获取其行;本仓库中不存储原始数据集。计数描述的是重点图表,而笔记本可进一步扩展。有关源、工作负载控制和设置,请参阅 示例指南。

Gaia DR3 · HR 图 250,000 颗绘制恒星 Gaia DR3 恒星颜色与绝对星等的关系。 打开笔记本gnomAD v4.1 · 等位基因频率 164,000 个绘制变异位点 gnomAD 所有常染色体等位基因频率。 打开笔记本Pan-UKBB · 曼哈顿图 814,294 个绘制变异位点 Pan-UKBB 所有常染色体身高关联。 打开笔记本
Dukascopy · 欧元/美元报价 101,427 个绘制报价 Dukascopy 欧元/美元中间价报价。 打开笔记本LIGO · GW150914 应变 16,777,216 原始 · 3,441 显示 GWOSC 重建的 GW150914 汉福德波形。 打开笔记本NYC TLC · 出租车乘客密度 300,000 条上客记录 本地投影的纽约黄色出租车乘客六边形密度。 打开笔记本

工作原理

大多数图表栈将每个值序列化为 JSON,并让浏览器绘制每一个标记。XY 将精确值保存在 ColumnStore 中,在 Rust 中计算细节级别,并传输类型化的二进制缓冲区。减采样和密度视图受可见结果限制。

flowchart TB
    API["Python API\n构建图表"]
    STORE["ColumnStore\n保留规范 f64 列"]
    CORE["原生 Rust 计算\n直接 · 减采样 · 密度"]
    PAYLOAD["紧凑载荷\n无数据 JSON 规范 + 类型化二进制缓冲区"]
    RENDER["浏览器或笔记本\nWebGL2 标记 · Canvas 坐标轴 · DOM 接口"]

    API --> STORE --> CORE --> PAYLOAD --> RENDER

因此,密集的概览可以聚合,而狭窄的视图可以返回精确的点。使用实时宿主时,平移和缩放会请求细化的载荷。规范 f64 数据保留在 Python 中,因此悬停和选择仍返回原始行。

完整设计请参见 设计文档。

路线图

首先覆盖广泛的 2D 图表,然后是地理、3D 和体素可视化。以下是即将推出的内容(不承诺具体时间):

  • 分类分布: 带状图、蜂群图、箱型图、地毯图
  • 回归诊断: 趋势线、残差图、Q-Q 图、P-P 图
  • 散点矩阵和联合图: SPLOM、配对网格、边际直方图
  • 饼图/甜甜圈图: 目前 xy.pyplot 中已有,计划提升为 xy.pie_chart(xy.pie(...))
  • K 线图/OHLC 及金融叠加: SMA、VWAP、布林带、RSI、MACD;已原型化,待全新落地
  • 瀑布图和漏斗图
  • 矩形树图、旭日图和冰柱图
  • 雷达图/极坐标图和仪表图: 需要先有极坐标轴
  • 斜率图、凹凸图和哑铃图
  • 3D 和体素: 散点图、曲面、网格、等值面和体素视图

完整的排序待办事项列表在 图表路线图 中。想要列表中未列出的图表或功能?请提交 Issue (https://github.com/reflex-dev/xy/issues/new)。

相似文章