XY Library: A Revolução da Visualização de Dados Interativos em Python Que Você PRECISA Conhecer!
Olá, pessoal! Aqui é o Lucas Tech, e hoje vamos mergulhar de cabeça em algo que vai mudar a forma como você enxerga seus dados: a biblioteca XY do Python! Se você, assim como eu, adora transformar números brutos em histórias visuais incríveis e interativas, prepare-se, porque o que vem por aí é simplesmente sensacional. O XY não é só mais uma ferramenta de gráficos; ele é um game changer para quem busca visualizações escaláveis, extensíveis e que realmente conversam com o usuário.
Vamos explorar juntos como essa ferramenta nos permite construir gráficos interativos e super completos, desde a base até as personalizações mais avançadas. Bora lá!
Configurando o Ambiente e o Primeiro Gráfico!
Para começar nossa jornada, precisamos do XY instalado e configurado, especialmente se você estiver usando o Google Colab para acompanhar. Esse primeiro bloco de código é a nossa porta de entrada, garantindo que tudo funcione direitinho, incluindo os widgets interativos que tornam o XY tão poderoso.
php
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"
{note}
"))
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")
Depois de instalar e preparar o terreno, a mágica começa! O XY usa um modelo de composição onde você combina diferentes "marcas" (linhas, pontos, bandas de erro) e as sobrepõe para criar gráficos riquíssimos. No exemplo acima, montamos uma visualização em camadas com eixos duplos, anotações (olha o pico de receita e a campanha!), dicas de ferramentas, legendas e até um tema personalizado. É tipo um lego para dados, mas com superpoderes interativos!
Explorando Seus Dados com Pandas e o Poder da Densidade!
Agora a coisa fica séria! O XY se integra perfeitamente com o Pandas, permitindo que a gente use nomes de colunas diretamente para mapear canais visuais. Isso é uma mão na roda para quem já está acostumado a trabalhar com DataFrames.
php
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’]}")
Aqui, criamos um DataFrame e usamos os nomes das colunas x, y e mag diretamente para gerar um gráfico de dispersão com cores. Mas o mais legal é quando dividimos o dataset em "facetas" regionais, mantendo os eixos compartilhados e interligados. Isso é ótimo para comparar categorias!
E para quem lida com Big Data, o XY simplesmente MANDA! Visualizamos 1.5 milhão de pontos usando o sistema de renderização por densidade. Isso significa que mesmo com uma montanha de dados, o gráfico permanece fluido e responsivo. É impressionante ver como ele otimiza o uso de memória e a transferência de dados.
Interatividade Total: Conectando o Navegador ao Python e Streaming de Dados!
Isso aqui é a cereja do bolo! O XY permite que as interações do navegador (tipo selecionar uma área no gráfico) "conversem" de volta com o Python. O gráfico não é só bonito, ele é INTELIGENTE!
php
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)
Neste trecho, demonstramos como podemos selecionar pontos exatos de uma visualização grande e obter seus valores originais diretamente no Python. Além disso, definimos funções de callback que reagem a seleções e mudanças de viewport no navegador, mantendo seus dados processáveis no kernel.
E se você precisa de dados em tempo real? O XY também tem isso! Criamos um gráfico de linha que recebe novos dados via streaming, atualizando a visualização dinamicamente. É perfeito para monitoramento ou dashboards ao vivo!
Design e Extensão Sem Limites: Customização, Marks Personalizados e Exportação!
Mas e se você quiser ir além na estética ou funcionalidade? O XY pensa em tudo! Podemos personalizar componentes do gráfico usando slots DOM, classes CSS e estilos inline. E as animações? Elas podem ter um toque especial com efeitos de mola!
php
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""))
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")
Mas o que eu achei mais insano é a capacidade de criar nossas próprias "marcas" personalizadas! Definimos um cálculo de mínimos quadrados ordinários que gera uma linha de tendência e uma banda de confiança. Depois, registramos isso como uma marca reutilizável do XY, combinando-a com os componentes já existentes. Imagina o poder de criar seus próprios tipos de gráfico!
php
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")
E pra fechar com chave de ouro: compatibilidade com Matplotlib! O xy.pyplot nos permite usar comandos familiares para criar plots, o que é ótimo para quem já tem intimidade com a biblioteca. E a exportação? Você pode salvar seus gráficos como HTML (mantendo a interatividade!), SVG (qualidade vetorial para publicações) ou PNG de alta resolução. Versatilidade é tudo!
Minha Visão
Cara, sério, depois de explorar o XY em detalhes, minha visão é uma só: essa biblioteca é um divisor de águas! Ela não só simplifica a criação de visualizações complexas e interativas em Python, como também abre um mundo de possibilidades para quem trabalha com análise de dados. A facilidade de lidar com milhões de pontos, a interatividade em tempo real e a flexibilidade para personalizar tudo – até criar seus próprios elementos gráficos – são algo que eu realmente buscava. É a prova de que podemos ter gráficos bonitos, funcionais e super inteligentes, tudo na mesma ferramenta. O XY eleva o nível da nossa comunicação de dados, tornando-a mais dinâmica e envolvente, e isso é o que um entusiasta de tecnologia como eu sempre sonhou!
E aí, o que você achou do XY? Você já usou alguma ferramenta que te surpreendeu tanto quanto essa? Deixa seu comentário aqui embaixo!
Referência: Matéria Original
Posts relacionados:

THINK The user wants me to rewrite a title in Portuguese for Google Discover. The original title is: "Experts warn 2.2 million cars could be at risk of hijacking via Bluetooth" The requirements are:
- Short
- Curious
- Return ONLY the final title text. No intros, no lists.
Let’s break down the original title’s core message:
- Who: Experts
- What: Warning about cars at risk
- How many: 2.2 million
- Risk: Hijacking
- Method: Via Bluetooth
Google Discover titles need to grab attention quickly. They often use questions, strong verbs, or create a sense of urgency/mystery.
Ideas:
- Focus on the vulnerability/risk directly: "Seu carro pode ser hackeado via Bluetooth?" (Is your car hackable via Bluetooth?) – Short, question, curious.
- Focus on the number and danger: "Milhões de carros em risco: O perigo Bluetooth." (Millions of cars at risk: The Bluetooth danger.) – Short, highlights scale, points to the method as the danger.
- More dramatic, direct threat: "Alerta: Carros podem ser roubados via Bluetooth!" (Alert: Cars can be stolen via Bluetooth!) – "Roubados" (stolen) is more impactful than "hijacked" in this context for Discover.
- Emphasize the hidden danger: "O perigo invisível: Seu carro vulnerável ao Bluetooth?" (The invisible danger: Your car vulnerable to Bluetooth?) – A bit longer, but "invisible danger" is good.
- Direct question about user’s car: "Seu carro está seguro? Hackers miram Bluetooth." (Is your car safe? Hackers target Bluetooth.) – Short, direct question.
Let’s evaluate for "short and curious":
"Seu carro pode ser hackeado via Bluetooth?"
- Short: Yes.
- Curious: Yes, it makes you wonder about your car.
"Milhões de carros em risco: O perigo Bluetooth."
- Short: Yes.
- Curious: Yes, "o perigo Bluetooth" is intriguing.
"Alerta: Carros podem ser roubados via Bluetooth!"
- Short: Yes.
- Curious: Yes, alarming and makes you want to know how.
"O perigo invisível: Seu carro vulnerável ao Bluetooth?"
- Short: A bit longer, but still manageable.
- Curious: Yes, "perigo invisível" and the question.
- "Seu carro está seguro? Hackers miram Bluetooth."
- Short: Yes.
- Curious: Yes, direct question.
I need to choose one and make sure it’s APENAS o texto.
Let’s go for something direct, impactful, and clearly implies a personal risk for the reader.
"Seu carro pode ser hackeado via Bluetooth?"
- Hits "short"
- Hits "curious" (raises a direct question to the reader)
- Uses "hackeado" which is very common and understood.
- Mentions "Bluetooth", the method.
Another strong option: "Alerta: Seu carro pode ser roubado via Bluetooth."
- "Alerta" (alert) adds urgency.
- "Roubado" (stolen) is a very strong, understood consequence of "hijacking".
- "Seu carro" personalizes it.
- "via Bluetooth" explains the mechanism.
- Short and curious.
I think "Alerta: Seu carro pode ser roubado via Bluetooth." is slightly stronger than the question for Discover, as it presents a direct warning, which often performs well. The question mark might make it sound too speculative. The exclamation mark in "Alerta!" adds to the urgency.
Let’s try to make it even more concise if possible. "Seu carro em risco: O ataque Bluetooth." (Your car at risk: The Bluetooth attack.)
- Short: Yes.
- Curious: Yes, "ataque Bluetooth" is intriguing.
"Perigo Bluetooth: Seu carro pode ser hackeado." (Bluetooth Danger: Your car can be hacked.)
- Short: Yes.
- Curious: Yes, direct threat.
"Milhões de carros em risco: O que o Bluetooth esconde?" (Millions of cars at risk: What does Bluetooth hide?)
- Longer, but "o que o Bluetooth esconde?" is very curious.
Let’s stick to the core "hijacking/hacking" idea and make it personal. "Seu carro pode ser hackeado via Bluetooth?" is good. "Alerta: Carros podem ser roubados via Bluetooth." is also good.
The original "hijacking" implies taking control, which "hackeado" (hacked) covers well. "Roubado" (stolen) is a consequence of hijacking, but "hackeado" is more precise to the technical aspect.
Let’s go with a direct, personal question. Questions often drive clicks on Discover.
"Seu carro pode ser hackeado via Bluetooth?" It’s short, directly addresses the reader, and uses a tech term that’s widely understood. It creates immediate curiosity.
Final check:
- Portuguese: Yes
- Google Discover: Yes (short, curious, direct)
- Short: Yes
- Curious: Yes (question form)
- Return ONLY the text: Yes
One last alternative: "Hackers podem roubar seu carro via Bluetooth?" – also good. But "Seu carro pode ser hackeado via Bluetooth?" sounds a bit more natural and less like a direct accusation to "hackers". It focuses on the car’s vulnerability.
Let’s go with "Seu carro pode ser hackeado via Bluetooth?". It’s concise and impactful.Seu carro pode ser hackeado via Bluetooth?
Nokia e AWS: IA turbina o 5G em tempo real.
Cripto: O que a IA vê em tempo real?
Imagem real ou gerada por IA? Conheça o novo aplicativo da Adobe e aprenda a usá-lo.