ITADN
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈

XY-shaped probability field shown as a binned scatter chart.

CI CodSpeed Python 3.11+ Docs Launch the examples on Binder

XY 是一个极其快速、可交互、可定制的 Python 图表库,适用于 Web、笔记本和静态导出。

图表可以通过声明式方式或遵循 matplotlib 约定进行组合。您可以 使用 Python、CSS 或 Tailwind 对其进行完全定制。

对于小型图表,每个点都会发送到浏览器。对于大型图表,Rust 核心 仅根据屏幕分辨率计算需要显示的内容。平移、缩放、悬停和选择 可以通过对新范围运行相同的过程来显示完整细节,并且选择会返回原始行。

使用 XY,我们渲染了整个 OpenStreetMap —— 一个包含 10,000,000,000 个点 的数据集。查看示例 →

[!IMPORTANT] XY 目前处于 alpha 阶段,并正在频繁进行增强。 ⭐️ 给仓库加星以关注其进展。

XY 适合我吗?

XY 适用于希望拥有一个灵活图表库的 Python 用户,该库可用于从日常 绘图到自定义应用程序视觉效果和大型数据集的一切。构建一次图表, 然后将其用于笔记本和 Web 应用,或将其导出为 HTML、PNG、 SVG 或 PDF。

安装

pip install xy

# or, with 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  # notebooks render it

相同的 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,
        # Grow and solidify markers once a view drills through to real rows.
        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="100 million points",
)
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="signal")
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"},
)

参见样式指南 以获取示例。有关可定制内容的详细分解,请参见 功能矩阵

基准测试

实时交互式图表,10k 至 100M 个点。每个库都包含每一行数据, 并在真实浏览器中通过其自身的输入路径驱动。计时仅在画布既正确(已验证植入的哨兵点已点亮) 又稳定(10 个字节相同的帧)时停止,因此渐进式渲染器会被计费直到 其最后一个块落地。

Time until every point is on screen, 10k to 100M points, for XY, Matplotlib, and Plotly. Lower is better.

XY 在 10k 时为 0.071 秒,在 100M 时为 0.081 秒,在四个数量级范围内保持平稳, 因为当行数超过 200k 时,它绘制的是屏幕受限的密度表面 而不是每行一个标记,并且缩放可以钻取回精确的行。所有 精确标记路径则随 N 扩展:Matplotlib 在约 3M 时超过一秒, 在 50M 时达到 13.4 秒;Plotly 在约 2.5M 时超过一秒,在 25M 时达到 9.8 秒。

浅色线条是带有 density=False 的 XY:同一引擎绘制每行一个标记, 没有聚合信用。它在 5.26 GiB 内存下以 1.34 秒渲染 100M 个精确标记。

所有点显示在屏幕上所需的时间,以秒为单位。 是库未能渲染的尺寸:Plotly 在 50M 时始终无法完成图形构建,而 Matplotlib 在 100M 时能够绘制,但始终无法解析随后的缩放。

点数10k100k500k1M2.5M5M10M25M50M100M
XY 加速比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

xy 捆绑的 Reflex 集成可将任何 XY 图表转换为标准的 Reflex 组件,无需 JavaScript、iframe 或独立的图表服务。 安装 reflex 附加组件以选择兼容的框架版本:

pip install "xy[reflex]"

# or, with uv
uv add "xy[reflex]"

导入命名空间仍为 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="Weekly signups",
)


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


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

对于状态驱动的图表,在页面中声明图表,并从 @reflex_xy.data 状态方法中提供列 —— 结构(通道、颜色映射、坐标轴) 在 reflex run 编译应用时进行验证,而列则通过 应用自身的 websocket 作为二进制缓冲区传输,绝不通过 Reflex 状态:

from typing import TypedDict

import numpy as np
import reflex as rx
import reflex_xy


class CloudData(TypedDict):
    x: np.ndarray
    y: np.ndarray
    mag: np.ndarray


class Dash(rx.State):
    points: int = 200_000

    @reflex_xy.data
    def cloud(self) -> CloudData:
        rng = np.random.default_rng(7)
        x = rng.normal(size=self.points)
        y = x * 0.6 + rng.normal(scale=0.6, size=self.points)
        return {"x": x, "y": y, "mag": np.hypot(x, y)}


def dashboard() -> rx.Component:
    return reflex_xy.scatter_chart(
        data=Dash.cloud,
        x="x", y="y", color="mag", colormap="viridis",
        height="460px",
    )

悬停、平移和缩放功能保持可用。对于由 Reflex 状态、事件或 实时流驱动的图表,请参阅 Reflex 集成指南可运行的示例应用

示例

每个笔记本从关联的公开数据源获取其行数据;本仓库中不存储任何原始数据集。计数描述的是特色图表,而笔记本可进一步扩展。请参阅 示例指南 以了解数据源、工作负载控制 和设置。

Gaia DR3 · HR diagram
250,000 plotted stars

Gaia DR3 stellar color versus absolute magnitude.

Open notebook
gnomAD v4.1 · allele frequency
164,000 plotted variants

gnomAD allele frequency across all autosomes.

Open notebook
Pan-UKBB · Manhattan plot
814,294 plotted variants

Pan-UKBB standing-height associations across all autosomes.

Open notebook
Dukascopy · EUR/USD ticks
101,427 plotted ticks

Dukascopy EUR/USD midpoint quotes.

Open notebook
LIGO · GW150914 strain
16,777,216 raw · 3,441 shown

GWOSC reconstructed Hanford waveform for GW150914.

Open notebook
NYC TLC · taxi pickup density
300,000 pickup records

Locally projected NYC yellow-taxi pickup hexbin density.

Open notebook

工作原理

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

flowchart TB
    API["Python API<br/>Build the chart"]
    STORE["ColumnStore<br/>Keep canonical f64 columns"]
    CORE["Native Rust compute<br/>Direct · decimated · density"]
    PAYLOAD["Compact payload<br/>Data-less JSON spec + typed binary buffers"]
    RENDER["Browser or notebook<br/>WebGL2 marks · Canvas axes · DOM interface"]

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

因此,密集概览可以聚合数据,而窄视图则返回精确的数据点。 对于实时主机,平移和缩放请求会获取更精细的负载。 规范的 f64 数据保留在 Python 中,因此悬停和选择操作仍返回原始行。

有关完整设计,请参阅 设计档案

路线图

首先实现广泛的 2D 覆盖,然后是地理、3D 和体积可视化。 已列入队列,未暗示具体日期:

  • 类别分布: 条带图、蜂群图、蜜蜂群图、箱线图、地毯图
  • 回归诊断: 趋势线、残差图、QQ 图、PP 图
  • 散点矩阵和联合图: SPLOM、配对网格、边缘直方图
  • 饼图 / 环形图: xy.pie_chart(labels, values, hole=...) 基于 不等宽核心极坐标条形图发布,并在 xy.pyplot 中提供 Matplotlib 风格的辅助函数; 嵌套环形图和可变半径组合仍待实现
  • K 线 / OHLC 和金融叠加: SMA、VWAP、布林带、RSI、MACD;已原型化,等待正式发布
  • 瀑布图和漏斗图
  • 矩形树图、旭日图和冰柱图
  • 仪表盘 / 指示器: 基于已发布的极坐标轴和可组合的径向标记构建
  • 斜率图、凸起图和哑铃图
  • 3D 和体积: 散点图、曲面、网格、等值面和体积视图

完整的排名待办事项列表位于 图表路线图。 想要未列出的图表或功能? 提交一个 issue