Sakhanda Wire
NVDA MSFT GOOGL META AMZN
← Към новините

Проектиране на мащабируеми интерактивни визуализации с Reflex XY: композиция, визуализиране на милиони точки, поточно предаване, персонализирани маркери и експорт

В този урок разглеждаме усъвършенстваните възможности за визуализация на Python библиотеката XY, като създаваме интерактивни, мащабируеми и разширяеми диаграми. Започваме с композиционния модел на XY, в който комбинираме множество графични елементи, двойни оси, анотации, подсказки, легенди, теми и интерактивни контроли в рамките на една декларация за диаграма. След това работим с Pandas DataFrame, фасетирани оформления, свързани изгледи и набори от данни с милиони точки, които автоматично преминават към визуализация на базата на плътност за ефективно изследване. Свързваме и взаимодействията в браузъра обратно с Python чрез селекции и обратни извиквания, актуализираме диаграмите динамично чрез поточно предаване, персонализираме визуалните компоненти с DOM слотове и CSS и разширяваме библиотеката с повторно използваем персонализиран елемент за трендова линия. Освен това използваме интерфейса, съвместим с Matplotlib, и експортираме визуализациите си като самостоятелни HTML, SVG и PNG файлове.

Копиране на кодаКопираноИзползвайте друг браузър
import subprocess, sys, os
subprocess.run([sys.executable, "-m", "pip", "install", "-q", "xy"], check=True)
WIDGETS_OK = True
try:
   from google.colab import output as _colab_output
   _colab_output.enable_custom_widget_manager()
except Exception:
   WIDGETS_OK = False
import numpy as np
import pandas as pd
import xy
from IPython.display import display, HTML
print("xy", xy.__version__, "| live widgets:", WIDGETS_OK)
def render(chart, note=""):
   if note:
       display(HTML(f"<h3 style='font:600 15px system-ui;margin:18px 0 6px'>{note}</h3>"))
   try:
       display(chart)
   except Exception:
       display(HTML(chart.to_html()))
   return chart
rng = np.random.default_rng(7)
days    = np.arange(180)
trend   = 200 + 0.9 * days + 18 * np.sin(days / 9.0)
revenue = trend + rng.normal(0, 12, days.size)
sigma   = 10 + 6 * np.abs(np.sin(days / 15.0))
conv    = 0.06 + 0.02 * np.sin(days / 21.0) + rng.normal(0, 0.003, days.size)
peak    = int(np.argmax(revenue))
layered = xy.chart(
   xy.error_band(days, revenue - 1.96 * sigma, revenue + 1.96 * sigma,
                 name="95% band", color="#7c3aed", opacity=0.16),
   xy.line(days, revenue, name="Revenue", color="#7c3aed", width=2.5,
           curve="smooth"),
   xy.scatter(days[::12], revenue[::12], name="Weekly check", color="#7c3aed",
              size=7, stroke="#ffffff", stroke_width=1.5),
   xy.line(days, conv, name="Conversion", color="#f59e0b", width=2,
           dash="dashed", y_axis="y2"),
   xy.x_axis(label="Day", grid=True),
   xy.y_axis(label="Revenue (k)", grid=True, format=",.0f"),
   xy.y_axis(id="y2", label="Conversion", side="right", grid=False, format=".1%"),
   xy.x_band(120, 150, text="Campaign", color="#22c55e", opacity=0.10),
   xy.hline(float(revenue.mean()), text="mean", color="#94a3b8"),
   xy.callout(float(days[peak]), float(revenue[peak]), "peak", dx=-60, dy=-40),
   xy.legend(loc="upper left", ncols=2, toggle=True),
   xy.tooltip(title="Day", format={"y": ",.1f"}),
   xy.modebar(True),
   xy.theme(palette=["#7c3aed", "#f59e0b"], grid_color="#e6e6ef"),
   title="Layered composition · dual axes · annotations",
   width=900, height=440, crosshair=True,
)
render(layered, "1 · Composition model")

Инсталираме и инициализираме библиотеката XY в Google Colab, като активираме поддръжка за интерактивни уиджети. Дефинираме повторно използваема функция за визуализация, която показва диаграми на живо и преминава към самостоятелен HTML, когато поддръжката на уиджети не е налична. След това създаваме многослойна визуализация с множество графични елементи, двойни оси, анотации, подсказки, легенди, теми и интерактивни контроли за навигация.

Копиране на кодаКопираноИзползвайте друг браузър
n = 4000
df = pd.DataFrame({
   "x":      rng.normal(0, 1, n),
   "noise":  rng.normal(0, 1, n),
   "region": rng.choice(["North", "South", "East", "West"], n),
})
df["y"]   = 2.1 * df["x"] + df["noise"] * 0.9
df["mag"] = np.abs(df["y"])
render(xy.scatter_chart(
   xy.scatter("x", "y", color="mag", colormap="plasma",
              size=5, opacity=0.7, color_domain=(0, 6)),
   xy.colorbar(title="|y|"),
   xy.x_axis(label="x"), xy.y_axis(label="y"),
   data=df, title="Columns resolved by name", width=760, height=420,
), "2 · DataFrame-driven channels")
render(xy.facet_chart(
   xy.scatter("x", "y", color="#0ea5e9", size=4, opacity=0.6),
   by="region", data=df, cols=2,
   share_x=True, share_y=True, link=True, link_select=True,
   width=760, height=220, gap=12, title="Faceted by region",
), "3 · Facets with linked axes")
N = 1_500_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.05, N)
big = xy.scatter_chart(
   xy.scatter(r * np.cos(theta), r * np.sin(theta),
              color=np.exp(-r / 2.2), colormap="magma_r",
              density=True,
              size=2.5, opacity=0.85,
              zoom_size_factor=2.6, zoom_opacity=0.95),
   xy.colorbar(title="density"),
   title=f"{N:,} points · drag to pan, scroll to zoom",
   width=760, height=520, zoom=True, pan=True, wheel_zoom=True,
)
render(big, "4 · Million-point density surface")
mem = big.memory_report()
print(f"canonical f64 held in Python : {mem['canonical_bytes']/1e6:.1f} MB")
print(f"bytes sent for first paint   : {mem['transport_bytes_first_paint']/1e6:.2f} MB "
     f"({mem['transport_bytes_per_point']:.3f} B/point)")
print(f"compute backend              : {mem['backend']}")

Създаваме структуриран Pandas DataFrame и използваме имената на колоните директно като канали за визуализация. Генерираме точкова диаграма с цветово кодиране, разделяме набора от данни на свързани фасети по региони и запазваме общото поведение на осите между панелите. Визуализираме също 1,5 милиона точки чрез системата за визуализация на плътността на XY и проверяваме използването на паметта и ефективността на трансфера на данни.

Копиране на кодаКопираноИзползвайте друг браузър
sel = big.select_range(-1.0, 1.0, -1.0, 1.0)
sx, sy = sel.xy(0)
print(f"\nselect_range hit {len(sel):,} rows; x array {sx.shape}")
print("first rows:", sel.rows(limit=2))
print("pick(trace=1, index=10):", layered.pick(1, 10))
def on_select(selection):
   xs, ys = selection.xy(0)
   print(f"[callback] {len(selection):,} rows selected, mean y = {ys.mean():.3f}")
def on_view_change(payload):
   print("[callback] viewport:", payload)
render(xy.scatter_chart(
   xy.scatter("x", "y", color="#ef4444", size=5, opacity=0.7),
   data=df, select=True, on_select=on_select, on_view_change=on_view_change,
   title="Shift-drag a box → payload lands in Python",
   width=760, height=380,
), "5 · Selections routed back to the kernel")
stream = xy.line_chart(
   xy.line([0.0], [0.0], color="#10b981", width=2, name="live"),
   xy.x_axis(label="t"), xy.y_axis(label="value", domain=(-3, 3)),
   title="Streaming via chart.append()", width=760, height=320,
)
render(stream, "6 · Streaming")
import time
for k in range(1, 60):
   t = k / 3.0
   stream.append(0, [t], [float(np.sin(t) + rng.normal(0, 0.08))])
   time.sleep(0.03)

Избираме точни точки от голямата визуализация и извличаме оригиналните им стойности на редовете директно от Python. Дефинираме функции за обратни извиквания, които получават селекции и промени в изгледа от браузъра, като същевременно запазваме основните данни в ядрото. Създаваме и поточна линейна диаграма и непрекъснато добавяме нови наблюдения, за да актуализираме визуализацията в реално време.

Копиране на кодаКопираноИзползвайте друг браузър
print("\navailable slots:", ", ".join(sorted(xy.CHART_DOM_SLOTS)))
CSS = """
.xy-card {background:#fafaf9;border:1px solid #e7e5e4;border-radius:16px;padding:10px}
.xy-title{font:600 16px/1.2 ui-sans-serif;letter-spacing:-.01em;color:#1c1917}
.xy-tip  {border-radius:10px;background:#1c1917;color:#fafaf9}
"""
display(HTML(f"<style>{CSS}</style>"))
styled = xy.line_chart(
   xy.line(days, revenue, color="#111827", width=2,
           animation=xy.animation(duration=700,
                                  easing=xy.spring(stiffness=180, damping=22))),
   xy.x_axis(label="Day"), xy.y_axis(label="Revenue"),
   title="Slot-addressed styling",
   class_name="xy-card",
   class_names={"title": "xy-title", "tooltip": "xy-tip"},
   styles={"canvas": {"border-radius": "12px"}},
   width=760, height=360,
)
render(styled, "7 · CSS slots, tokens, spring animation")
def _fit(cols):
   x = np.asarray(cols["x"], float); y = np.asarray(cols["y"], float)
   b, a = np.polyfit(x, y, 1)
   order = np.argsort(x); xs = x[order]
   fit   = a + b * xs
   resid = float(np.std(y - (a + b * x)))
   return {"x": xs, "y": y[order], "fit": fit,
           "lo": fit - 1.96 * resid, "hi": fit + 1.96 * resid}
def _build(ctx):
   color = ctx.options.get("color", "#2563eb")
   c, nm = ctx.columns, (ctx.name or "trend")
   return [
       xy.error_band(c["x"], c["lo"], c["hi"], color=color, opacity=0.18, name=f"{nm} CI"),
       xy.line(c["x"], c["fit"], color=color, width=2.5, name=nm),
   ]
if "trendline" not in xy.registered_marks():
   xy.register_mark(xy.MarkPlugin(name="trendline", build=_build,
                                  columns=("x", "y"), calc=_fit,
                                  doc="OLS fit with a 95% band."))
render(xy.chart(
   xy.scatter("x", "y", color="#94a3b8", size=4, opacity=0.5, name="observations"),
   xy.mark("trendline", x="x", y="y", color="#e11d48", name="OLS"),
   xy.legend(loc="upper left"),
   data=df, title="Third-party mark kind", width=760, height=400,
), "8 · Custom mark plugin")

Персонализираме компонентите на диаграмите чрез стабилни DOM слотове, CSS класове, вградени стилове и анимации, базирани на пружинен модел. Дефинираме изчисление по метода на обикновените най-малки квадрати, което създава напасната трендова линия и 95% доверителен интервал от предоставените данни. След това регистрираме това изчисление като повторно използваем персонализиран елемент на XY и го комбинираме с вградени компоненти за точкова диаграма, линейна диаграма, доверителен интервал и легенда.

Копиране на кодаКопираноИзползвайте друг браузър
import xy.pyplot as plt
t = np.linspace(0, 10, 400)
fig, ax = plt.subplots(figsize=(8, 3.5))
ax.plot(t, np.sin(t), "r--", label="sin")
ax.plot(t, np.cos(t), label="cos")
ax.set_xlabel("t"); ax.set_ylabel("amplitude"); ax.set_title("xy.pyplot compatibility")
ax.legend(); ax.grid(True, alpha=0.3)
plt.show()
os.makedirs("out", exist_ok=True)
layered.to_html("out/chart.html")
layered.to_svg("out/chart.svg")
layered.to_png("out/chart.png", scale=2)
for f in ("chart.html", "chart.svg", "chart.png"):
   print(f"out/{f}: {os.path.getsize('out/'+f)/1024:.0f} KB")
print("\n tutorial complete")

Използваме слоя за съвместимост с xy.pyplot, за да създаваме графики с познати команди в стил Matplotlib. Генерираме синусоидални и косинусоидални криви, конфигурираме етикети, заглавия, легенди и настройки на мрежата, след което показваме получената фигура. Накрая експортираме многослойната диаграма като самостоятелни HTML, SVG и PNG файлове с висока резолюция и проверяваме размера на всеки генериран файл.

В заключение изградихме цялостно разбиране за това как XY поддържа съвременни работни процеси за интерактивна визуализация директно от Python. Създадохме многослойни и фасетирани диаграми, анализирахме ефективно големи набори от данни, извличахме точни избрани редове от ядрото, добавяхме нови наблюдения към визуализации на живо и персонализирахме външния вид на диаграмите чрез теми, анимации, CSS класове и стабилни DOM слотове. Демонстрирахме също как разширяваме XY със собствен плъгин за статистически графичен елемент и използваме повторно познати команди за чертане в стил Matplotlib чрез моста xy.pyplot. Завършихме с преносими експорти, които ни позволяват да споделяме диаграми извън ноутбука, като същевременно запазваме интерактивността или качеството на графиките, подходящо за публикуване.


Вижте ПЪЛНИТЕ КОДОВЕ тук.Също така, не се колебайте да ни последвате в Twitter и не забравяйте да се присъедините към нашия ML SubReddit с над 150 хил. членове и да се абонирате за нашия бюлетин. Чакайте! В Telegram ли сте? Вече можете да се присъедините към нас и в Telegram.

Имате нужда от партньорство с нас за популяризиране на вашето GitHub хранилище, страница в Hugging Face, представяне на продукт, уебинар и т.н.? Свържете се с нас

Преведено автоматично от английски. Оригиналната статия е на връзката по-долу.

Първоначално публикувано от MarkTechPost на

Прочетете оригинала в MarkTechPost ↗

Текстът и изображенията са собственост на MarkTechPost и са възпроизведени тук с посочване на авторството и връзка към оригиналната публикация.

← Към новините

Още новини

Всички последни новини