SeraPlot for visualization, SeraML for machine learning, SeraDFrame for dataframes — compiled into a single native binary, calling each other directly instead of serializing across library boundaries.
Plotly, scikit-learn, and pandas are each excellent at what they do — and each is a separate library with its own object model. Connecting them means converting DataFrames to NumPy arrays to lists and back, at every boundary. Sera exists because that conversion cost is real, and because a chart, a trained model, and the table that fed both of them don't need to live in different worlds.
sp.kmeans() computes the clustering and renders the chart in the same Rust call — no round-trip through scikit-learn, no intermediate serialization. That is the whole design principle behind Sera: build once, natively, and let every pillar call the others directly.
Pillar
What it is
Where to go deeper
SeraPlot
The rendering engine — 61 2D chart families, 24 3D types, 2 map types, each with several variants. Self-contained HTML/SVG output, no JS bundle.
A scikit-learn-shaped machine learning layer written in Rust — linear models, trees, ensembles (random forest, gradient boosting, AdaBoost), SVM, k-NN, Naive Bayes, PCA, clustering, anomaly detection, model selection, metrics, preprocessing. Plus a model registry, PowerBI/Tableau export, and GPU/distributed backends that scikit-learn itself doesn't ship.
A native columnar dataframe, built for the same pipeline rather than adapted from one — no pandas dependency, no copy at the boundary when it feeds a chart or a model.
The three pillars share the same conventions on purpose, so that switching between them costs nothing:
One call, one result.sp.bar(...), sp.linear_regression(...), df.groupby(...) all follow the same flat-function shape — no builder object to assemble first, no separate .fit() then .transform() unless the algorithm genuinely needs state across calls.
Aliases are free. Every function is reachable under several names (sp.bar / sp.bars / sp.bar_chart / sp.bar_unified) resolved through a shared alias registry — pick the name that reads best in your code, they compile to the same call.
Native output, always. A chart is real HTML/SVG, a model result is a real Python dict, a DataFrame column is real Rust-backed memory — never an opaque wrapper object you have to unwrap to get to the actual data.
The fastest path is the default path. Because computation happens in Rust before anything crosses into Python, the ergonomic way to write code and the fast way to write code are the same code.
Every pillar reads from the same global controller instead of repeating options per call. Set it once — font, palette, animation, locale — and every Chart built afterward inherits it automatically, with any single call still free to override a value locally.
import seraplot as sp
sp.config(
font="Inter", font_size=13,
palette="viridis", background="#0d1117",
animation=True, animation_duration=420,
crosshair=True, zoom=True, tooltip=True,
locale="en-US", thousands_sep=",",
responsive=True, export_button=True,
)
sp.bar("Revenue", labels, values) # inherits every setting above
sp.bar("Revenue", labels, values, font_size=16) # overrides just this one
sp.config() exposes 30+ knobs this way — typography, palette/background, gridlines and despine, text auto-formatting and rotation, bar corner radius, watermark, drop shadow, crosshair/zoom/tooltip behavior, locale-aware number formatting, responsive layout, export button visibility. sp.reset_config() reverts to defaults, sp.theme() swaps a whole preset in one call. See Chart Methods for the full controller reference and every chainable per-chart method, and Configuration for the config surface in depth.
Not a claim — verified this way: axe-core runs against real Chromium for every chart family with zero serious/critical violations; text and non-text contrast is measured (relative luminance, not eyeballed) against WCAG AA; keyboard navigation (roving tabindex, arrow/Home/End) works on data points and legends; the whole catalog loads with zero console errors on real Chromium, Firefox, and WebKit engines, not just one.
Plotly, scikit-learn et pandas sont chacun excellents dans leur domaine — et chacun est une bibliothèque séparée avec son propre modèle d'objets. Les connecter implique de convertir des DataFrames en tableaux NumPy puis en listes et inversement, à chaque frontière. Sera existe parce que ce coût de conversion est réel, et parce qu'un graphique, un modèle entraîné et la table qui a nourri les deux n'ont pas besoin de vivre dans des mondes différents.
sp.kmeans() calcule le clustering et rend le graphique dans le même appel Rust — aucun aller-retour vers scikit-learn, aucune sérialisation intermédiaire. C'est tout le principe de conception derrière Sera : construire une seule fois, nativement, et laisser chaque pilier appeler les autres directement.
Pilier
Ce que c'est
Pour aller plus loin
SeraPlot
Le moteur de rendu — 61 familles de graphiques 2D, 24 types 3D, 2 types de cartes, chacun avec plusieurs variantes. Sortie HTML/SVG autonome, sans bundle JS.
Une couche de machine learning façon scikit-learn écrite en Rust — modèles linéaires, arbres, ensembles (random forest, gradient boosting, AdaBoost), SVM, k-NN, Naive Bayes, PCA, clustering, détection d'anomalies, sélection de modèle, métriques, preprocessing. Plus un registre de modèles, un export PowerBI/Tableau, et des backends GPU/distribués que scikit-learn lui-même n'a pas.
Un dataframe colonnaire natif, conçu pour le même pipeline plutôt qu'adapté depuis un autre — pas de dépendance pandas, pas de copie à la frontière quand il alimente un graphique ou un modèle.
Les trois piliers partagent les mêmes conventions volontairement, pour que passer de l'un à l'autre ne coûte rien :
Un appel, un résultat.sp.bar(...), sp.linear_regression(...), df.groupby(...) suivent tous la même forme de fonction à plat — pas d'objet builder à assembler d'abord, pas de .fit() puis .transform() séparés sauf si l'algorithme a réellement besoin d'état entre les appels.
Les alias sont gratuits. Chaque fonction est accessible sous plusieurs noms (sp.bar / sp.bars / sp.bar_chart / sp.bar_unified) résolus via un registre d'alias partagé — choisissez le nom qui se lit le mieux dans votre code, ils compilent vers le même appel.
Sortie native, toujours. Un graphique est du vrai HTML/SVG, un résultat de modèle est un vrai dict Python, une colonne de DataFrame est de la vraie mémoire portée par Rust — jamais un objet wrapper opaque qu'il faut déballer pour accéder à la donnée réelle.
Le chemin le plus ergonomique est le chemin le plus rapide. Parce que le calcul se fait en Rust avant de traverser vers Python, la façon la plus naturelle d'écrire du code et la façon la plus rapide sont le même code.
Chaque pilier lit depuis le même contrôleur global plutôt que de répéter les options à chaque appel. Configurez une fois — police, palette, animation, locale — et chaque Chart construit ensuite hérite automatiquement, tout en restant libre de surcharger une valeur localement.
import seraplot as sp
sp.config(
font="Inter", font_size=13,
palette="viridis", background="#0d1117",
animation=True, animation_duration=420,
crosshair=True, zoom=True, tooltip=True,
locale="fr-FR", thousands_sep=" ",
responsive=True, export_button=True,
)
sp.bar("Revenu", labels, values) # hérite de tous les réglages ci-dessus
sp.bar("Revenu", labels, values, font_size=16) # surcharge juste celui-ci
sp.config() expose 30+ réglages de cette façon — typographie, palette/fond, gridlines et despine, formatage/rotation automatique du texte, rayon des coins de barre, watermark, ombre portée, comportement crosshair/zoom/tooltip, formatage numérique localisé, mise en page responsive, visibilité du bouton d'export. sp.reset_config() revient aux défauts, sp.theme() change tout un preset en un appel. Voir Méthodes des graphiques pour la référence complète du contrôleur et chaque méthode chainable par graphique, et Configuration pour la surface de config en détail.
Pas une affirmation — vérifié ainsi : axe-core s'exécute contre un vrai Chromium pour chaque famille de graphique, zéro violation serious/critical ; le contraste texte et non-texte est mesuré (luminance relative, pas à l'œil) contre WCAG AA ; la navigation clavier (tabindex flottant, flèches/Home/End) fonctionne sur les points de données et les légendes ; tout le catalogue charge sans erreur console sur de vrais moteurs Chromium, Firefox et WebKit, pas un seul.
SeraPlot ships as a compiled Rust extension (.pyd / .so) bundled in the wheel. There is no compiler required on the user side — the binary is pre-built for each platform.
SeraPlot has zero required Python dependencies. The Rust extension is entirely self-contained — the HTML output embeds its own JavaScript inline and does not load anything from a CDN.
🌐 Works offlineCharts render in air-gapped environments, emails, PDF exports via browser print — no CDN, no internet.
🔒 No conflictsZero dependency on numpy, pandas, or scipy — nothing to conflict with your existing stack.
🚀 All platformsPre-built wheels for Windows, Linux, and macOS. No compiler, no Rust toolchain needed.
SeraPlot se distribue sous forme d'extension Rust compilée (.pyd / .so) incluse dans le wheel. Aucun compilateur n'est requis côté utilisateur — le binaire est pré-compilé pour chaque plateforme.
SeraPlot n'a aucune dépendance Python requise. L'extension Rust est entièrement autonome — le HTML embarque son propre JavaScript sans rien charger depuis un CDN.
🌐 Fonctionne hors ligneLes graphiques se génèrent en environnement isolé, dans les e-mails, en impression PDF — sans CDN ni internet.
🔒 Zéro conflitAucune dépendance sur numpy, pandas ou scipy — rien qui puisse entrer en conflit avec votre stack.
🚀 Toutes plateformesWheels pré-compilés pour Windows, Linux et macOS. Aucun compilateur, aucune toolchain Rust requise.
SeraPlot is a complete data toolkit written in Rust. The same engine powers your visualizations, your machine learning, and the way you ship results to other people.
Pillar
What you get
Plot
57 chart types — 33 in 2D, 17 in 3D (WebGL), 2 maps. Built-in themes, palettes, animation, zoom, crosshair, export.
Train
Scikit-learn-compatible ML in Rust — DBSCAN, K-Means, RandomForest, GradientBoosting, SVM, PCA, GridSearchCV, train/test split. 1.3× to 686× faster.
Stream & scale
Live updates, downsampling for millions of points, drift detection, AutoML, diff mode, facet grids.
Ship
Self-contained 21 KB HTML — no CDN, no backend, works offline, by email, in S3, in PDFs, in Notion, in air-gapped CI.
As you’ve probably understood by now, Seraplot is a tool designed to be extremely customizable, while also being much faster and more resource-efficient than existing solutions. It also provides a wide range of helpful features, such as the Seraplot extension for VSCode, which allows you to generate plots or ML methods very quickly and live, between each save of your scripts.
In addition, Seraplot is available across multiple languages such as: JS/TS, C (C# & C++), Java, Rust, Python, R & Scala. The main goal is to be highly accessible: from one language to another, the commands remain the same for greater simplicity.
In summary, Seraplot is a much more practical and independent tool that enables the generation of 2D & 3D plots, while also aiming to provide machine learning-related methods that you will find throughout the documentation. More surprises await you, such as the ability to choose different themes, a chunk system in case of crashes to resume from the error point, and even multiple aliases to use the same method (e.g., sp.build_bar_chart / sp.bar_chart / sp.bar / sp.bars).
Same code, same random data, same machine. Full HTML output timed.
import seraplot as sp
categories = ["Electronics", "Clothing", "Food", "Books", "Sports", "Toys", "Health", "Auto"]
data = [...] # 1000 pre-generated lists
for i in range(1000):
sp.bar(f"Report #{i+1}", categories, data[i]).html
1000 charts in 6 ms — 6 µs/chart
import plotly.graph_objects as go
categories = ["Electronics", "Clothing", "Food", "Books", "Sports", "Toys", "Health", "Auto"]
data = [...] # same 1000 pre-generated lists
for i in range(1000):
fig = go.Figure(data=[go.Bar(x=categories, y=data[i])])
fig.update_layout(title=f"Report #{i+1}", template="plotly_dark")
fig.to_html(full_html=True, include_plotlyjs="cdn")
1000 charts in 37,023 ms — 6,170× slower
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
categories = ["Electronics", "Clothing", "Food", "Books", "Sports", "Toys", "Health", "Auto"]
data = [...] # same 1000 pre-generated lists
for i in range(1000):
fig, ax = plt.subplots(figsize=(9, 5))
ax.bar(categories, data[i])
ax.set_title(f"Report #{i+1}")
fig.savefig(f"chart_{i}.png")
plt.close()
SeraPlot is not a wrapper around Plotly, Chart.js, or D3.
It is a Rust-native rendering engine that generates minimal HTML + JS per chart.
A Pie chart gets Pie JS. A Bar chart gets Bar JS. Nothing else is bundled.
Plotly returns 4.7 MB per request. Matplotlib requires disk I/O and returns a static PNG.
SeraPlot returns 21 KB of interactive HTML directly from RAM.
« Tout tracer. Tout entraîner. Partout déployer. »
SeraPlot est une boîte à outils data complète écrite en Rust. Le même moteur propulse vos visualisations, votre machine learning, et la façon dont vous livrez les résultats à vos collègues.
Pilier
Ce que vous obtenez
Tracer
57 types de graphiques — 33 en 2D, 17 en 3D (WebGL), 2 cartes. Thèmes intégrés, palettes, animations, zoom, crosshair, export.
Entraîner
ML compatible scikit-learn en Rust — DBSCAN, K-Means, RandomForest, GradientBoosting, SVM, PCA, GridSearchCV, train/test split. De 1,3× à 686× plus rapide.
Streamer & passer à l'échelle
Mises à jour en direct, downsampling pour des millions de points, détection de drift, AutoML, mode diff, grilles de facettes.
Déployer
HTML autonome de 21 Ko — pas de CDN, pas de backend, fonctionne hors-ligne, par e-mail, sur S3, dans des PDF, dans Notion, en CI isolée.
Extension VS Code native — aperçu en direct, galerie, studio de thèmes, snippets, détection automatique des labels / values depuis votre code.
Persister & exporter
Export HTML, PNG, SVG, PDF, pickle. Rechargement des modèles ML entraînés. Sortie compatible CSP.
Rester accessible
SVG balisé a11y, HTML sémantique, navigation clavier, formatage numérique localisé.
Une seule librairie remplace : matplotlib + plotly + dash + streamlit + seaborn + une partie de scikit-learn — avec un seul pip install et zéro dépendance d'exécution.
Comme vous l’aurez compris en arrivant jusqu’ici, Seraplot est un outil qui a pour objectif d’être extrêmement personnalisable, mais aussi beaucoup plus rapide et moins gourmand que ce qui existe déjà, en plus de proposer tout un panel d’aides, comme l’extension Seraplot dans VSCode, qui vous permettra de générer des plots ou des méthodes ML très rapidement et en live, entre chaque sauvegarde de vos scripts.
En plus de cela, Seraplot se voit distribué dans différents langages comme : JS/TS, C (C# & C++), Java, Rust, Python, R & Scala. L’objectif étant vraiment d’être ultra accessible : d’un langage à un autre, les commandes restent les mêmes pour plus de simplicité.
Pour résumer, Seraplot est un outil beaucoup plus pratique de ce qui existe déjà et complétement indépendant, en plus de compilé différente fonctionnalitée. En autre il permet la génération de plots 2D & 3D, mais aussi qui tend à proposer des méthodes liées au ML, que vous pourrez retrouver au cours de votre documentation. D’autres surprises vous attendent, que ce soit la possibilité de choisir différents thèmes, ou bien le système de chunks en cas de crash pour reprendre au point d’erreur, ou encore pour n'en citer que un dernier, le fait d’avoir différents alias pour utiliser une même méthode (ex : sp.build_bar_chart / sp.bar_chart / sp.bar / sp.bars).
Même code, mêmes données aléatoires, même machine. Sortie HTML complète chronométrée.
import seraplot as sp
categories = ["Électronique", "Vêtements", "Alimentation", "Livres", "Sport", "Jouets", "Santé", "Auto"]
data = [...]
for i in range(1000):
sp.bar(f"Rapport #{i+1}", categories, data[i]).html
1 000 graphiques en 6 ms — 6 µs/graphique
import plotly.graph_objects as go
categories = ["Électronique", "Vêtements", "Alimentation", "Livres", "Sport", "Jouets", "Santé", "Auto"]
data = [...]
for i in range(1000):
fig = go.Figure(data=[go.Bar(x=categories, y=data[i])])
fig.update_layout(title=f"Rapport #{i+1}", template="plotly_dark")
fig.to_html(full_html=True, include_plotlyjs="cdn")
1 000 graphiques en 37 023 ms — 6 170× plus lent
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
categories = ["Électronique", "Vêtements", "Alimentation", "Livres", "Sport", "Jouets", "Santé", "Auto"]
data = [...]
for i in range(1000):
fig, ax = plt.subplots(figsize=(9, 5))
ax.bar(categories, data[i])
ax.set_title(f"Rapport #{i+1}")
fig.savefig(f"chart_{i}.png")
plt.close()
Plotly retourne 4,7 Mo par requête. Matplotlib nécessite des I/O disque et retourne un PNG statique.
SeraPlot retourne 21 Ko de HTML interactif directement depuis la RAM.
Every Chart object returned by SeraPlot supports the same fluent API. Set defaults once with sp.config() and tweak per chart with chainable methods. All methods return a new Chart, so chaining is always safe.
Fluent APIGlobal + per-chart override60+ chart typesDoc-as-code: every card below is generated from the Rust source
Every card below is generated straight from the #[sera_doc(...)] annotation on the matching Rust method — name, parameters and description always match the actual implementation.
Pass a list of annotation dicts to any chart builder. Coordinates default to fractional canvas space (0.0–1.0); set "frac": false to use raw pixels. Supported kind: "hline", "vline", "line", "arrow", "rect", "text".
Compose any number of pre-built charts into a responsive CSS-grid, each chart isolated in its own iframe.
chartslist[Chart]Charts to arrange
colsint = 3Number of columns
gapint = 16Gap between cells in px
bg_colorstrGrid background
titlestr = ""Optional header title
cell_heightint | NoneOverride each cell height
bar = sp.bar("Sales", labels=["Q1","Q2","Q3","Q4"], values=[120,180,150,210])
line = sp.line("Trend", labels=months, values=sales)
pie = sp.pie("Mix", labels=["A","B","C"], values=[40,35,25])
sp.grid([bar, line, pie], cols=3, gap=14, title="Dashboard").show()
▶
sp.build_slideshow()navigable HTML carousel
Build a navigable HTML carousel with prev / next buttons and an auto-advance progress bar — tell a story without a slide deck.
chartslist[Chart]Ordered slide list
interval_msint = 2500Auto-advance delay in ms
titlestr = ""Carousel title
widthint = 900Output width in px
heightint = 520Output height in px
slides = [sp.bar(f"Slide {i+1}", labels=["A","B"], values=[i, i+1]) for i in range(4)]
sp.build_slideshow(slides, interval_ms=2200, title="Quarterly Story").show()
Tout objet Chart renvoyé par SeraPlot expose la même API fluide. Définis les valeurs par défaut une fois avec sp.config(), ajuste chart par chart avec des méthodes chaînées. Toutes les méthodes retournent un nouveau Chart : le chaînage est toujours sûr.
API fluideGlobal + override par chart60+ types de graphiquesDoc-as-code : chaque carte ci-dessous vient du code Rust
Chaque carte ci-dessous est générée directement depuis l'annotation #[sera_doc(...)] de la méthode Rust correspondante — nom, paramètres et description correspondent toujours à l'implémentation réelle.
Passe une liste de dicts à n'importe quel builder. Coordonnées fractionnaires par défaut (0.0–1.0) ; mets "frac": false pour des pixels. Valeurs de kind : "hline", "vline", "line", "arrow", "rect", "text".
Assemble N charts déjà construits dans une grille CSS responsive, chaque chart isolé dans son iframe.
chartslist[Chart]Charts à disposer
colsint = 3Nombre de colonnes
gapint = 16Espacement en px
bg_colorstrFond de la grille
titlestr = ""Titre en en-tête
cell_heightint | NoneHauteur de chaque cellule
bar = sp.bar("Ventes-Q", labels=["Q1","Q2","Q3","Q4"], values=[120,180,150,210])
line = sp.line("Tendance", labels=mois, values=ventes)
pie = sp.pie("Mix", labels=["A","B","C"], values=[40,35,25])
sp.grid([bar, line, pie], cols=3, gap=14, title="Tableau de bord").show()
▶
sp.build_slideshow()carrousel HTML navigable
Construit un carrousel HTML avec boutons précédent / suivant et barre de progression — parfait pour raconter une histoire.
chartslist[Chart]Slides dans l'ordre
interval_msint = 2500Délai d'avancement auto (ms)
titlestr = ""Titre du carrousel
widthint = 900Largeur en px
heightint = 520Hauteur en px
slides = [sp.bar(f"Slide {i+1}", labels=["A","B"], values=[i, i+1]) for i in range(4)]
sp.build_slideshow(slides, interval_ms=2200, title="Histoire trimestrielle").show()
Two layers, and every page in this section names which one it documents:
Global, standalone functions — sp.set_global_background(...), sp.theme(...), sp.config(...) — apply before charts are built and affect everything created afterward. See Chart Methods for the full generated list.
Chainable Chart methods — chart.export_svg(path), chart.hover_json(payload), chart.downsample() — apply to one already-built chart and return a new Chart, the same way every other per-chart method chains.
Every code sample in this section is checked against a real build rather than
written from memory — where a feature exists only on the JS/WASM side, or
isn't wired up as a Python function yet, that is stated explicitly instead of
shown as if it worked.
Deux couches, et chaque page de cette section précise laquelle elle documente :
Fonctions globales autonomes — sp.set_global_background(...), sp.theme(...), sp.config(...) — s'appliquent avant la construction des graphiques et affectent tout ce qui est créé ensuite. Voir Méthodes des graphiques pour la liste complète générée.
Méthodes chaînables sur Chart — chart.export_svg(path), chart.hover_json(payload), chart.downsample() — s'appliquent à un chart déjà construit et retournent un nouveau Chart, comme toute autre méthode par graphique.
Chaque exemple de code de cette section est vérifié contre un vrai build
plutôt qu'écrit de mémoire — quand une fonctionnalité n'existe que côté
JS/WASM, ou n'est pas encore câblée comme fonction Python, c'est dit
explicitement plutôt que présenté comme si ça fonctionnait.
import seraplot as sp
sp.set_global_background("#0f172a")
bar = sp.build_bar_chart("Revenue", labels=["A", "B"], values=[300, 200])
pie = sp.build_pie_chart("Share", labels=["A", "B"], values=[60, 40])
sp.reset_global_background()
sp.set_global_background(...) is equivalent to sp.config(background=...), and
is overridden by sp.theme(name) if a theme is applied afterward — see
Themes for the built-in background/palette/gridlines bundles.
sp.set_global_background(...) équivaut à sp.config(background=...), et est
écrasé par sp.theme(name) si un thème est appliqué ensuite — voir
Thèmes pour la liste des thèmes intégrés (fond + palette +
quadrillage).
There is no standalone sp.PALETTE_* constant — the built-in color sets ship
bundled inside a theme (background + palette + gridlines together). Call
sp.theme(name) to apply one, or read Themes for the full list
and every palette's exact hex values.
Il n'existe pas de constante sp.PALETTE_* autonome — les jeux de couleurs
intégrés sont livrés à l'intérieur d'un thème (fond + palette + quadrillage
ensemble). Appelez sp.theme(name) pour en appliquer un, ou consultez
Thèmes pour la liste complète et les valeurs hex exactes de
chaque palette.
sp.theme() sets the global background, palette, and gridlines. It is equivalent to calling sp.config(background=..., palette=..., gridlines=...) with the preset values.
Themes persist until sp.reset_theme() or sp.config() overrides them.
You can further override individual properties after calling a theme:
sp.theme() définit le fond global, la palette et le quadrillage. C'est équivalent à sp.config(background=..., palette=..., gridlines=...) avec les valeurs du préréglage.
Les thèmes persistent jusqu'à sp.reset_theme() ou un appel sp.config() qui les écrase.
Vous pouvez continuer à surcharger des propriétés individuelles après avoir appliqué un thème :
hover_json is a chainable Chart method — not a standalone sp. function.
It takes a single JSON string and returns a new Chart with the tooltip
override applied, the same chaining shape as every other Chart method
(chart.subtitle(...).hover_json(...).show()).
Overrides the tooltip content shown per data point, independent of the
values actually plotted — useful when the label/value combination on the
axes isn't the whole story (currency formatting, a delta vs. last period, a
category not otherwise on the chart).
hover_json est une méthode chaînable sur Chart — pas une fonction sp.
autonome. Elle prend une seule chaîne JSON et retourne un nouveau Chart
avec l'override de tooltip appliqué, la même forme chaînable que toute autre
méthode Chart (chart.subtitle(...).hover_json(...).show()).
Remplace le contenu du tooltip affiché par point de donnée, indépendamment
des valeurs réellement tracées — utile quand la combinaison label/valeur des
axes ne raconte pas toute l'histoire (formatage monétaire, un delta vs la
période précédente, une catégorie absente du graphique lui-même).
sp.set_auto_display(False)
charts = []
for name, values in datasets.items():
charts.append(sp.build_bar_chart(name, labels=["A","B","C"], values=values))
for c in charts:
c.show()
sp.set_auto_display(False)
graphiques = []
for nom, valeurs in jeux_de_donnees.items():
graphiques.append(sp.build_bar_chart(nom, labels=["A","B","C"], values=valeurs))
for g in graphiques:
g.show()
Reduce massive datasets while preserving visual shape using the Largest-Triangle-Three-Buckets algorithm. A 10M-point scatter chart becomes 5K points indistinguishable to the eye.
import seraplot as sp
chart = sp.scatter(title="Big scatter", x_values=big_x, y_values=big_y).downsample()
downsample is a chainable Chart method, not a standalone sp. function —
there is no Python sp.lttb(); the algorithm is only registered on the
JS/WASM side today (downsampleLttb, below).
Réduit les datasets massifs en préservant la forme visuelle avec l'algorithme Largest-Triangle-Three-Buckets. Un scatter de 10M points devient 5K points indistinguables à l'œil.
import seraplot as sp
chart = sp.scatter(title="Grand scatter", x_values=big_x, y_values=big_y).downsample()
downsample est une méthode chaînable sur Chart, pas une fonction sp.
autonome — il n'y a pas de sp.lttb() en Python ; l'algorithme n'est
enregistré que côté JS/WASM aujourd'hui (downsampleLttb, ci-dessous).
LiveStream is a bounded ring-buffer accumulator that turns a series of
(x, y) samples arriving over time into a continuously redrawn chart. Memory
stays constant whatever the duration of the stream, since older samples are
dropped past max_points.
What makes it smooth in a notebook: push() / extend() / clear() don't
just re-render — they repaint the same Jupyter output cell in place via
IPython's display_id mechanism (display() once, then update_display()
on every following call). No new cells get appended, no scroll-jump, no
clear-then-flash; the chart updates where it already is.
import seraplot as sp
import time, random
stream = sp.LiveStream(kind="line", title="Temperature sensor (°C)", max_points=60)
value = 21.0
for tick in range(300):
value += random.uniform(-0.4, 0.4)
stream.push(tick, value)
time.sleep(0.05)
Run this in a single notebook cell: the chart appears once, then updates
smoothly in place on every push() — no flicker, no new output cells.
See v2/examples/iot_live_dashboard.py for a fuller multi-sensor simulation.
LiveStream est un accumulateur à buffer circulaire borné qui transforme une
série d'échantillons (x, y) arrivant dans le temps en un graphique redessiné
en continu. La mémoire reste constante quelle que soit la durée du flux,
puisque les échantillons les plus anciens sont supprimés au-delà de
max_points.
Ce qui le rend fluide dans un notebook : push() / extend() / clear() ne
font pas que re-rendre — ils repeignent la même cellule de sortie Jupyter
sur place via le mécanisme display_id d'IPython (display() une fois, puis
update_display() à chaque appel suivant). Aucune nouvelle cellule n'est
ajoutée, pas de saut de défilement, pas de clear-puis-flash ; le graphique se
met à jour là où il est déjà.
import seraplot as sp
import time, random
stream = sp.LiveStream(kind="line", title="Capteur de température (°C)", max_points=60)
value = 21.0
for tick in range(300):
value += random.uniform(-0.4, 0.4)
stream.push(tick, value)
time.sleep(0.05)
Exécute ceci dans une seule cellule de notebook : le graphique apparaît une
fois, puis se met à jour de manière fluide sur place à chaque push() — sans
scintillement, sans nouvelle cellule de sortie.
Voir v2/examples/iot_live_dashboard.py pour une simulation multi-capteurs
plus complète.
Compare two charts structurally — useful for visual CI / regression testing. Implemented natively in Rust (plot::utils::chart_diff).
chart_diff is currently wired for the WASM/JS build only — it is not yet exposed as a Python method (Chart.diff() does not exist). Documented here as-is rather than papered over with a Python example that would not run.
a/b are compared on the <svg>...</svg> slice extracted from each chart's .html. common_prefix is the number of leading bytes the two SVGs share before the first difference; similarity is that shared length divided by the longer of the two.
Compare deux charts structurellement — utile pour les CI visuelles / tests de régression. Implémenté nativement en Rust (plot::utils::chart_diff).
chart_diff n'est aujourd'hui câblé que pour le build WASM/JS — pas encore exposé comme méthode Python (Chart.diff() n'existe pas). Documenté tel quel plutôt que masqué derrière un exemple Python qui ne s'exécuterait pas.
a/b sont comparés sur la tranche <svg>...</svg> extraite du .html de chaque chart. common_prefix est le nombre d'octets identiques au début des deux SVG avant la première différence ; similarity est cette longueur commune divisée par la plus longue des deux.
Detect distribution shift between a reference dataset and a current one using the Kolmogorov-Smirnov two-sample test — implemented natively in Rust (plot::utils::drift_ks).
drift_ks is currently wired for the WASM/JS build only — it is not yet exposed as a Python function (sp.drift does not exist). Documented here as-is rather than papered over with a Python example that would not run.
Détecte un drift de distribution entre un dataset de référence et un dataset actuel via le test à deux échantillons de Kolmogorov-Smirnov — implémenté nativement en Rust (plot::utils::drift_ks).
drift_ks n'est aujourd'hui câblé que pour le build WASM/JS — pas encore exposé comme fonction Python (sp.drift n'existe pas). Documenté tel quel plutôt que masqué derrière un exemple Python qui ne s'exécuterait pas.
Export is exposed two ways: as Chart methods in Python (verified below against
a real build), and as standalone for_each_fn!-registered functions callable
from JavaScript / the C-FFI. The two are not a 1:1 mirror of each other today —
chart_info is the one function that is genuinely callable from both sides.
Write the complete HTML of the chart to disk. The file is fully
self-contained: it embeds the SVG, its scripts and styles. It can be opened in
any browser, attached to an email, or served statically — no server, no CDN.
Extracts the <svg>...</svg> block from the chart and writes it to path.
Useful for embedding into a LaTeX / Word / Illustrator workflow, or
post-processing the geometry (CSS overrides, masks, custom filters).
Rasterizes the chart to PNG via cairosvg. Requires pip install cairosvg —
raises a clear error naming the missing dependency if it is not installed,
rather than failing silently.
Only buildBarChart (and the equivalent build* function per chart family)
and exportHtmlFile are registered on the JS/WASM side today — there is no
JS equivalent of export_svg/export_png/chart_info yet.
import * as sp from "seraplot";
const chart = sp.buildBarChart(JSON.stringify({
title: "Revenue",
labels: ["Q1", "Q2"],
values: [120, 180],
}));
exportHtmlFile exists but currently always fails at runtime — the crate
targets wasm32-unknown-unknown, which has no filesystem access to back
std::fs::write, in Node or the browser alike:
sp.exportHtmlFile(JSON.stringify({ html: chart, path: "out.html" }));
// => '{"ok":false,"error":"operation not supported on this platform"}'
Write the returned HTML string to disk on the JS side instead
(fs.writeFileSync("out.html", chart) in Node, or a Blob download in the
browser).
L'export est exposé de deux façons : comme méthodes sur Chart en Python
(vérifié ci-dessous contre un vrai build), et comme fonctions autonomes
enregistrées via for_each_fn!, appelables depuis JavaScript / le C-FFI. Les
deux ne sont pas un miroir 1:1 aujourd'hui — chart_info est la seule
fonction réellement appelable des deux côtés.
Écrit l'intégralité du HTML du chart sur disque. Le fichier est
auto-suffisant : il embarque le SVG, ses scripts et ses styles. Il s'ouvre
dans n'importe quel navigateur, peut être joint à un mail ou servi en
statique — aucun serveur, aucun CDN.
Extrait le bloc <svg>...</svg> du chart et l'écrit sous path. Utile pour
l'intégrer dans un flux LaTeX / Word / Illustrator, ou post-traiter la
géométrie (overrides CSS, masques, filtres custom).
Rasterise le chart en PNG via cairosvg. Nécessite pip install cairosvg —
lève une erreur claire nommant la dépendance manquante plutôt que d'échouer
silencieusement.
Retourne une chaîne JSON contenant des métadonnées structurelles sur le chart
rendu. Pratique pour le logging, la télémétrie ou pour tester la complexité
d'un chart dans des tests unitaires.
import json
info = json.loads(sp.chart_info(chart))
print(info)
Seuls buildBarChart (et l'équivalent build* par famille de chart) et
exportHtmlFile sont enregistrés côté JS/WASM aujourd'hui — pas encore
d'équivalent JS pour export_svg/export_png/chart_info.
import * as sp from "seraplot";
const chart = sp.buildBarChart(JSON.stringify({
title: "Chiffre d'affaires",
labels: ["T1", "T2"],
values: [120, 180],
}));
exportHtmlFile existe mais échoue toujours à l'exécution aujourd'hui — le
crate cible wasm32-unknown-unknown, qui n'a aucun accès au système de
fichiers pour appuyer std::fs::write, ni dans Node ni dans le navigateur :
sp.exportHtmlFile(JSON.stringify({ html: chart, path: "out.html" }));
// => '{"ok":false,"error":"operation not supported on this platform"}'
Écrivez plutôt la chaîne HTML retournée sur disque côté JS
(fs.writeFileSync("out.html", chart) en Node, ou un téléchargement Blob
dans le navigateur).
sp.ml_save_model() / sp.ml_load_model() are real, verified functions backed
by an in-process, name-versioned model registry — not a file-path envelope.
Every save under the same name gets the next version automatically; load
without a version returns the latest.
import json
import seraplot as sp
result = sp.knn_classifier(data=X_train, target=y_train, k=5)
sp.ml_save_model(
name="knn_v1", kind="knn_classifier",
payload=json.dumps(result), params={"k": "5"}, metrics={}, tags=[],
)
loaded = sp.ml_load_model(name="knn_v1")
restored = json.loads(loaded["payload"])
assert restored == result
Saving again under the same name bumps the version rather than overwriting:
sp.ml_save_model(name="knn_v1", kind="knn_classifier", payload=json.dumps(result), params={}, metrics={}, tags=[])
# {'name': 'knn_v1', 'version': 2}
sp.ml_load_model(name="knn_v1", version=1) # the original save is still there
sp.ml_save_model() / sp.ml_load_model() sont des fonctions réelles et
vérifiées, adossées à un registre de modèles en mémoire, indexé par nom et
versionné — pas une enveloppe fichier. Chaque sauvegarde sous le même name
obtient automatiquement la version suivante ; charger sans version
retourne la dernière.
import seraplot as sp
chart = (
sp.bar(labels=["EU","US","APAC"], values=[10,20,30])
.a11y(title="Quarterly revenue by region",
desc="Bar chart, EU 10M, US 20M, APAC 30M")
)
The resulting SVG includes role="group" (set by default so keyboard-navigable data points and legend buttons remain valid descendants — role="img" forbids focusable children per the SVG-AAM spec), plus aria-label, <title>, <desc> from .a11y() — recognized by NVDA, JAWS, VoiceOver.
Injecte les rôles ARIA, <title> et <desc> dans le SVG pour les lecteurs d'écran et la conformité B2B (WCAG 2.1).
import seraplot as sp
chart = (
sp.bar(labels=["EU","US","APAC"], values=[10,20,30])
.a11y(title="Revenu trimestriel par région",
desc="Bar chart, EU 10M, US 20M, APAC 30M")
)
Le SVG résultant inclut role="group" (posé par défaut pour que les points de données et boutons de légende navigables au clavier restent des descendants valides — role="img" interdit tout enfant focusable selon la spec SVG-AAM), plus aria-label, <title>, <desc> ajoutés par .a11y() — reconnus par NVDA, JAWS, VoiceOver.
Strict Content Security Policies block inline <script>. The csp_safe() method extracts JS into a <script type="application/json"> payload + a single nonce-able loader.
Apply your CSP script-src 'nonce-sp-nonce' and the chart still renders.
Les CSP strictes bloquent les <script> inline. La méthode csp_safe() extrait le JS dans un payload <script type="application/json"> + un loader unique compatible nonce.
SeraPlot provides 41 two-dimensional chart types, from basic bar and line charts to specialized plots like ridgeline, dumbbell, sankey, chord, dendrogram, venn, correlogram, hive, pulse, and orbita.
sp.bar() is the unified entry point for the SeraPlot bar-chart family. It renders standalone Rust-generated HTML/SVG charts. The variant keyword selects the renderer, and shared chart options are applied by the common chart pipeline.
The default renderer is a vertical categorical bar chart. The same API also covers every bar variant registered in Rust.
labels are category labels for bar variants. Single-series variants use values. Multi-series variants use series, where each inner list is one series, and series_names supplies legend names.
When series is missing but series_names is provided, values is interpreted as a flattened matrix split by len(labels): the first category-length block is the first series, the next block is the second series, and so on.
Bars arranged radially around a center, length proportional to value. Pass show_values=True for a value at each bar's tip, gridlines=True for labeled concentric rings. Aliases: "circular_basic", "radial_bar", "polar_bar".
Circular bars split into groups via color_groups, with an extra gap between groups. Same show_values/gridlines options as circular. Aliases: "radial_grouped", "circular_groups".
Two horizontal bar sets mirrored left/right around a shared category axis, from the first two entries of series. Aliases: "pyramid", "age_pyramid".
Variant"population_pyramid"Requiredlabels, series (≥ 2), series_namesReturnsChart
Preview
Horizontal bars extending left or right from a zero line, one color regardless of sign, value printed inside the bar (white) when it's wide enough or just outside otherwise. Aliases: "signed", "delta", "bidirectional". The value label is controlled by show_values (bool) like every other bar variant — it defaults to True here so existing charts keep their look, unlike other variants where it defaults to False.
Bar + boxplot fusion: a semi-transparent bar up to each category's mean, with a real box (Q1/median/Q3, whiskers) overlaid on top showing the distribution behind that mean — pass series as one raw sample array per category (same shape as boxplot's grouped input) instead of single aggregate values. Aliases: "bar_box", "boxbar", "bar_boxplot".
sp.bar() est le point d'entrée unifié de la famille de graphiques en barres de SeraPlot. Il génère des graphiques HTML/SVG autonomes depuis Rust. Le mot-clé variant choisit le renderer, et les options communes passent par le pipeline commun.
Le rendu par défaut est un bar chart catégoriel vertical. La même API couvre toutes les variantes bar enregistrées côté Rust.
labels sert de liste de catégories pour les variantes bar. Les variantes mono-série utilisent values. Les variantes multi-séries utilisent series, où chaque liste interne est une série, et series_names fournit les noms de légende.
Quand series manque mais que series_names est fourni, values est interprété comme une matrice aplatie découpée par len(labels) : le premier bloc appartient à la première série, le suivant à la deuxième, etc.
Barres disposées radialement autour d'un centre, longueur proportionnelle à la valeur. Passe show_values=True pour une valeur à l'extrémité de chaque barre, gridlines=True pour des anneaux concentriques étiquetés. Alias : "circular_basic", "radial_bar", "polar_bar".
Barres circulaires réparties en groupes via color_groups, avec un écart supplémentaire entre groupes. Mêmes options show_values/gridlines que circular. Alias : "radial_grouped", "circular_groups".
Deux jeux de barres horizontales en miroir de part et d'autre d'un axe catégoriel commun, à partir des deux premières entrées de series. Alias : "pyramid", "age_pyramid".
Variante"population_pyramid"Requislabels, series (≥ 2), series_namesRetourneChart
Aperçu
Barres horizontales partant d'une ligne zéro vers la gauche ou la droite, une seule couleur peu importe le signe, valeur imprimée à l'intérieur de la barre (blanc) si assez large, sinon juste à l'extérieur. Alias : "signed", "delta", "bidirectional". L'affichage de la valeur est piloté par show_values (bool) comme pour toutes les autres variantes de bar — il vaut True par défaut ici pour préserver l'apparence existante, contrairement aux autres variantes où il vaut False par défaut.
Fusion bar + boxplot : une barre semi-transparente jusqu'à la moyenne de chaque catégorie, avec une vraie boîte (Q1/médiane/Q3, moustaches) superposée montrant la distribution derrière cette moyenne — passez series comme un tableau d'échantillons bruts par catégorie (même forme que l'entrée groupée de boxplot) au lieu de valeurs agrégées uniques. Alias : "bar_box", "boxbar", "bar_boxplot".
sp.line() is the unified entry point for the entire line-chart family. The variant keyword selects the rendering strategy — every other argument is shared across variants.
sp.line() est le point d'entrée unifié pour toute la famille de graphiques en ligne. Le mot-clé variant sélectionne la stratégie de rendu — tous les autres arguments sont partagés entre les variantes.
sp.area() is the unified entry point for the entire area-chart family. The variant keyword selects the rendering strategy — every other argument keeps the same name across variants. Area charts fill the space between a line and the baseline, making the emphasis on cumulative magnitude rather than the line chart's point-to-point comparison. SeraPlot renders everything in pure Rust SVG with native multi-series overlay, stacking, 100%-normalized stacking, smooth splines, step interpolation and gradient fills.
Each series drawn as an independent semi-transparent filled area from its own value down to the baseline - overlapping regions blend visually, useful to compare overlapping magnitudes directly.
Step-interpolated boundary - the fill jumps at each data point instead of interpolating, correct for values that hold constant between samples (inventory levels, active counts...).
100%-stacked area with bold black borders around every band instead of colored ones - the ggplot2 geom_area look, ideal for many groups (works well beyond two) where the outline is what separates adjacent bands visually.
Preview
Variant"wave"Aliaseswave / signed / oscillating / stackplotReturnsChart
True signed stacking - unlike stacked, negative values are never clamped to zero, so oscillating series (sin/cos-like data crossing above and below the baseline) stack correctly on both sides of zero. Matches matplotlib's stackplot() behavior for signed data.
sp.area() est le point d'entrée unifié de toute la famille des graphiques en aires. Le mot-clé variant sélectionne la stratégie de rendu — tous les autres arguments gardent le même nom d'une variante à l'autre. Les graphiques en aires remplissent l'espace entre une courbe et la ligne de base, mettant l'accent sur la magnitude cumulée plutôt que sur la comparaison point à point du graphique en ligne. SeraPlot rend tout en SVG Rust pur, avec superposition multi-séries native, empilement, empilement normalisé à 100%, courbes lissées, interpolation en escalier et remplissages en dégradé.
Chaque série dessinée comme une aire remplie semi-transparente indépendante depuis sa propre valeur jusqu'à la ligne de base - les zones qui se chevauchent se mélangent visuellement, utile pour comparer directement des magnitudes qui se recouvrent.
Aperçu
Variante"stacked"Aliasstacked / stackRetourChart
Séries dessinées les unes sur les autres pour que la frontière supérieure suive le total cumulé - lit à la fois la contribution individuelle et la magnitude combinée.
Frontière lissée par Catmull-Rom passant par chaque point au lieu de segments droits - une silhouette plus douce et organique pour les séries orientées tendance.
Frontière interpolée en escalier - le remplissage saute à chaque point de donnée au lieu d'interpoler, correct pour des valeurs constantes entre échantillons (niveaux de stock, compteurs actifs...).
Frontière lissée remplie d'un dégradé vertical s'estompant de la couleur de la série vers la transparence à la ligne de base - un look de tableau de bord moderne.
Aire empilée à 100% avec des bordures noires marquées autour de chaque bande au lieu de bordures colorées - le look geom_area de ggplot2, idéal pour de nombreux groupes (fonctionne bien au-delà de deux) où le contour est ce qui sépare visuellement les bandes adjacentes.
Aperçu
Variante"wave"Aliaswave / signed / oscillating / stackplotRetourChart
Empilement signé véritable - contrairement à stacked, les valeurs négatives ne sont jamais ramenées à zéro, si bien que des séries oscillantes (type sinus/cosinus traversant la ligne de base) s'empilent correctement des deux côtés de zéro. Reproduit le comportement de stackplot() de matplotlib pour des données signées.
sp.scatter() is the unified entry point for the entire scatter family. The variant keyword selects the rendering strategy — every other argument keeps the same name across variants. Scatter plots are the canonical way to display the joint distribution of two numeric variables; SeraPlot adds optional grouping, continuous color, distinct marker shapes, on-point labels and OLS regression — all in pure Rust SVG, thousands of times faster than Plotly.
Points colored by a continuous numeric variable (color_values) interpolated between color_low and color_high, with a gradient legend bar - seaborn's numeric hue mapping.
Splits the data into one small-multiple panel per unique categories value, all sharing the same x/y domain - a native equivalent of seaborn's relplot() faceting.
Both marker radius and color are driven by the same continuous variable (color_values, scaled between min_size and max_size) with a combined size+color legend - seaborn's hue= and size= mapped to the same column, with sizes=(min, max).
Plots several numeric columns (series, named via series_names) against one shared x_values axis, one color per column with a legend - the native equivalent of calling seaborn.scatterplot(data=wide_dataframe) directly on a wide-form table.
sp.scatter() est le point d'entrée unifié de toute la famille scatter. Le mot-clé variant sélectionne la stratégie de rendu — tous les autres arguments gardent le même nom d'une variante à l'autre. Les nuages de points sont la façon canonique d'afficher la distribution conjointe de deux variables numériques ; SeraPlot ajoute groupement optionnel, couleur continue, formes de marqueurs distinctes, étiquettes sur les points et régression OLS — le tout en SVG Rust pur, des milliers de fois plus rapide que Plotly.
Deux variables catégorielles indépendantes : categories pilote la couleur, categories2 pilote la forme du marqueur - comme l'exemple seaborn "hue and style" avec des variables différentes.
Points colorés selon une variable numérique continue (color_values) interpolée entre color_low et color_high, avec une barre de légende en dégradé - le mapping de teinte numérique de seaborn.
Sépare les données en un panneau petit-multiple par valeur unique de categories, tous partageant le même domaine x/y - un équivalent natif du facettage relplot() de seaborn.
Le rayon du marqueur et sa couleur sont pilotés par la même variable continue (color_values, mise à l'échelle entre min_size et max_size) avec une légende combinée taille+couleur - l'équivalent de hue= et size= pointant sur la même colonne en seaborn, avec sizes=(min, max).
Trace plusieurs colonnes numériques (series, nommées via series_names) sur un même axe x_values partagé, une couleur par colonne avec une légende - l'équivalent natif d'appeler seaborn.scatterplot(data=wide_dataframe) directement sur un tableau au format large.
sp.bubble() is the unified entry point for the entire bubble-chart family. The variant keyword selects the rendering strategy — all other arguments remain consistent across variants. A bubble chart extends a 2D scatter plot with a third numeric dimension represented as bubble area (not radius), following best practices for perceptual accuracy.
A categorical bubble matrix: x_categories and y_categories place bubbles on a grid, each split into a left/right half-circle by a binary categories value, sized by sizes - a native version of matplotlib's MarkerStyle(fillstyle="left"/"right") split-marker bubble chart, plus a magenta size-scale legend column.
sp.bubble() est le point d'entrée unique de toute la famille des graphiques à bulles. Le paramètre variant choisit la stratégie de rendu — tous les autres arguments restent cohérents entre variantes. Une bulle représente une troisième dimension via son aire (et non son rayon), pour une lecture perceptive correcte.
Une matrice de bulles catégorielle : x_categories et y_categories placent les bulles sur une grille, chacune divisée en demi-cercle gauche/droite selon une valeur binaire de categories, dimensionnée par sizes - une version native du graphique à bulles à marqueurs divisés MarkerStyle(fillstyle="left"/"right") de matplotlib, plus une colonne de légende de taille en magenta.
sp.histogram() is the unified entry point for the entire histogram family. The variant keyword selects the rendering strategy — every other argument keeps the same name across variants. Histograms are the canonical way to visualize the distribution of a single numeric variable; SeraPlot adds horizontal layout, density normalization, cumulative distribution, stacked groups, A/B overlay and step outline — all in pure Rust SVG, thousands of times faster than Plotly.
Pass theme= to restyle any variant above — themes are a cross-cutting rendering
pass, not a separate variant, and apply the same way across every chart family.
sp.histogram() est le point d'entrée unifié de toute la famille histogramme. Le mot-clé variant sélectionne la stratégie de rendu — tous les autres arguments gardent le même nom d'une variante à l'autre. Les histogrammes sont la façon canonique de visualiser la distribution d'une variable numérique ; SeraPlot ajoute layout horizontal, normalisation densité, distribution cumulative, groupes empilés, superposition A/B et contour en escalier — le tout en SVG Rust pur, des milliers de fois plus rapide que Plotly.
Passez theme= pour restyliser n'importe quelle variante ci-dessus — les thèmes sont
une passe de rendu transversale, pas une variante séparée, et s'appliquent de la même
façon sur toutes les familles de graphiques.
sp.heatmap() is the unified entry point for the entire heatmap family. The variant keyword selects the rendering strategy — every other argument stays consistent across variants. Cell colors are computed in pure Rust, no NumPy required. The matrix is passed as a flat list of length len(labels) * len(col_labels) (row-major).
Rows and columns are reordered by average-linkage hierarchical clustering (Euclidean distance on each row/column vector) so similar rows/columns sit next to each other, and the merge tree is drawn as a real dendrogram in the left and top margins — seaborn's clustermap / structured_heatmap look, fully native.
sp.heatmap() est le point d'entrée unique pour toute la famille des cartes de chaleur. Le paramètre variant sélectionne la stratégie de rendu — annotée, catégorielle, log, contour, clustering hiérarchique, etc. — tout en partageant la même API de base.
Les lignes et colonnes sont réordonnées par classification hiérarchique average-linkage (distance euclidienne sur chaque vecteur ligne/colonne) pour que les lignes/colonnes similaires se retrouvent côte à côte, et l'arbre de fusion est dessiné comme un vrai dendrogramme dans les marges gauche et haute — le look clustermap / structured_heatmap de seaborn, entièrement natif.
sp.pie() is the unified entry point for the entire pie-chart family. The variant keyword selects the rendering strategy — all other arguments remain consistent across variants.
Pass labeled=True to any variant built on the shared angle-sweep engine (basic, donut, exploded, kpi, pattern, semi) to replace the in-wedge percentage text with an outside callout: a thin connector line from the wedge edge to a label showing the percentage and the category name, colored to match its slice. This is the same style used by the reference "total breakdown" donut screenshots — it is not a separate variant, it is a display option any of those variants can turn on.
The "square pie" — a 10x10 grid of 100 cells filled proportionally to each category's share (largest-remainder allocation, so the cell count always sums to exactly 100), with a color-matched legend. Easier to read precise percentages from than wedge angles.
sp.pie() est le point d'entrée unique pour toute la famille des camemberts. Le paramètre variant sélectionne la stratégie de rendu — donut, éclaté, KPI, semi-cercle ou imbriqué — tout en conservant la même API simple.
Passe labeled=True à n'importe quelle variante construite sur le moteur partagé à secteurs angulaires (basic, donut, exploded, kpi, pattern, semi) pour remplacer le texte de pourcentage à l'intérieur du secteur par une étiquette extérieure : une fine ligne de connexion depuis le bord du secteur jusqu'à un label affichant le pourcentage et le nom de catégorie, colorée comme sa part. C'est le même style que les captures "répartition totale" en donut données en référence — ce n'est pas une variante séparée, c'est une option d'affichage que n'importe laquelle de ces variantes peut activer.
Le « pie carré » — une grille 10x10 de 100 cellules remplies proportionnellement à la part de chaque catégorie (allocation par plus grand reste, la somme des cellules vaut donc toujours exactement 100), avec une légende assortie aux couleurs. Plus facile à lire précisément que des angles de camembert.
sp.boxplot() is the unified entry point for the entire box-plot family. The variant keyword selects the rendering strategy — every other argument stays consistent across variants. Quartiles, 1.5×IQR whiskers and outliers are computed in pure Rust without NumPy or pandas.
sp.boxplot() est le point d'entrée unique pour toute la famille des boîtes à moustaches. Le paramètre variant sélectionne la stratégie de rendu — tous les autres arguments restent identiques entre les variantes. Quartiles, moustaches 1,5×IQR et valeurs aberrantes sont calculés en pur Rust, sans NumPy ni pandas.
sp.violin() is the unified entry point for the entire violin-plot family. The variant keyword selects the rendering strategy — every other argument stays consistent across variants. The kernel-density estimation, quartiles and statistics are computed in pure Rust, no NumPy or pandas required.
Raincloud layout - a translucent half-violin KDE silhouette on the left of each category paired with individual jittered points strictly on the right, so the density shape and the raw sample are both visible without ever overlapping.
sp.violin() est le point d'entrée unique pour toute la famille des violons. Le paramètre variant sélectionne la stratégie de rendu — tous les autres arguments restent identiques entre les variantes. L'estimation de densité par noyau (KDE), les quartiles et les statistiques sont calculés en pur Rust, sans NumPy ni pandas.
Disposition raincloud - une silhouette KDE translucide en demi-violon à gauche de chaque catégorie associée à des points individuels dispersés strictement à droite, pour que la forme de densité et l'échantillon brut soient tous deux visibles sans jamais se chevaucher.
sp.kde() is the unified entry point for the entire Kernel Density Estimate family. The variant keyword selects the rendering strategy — every other argument keeps the same name across variants. KDE produces a smooth, continuous density estimate from a sample of points using a Gaussian kernel with Scott's rule for automatic bandwidth selection. SeraPlot renders the curves as pure Rust SVG, with native multi-series, normalization, CDF, rug, histogram overlay and gradient fills.
Bivariate (2D) kernel density estimate — a product-kernel Gaussian evaluated on a grid and rendered as a shaded density surface with the raw points overlaid. Pass categories to overlay one density surface per group, each in its own color with a legend.
Bivariate KDE quantized into discrete iso-density bands with a visible ring border at each band boundary — matches seaborn's kdeplot(x=, y=, hue=, fill=True) stepped-level look, as opposed to contour's continuous gradient.
Each group's density curve stacked on top of the previous one's (cumulative running total), all evaluated on one shared x-grid - matches seaborn's kdeplot(hue=, multiple="stack").
100%-stacked density - at every x position the groups sum to 100%, showing the changing share of the total instead of absolute density - matches seaborn's kdeplot(hue=, multiple="fill").
sp.kde() est le point d'entrée unifié pour toute la famille KDE (Kernel Density Estimate). Le mot-clé variant sélectionne la stratégie de rendu — tous les autres arguments conservent le même nom d'une variante à l'autre. La KDE produit une estimation de densité continue lissée à partir d'un échantillon de points avec un noyau gaussien et la règle de Scott pour le choix automatique de la bande passante. SeraPlot rend les courbes en SVG Rust natif, avec multi-séries, normalisation, CDF, rug, histogramme superposé et remplissage en dégradé.
Estimation de densité bivariée (2D) — un noyau gaussien produit évalué sur une grille et rendu comme une surface de densité ombrée, avec les points bruts superposés. Passez categories pour superposer une surface de densité par groupe, chacune dans sa propre couleur avec une légende.
KDE bivariée quantifiée en bandes iso-densité discrètes avec une bordure visible à chaque frontière de bande — reproduit le rendu par niveaux de kdeplot(x=, y=, hue=, fill=True) de seaborn, par opposition au dégradé continu de contour.
La courbe de densité de chaque groupe est empilée sur celle du précédent (total cumulé), toutes évaluées sur une même grille x partagée - reproduit kdeplot(hue=, multiple="stack") de seaborn.
Densité empilée à 100% - à chaque position x, les groupes totalisent 100%, montrant la part changeante du total plutôt que la densité absolue - reproduit kdeplot(hue=, multiple="fill") de seaborn.
sp.ridgeline() is the unified entry point for the entire ridgeline family — also known as joyplot. The variant keyword selects the rendering strategy — every other argument keeps the same name across variants. A ridgeline plot stacks one KDE curve per category along a shared X axis with a controllable vertical overlap, making it ideal to compare distributions across many groups (years, regions, segments…). SeraPlot renders everything in pure Rust SVG, with quartile/mean overlays, rug ticks, gradient fills and a built-in viridis colormap. priority takes a list of row indices (in their displayed order) to draw last, on top of every other ridge - use it to force a specific distribution to visually climb over its neighbors regardless of its position in the stack.
sp.ridgeline() est le point d'entrée unifié pour toute la famille ridgeline — aussi appelé joyplot. Le mot-clé variant sélectionne la stratégie de rendu — tous les autres arguments conservent le même nom d'une variante à l'autre. Un ridgeline empile une courbe KDE par catégorie sur un axe X partagé avec un recouvrement vertical réglable, idéal pour comparer des distributions à travers plusieurs groupes (années, régions, segments…). SeraPlot rend tout en SVG Rust natif, avec marqueurs quartiles/moyenne, ticks rug, dégradés et palette viridis intégrée. priority prend une liste d'index de lignes (dans leur ordre affiché) à dessiner en dernier, par-dessus toutes les autres crêtes - utile pour forcer une distribution donnée à visuellement monter sur ses voisines quelle que soit sa position dans l'empilement.
sp.radar() is the unified entry point for the entire radar / spider / star chart family. The variant keyword selects the rendering strategy — every other argument keeps the same name across variants. Radar charts are ideal for multivariate comparison across 3+ axes — performance profiles, KPIs, skill maps, scoring systems. SeraPlot draws everything in pure Rust SVG with concentric grid rings, axis lines, automatic ring tick labels, optional legend and per-series palette colors. The polar-bar variant turns the chart into a categorical polar histogram, the stacked variant builds a cumulative composition view.
sp.radar() est le point d'entrée unifié pour toute la famille radar / spider / star. Le mot-clé variant sélectionne la stratégie de rendu — tous les autres arguments conservent le même nom d'une variante à l'autre. Le radar est idéal pour comparer plusieurs séries sur 3 axes ou plus — profils de performance, KPI, cartographie de compétences, systèmes de notation. SeraPlot dessine tout en SVG Rust natif avec anneaux de grille concentriques, axes, labels automatiques de graduation, légende optionnelle et couleurs de palette par série. La variante polar_bar transforme le radar en histogramme polaire catégoriel, la variante stacked construit une vue de composition cumulative.
sp.slope() renders the entire slope-chart family: two parallel value axes (left / right) with one connector per row. The variant keyword swaps the connector style without changing any other parameter. Slope charts excel at before/after comparisons, A/B test outcomes, ranking shifts, KPI changes between periods, and any pair-wise change across many entities.
sp.slope() produit toute la famille des slope charts : deux axes de valeurs parallèles (gauche / droite) avec un connecteur par ligne. Le mot-clé variant permute le style du connecteur sans changer aucun autre paramètre. Idéal pour comparer avant/après, résultats A/B, changements de classement, KPI entre périodes, et toute évolution par paire sur de nombreuses entités.
sp.funnel() renders the entire funnel-chart family: a stacked sequence of stages where each step’s width encodes a value. The variant keyword switches the geometry without changing any other parameter. Funnels are the standard for conversion analytics (visitors → signups → paid), recruiting pipelines, sales pipelines, process drop-off and any descending-cohort analysis.
Basic trapezoid plus drop-off percentage between consecutive stages displayed in red.
Preview
Variant"compare"Aliasescompare / multi / side_by_side / funnelsRequiredseriesOptionalseries_names, category_series, text_infoReturnsChart
Several independent funnels side by side, each with its own stage count and its own scale - the native equivalent of composing multiple go.Funnel() traces in Plotly, without ever building a trace by hand. Pass series (one value list per funnel), series_names (funnel names) and category_series (one stage-label list per funnel, since funnels can have different stage counts). Automatically selected whenever series has more than one entry and category_series is given - use "grouped" instead when every group shares the same stages. text_info combines "value", "percent_initial", "percent_previous" and "percent_total" with +.
One funnel, several colored groups sharing the exact same stages - each stage's band splits into adjacent, touching segments (no gap) whose widths are proportional to each group's own value on one shared linear scale, so the combined shape tapers naturally like a single funnel. The native equivalent of px.funnel(df, x="number", y="stage", color="office") in Plotly - works with any number of groups, not just two. Auto-selected whenever series has more than one entry and category_series is not given.
sp.funnel() produit toute la famille des entonnoirs : une séquence d’étapes empilées dont la largeur encode une valeur. Le mot-clé variant permute la géométrie sans changer aucun autre paramètre. Standard pour l’analyse de conversion (visiteurs → inscrits → payants), pipelines de recrutement, pipelines commerciaux, fuites de processus et toute analyse de cohorte décroissante.
Trapèze de base avec le pourcentage de chute entre étapes affiché en rouge.
Preview
Variante"compare"Aliascompare / multi / side_by_side / funnelsRequisseriesOptionnelseries_names, category_series, text_infoRetourChart
Plusieurs entonnoirs indépendants côte à côte, chacun avec son propre nombre d'étapes et sa propre échelle - l'équivalent natif de composer plusieurs traces go.Funnel() en Plotly, sans jamais construire de trace à la main. Passez series (une liste de valeurs par entonnoir), series_names (noms des entonnoirs) et category_series (une liste d'étiquettes d'étapes par entonnoir, puisque les entonnoirs peuvent avoir des nombres d'étapes différents). Sélectionné automatiquement dès que series a plus d'une entrée et que category_series est fourni - utilisez "grouped" quand tous les groupes partagent les mêmes étapes. text_info combine "value", "percent_initial", "percent_previous" et "percent_total" avec +.
Un seul entonnoir, plusieurs groupes colorés partageant exactement les mêmes étapes - la bande de chaque étape se divise en segments adjacents et jointifs (sans espace) dont les largeurs sont proportionnelles à la valeur de chaque groupe sur une même échelle linéaire partagée, si bien que la forme combinée se rétrécit naturellement comme un seul entonnoir. L'équivalent natif de px.funnel(df, x="number", y="stage", color="office") en Plotly - fonctionne avec n'importe quel nombre de groupes, pas seulement deux. Sélectionné automatiquement dès que series a plus d'une entrée et que category_series n'est pas fourni.
sp.waterfall() renders the entire waterfall-chart family: a sequence of bars where each step adds (positive) or subtracts (negative) from a running total. The variant keyword selects the geometry without touching any other parameter. Waterfalls are the standard for P&L bridges, variance analysis, cohort decomposition, fee/tax breakdowns and any "from A to B, what changed?" narrative.
Totals — set a value to 0 and use a label containing total, net, final, gross or ebitda to mark a subtotal bar; it is rendered with the totals color and anchored on the running sum.
Rotated 90 degrees: each step becomes a horizontal row stacking downward, anchored to the previous running total. Editorial layout for reports with long labels or vertical storytelling.
sp.waterfall() rassemble toute la famille des graphiques waterfall : une suite de barres ou chaque etape ajoute (positif) ou retranche (negatif) au cumul courant. Le mot-cle variant change la geometrie sans toucher aux autres parametres. Les waterfalls sont la reference pour les ponts de P&L, l analyse d ecarts, la decomposition de cohortes, le detail des frais/taxes et tout recit du type "de A vers B, qu est-ce qui a change ?".
Totaux — mettez la valeur a 0 et utilisez un libelle contenant total, net, final, gross ou ebitda pour marquer une barre de sous-total ; elle est rendue avec la couleur des totaux et ancree sur le cumul courant.
Waterfall pivote a 90 degres : les etapes deviennent des lignes empilees verticalement, chacune partant du cumul precedent. Layout editorial pour rapports avec libelles longs ou storytelling vertical.
sp.sunburst() is the unified entry point for the entire sunburst-chart family. A sunburst represents a hierarchy as concentric rings: the innermost ring is the root, each outer ring is a deeper level, and angular size encodes value. The variant keyword selects the visual style without changing any other parameter. Sunbursts are the standard for visualizing nested taxonomies (org charts, file systems, market segmentation, expense categories, phylogenetic trees) and outperform classic pie charts as soon as a real hierarchy exists.
Hierarchy encoding — labels lists every node, parents gives the parent label of each node ("" for a root). Leaf values are taken from values; internal node values are auto-rolled-up from descendants when set to 0.
sp.sunburst() est le point d entree unifie pour toute la famille des graphiques sunburst. Un sunburst represente une hierarchie sous forme d anneaux concentriques : l anneau interieur est la racine, chaque anneau exterieur est un niveau plus profond, et l angle code la valeur. Le mot-cle variant change le style sans toucher aux autres parametres. Les sunbursts sont la reference pour visualiser des taxonomies imbriquees (organigrammes, systemes de fichiers, segmentation marche, categories de depenses, arbres phylogenetiques) et surpassent le camembert des qu une vraie hierarchie existe.
Encodage de la hierarchie — labels liste tous les noeuds, parents donne le libelle du parent de chaque noeud ("" pour une racine). Les valeurs des feuilles viennent de values ; les noeuds internes a 0 sont calcules automatiquement comme la somme de leurs descendants.
sp.treemap() is the unified entry point for the entire treemap-chart family. A treemap divides a rectangle into proportional sub-rectangles whose area encodes value; when a parents list is given the layout becomes hierarchical (each parent gets its own block, leaves are squarified within). The variant keyword switches the visual style without touching the data. Treemaps are the standard for visualizing budgets, market cap, disk usage, portfolio weights, file systems and any 'whole = sum of parts' breakdown.
Hierarchical mode — pass parents (one parent label per leaf, can be empty string "" for a flat treemap). Internal totals are auto-computed from leaves. Sort leaves with the sort_order parameter ("desc" recommended).
sp.treemap() est le point d entree unifie pour toute la famille treemap. Un treemap decoupe un rectangle en sous-rectangles proportionnels dont l aire code la valeur ; lorsqu une liste parents est fournie le rendu devient hierarchique (chaque parent recoit son propre bloc, les feuilles y sont squarifiees). Le mot-cle variant change le style sans toucher aux donnees. Les treemaps sont la reference pour visualiser budgets, capitalisations boursieres, occupation disque, poids de portefeuille, systemes de fichiers et toute decomposition 'tout = somme des parties'.
Mode hierarchique — passez parents (un libelle parent par feuille, chaine vide "" pour un treemap plat). Les totaux internes sont auto-calcules. Triez les feuilles avec sort_order ("desc" recommande).
sp.candlestick() is the unified entry point for the entire candlestick-chart family. A candlestick chart shows OHLC (Open, High, Low, Close) bars over time and is the de facto standard for financial markets, crypto, commodities, energy spot prices and any time-series with intra-period spread. The variant keyword switches the visual style without touching the data — including derived views like Heikin-Ashi smoothing, close-only line, mountain area and high-low range bars.
Color convention — by default green = up (close >= open) and red = down. Override with palette=[up_color, down_color]. Bars are rendered left-to-right in input order; use sort_order="asc" to sort by close price.
Candlestick + volume histogram fusion — the standard crypto/trading terminal layout. Candles fill the upper ~78% of the plot; a volume bar strip (colored by that period's up/down candle) fills the remaining band beneath, sharing the same x-axis. Pass a volume array alongside open/high/low/close.
sp.candlestick() est le point d entree unifie pour toute la famille des chandeliers. Un graphique en chandeliers affiche des barres OHLC (Ouverture, Haut, Bas, Cloture) dans le temps et constitue le standard de fait pour les marches financiers, la crypto, les matieres premieres, le spot energie et toute serie temporelle avec spread intra-periode. Le mot-cle variant change le style sans toucher aux donnees — y compris des vues derivees comme le lissage Heikin-Ashi, la ligne de cloture, l aire mountain et les barres haut-bas.
Convention de couleur — par defaut vert = hausse (close >= open) et rouge = baisse. Surchargez avec palette=[couleur_hausse, couleur_baisse]. Les barres sont rendues de gauche a droite dans l ordre d entree ; sort_order="asc" pour trier par prix de cloture.
Fusion chandelier + histogramme de volume — la mise en page standard des terminaux crypto/trading. Les chandeliers occupent les ~78% superieurs du graphique ; une bande de barres de volume (colorees selon la hausse/baisse de la periode) occupe le reste, partageant le meme axe x. Passez un tableau volume en plus de open/high/low/close.
sp.dumbbell() is the unified entry point for the dumbbell-chart family. Each row plots two values - typically a before and an after - linked by a connector, making it the chart of choice for change, gap or comparison-over-time analyses (salary equity, turnaround KPIs, A/B uplifts, etc.). The variant keyword switches the visual treatment without touching the data.
sp.dumbbell() est le point d entree unique pour la famille dumbbell. Chaque ligne montre deux valeurs - typiquement avant/apres - reliees par un connecteur, ce qui en fait le choix naturel pour visualiser un changement, un ecart ou une evolution (equite salariale, KPIs de redressement, uplifts A/B, etc.). Le mot-cle variant change le style visuel sans toucher aux donnees.
sp.bullet() is the unified entry point for the bullet-chart family. Inspired by Edward Tufte, a bullet packs an actual value, a target, qualitative ranges and a scale into a single horizontal row - perfect for KPI dashboards where space is precious. The variant keyword switches the visual treatment (zones, traffic light, thermometer, progress pill, dot, ghost-bar comparison) without touching the data.
sp.bullet() est le point d entree unique pour la famille bullet. Inspire par Edward Tufte, le bullet condense valeur, cible, zones qualitatives et echelle dans une seule ligne horizontale - parfait pour des dashboards KPIs serres. Le mot-cle variant change l aspect (zones, feu tricolore, thermometre, pillule de progression, point, comparaison par barre fantome) sans toucher aux donnees.
sp.gauge() is the unified entry point for the gauge family. A gauge maps a single scalar to a colored arc with optional thresholds - perfect for status / health / utilization KPIs. The variant keyword switches the geometry (half, three-quarter, full ring), the embellishments (needle, ticks, glow) and the layering (single arc vs. concentric arcs for value-vs-target).
sp.gauge() est le point d entree unique pour la famille jauge. Une jauge associe un scalaire unique a un arc colore avec des seuils optionnels - parfait pour des KPIs de statut / sante / utilisation. Le mot-cle variant change la geometrie (demi, trois-quart, anneau complet), les ornements (aiguille, ticks, glow) et la composition (arc simple ou arcs concentriques pour valeur-vs-cible).
sp.lollipop() is the unified entry point for the lollipop family. Each item becomes a thin stick capped by a dot - lighter ink than a bar chart for the same ranking, and the family includes circular, diverging and grouped editorial layouts (the Office variant reproduces the season-rating panel pattern). To spotlight a single point on any variant, chain the generic .highlight(index) method instead of picking a dedicated variant for it.
Sticks colored by a continuous diverging colormap (magnitude from zero), a smoothed moving-average trend line, and an arrow annotation on the most extreme point - matching the "lollipop with colormap and arrow" gallery recipe.
sp.lollipop() est le point d entree unique pour la famille lollipop. Chaque item devient un baton fin termine par un point - moins d encre qu un bar chart pour le meme classement, et la famille couvre des layouts circulaires, divergents et editoriaux groupes (la variante Office reproduit le motif des saisons IMDb de The Office). Pour mettre en avant un seul point sur n'importe quelle variante, chainez la methode generique .highlight(index) plutot que de choisir une variante dediee.
Batons horizontaux pivotant sur zero, colores selon le seul signe (orange ≥ 0, bleu ciel < 0) - la recette "lollipop with conditional color" de la galerie seaborn.
Batons colores par un degrade divergent continu (magnitude depuis zero), une courbe de tendance lissee, et une fleche d'annotation sur le point le plus extreme - la recette "lollipop with colormap and arrow".
sp.build_parallel() renders a parallel-coordinates chart - one vertical axis per dimension, one polyline per row. Six variants cover the classical use cases: straight lines, smooth Bezier curves, categorical coloring, single-row highlight, density-blended overlay, and gradient coloring driven by any axis. Perfect for high-dimensional EDA, profile comparison, and class separability inspection.
Smooth cubic bezier curves with per-series gradient coloring. Reduces visual clutter compared to straight lines while preserving individual series identity.
Preview
Variant"ribbon"Aliasesribbon / flow / band / filled_bezierReturnsChart
Filled bezier bands between adjacent axes. Each series is rendered as a translucent ribbon + thin solid stroke, creating a flowing Sankey-lite effect.
sp.build_parallel() rend un graphique parallel-coordinates - un axe vertical par dimension, une polyligne par ligne. Six variantes couvrent les cas classiques : lignes droites, courbes Bezier, couleur par categorie, mise en avant d une ligne, overlay de densite, et degrade pilote par un axe. Ideal pour l EDA haute-dimension, la comparaison de profils, et l inspection de separabilite de classes.
Courbes de Bezier cubiques avec degrade de couleur par serie. Reduit l encombrement visuel par rapport aux lignes droites tout en preservant l identite de chaque serie.
Apercu
Variante"ribbon"Aliasribbon / flow / band / filled_bezierRetourneChart
Bandes de Bezier remplies entre les axes adjacents. Chaque serie est rendue comme un ruban translucide + trait fin, creant un effet de flux style Sankey simplifie.
sp.build_wordcloud() packs weighted tokens into six rendering architectures. Basic is the canonical spiral packer driven by a parametric shape= mask (rect, circle, heart, bird, glasses, diamond, star). Bubble gives each word its own color-filled disc sized by frequency - a packed-bubble layout. Context is an InfraNodus-style text-network cloud: words positioned by a force-directed layout driven by co-occurrence edges so semantically close words cluster spatially, colored by community. Image accepts any binary pixel mask (logo, icon, photo). LabelMap draws a datamapplot-style clustered scatter with leader-line labels. Network renders a keyword co-occurrence graph with bezier-curved edges.
Text-network word cloud (InfraNodus style): words positioned by force-directed layout based on co-occurrence edges, colored by semantic cluster - semantically close words appear spatially close.
Words placed on a phyllotaxis (sunflower-seed) spiral on a starfield background, largest/most frequent words nearest the center — a constellation-like layout instead of a packed rectangle.
sp.build_wordcloud() propose six architectures de rendu. Basic est le packer spirale canonique pilote par un masque shape= (rect, circle, heart, bird, glasses, diamond, star). Bubble donne a chaque mot un disque colore dimensionne par frequence - un layout bubble-packed. Context est un nuage texte-reseau style InfraNodus : mots positionnes par layout force-dirige base sur les aretes de co-occurrence, colores par communaute. Image accepte n importe quel masque binaire de pixels. LabelMap dessine un scatter clusterise style datamapplot avec etiquettes en lignes de rappel. Network rend un graphe de co-occurrence de mots-cles avec aretes bezier.
Chaque mot obtient un disque colore dimensionne par frequence - un nuage de bulles pack. Les mots flottent dans des cercles etiquetes, sans chevauchement.
Nuage de mots texte-reseau (style InfraNodus) : mots positionnes par layout force-dirige base sur les aretes de co-occurrence, colores par cluster semantique - les mots proches semantiquement sont proches spatialement.
Carte thematique style datamapplot - scatter de points clusterises colores par categorie, avec etiquettes de cluster positionnees via lignes de rappel.
Graphe de co-occurrence de mots-cles - layout en angle d or, aretes courbes bezier, cercles dimensionnes par frequence. Style editorial des cartes de mots-cles academiques.
Nuage de mots style reseau de neurones sur fond sombre. Les mots deviennent des noeuds lumineux relies par des aretes fines evoquant un graphe synaptique.
Mots places sur une spirale de phyllotaxie (graines de tournesol) sur fond etoile, les mots les plus frequents pres du centre - une disposition en constellation plutot qu'un rectangle compact.
Sankey diagrams visualize flows between nodes. Node widths and link widths are proportional to flow volumes. Edges are defined by source indices (edges_i), target indices (edges_j), and weights (edges_w). Nodes are laid out in columns by BFS depth.
Reorders nodes within each depth column by descending total throughput, so the dominant flows cluster together instead of sitting in input order — makes it easy to spot which nodes carry the most volume.
Les diagrammes de Sankey visualisent des flux entre nœuds. La largeur des nœuds et des liens est proportionnelle au volume du flux. Les arêtes sont définies par des indices source (edges_i), des indices cible (edges_j), et des poids (edges_w). Les nœuds sont disposés en colonnes par profondeur BFS.
labels (list[str]) — Noms des nœuds. edges_i (list[int]) — Indices des nœuds source. edges_j (list[int]) — Indices des nœuds cible. edges_w (list[float]) — Poids des flux. width / height (int) — Dimensions du graphique.
Réordonne les nœuds de chaque colonne de profondeur par débit total décroissant, pour que les flux dominants se regroupent au lieu de rester dans l'ordre d'entrée — facilite le repérage des nœuds qui transportent le plus de volume.
Chord diagrams show relationships between entities using arcs and ribbons around a circle. The matrix is an N×N flow matrix where matrix[i][j] is the flow from node i to node j.
Draws a small arrowhead on each ribbon pointing toward whichever side receives more — reads the matrix's row/column asymmetry directly off the diagram instead of just the ribbon's natural taper.
Les diagrammes en accords (chord) montrent les relations entre entités à l'aide d'arcs et de rubans autour d'un cercle. matrix est une matrice de flux N×N où matrix[i][j] est le flux du nœud i vers le nœud j.
Trace une petite flèche sur chaque ruban pointant vers le côté qui reçoit le plus — lit l'asymétrie ligne/colonne de la matrice directement sur le diagramme plutôt que par le seul évasement naturel du ruban.
Circle packing represents hierarchical data as nested circles, where the area of each circle is proportional to its value. Parent–child relationships are defined by the parents list (empty string = root node).
Runs a real greedy circle-packing layout: each circle is placed tangent to the best pair of already-placed circles (falling back to a spiral search when no tangent placement fits), producing a genuinely nested, non-overlapping bubble cluster instead of the grid-like flat layout.
Variant"bubble"Aliasesbubble / packed
Preview
Fills only the leaf circles with color and shrinks every container circle to a faint dashed outline — declutters the hierarchy chrome so attention goes straight to the actual data points instead of the grouping structure.
Le circle packing représente des données hiérarchiques sous forme de cercles imbriqués, où l'aire de chaque cercle est proportionnelle à sa valeur. Les relations parent-enfant sont définies par la liste parents (chaîne vide = nœud racine).
labels (list[str]) — Noms des nœuds. parents (list[str]) — Nom du parent de chaque nœud ("" = racine). values (list[float]) — Taille de chaque nœud feuille. width / height (int) — Dimensions du graphique.
Cercles imbriqués pleins, opacité selon la profondeur
Variante"basic"Aliasbasic / default / nested
Aperçu
Disposition en bulles à un seul niveau (pas d'imbrication)
Variante"flat"Aliasflat / single
Aperçu
Cercles en contour seul
Variante"outlined"Aliasoutlined / stroke / border
Aperçu
Applique un véritable algorithme de tassement de cercles (« circle packing ») glouton : chaque cercle est placé tangent à la meilleure paire de cercles déjà posés (avec repli sur une recherche en spirale si aucun placement tangent ne convient), pour obtenir un amas de bulles réellement imbriqué et sans chevauchement, au lieu de la disposition en grille de la variante à plat.
Variante"bubble"Aliasbubble / packed
Aperçu
Ne remplit en couleur que les cercles feuilles et réduit chaque cercle conteneur à un fin contour pointillé — désencombre le chrome hiérarchique pour que l'attention aille directement aux points de données plutôt qu'à la structure de regroupement.
Arc diagrams place nodes on a horizontal axis and draw quadratic bezier arcs above (and optionally below) the axis to represent connections. They are particularly effective for showing sequential or ordered relationships.
Thin, low-opacity arcs with small strokeless nodes — strips away the visual weight so overlapping arcs stay legible in dense diagrams.
Variant"minimal"Aliasesminimal / thin / clean
Preview
Draws a small arrowhead where each arc lands on its target node — turns the diagram into a proper directed graph, e.g. for dependency or citation edges where "which way" matters as much as "how much".
Les diagrammes en arcs placent les nœuds sur un axe horizontal et tracent des arcs de bézier quadratiques au-dessus (et optionnellement en-dessous) de l'axe pour représenter les connexions. Ils sont particulièrement efficaces pour montrer des relations séquentielles ou ordonnées.
labels (list[str]) — Noms des nœuds. edges_i (list[int]) — Indices des nœuds source. edges_j (list[int]) — Indices des nœuds cible. edges_w (list[float]) — Poids des arêtes. width / height (int) — Dimensions du graphique.
Épaisseur du trait proportionnelle au poids de l'arête
Variante"weighted"Aliasweighted / width / value
Aperçu
Arcs fins et peu opaques, avec de petits nœuds sans contour — allège le rendu visuel pour que les arcs qui se chevauchent restent lisibles dans les diagrammes denses.
Variante"minimal"Aliasminimal / thin / clean
Aperçu
Trace une petite flèche là où chaque arc arrive sur son nœud cible — transforme le diagramme en véritable graphe orienté, par ex. pour des arêtes de dépendance ou de citation où le sens compte autant que la quantité.
Dendrograms display hierarchical tree structures using right-angle elbow connectors (vertical/horizontal) or smooth bezier curves (elegant) or a radial circular layout. Pass matrix -- one row of numeric coordinates per label -- for a genuine average-linkage agglomerative clustering with real merge heights (matching hclust/scipy), including automatic coloring of the top clusters groups with the trunk above the cut shown in neutral gray. Without matrix, parents describes a plain hand-specified hierarchy (each entry names its parent's label, empty string for a root) with no real distance semantics.
Variant"vertical"Aliasesvertical / top / default / classic
Preview
Root at left, leaves at right
Variant"horizontal"Aliaseshorizontal / left / h
Preview
Circular radial tree layout
Variant"radial"Aliasesradial / circular / polar
Preview
Tighter spacing, smaller font
Variant"compact"Aliasescompact / dense / tight
Preview
Smooth cubic bezier curves
Variant"elegant"Aliaseselegant / smooth / rounded
Preview
Connects parent to child with a single straight diagonal line — a third connector style alongside the right-angle elbows (vertical/horizontal) and smooth beziers (elegant).
Les dendrogrammes affichent des structures arborescentes hiérarchiques à l'aide de connecteurs en coude à angle droit (vertical/horizontal), de courbes de bézier lisses (elegant), ou d'une disposition radiale circulaire. Passez matrix -- une ligne de coordonnées numériques par label -- pour un vrai clustering hiérarchique par liaison moyenne avec des hauteurs de fusion réelles (comme hclust/scipy), avec coloration automatique des clusters groupes principaux et le tronc au-dessus de la coupe en gris neutre. Sans matrix, parents décrit une hiérarchie manuelle simple (chaque entrée nomme le label de son parent, chaîne vide pour une racine) sans réelle sémantique de distance.
Racine en haut, feuilles en bas (connecteurs en coude)
Variante"vertical"Aliasvertical / top / default / classic
Aperçu
Racine à gauche, feuilles à droite
Variante"horizontal"Aliashorizontal / left / h
Aperçu
Disposition arborescente radiale circulaire
Variante"radial"Aliasradial / circular / polar
Aperçu
Espacement resserré, police plus petite
Variante"compact"Aliascompact / dense / tight
Aperçu
Courbes de bézier cubiques lisses
Variante"elegant"Aliaselegant / smooth / rounded
Aperçu
Relie parent et enfant par une seule ligne diagonale droite — un troisième style de connecteur, aux côtés des coudes à angle droit (vertical/horizontal) et des courbes de bézier lisses (elegant).
Venn diagrams show set relationships using overlapping circles. Supply one value per set for circle sizes. The "euler" variant scales circle radii proportionally to the first N values.
Forces every circle into its stroke-only outline form regardless of the configured fill opacity, for a clean contour-only read of the set overlaps.
Variant"minimal"Aliasesminimal / outline / thin
Preview
Masks each circle down to the region that belongs to it alone and renders that region at full color, while shared/overlapping areas fade into the background — makes it obvious what's unique to each set versus what's shared.
Les diagrammes de Venn montrent des relations entre ensembles à l'aide de cercles qui se chevauchent. Fournissez une valeur par ensemble pour la taille des cercles. La variante "euler" met à l'échelle les rayons des cercles proportionnellement aux N premières valeurs.
labels (list[str]) — Noms des ensembles. values (list[float]) — Tailles des ensembles (les N premières entrées pilotent les rayons Euler). width / height (int) — Dimensions du graphique.
Aires de cercles proportionnelles (diagramme d'Euler)
Variante"euler"Aliaseuler / proportional / area
Aperçu
Cercles entièrement opaques
Variante"filled"Aliasfilled / solid / opaque
Aperçu
Force chaque cercle dans sa forme en contour seul, quelle que soit l’opacité de remplissage configurée, pour une lecture épurée des recouvrements d’ensembles en simples contours.
Variante"minimal"Aliasminimal / outline / thin
Aperçu
Masque chaque cercle jusqu'à la région qui lui appartient en propre et l'affiche en pleine couleur, tandis que les zones partagées/superposées s'estompent — rend évident ce qui est unique à chaque ensemble par rapport à ce qui est partagé.
A correlogram visualizes a correlation matrix as a grid. Each cell encodes the Pearson correlation coefficient (–1 to +1) using color (red = positive, blue = negative) and either circle area, square fill, or text. matrix is a nested N×N list — one inner list per row.
Every variant is really just a preset of three lower-level params you can mix freely on the base circle variant instead of picking a named one: cell_shape ("circle" | "square" | "ellipse" | "pie" | "number") controls how a single cell is drawn, cell_shape2 sets a second shape for the lower triangle when layout="mixed", and layout ("full" | "upper" | "lower" | "mixed") controls which half of the matrix gets filled — e.g. sp.correlogram(labels=..., matrix=..., cell_shape="ellipse", layout="upper").
Each cell is an ellipse tilted "/" for positive correlation, "\" for negative, flattening toward a line as |r| approaches 1 and toward a circle as it approaches 0.
Variant"ellipse"Aliasesellipse / oval
Preview
Upper triangle as pie wedges (wedge angle = |r|, color = sign), lower triangle as flat colored squares, diagonal left blank - the classic mixed correlogram layout, unsorted.
Variant"pie_square"Aliasespie_square / pie / mixed_pie
Preview
Upper-triangle-only circles (lower triangle and diagonal left blank) with a color/value legend bar alongside instead of numeric labels on the cells.
Un correlogramme visualise une matrice de corrélation sous forme de grille. Chaque cellule encode le coefficient de corrélation de Pearson (–1 à +1) à l'aide de la couleur (rouge = positif, bleu = négatif) et soit l'aire d'un cercle, soit le remplissage d'un carré, soit du texte. matrix est une liste imbriquée N×N — une liste interne par ligne.
labels (list[str]) — Noms des variables (longueur N). matrix (list[list[float]]) — Matrice de corrélation N×N, une ligne par liste interne. width / height (int) — Dimensions du graphique.
Chaque variante n'est en fait qu'un préréglage de trois paramètres de plus bas niveau que tu peux combiner librement sur la variante circle de base plutôt que d'en choisir une nommée : cell_shape ("circle" | "square" | "ellipse" | "pie" | "number") contrôle le dessin d'une cellule, cell_shape2 définit une seconde forme pour le triangle inférieur quand layout="mixed", et layout ("full" | "upper" | "lower" | "mixed") contrôle quelle moitié de la matrice est remplie — ex. sp.correlogram(labels=..., matrix=..., cell_shape="ellipse", layout="upper").
Chaque cellule est une ellipse inclinée "/" pour une corrélation positive, "\" pour une négative, s'aplatissant vers une ligne quand |r| approche 1 et vers un cercle quand |r| approche 0.
Variante"ellipse"Aliasellipse / oval
Aperçu
Triangle supérieur en camemberts (angle = |r|, couleur = signe), triangle inférieur en carrés pleins colorés, diagonale laissée vide - la disposition mixte classique, non triée.
Variante"pie_square"Aliaspie_square / pie / mixed_pie
Aperçu
Cercles uniquement dans le triangle supérieur (triangle inférieur et diagonale vides) avec une barre de légende couleur/valeur au lieu de libellés numériques sur les cellules.
Hive plots organize network nodes on radial axes by category. Each axis corresponds to one node group (axes). Node position along the axis is determined by values (0–1). Edges between nodes are drawn as straight or curved lines through the center.
Straight (uncurved), thin, low-opacity edges with small strokeless nodes — a decluttered reading of dense hive plots that trades the curved-edge chrome for raw connectivity.
Variant"minimal"Aliasesminimal / thin / clean
Preview
Draws a small arrowhead where each edge lands on its target node — hive plots are frequently used for directed graphs (network traffic, citations), and this makes the direction readable at a glance instead of implied.
Les hive plots organisent les nœuds d'un réseau sur des axes radiaux par catégorie. Chaque axe correspond à un groupe de nœuds (axes). La position d'un nœud le long de l'axe est déterminée par values (0–1). Les arêtes entre nœuds sont tracées en lignes droites ou courbes passant par le centre.
axes (list[str]) — Noms des axes (groupes). labels (list[str]) — Noms des nœuds. categories (list[str]) — Groupe assigné à chaque nœud. values (list[float]) — Position du nœud le long de l'axe (0–1). edges_i (list[int]) — Indices des nœuds source. edges_j (list[int]) — Indices des nœuds cible. edges_w (list[float]) — Poids des arêtes. width / height (int) — Dimensions du graphique.
Épaisseur du trait proportionnelle au poids de l'arête
Variante"weighted"Aliasweighted / width / value
Aperçu
Arêtes droites (non courbées), fines et peu opaques, avec de petits nœuds sans contour — une lecture épurée des hive plots denses qui sacrifie le chrome des arêtes courbées au profit de la connectivité brute.
Variante"minimal"Aliasminimal / thin / clean
Aperçu
Trace une petite flèche là où chaque arête arrive sur son nœud cible — les hive plots servent souvent à représenter des graphes orientés (trafic réseau, citations), et ceci rend le sens lisible d'un coup d'œil plutôt qu'implicite.
The Pulse chart is an original SeraPlot chart type that maps temporal or cyclic data onto a radial clock-face layout. Each slice is a time period (hour, day, month…) and the bar height encodes the intensity value. The "wave" variant connects data points into a smooth radial polygon.
Le Pulse chart est un type de graphique original de SeraPlot qui projette des données temporelles ou cycliques sur une disposition radiale façon cadran d'horloge. Chaque secteur est une période temporelle (heure, jour, mois…) et la hauteur de la barre encode la valeur d'intensité. La variante "wave" relie les points de données en un polygone radial lissé.
Étend chaque secteur en un anneau continu, sans espace, à pleine opacité et sans contour — se lit comme un cadran plein plutôt qu’un ensemble de barres en arc séparées.
Variante"filled"Aliasfilled / area / solid
Aperçu
Secteurs en arc tracés en contour seul, sans remplissage — la même disposition radiale en cadran, allégée en simples contours pour un rendu plus léger, adapté à l'impression.
The Orbita chart is an original SeraPlot chart type that places multiple series on concentric ring orbits. Each series (orbit) maps categories to angular positions. The result is a planetary-system-style comparison across series and categories simultaneously, ideal for multi-period cross-category analysis.
matrix is a nested S×C list — one inner list per series (S = number of series, C = number of categories).
Drops the dashed orbit rings and radial spoke lines, and shrinks each point to a small strokeless dot — a decluttered read focused purely on relative position.
Variant"minimal"Aliasesminimal / thin / clean
Preview
Colors each point green or red depending on whether its value rose or fell versus the same category on the previous orbit — turns concentric orbits (e.g. one per year) into a trend view instead of a static snapshot.
L'Orbita chart est un type de graphique original de SeraPlot qui place plusieurs séries sur des orbites en anneaux concentriques. Chaque série (orbite) associe des catégories à des positions angulaires. Le résultat est une comparaison façon système planétaire à travers séries et catégories simultanément, idéale pour l'analyse croisée multi-période/multi-catégorie.
matrix est une liste imbriquée S×C — une liste interne par série (S = nombre de séries, C = nombre de catégories).
series_names (list[str]) — Un nom par orbite (ex. années). labels (list[str]) — Noms des catégories (positions angulaires). matrix (list[list[float]]) — Matrice de valeurs S×C, une ligne par série. width / height (int) — Dimensions du graphique (défaut 580×580).
Traînée en polygone fermé reliant les points d'une série
Variante"trail"Aliastrail / line / connected
Aperçu
Effet de lueur (flou gaussien) sur les points
Variante"glow"Aliasglow / neon / light
Aperçu
Supprime les anneaux d'orbite en pointillés et les rayons radiaux, et réduit chaque point à un petit point sans contour — une lecture épurée centrée uniquement sur la position relative.
Variante"minimal"Aliasminimal / thin / clean
Aperçu
Colore chaque point en vert ou rouge selon que sa valeur a augmenté ou diminué par rapport à la même catégorie sur l'orbite précédente — transforme des orbites concentriques (ex. une par année) en vue de tendance plutôt qu'un instantané statique.
sp.eventplot() draws a short vertical tick for every discrete event, one row per category — the standard chart for point-in-time occurrences without duration (neuron spike trains, log/error timestamps, user session starts). Unlike gantt(), events have no end time. Reuses x_values and the existing categories field on ChartArgs (the same grouping mechanism used by bubble()'s categorical variant) — no new parameter shape, rows are formed automatically from distinct category values in order of first appearance.
Tick marks plus a smoothed density curve per row, computed with the same native kernel density estimator as [`kde()`](kde.md) (`scott_bw` bandwidth selection, Gaussian kernel) — a rug plot and a KDE in one chart.
Variant"density"Aliasesdensity / kde / smoothed / rug
Preview
Draws a thin line connecting each row's events in chronological order, on top of the usual ticks — traces the sequence/trajectory through time, not just where events landed.
sp.eventplot() trace un court trait vertical pour chaque événement discret, une ligne par catégorie — le graphique de référence pour des occurrences ponctuelles sans durée (trains de potentiels d'action, horodatages de logs/erreurs, débuts de session utilisateur). Contrairement à gantt(), les événements n'ont pas de fin. Réutilise x_values et le champ categories déjà existant sur ChartArgs (le même mécanisme de regroupement que la variante catégorielle de bubble()) — aucune nouvelle forme de paramètre, les lignes sont formées automatiquement à partir des valeurs de catégorie distinctes dans leur ordre de première apparition.
Traits verticaux plus une courbe de densité lissée par ligne, calculée avec le même estimateur de densité par noyau natif que [`kde()`](kde.md) (sélection de bande passante `scott_bw`, noyau gaussien) — un rug plot et un KDE en un seul graphique.
Variante"density"Aliasdensity / kde / smoothed / rug
Aperçu
Trace un trait fin reliant les événements de chaque ligne dans l'ordre chronologique, par-dessus les graduations habituelles — trace la séquence/trajectoire dans le temps, pas seulement où les événements sont tombés.
sp.gantt() draws one horizontal bar per task spanning from start to end on a shared numeric timeline (day index, epoch, or any consistent unit — no date-typed axis required). Tasks are sorted like every other chart via sort_order, and rows can be grouped by an optional categories list (e.g. team or phase), which colors bars by category with an automatic legend instead of per-row palette rotation. Reuses the labels/start/end/categories fields already on ChartArgs (the same start/end pair used by dumbbell()) — no new parameter shape.
labels (list[str]) — Task names. start (list[float]) — Task start (numeric timeline unit). end (list[float]) — Task end. categories (list[str]) — Optional group per task; colors bars by group with a legend. color_values (list[float]) — Completion fraction (0–1) per task, used by the "progress" variant.
Renders zero-duration tasks (`start == end`) as a diamond marker instead of a degenerate bar — the standard way project-planning tools distinguish milestones from work items.
sp.gantt() trace une barre horizontale par tâche, de start à end sur une échelle temporelle numérique partagée (index de jour, epoch, ou toute unité cohérente — aucun axe de type date requis). Les tâches sont triées comme sur tous les autres graphiques via sort_order, et les lignes peuvent être groupées par une liste categories optionnelle (ex : équipe ou phase), qui colore les barres par groupe avec une légende automatique au lieu d'une rotation de palette par ligne. Réutilise les champs labels/start/end/categories déjà présents sur ChartArgs (la même paire start/end utilisée par dumbbell()) — aucune nouvelle forme de paramètre.
labels (list[str]) — Noms des tâches. start (list[float]) — Début de tâche (unité temporelle numérique). end (list[float]) — Fin de tâche. categories (list[str]) — Groupe optionnel par tâche ; colore les barres par groupe avec légende. color_values (list[float]) — Fraction de complétion (0–1) par tâche, utilisée par la variante "progress".
Affiche les tâches de durée nulle (`start == end`) sous forme de losange plutôt qu'une barre dégénérée — la façon standard dont les outils de planification distinguent les jalons des tâches.
sp.hexbin() bins a 2D scatter cloud into a regular hexagonal grid and colors each hexagon by point density (count), the standard alternative to a scatter plot once point overlap makes individual markers unreadable. Points are assigned to hexagon cells directly in pixel space using the true nearest-center rule (two candidate offset grids, closest wins), so cells tile without gaps or overlap regardless of the data's aspect ratio. Cell color reuses the same continuous colorscale engine as heatmap() and bubble(variant="gradient") — any of viridis / plasma / inferno / magma / cividis / turbo / rdbu / blues / reds / greens works via colorscale=.
Hexagons drawn at 72% size with a visible gap between neighbors — a "confetti" look instead of a solid tiled surface.
Variant"spaced"Aliasesspaced / gapped / confetti
Preview
Dims every cell except the densest ~15% (full opacity, white outline, count label) — draws the eye straight to the hotspots instead of the full density gradient.
Variant"highlight"Aliaseshighlight / top / hotspot / peak
Preview
Bins below min_count are skipped entirely (left transparent) instead of drawn faint - a hard threshold rather than a dimmed gradient, matching R's hexbin(mincnt=).
Variant"mincnt"Aliasesmincnt / threshold / sparse
Preview
Each cell's count is classed into an order-of-magnitude band (ones/tens/hundreds/thousands/10 thousands), colored and sized by band, with a smaller nested hexagon inside in the previous band's color - matching R hexbin's nested/centroid styles - plus a size+color legend.
Cell color is mapped on log(count + 1) instead of the raw count, matching matplotlib's hexbin(bins="log") — compresses the huge dynamic range that skewed point clouds produce so low-density cells stay visually distinguishable instead of collapsing near zero.
Cell color encodes the average of a third variable (values=) inside each bin instead of the point count — the native equivalent of matplotlib's hexbin(C=..., reduce_C_function=numpy.mean), for when the quantity of interest isn't density itself.
Variant"weighted"Aliasesweighted / mean / aggregate / reduce_mean
Preview
White dashed cell borders over a full continuous colorscale (defaults to magma) with no plot border — matches matplotlib's hexbin(edgecolor="white", linestyle="dotted", linewidth=1.5) styling exactly.
Variant"dotted"Aliasesdotted / dashed / styled / magma
Preview
Adds 1D density strips above and to the right of the hexbin grid — a joint-plot style combination, showing the marginal distribution of each axis alongside the 2D density.
sp.hexbin() regroupe un nuage de points 2D dans une grille hexagonale régulière et colore chaque hexagone selon la densité de points (comptage) — l'alternative standard au nuage de points classique dès que le chevauchement des marqueurs le rend illisible. Les points sont assignés directement en espace pixel via la règle du centre le plus proche (deux grilles candidates décalées, la plus proche l'emporte), donc les cellules pavent sans trou ni recouvrement quel que soit le ratio d'aspect des données. La couleur des cellules réutilise le même moteur de dégradés continus que heatmap() et bubble(variant="gradient") — viridis / plasma / inferno / magma / cividis / turbo / rdbu / blues / reds / greens fonctionnent via colorscale=.
Hexagones dessinés à 72% de leur taille avec un espace visible entre voisins — un rendu confetti plutôt qu'une surface pavée pleine.
Variante"spaced"Aliasspaced / gapped / confetti
Aperçu
Estompe toutes les cellules sauf les ~15% les plus denses (pleine opacité, contour blanc, effectif affiché) — attire l'œil directement sur les zones chaudes plutôt que sur le dégradé complet.
Variante"highlight"Aliashighlight / top / hotspot / peak
Aperçu
Les cellules sous min_count sont totalement ignorées (laissées transparentes) plutôt que dessinées en estompé - un seuil dur plutôt qu'un dégradé atténué, comme hexbin(mincnt=) en R.
Variante"mincnt"Aliasmincnt / threshold / sparse
Aperçu
L'effectif de chaque cellule est classé dans une bande d'ordre de grandeur (unités/dizaines/centaines/milliers/dizaines de milliers), colorée et dimensionnée selon la bande, avec un hexagone imbriqué plus petit à l'intérieur dans la couleur de la bande précédente - comme les styles imbriqués/centroïdes du package R hexbin - plus une légende taille+couleur.
La couleur des cellules est basée sur log(effectif + 1) plutôt que sur l'effectif brut, comme hexbin(bins="log") en matplotlib — comprime la large plage dynamique des nuages de points asymétriques pour que les cellules peu denses restent visuellement distinguables au lieu de s'écraser près de zéro.
La couleur des cellules encode la moyenne d'une troisième variable (values=) dans chaque cellule plutôt que l'effectif — l'équivalent natif de hexbin(C=..., reduce_C_function=numpy.mean) en matplotlib, quand la grandeur d'intérêt n'est pas la densité elle-même.
Variante"weighted"Aliasweighted / mean / aggregate / reduce_mean
Aperçu
Contours pointillés blancs sur un dégradé continu complet (par défaut magma) sans bordure de graphique — reproduit exactement le style hexbin(edgecolor="white", linestyle="dotted", linewidth=1.5) de matplotlib.
Variante"dotted"Aliasdotted / dashed / styled / magma
Aperçu
Ajoute des bandes de densité 1D au-dessus et à droite de la grille hexbin — une combinaison façon joint-plot, montrant la distribution marginale de chaque axe en plus de la densité 2D.
sp.joint() composes a bivariate main panel with top and right marginal strips — the SeraPlot equivalent of seaborn's jointplot() / JointGrid. variant= (the panel, "inside") and marginal= (the top/right strips, "outside") are independent, fully open choices — not a fixed enum. Each accepts the name of any registered SeraPlot family from services/plot/statistical/chart_registry.rs (currently ~50: hexbin, scatter, kde, histogram, bar, violin, boxplot, heatmap, orbita, splom, … the same inventory facet() dispatches through), and panel_variant= / marginal_variant= forward straight through as that family's own variant=, so hexbin's outlined/spaced/highlight styles, histogram's cumulative, etc. all work exactly as they do standalone.
sp.joint(x, y, variant="hexbin", marginal="kde")
sp.joint(x, y, variant="kde", marginal="histogram")
sp.joint(x, y, variant="scatter", marginal="bar")
sp.joint(x, y, variant="hexbin", panel_variant="outlined", marginal="kde")
Each region — panel, top strip, right strip — is a real, independently-rendered SeraPlot chart embedded in its own frame, calling the exact same native build_* function facet() uses for its cells; nothing is reimplemented. That also means regions are not pixel-aligned to a shared coordinate system the way a hand-tuned single-SVG composition would be — each chart keeps its own axes/padding — an inherent tradeoff for genuine "any family, any slot" freedom. Not every family produces something meaningful in every slot — that limitation belongs to the target family's own data shape, not to joint() itself. As a marginal, a family only ever receives the single axis of values being summarized (plus generic synthetic labels/categories/series/sizes/words so families expecting those shapes still degrade gracefully); families whose own data model is inherently 2D/matrix/hierarchical/paired — candlestick, chord, circle_pack, correlogram, dendrogram, dumbbell, gantt, hive, heatmap, icicle, orbita, parcats, radar, sankey, scatterternary, slope, splom, sunburst — render blank there, the same honest way orbita renders blank as a panel without a hierarchy.
For a true bivariate (2D) density surface — not just kde's own 1D curve — use variant="kde", panel_variant="contour": kde()'s contour variant fits a product-kernel Gaussian KDE jointly over x and y and shades a smooth density surface with the raw points overlaid, matching seaborn's smooth_bivariate_kde / joint_kde examples. Legacy preset names (layered_bivariate, joint_kde, kde_smooth, smooth_bivariate_kde, …) already resolve to exactly this.
The preset names from earlier releases (hexbin_marginal, joint_histogram / histogram2d, layered_bivariate, joint_kde, kde_smooth, multiple_bivariate_kde, marginal_ticks, regression_marginals) still work as variant= values and resolve to a real family under the hood (resolve_legacy_panel() in joint/variant.rs), so existing code keeps working. heat_scatter — seaborn's correlation matrix drawn as sized/colored scatter dots — isn't a joint/marginal plot at all; use heatmap(variant="bubble") directly.
Chart — object with .html property and .show() method.
The raw entry point — variant= is simply the panel family's own name, and any registered family can fill it.
Callsp.joint(x, y, variant="hexbin", marginal="histogram")
Preview
Hexagonal density panel with KDE curve marginals instead of the default histograms.
Callsp.joint(x, y, variant="hexbin", marginal="kde")
Preview
A 1D KDE panel with histogram marginals — mixing families freely, not just bivariate-native ones.
Callsp.joint(x, y, variant="kde", marginal="histogram")
Preview
Scatter panel with bar-chart marginals.
Callsp.joint(x, y, variant="scatter", marginal="bar")
Preview
panel_variant= forwards to the panel family's own variant — here hexbin's outlined cell style, combined with KDE marginals.
Callsp.joint(x, y, variant="hexbin", panel_variant="outlined", marginal="kde")
Preview
The pre-existing layered_bivariate preset name still resolves (to variant="kde", panel_variant="contour" under the hood — a genuine bivariate density surface, not a flat 1D curve) — old code keeps working, and now renders correctly.
sp.joint() compose un panneau bivarié principal avec des bandes marginales en haut et à droite — l'équivalent SeraPlot de jointplot() / JointGrid de seaborn. variant= (le panneau, « intérieur ») et marginal= (les bandes haut/droite, « extérieur ») sont des choix indépendants et totalement ouverts — pas une énumération figée. Chacun accepte le nom de n'importe quelle famille SeraPlot enregistrée dans services/plot/statistical/chart_registry.rs (environ 50 actuellement : hexbin, scatter, kde, histogram, bar, violin, boxplot, heatmap, orbita, splom, … le même inventaire que celui utilisé par facet()), et panel_variant= / marginal_variant= sont transmis tels quels comme le variant= propre à cette famille.
sp.joint(x, y, variant="hexbin", marginal="kde")
sp.joint(x, y, variant="kde", marginal="histogram")
sp.joint(x, y, variant="scatter", marginal="bar")
sp.joint(x, y, variant="hexbin", panel_variant="outlined", marginal="kde")
Chaque région — panneau, bande haute, bande droite — est un vrai graphique SeraPlot rendu indépendamment dans son propre cadre, appelant exactement la même fonction build_* native que facet() pour ses cellules ; rien n'est réimplémenté. Cela signifie aussi que les régions ne sont pas alignées pixel par pixel sur un système de coordonnées partagé comme le serait une composition SVG unique ajustée à la main — chaque graphique garde ses propres axes/marges — une contrepartie inhérente à une liberté « n'importe quelle famille, n'importe quel emplacement » authentique. Toutes les familles ne produisent pas quelque chose de pertinent dans tous les emplacements — cette limite appartient à la forme de données propre à la famille cible, pas à joint() lui-même. En tant que marginale, une famille ne reçoit que l'axe unique de valeurs résumé (plus des labels/categories/series/sizes/words génériques synthétisés pour que les familles attendant ces formes se dégradent tout de même proprement) ; les familles dont le modèle de données est intrinsèquement 2D/matriciel/hiérarchique/apparié — candlestick, chord, circle_pack, correlogram, dendrogram, dumbbell, gantt, hive, heatmap, icicle, orbita, parcats, radar, sankey, scatterternary, slope, splom, sunburst — restent vides dans cet emplacement, de la même façon honnête qu'orbita reste vide en tant que panneau sans hiérarchie.
Pour une véritable surface de densité bivariée (2D) — pas seulement la courbe 1D propre à kde — utilisez variant="kde", panel_variant="contour" : la variante contour de kde() ajuste une estimation par noyau gaussien (produit de noyaux) conjointement sur x et y et affiche une surface de densité lissée avec les points bruts superposés, à l'image des exemples seaborn smooth_bivariate_kde / joint_kde. Les noms hérités (layered_bivariate, joint_kde, kde_smooth, smooth_bivariate_kde, …) se résolvent déjà exactement ainsi.
Les noms préréglés des versions précédentes (hexbin_marginal, joint_histogram / histogram2d, layered_bivariate, joint_kde, kde_smooth, multiple_bivariate_kde, marginal_ticks, regression_marginals) fonctionnent toujours comme valeurs de variant= et se résolvent vers une vraie famille en interne (resolve_legacy_panel() dans joint/variant.rs), donc le code existant continue de fonctionner. heat_scatter — la matrice de corrélation de seaborn tracée en points dimensionnés/colorés — n'est pas un graphique joint/marginal du tout ; utilisez directement heatmap(variant="bubble").
Chart — objet avec une propriété .html et une méthode .show().
Le point d'entrée brut — variant= est simplement le nom de la famille du panneau, et n'importe quelle famille enregistrée peut le remplir.
Appelsp.joint(x, y, variant="hexbin", marginal="histogram")
Aperçu
Panneau de densité hexagonale avec des marges en courbes de KDE plutôt qu'en histogrammes par défaut.
Appelsp.joint(x, y, variant="hexbin", marginal="kde")
Aperçu
Un panneau de KDE 1D avec des marges en histogrammes — mélange libre de familles, pas seulement celles nativement bivariées.
Appelsp.joint(x, y, variant="kde", marginal="histogram")
Aperçu
Panneau en nuage de points avec des marges en bar chart.
Appelsp.joint(x, y, variant="scatter", marginal="bar")
Aperçu
panel_variant= est transmis à la variante propre de la famille du panneau — ici le style de cellule outlined de hexbin, combiné à des marges KDE.
Appelsp.joint(x, y, variant="hexbin", panel_variant="outlined", marginal="kde")
Aperçu
L'ancien nom préréglé layered_bivariate se résout toujours (vers variant="kde", panel_variant="contour" en interne — une véritable surface de densité bivariée, pas une simple courbe 1D) — le code existant continue de fonctionner, et s'affiche désormais correctement.
sp.facet() is not a chart family of its own — it is the framework's generic small-multiples
mechanism, the SeraPlot equivalent of seaborn's FacetGrid. It takes the name of any existing
2D chart family (family=), splits every data array in the call by a facet_by group key, and
calls that family's own builder once per group, laying the results out in a grid. Every current
and future chart family gets faceting for free — there is no per-chart code, no separate variant
enum, and no duplicated geometry: services/plot/statistical/facet/mod.rs dispatches straight to
the target family's real build_* function (histogram::build_histogram,
line::build_line, joint::build_joint, …), so any **kwargs valid for that family
(colorscale=, bins=, variant=, …) is simply forwarded through unfiltered.
Only arguments whose array length matches facet_by get split; everything else (scalars like
bins=, variant=, colorscale=) is copied to every cell unchanged. cols= sets the grid's
column count, cell_width= / cell_height= size each panel.
Currently wired families: area, bar, boxplot, bubble, bullet, candlestick, dumbbell,
eventplot, funnel, gantt, gauge, heatmap, hexbin, histogram, icicle, joint,
kde, line, lollipop, parallel, pie, radar, ridgeline, scatter, slope, splom,
stackplot, sunburst, treemap, violin, waterfall, wordcloud — see
facet::dispatch() for the exact match table.
Any array field accepted by the target family= (values, labels, x, y, …), plus
facet_by (list[str]) — the group label for each row, same length as the arrays being split.
Pair grid (KDE diagonal, paired point plots, dot-plot matrix) — splom(variant="density") or splom(variant="basic"): SPLOM is SeraPlot's scatterplot-matrix family and already covers the pairwise-comparison layout these seaborn examples show.
Radial facets — facet(family="bar", facet_by=...): SeraPlot doesn't have a native polar/rose histogram variant yet, so this reproduces the faceting technique with a regular bar chart per facet rather than a pixel-perfect polar match.
Facetted time series — same mechanism as the Faceted Lineplot recipe below; swap in a time-ordered labels= array.
Facets a bivariate joint(variant="histogram2d") panel by a third variable — matches seaborn's three_variable_histogram example, combining the facet mechanism with the [`joint`](joint.md) family's `histogram2d` alias.
sp.facet() n'est pas une famille de graphique en soi — c'est le mécanisme générique de petits
multiples du framework, l'équivalent SeraPlot de FacetGrid de seaborn. Il prend le nom de
n'importe quelle famille 2D existante (family=), découpe chaque tableau de données de l'appel
selon une clé de groupe facet_by, et appelle le builder de cette famille une fois par groupe, en
disposant les résultats dans une grille. Chaque famille actuelle et future obtient le facettage
gratuitement — aucun code par graphique, aucune énumération de variante séparée, aucune géométrie
dupliquée : services/plot/statistical/facet/mod.rs appelle directement le vrai build_* de la
famille cible (histogram::build_histogram, line::build_line, joint::build_joint, …), donc
tout **kwargs valide pour cette famille (colorscale=, bins=, variant=, …) est simplement
transmis tel quel.
Seuls les arguments dont la longueur de tableau correspond à facet_by sont découpés ; le reste
(scalaires comme bins=, variant=, colorscale=) est copié tel quel dans chaque cellule.
cols= fixe le nombre de colonnes de la grille, cell_width= / cell_height= dimensionnent
chaque panneau.
Familles déjà câblées : area, bar, boxplot, bubble, bullet, candlestick, dumbbell,
eventplot, funnel, gantt, gauge, heatmap, hexbin, histogram, icicle, joint,
kde, line, lollipop, parallel, pie, radar, ridgeline, scatter, slope, splom,
stackplot, sunburst, treemap, violin, waterfall, wordcloud — voir facet::dispatch()
pour la table de correspondance exacte.
Tout champ tableau accepté par la famille family= cible (values, labels, x, y, …), plus
facet_by (list[str]) — l'étiquette de groupe de chaque ligne, de même longueur que les
tableaux à découper.
Pair grid (diagonale KDE, point plots appairés, matrice de dot-plots) — splom(variant="density") ou splom(variant="basic") : SPLOM est la famille matrice de nuages de points de SeraPlot et couvre déjà la disposition en comparaison par paires de ces exemples.
Facettes radiales — facet(family="bar", facet_by=...) : SeraPlot n'a pas encore de variante native d'histogramme polaire/rose des vents, donc ceci reproduit la technique de facettage avec un bar chart classique par facette plutôt qu'une correspondance polaire exacte.
Séries temporelles facettées — même mécanisme que la recette Faceted Lineplot ci-dessous ; remplacez labels= par un tableau ordonné dans le temps.
Un panneau de courbe par groupe — correspond aux exemples faceted_lineplot / timeseries_facets de seaborn (utilisez un labels= ordonné dans le temps pour le second).
Facette un panneau bivarié joint(variant="histogram2d") par une troisième variable — correspond à l'exemple three_variable_histogram de seaborn, combinant le mécanisme de facettage avec l'alias histogram2d de la famille [`joint`](joint.md).
sp.icicle() renders a hierarchy as stacked bands: each depth level of the tree is one horizontal row, and the width of each node inside its row is proportional to its value. It is a rectangular alternative to sunburst() and shares the exact same input schema (labels / parents / values), so any dataset already used with sunburst() or treemap() works unchanged.
Hierarchy encoding — labels lists every node, parents gives the parent label of each node ("" for a root). Leaf values come from values; internal-node values at 0 are auto-rolled-up from descendants.
Root column on the left, depth grows rightward instead of downward.
Variant"horizontal"Aliaseshorizontal / h / sideways / left_to_right
Preview
The same hierarchy in polar coordinates instead of cartesian — depth becomes ring radius, horizontal span becomes angular span. The classic icicle/sunburst duality: identical data, identical layout math (`xspan`, `depth`), different coordinate system.
Colors every node by its value rank among its same-depth peers (indigo → red, lowest to highest) instead of by raw share of the grand total — makes cross-branch comparisons at a given level obvious even when one branch is much bigger than another.
sp.icicle() représente une hiérarchie sous forme de bandes empilées : chaque niveau de profondeur de l'arbre est une rangée horizontale, et la largeur de chaque nœud dans sa rangée est proportionnelle à sa valeur. C'est une alternative rectangulaire à sunburst(), avec exactement le même schéma d'entrée (labels / parents / values) — tout jeu de données déjà utilisé avec sunburst() ou treemap() fonctionne sans modification.
Encodage de la hiérarchie — labels liste tous les nœuds, parents donne le libellé du parent de chaque nœud ("" pour une racine). Les valeurs des feuilles viennent de values ; les nœuds internes à 0 sont calculés automatiquement comme la somme de leurs descendants.
labels (list[str]) — Libellés des nœuds (un par ligne). parents (list[str]) — Parent de chaque nœud ("" pour les racines). values (list[float]) — Valeurs feuilles ; zéros internes calculés auto.
Colonne racine à gauche, la profondeur se développe vers la droite au lieu du bas.
Variante"horizontal"Aliashorizontal / h / sideways / left_to_right
Aperçu
La même hiérarchie en coordonnées polaires plutôt que cartésiennes — la profondeur devient un rayon d'anneau, l'empan horizontal devient un empan angulaire. La dualité classique icicle/sunburst : mêmes données, même calcul de mise en page (`xspan`, `depth`), système de coordonnées différent.
Colore chaque nœud selon son rang de valeur parmi ses pairs de même profondeur (indigo → rouge, du plus faible au plus élevé) plutôt que sa part brute du total général — rend évidentes les comparaisons entre branches à un niveau donné, même quand une branche est bien plus grande qu'une autre.
sp.parcats() is the categorical counterpart to parallel(): instead of numeric axes with one polyline per row, each axis holds a set of discrete category values, and ribbons flow between adjacent axes with width proportional to how many rows share that pair of categories — the standard chart for tracing how categorical attributes co-occur across a dataset (e.g. gender → survival → class). Internally it builds a node for every distinct (axis, category) pair and an edge for every consecutive-axis transition, then reuses the exact same layered layout engine and bezier ribbon renderer as sankey() (sankey::common::compute_layout / sankey_link_path) — the two charts share their positioning math, not just their visual family.
Data shape — category_series is a list of rows, each row a list of category values with one entry per axis (category_series[row][axis]), mirroring exactly the row-major shape already used by parallel(series=...).
Dims every ribbon except each node's single heaviest outgoing flow, which is boosted to full opacity with a thin white outline — traces the dominant path through the categories.
sp.parcats() est l'équivalent catégoriel de parallel() : au lieu d'axes numériques avec une polyligne par ligne, chaque axe contient un ensemble de valeurs catégorielles discrètes, et des rubans relient les axes adjacents avec une largeur proportionnelle au nombre de lignes partageant cette paire de catégories — le graphique de référence pour tracer comment des attributs catégoriels co-occurrent dans un jeu de données (ex : genre → survie → classe). En interne, un nœud est créé pour chaque paire distincte (axe, catégorie) et une arête pour chaque transition entre axes consécutifs, puis le moteur de layout en couches et le rendu de rubans en bézier de sankey() sont réutilisés tels quels (sankey::common::compute_layout / sankey_link_path) — les deux graphiques partagent leurs mathématiques de positionnement, pas seulement leur famille visuelle.
Forme des données — category_series est une liste de lignes, chaque ligne une liste de valeurs catégorielles avec une entrée par axe (category_series[ligne][axe]), reprenant exactement la forme ligne-par-ligne déjà utilisée par parallel(series=...).
Estompe tous les rubans sauf le flux sortant le plus lourd de chaque nœud, poussé à pleine opacité avec un fin contour blanc — trace le chemin dominant à travers les catégories.
sp.scatterternary() plots three-component compositions (e.g. soil sand/silt/clay, alloy element fractions, poll shares) inside an equilateral triangle — the standard chart whenever three parts sum to a whole. Each point's three values are barycentric weights normalized internally (a+b+c need not equal 1 or 100, only their ratio matters), converted to Cartesian coordinates and rendered with a full gridline mesh (three families of lines at 20/40/60/80%, one parallel to each triangle side). Reuses the exact same x_values/y_values/z_values and x_label/y_label/z_label inputs already used by 3D scatter charts — no new parameter shape introduced.
x_values (list[float]) — First component (A, top vertex). y_values (list[float]) — Second component (B, right vertex). z_values (list[float]) — Third component (C, left vertex). x_label (str) — Label for the top vertex. y_label (str) — Label for the right vertex. z_label (str) — Label for the left vertex. color_values (list[float]) — Continuous values for the "gradient" variant.
Point radius scales with `color_values` (4.5× range between smallest and largest) instead of a fixed `point_size` — a fourth variable encoded as size on top of the three ternary axes.
Same layout as basic, with each point's label printed beside it — useful once you need to identify specific observations, not just see the overall distribution.
Variant"labeled"Aliaseslabeled / labelled / annotated / named
sp.scatterternary() trace des compositions à trois composantes (ex : sable/limon/argile d'un sol, fractions d'éléments d'un alliage, parts de sondage) à l'intérieur d'un triangle équilatéral — le graphique de référence dès que trois parts forment un tout. Les trois valeurs de chaque point sont des poids barycentriques normalisés en interne (a+b+c n'a pas besoin de valoir 1 ou 100, seul leur ratio compte), converties en coordonnées cartésiennes et rendues avec une grille complète (trois familles de lignes à 20/40/60/80%, une parallèle à chaque côté du triangle). Réutilise exactement les mêmes entrées x_values/y_values/z_values et x_label/y_label/z_label déjà utilisées par les nuages de points 3D — aucune nouvelle forme de paramètre introduite.
Le rayon des points varie selon `color_values` (facteur 4.5 entre le plus petit et le plus grand) au lieu d'un `point_size` fixe — une quatrième variable encodée en taille en plus des trois axes ternaires.
Même disposition que basic, avec le label de chaque point affiché à côté — utile dès qu'il faut identifier des observations précises, pas seulement voir la distribution globale.
Variante"labeled"Aliaslabeled / labelled / annotated / named
sp.splom() lays out every pairwise combination of a set of numeric dimensions as an M×M grid of small scatter plots in a single self-contained chart — the standard first look at a multivariate numeric dataset. It reuses exactly the same row-major data shape as parallel() (axes + series, one row per observation with one value per axis), so any dataset already wired up for parallel coordinates works unchanged. Diagonal cells show the axis name instead of a self-scatter.
Each off-diagonal cell's background is tinted by the Pearson correlation coefficient between its two dimensions (via the same continuous colorscale engine as [`heatmap()`](heatmap.md)), with points overlaid in a neutral color — a heatmap and a SPLOM in one chart.
Every point drawn at 14% opacity with no stroke — overlapping points accumulate into darker regions, revealing density in datasets too large for solid dots to stay readable.
Adds a least-squares trend line to every off-diagonal panel — the classic pairplot-with-regression view for spotting linear relationships across every pair of variables at once.
Variant"regression"Aliasesregression / trend / fit / lm
sp.splom() dispose chaque combinaison par paires d'un ensemble de dimensions numériques en une grille M×M de petits nuages de points dans un seul graphique autonome — le premier regard standard sur un jeu de données numérique multivarié. Il réutilise exactement la même forme de données ligne-par-ligne que parallel() (axes + series, une ligne par observation avec une valeur par axe), donc tout jeu de données déjà câblé pour les coordonnées parallèles fonctionne sans modification. Les cellules diagonales affichent le nom de l'axe plutôt qu'un nuage de points contre lui-même.
Le fond de chaque cellule hors-diagonale est teinté selon le coefficient de corrélation de Pearson entre ses deux dimensions (via le même moteur de dégradés continus que [`heatmap()`](heatmap.md)), avec les points superposés en couleur neutre — une heatmap et un SPLOM en un seul graphique.
Chaque point dessiné à 14% d'opacité sans contour — les points superposés s'accumulent en zones plus sombres, révélant la densité sur des jeux de données trop grands pour rester lisibles en points pleins.
Ajoute une droite de régression aux moindres carrés à chaque panneau hors-diagonale — la vue classique pairplot-avec-régression pour repérer les relations linéaires entre toutes les paires de variables à la fois.
Variante"regression"Aliasregression / trend / fit / lm
sp.stackplot() draws multiple series as cumulatively stacked areas over a shared x-axis — reuses the exact same x_labels/series/series_names input shape as multiline(), so any dataset already used with multiline() works unchanged. Negative values are clamped to 0 before stacking (a stack has no meaningful negative contribution).
x_labels (list[str]) — Shared x-axis point labels. series (list[list[float]]) — One list of values per series, same length as x_labels. series_names (list[str]) — Legend label per series.
Centered ("silhouette") baseline — at every x-point the stack is centered around zero (`baseline = -total/2`) instead of starting at zero, giving the flowing ThemeRiver look.
100%-stacked — every series is divided by the x-point's total before stacking, so the top of the stack is always 1.0. Shows share of total over time instead of absolute magnitude.
Polar-wrapped stacking — each x-point becomes an angle around a circle instead of a position along an axis, and the cumulative bands grow outward as concentric rings from a small central hole. A genuinely different read on the same stacked data: total magnitude becomes an overall silhouette size, and each series' share becomes a colored band thickness at that angle.
Cartesian stacking rendered as smooth, glowing ribbons: quadratic-bezier-smoothed band edges, a top-to-bottom depth gradient per series, and a soft drop-shadow separating each layer — a more atmospheric, editorial look than the sharp-edged basic stack.
sp.stackplot() trace plusieurs séries en aires empilées cumulativement sur un axe x partagé — réutilise exactement la même forme d'entrée x_labels/series/series_names que multiline(), donc tout jeu de données déjà utilisé avec multiline() fonctionne sans modification. Les valeurs négatives sont ramenées à 0 avant l'empilement (un empilement n'a pas de contribution négative significative).
x_labels (list[str]) — Libellés des points sur l'axe x partagé. series (list[list[float]]) — Une liste de valeurs par série, même longueur que x_labels. series_names (list[str]) — Libellé de légende par série.
Ligne de base centrée ("silhouette") — à chaque point x, l'empilement est centré autour de zéro (`baseline = -total/2`) au lieu de partir de zéro, donnant le rendu fluide façon ThemeRiver.
Empilement à 100% — chaque série est divisée par le total du point x avant l'empilement, donc le sommet est toujours à 1.0. Montre la part du total dans le temps plutôt que la magnitude absolue.
Empilement enroulé en polaire — chaque point x devient un angle autour d'un cercle au lieu d'une position sur un axe, et les bandes cumulées s'étendent vers l'extérieur en anneaux concentriques depuis un petit trou central. Une lecture vraiment différente des mêmes données empilées : la magnitude totale devient une silhouette globale, et la part de chaque série devient l'épaisseur d'une bande colorée à cet angle.
Empilement cartésien rendu en rubans lisses et lumineux : bords de bandes lissés par courbes de Bézier quadratiques, dégradé de profondeur haut-bas par série, et une ombre portée douce séparant chaque couche — un rendu plus atmosphérique et éditorial que l'empilement basique aux arêtes nettes.
sp.plot_web() places each data point as a sized bubble node and lets the two positional axes (x_values/y_values) drive layout instead of a fixed cartesian grid — the scatter variant reads them as literal coordinates on a light-trail canvas, the radial variant re-projects them onto concentric rings around a center point. Bubble radius scales between min_r and max_r from the sizes array (or a constant radius if omitted), and groups assigns a categorical color from palette per node.
x_values (list[float]) — Horizontal position (scatter) or angle-driving value (radial). y_values (list[float]) — Vertical position (scatter) or radius-driving value (radial). sizes (list[float]) — Per-node value driving bubble radius between min_r and max_r. labels (list[str]) — Per-node hover label. groups (list[str]) — Per-node category, colored from palette.
sp.plot_web() place chaque point de donnée comme un nœud-bulle et laisse les deux axes positionnels (x_values/y_values) piloter la disposition plutôt qu'une grille cartésienne fixe — la variante scatter les lit comme des coordonnées littérales sur un canvas à traînées lumineuses, la variante radial les reprojette sur des anneaux concentriques autour d'un point central. Le rayon des bulles est mis à l'échelle entre min_r et max_r à partir du tableau sizes (ou un rayon constant si omis), et groups assigne une couleur catégorielle depuis palette par nœud.
x_values (list[float]) — Position horizontale (scatter) ou valeur pilotant l'angle (radial). y_values (list[float]) — Position verticale (scatter) ou valeur pilotant le rayon (radial). sizes (list[float]) — Valeur par nœud pilotant le rayon de bulle entre min_r et max_r. labels (list[str]) — Étiquette de survol par nœud. groups (list[str]) — Catégorie par nœud, colorée depuis palette.
sp.radar3d() renders a 3D radar (spider) chart in a WebGL-like canvas scene. Each series becomes one ring of points around the shared axes, stacked along the depth axis instead of overlaid flat like the 2D radar.
ring_gap controls the distance between series rings along the depth axis: 1.0 (default) keeps the original one-unit spacing per series, while lower values pull the rings closer together, down to 0.0 where every ring collapses onto the same plane.
sp.radar3d() génère un graphique radar (toile d'araignée) en 3D dans une scène de type WebGL. Chaque série forme un anneau de points autour des axes partagés, empilés le long de l'axe de profondeur plutôt que superposés à plat comme le radar 2D.
ring_gap contrôle la distance entre les anneaux de séries le long de l'axe de profondeur : 1.0 (défaut) conserve l'espacement d'origine d'une unité par série, tandis que des valeurs plus basses rapprochent les anneaux, jusqu'à 0.0 où tous les anneaux se superposent sur le même plan.
World map with proportional bubbles at geographic coordinates.
Use iso_codes for country-level data (the library resolves centroids automatically), or pass explicit latitudes / longitudes.
Carte mondiale avec des bulles proportionnelles aux coordonnées géographiques. Utilisez iso_codes pour les données par pays (la bibliothèque résout les centroïdes automatiquement), ou passez des latitudes / longitudes explicites.
Carte choro-plèthe — polygones de pays/régions colorés par une valeur scalaire. Les pays sans données reçoivent la null_color. Fournissez des iso_codes (ISO-3166 alpha-3) pour associer les pays automatiquement.
Canvas is SeraPlot's composition layer: a free-form surface where you place
multiple independently-built Chart objects, images, shapes and text, wire
them together, and export the whole thing as one Chart. It is the tool for
building dashboards, annotated stories and multi-panel figures that a single
chart function can't produce on its own.
Every placement/drawing method accepts an optional name keyword. A named
element gets a data-sp-name="..." attribute in the rendered HTML, which is
what makes it addressable by every other method below (nudge, resize,
style, script, group, link, refill, dev-mode dragging, ...). Naming
is free and has no runtime cost — name anything you might want to touch again.
place(chart, x, y, w, h, rotation=0, opacity=1, clip="", group="", name="")
Places a Chart on the canvas at (x, y) sized w×h. Returns a chart_ref (int) used by pin/connect/attach_*.
image(src, x, y, w, h, rotation=0, opacity=1, clip="", group="", name="")
Places a PNG/JPEG/GIF/WebP/SVG image. src is a local file path (read and base64-embedded), a data: URI, or an http(s):// URL.
slot(name, x, y, w, h)
Reserves a named region without placing anything yet.
fill(slot_name, chart, ...)
Places a chart using a previously-declared slot's geometry.
grid(x, y, w, h, rows, cols, gap_x=0, gap_y=0)
Declares a rows × cols grid of slots named cell_{row}_{col} and returns the list of names in row-major order — no manual coordinate math for dashboards.
refill(name, chart) -> bool
Replaces a named, already-placed chart's content in place — same position, size, rotation, style. Use this (not place/fill again) whenever you're refreshing a panel with new data.
Calling place/fill again with a name that's already taken by a placed
chart updates that chart in place instead of stacking a duplicate — so
fill(slot, new_chart, name="panel") is safe to call repeatedly.
text, line, curve, connector, circle, ring, rect, polygon,
path, arrow, annotate and gradient are the low-level drawing
primitives everything else in Canvas (including voronoi(), below) is
built from. They draw directly on the canvas SVG, and share two keywords:
layer="fg"|"bg" — "bg" renders under every place()d chart/image,
"fg" (the default) renders on top. Use "bg" for backdrops, card
panels and watermark-style decoration; "fg" for annotations, callouts
and anything that should sit above the data.
name="" — makes the element addressable afterward by nudge, resize,
style, script, group, link, and draggable in dev() mode. Naming
is free; name anything you might want to touch again.
None of them require a Chart — a canvas full of nothing but these
primitives is a valid way to hand-draw a diagram SeraPlot has no dedicated
chart function for (see the closing example on this page).
Catmull-Rom tension: 0 collapses to straight segments between points, 1 is a standard smooth spline, higher values pull the curve into more pronounced bulges past each point
fill
str
"none"
Fills the area under the curve when set — a hand-drawn area-chart look
(color / width / opacity / layer / name — same as line)
Unlike connector (below), which always routes between exactly two points,
curve interpolates through an arbitrary polyline of waypoints. Reach for
it for hand-drawn trend lines, sparkline-style decoration, or free-form
organic strokes that aren't tied to a Chart's own axes:
Fraction along the dominant axis (whichever of dx/dy is larger) where the bezier control points sit — 0.5 gives a symmetric S-curve; values toward 0 or 1 skew the curve's midpoint toward one end
(others — same as line)
The "flowchart wire" primitive: one cubic bezier that always produces a
clean S- or L-shaped route between two points, regardless of their relative
position. Use it to link two place()d panels or two named elements
without hand-computing control points — connect() (under Connecting
two charts, below) draws the exact same curve, but reads its endpoints
from registered pins instead of raw coordinates.
ring is a donut: the filled region strictly between inner_r and
outer_r, built from two arcs combined with fill-rule="evenodd" rather
than a solid <circle>. Use it for radial progress rings, avatar frames,
or halo highlights — anywhere circle's solid disc would cover whatever
sits underneath it.
rx rounds the corners; rotation (degrees) spins the rect around its own
center. The two together cover card backgrounds, chips/badges, and simple
category-key swatches.
A closed shape through an arbitrary list[[x, y]] of vertices — the
primitive voronoi() itself is built from (each cell it returns is one
polygon() call under the hood). Use it directly for triangular/diamond
markers, custom badge shapes, or any closed region rect/circle can't
express.
The escape hatch: d is a raw SVG path-data string ("M ... L ... A ... Z")
for shapes none of the other primitives cover — logos, icons, arcs with a
specific sweep, or geometry computed by your own code. This is exactly how
icicle()'s "radial" variant draws its annular
sectors internally: hand-built M/A/L/Z strings, no separate arc
primitive needed.
anchor is the SVG text-anchor ("start", "middle", "end") relative
to (x, y) — "middle" centers a title over a panel, "end" right-aligns
a value next to an axis.
The point being annotated — where the leader line starts
tx, ty
float
required
Where the text itself sits — where the leader line ends
text
str
required
Supports \n for multi-line labels
line_dash
str
""
Dash pattern for the leader line, e.g. "4,3"
bg
str
""
Background color behind the text; ""/"none" draws no box
Unlike a plain text() + line() pair, annotate() auto-routes a clean
two-segment elbow between (ax, ay) and (tx, ty) (picking the elbow
point from whichever axis has the larger offset) and sizes its own
background box to fit the text. The tool for "this specific point,
labeled, with a callout line" — a bar's peak, a scatter outlier.
annotate_at() (under Connecting two charts, below) is the
pin-aware version of this same primitive, for labeling a point inside a
place()d chart instead of a raw canvas coordinate.
Registers an SVG linearGradient definition — x1/y1/x2/y2 live in the
0..1 objectBoundingBox space, so (0,0)→(1,0) is left-to-right and
(0,0)→(0,1) is top-to-bottom. It draws nothing by itself; call it once,
then reference fill=f"url(#{id})" on any subsequent rect/circle/
polygon/path:
None of the primitives above need a Chart at all — a canvas built only
from them is a fully valid way to hand-draw a widget SeraPlot has no
dedicated chart function for. ring only ever draws a complete annulus,
so a partial-sweep progress dial needs path with a hand-computed SVG arc
— exactly the "escape hatch" role described above:
ring draws the static background track, path draws the live progress
arc on top of it (stroked with the gradient defined a line earlier), and
text centers the number — three primitives from three different sections
above, one small self-contained gauge.
The hand-rolled arc_path above is exactly what arc/wedge below do
internally — reach for them first; path stays the escape hatch for shapes
neither one covers.
arc, wedge, ribbon, polar and radial_gradient share one angle
convention: degrees, 0° at the top, increasing clockwise — the same
convention pie/donut/gauge charts already use, so radial compositions read
the same way.
A filled donut segment; r_inner=0 collapses it to a pie slice. The building block for radial bar charts — one wedge per bar, r_outer mapped to the value.
A curved band connecting two arc spans on the same circle through its center — chord-diagram-style links between categories.
polar(cx, cy, r, deg) -> (x, y)
Pure coordinate math, no drawing — converts a radial position to (x, y) so you can place any other primitive (text, circle, line, a placed Chart) at a computed angle instead of hand-deriving trig every time.
This is the same shape as the radial pieces at
visualcinnamon.com — a center, a set of
wedges swept around it, labels placed with polar, and ribbons crossing
between spans. Nothing here is a dedicated "radial bar chart" type; it's the
five primitives above composed by hand, the same way the rest of Canvas
works — including mixing in a placed Chart at a polar-computed position
if the story calls for it.
Three more shapes built from the same handful of primitives — no new API
below this line, just polar, curve, wedge and connector combined
differently each time.
A spiral — each point's radius grows with its index instead of staying
fixed, the technique behind timeline-as-spiral pieces like Searching for
Birds:
cx, cy = 300, 300
n = 60
cv = sp.canvas(600, 600, "#0a0a12")
pts = []
for i in range(n):
deg = i * 12
r = 20 + i * 4.2
pts.append(list(cv.polar(cx, cy, r, deg)))
cv.curve(pts, color="#a78bfa", width=2, tension=0.8, name="spiral")
for i in range(0, n, 4):
x, y = pts[i]
cv.circle(x, y, 3 + (i / n) * 5, fill="#22d3ee", name=f"pt-{i}")
chart = cv.build()
A sunburst — two rings of wedge, the outer ring's spans computed from
the inner ring's proportions instead of an even split, giving a hierarchical
part-of-a-part-of-a-whole read:
A radial network — nodes placed on a circle with polar, random pairs
joined with connector's bend for a soft curve instead of a straight
chord; the same "relationships between things" story ribbon tells, drawn
node-and-edge instead of band-and-arc:
import random
cx, cy = 300, 300
n = 14
cv = sp.canvas(600, 600, "#0a0a12")
cv.radial_gradient("net-glow", "#1e1b4b", "#0a0a12", r=0.85)
cv.circle(cx, cy, 280, fill="url(#net-glow)", name="bg")
nodes = [cv.polar(cx, cy, 220, i * 360 / n) for i in range(n)]
edges = set()
while len(edges) < 22:
a, b = random.sample(range(n), 2)
edges.add((min(a, b), max(a, b)))
for a, b in edges:
ax, ay = nodes[a]
bx, by = nodes[b]
cv.connector(ax, ay, bx, by, color="#4c1d95", width=1, opacity=0.5, bend=0.15, name=f"edge-{a}-{b}")
for i, (x, y) in enumerate(nodes):
cv.circle(x, y, 8, fill="#a78bfa", stroke="#0a0a12", stroke_width=2, name=f"node-{i}")
chart = cv.build()
RéciTAC (Nadieh Bremer)
is a d3.js/Canvas network diagram organizing a research program's projects,
people, and outcomes into a central cluster flanked by two semi-circles —
researchers' disciplines on one side, activities/outputs on the other —
with small donut rings around each project node encoding the disciplinary
mix of everyone connected to it. None of that needs a dedicated "network
chart" type: it's polar for every node position, wedge stacked into a
per-node donut, connector for the edges, and link so hovering a project
highlights its whole neighborhood — the same primitives from every section
above, composed around one idea.
import seraplot as sp
DISCIPLINES = {"Climate": "#38bdf8", "Social": "#f472b6", "Engineering": "#a3e635", "Policy": "#fbbf24"}
RESEARCHERS = [("Amara", "Climate"), ("Lian", "Climate"), ("Devi", "Social"),
("Noah", "Social"), ("Jamal", "Engineering"), ("Elin", "Policy")]
ACTIVITIES = ["Workshops", "Sensors", "Policy Brief", "Dataset", "Exhibition"]
PROJECTS = [
("Coastal Lab", [0, 1, 3, 5], [0, 1]),
("Urban Heat", [1, 2, 4], [1, 3]),
("Youth Climate", [2, 3, 5], [2, 4]),
]
cx, cy = 450, 450
cv = sp.Canvas(900, 900)
cv.radial_gradient("bg", "#141b2e", "#05070d", cx=0.5, cy=0.46, r=0.75)
cv.rect(0, 0, 900, 900, fill="url(#bg)", layer="bg")
left = [cv.polar(cx, cy, 340, 210 + i * 25) for i in range(len(RESEARCHERS))]
right = [cv.polar(cx, cy, 340, 30 + i * 25) for i in range(len(ACTIVITIES))]
proj_pos = [cv.polar(cx, cy, 150, i * 360 / len(PROJECTS)) for i in range(len(PROJECTS))]
for i, (name, disc) in enumerate(RESEARCHERS):
x, y = left[i]
cv.circle(x, y, 8, fill=DISCIPLINES[disc], stroke="#0b0e18", stroke_width=2, name=f"res{i}")
for i, name in enumerate(ACTIVITIES):
x, y = right[i]
cv.rect(x - 6, y - 6, 12, 12, fill="#e2e8f0", rotation=45, name=f"act{i}")
for pi, (name, r_idx, a_idx) in enumerate(PROJECTS):
px, py = proj_pos[pi]
for ri in r_idx:
rx, ry = left[ri]
cv.connector(rx, ry, px, py, color=DISCIPLINES[RESEARCHERS[ri][1]], width=1.2, opacity=0.35, bend=0.2)
for ai in a_idx:
ax, ay = right[ai]
cv.connector(px, py, ax, ay, color="#64748b", width=1, opacity=0.3, bend=0.2)
counts = {}
for ri in r_idx:
counts[RESEARCHERS[ri][1]] = counts.get(RESEARCHERS[ri][1], 0) + 1
start = 0.0
for d, count in counts.items():
end = start + count / len(r_idx) * 360
cv.wedge(px, py, 34, 42, start, end, fill=DISCIPLINES[d])
start = end
cv.circle(px, py, 30, fill="#111827", stroke="#f8fafc", stroke_width=2, name=f"proj{pi}")
cv.text(name, px, py + 4, size=11, color="#f8fafc", weight="600", anchor="middle")
members = [f"proj{pi}"] + [f"res{ri}" for ri in r_idx] + [f"act{ai}" for ai in a_idx]
cv.link(f"cluster{pi}", members)
chart = cv.build()
The dataset above is synthetic — the point is the composition pattern, not
a literal port of Nadieh's real research-program data (which comes from a
private Google Sheet). Everything scales with the data: add a discipline to
the dict and every donut ring picks it up automatically; add a researcher
to a project's list and a new connector + donut slice appear on the next
build().
place() embeds a full Chart — not just a primitive — inside a canvas,
which means canvas composition isn't limited to hand-drawn shapes: real
sp.line(), sp.bar(), sp.gauge(), sp.area() panels can sit framed,
connected, and annotated by the exact same primitives used everywhere else
on this page. The part that actually sells "dashboard" over "four charts
in boxes" is the same trick as the RéciTAC network above: a shared
center every panel connects to. One glowing hub, four color-matched
spokes (connector + a circle anchor at each end), each spoke tinted to
match the panel it comes from via the chart-level chainable methods
(palette(), gridlines(), width()/height() — see
Chart Methods) applied before
place():
The hub itself is a small radial gauge in disguise — four wedge slices
(one per panel, in that panel's color) inside two ring pulse tracks,
built from exactly the same primitives as the "Composing micro-tools"
dial and the RéciTAC donut rings above. Reusing one visual language across
every worked example on this page is the actual point: primitives don't
know or care whether they're drawing a progress dial, a discipline ring,
or a dashboard hub.
panel_frame() draws a rounded card plus a thin color-matched top border
— that accent color is the same one used for that panel's spoke and
palette(), so the eye connects "this line is orange" to "this panel is
orange" to "this spoke is orange" without a legend. The one easy-to-miss
detail behind all of it: any decoration meant to sit behind a placed
chart (background fill, panel frames) needs layer="bg" explicitly —
rect()'s default layer="fg" renders on top of placed charts by
design (so connectors and callouts can cross over them), which will
silently hide a panel's contents if the panel itself is drawn on the
foreground layer.
voronoi(sites, x, y, w, h, fills=None, stroke=..., stroke_width=..., opacity=...)
computes a bounded Voronoi diagram — one cell per site, each cell the region
closer to that site than to any other — and adds every cell to the canvas as
a polygon() in one call, returning their element indices for later
addressing (hover groups, derive(), etc.). This is the same organic,
cell-based layout technique behind editorial data-journalism pieces like
Nadieh Bremer's Highly Hazardous Pesticides
— no off-the-shelf "voronoi chart type" needed, just the primitive.
import random
cv = sp.Canvas(900, 540)
sites = [[random.uniform(30, 870), random.uniform(30, 510)] for _ in range(22)]
palette = ["#6366f1", "#ec4899", "#22c55e", "#f59e0b", "#06b6d4", "#8b5cf6", "#ef4444"]
fills = [palette[i % len(palette)] for i in range(len(sites))]
cv.voronoi(sites, 0, 0, 900, 540, fills=fills, stroke="#0d1117", stroke_width=2, opacity=0.88)
Cell size follows site density automatically — cluster sites tightly to
shrink their cells, useful for a treemap-like "one cell per record, colored
by category, sized by local density" layout without a separate packing
algorithm. Implemented natively (iterative half-plane clipping against
every other site, no external geometry crate) rather than pulled in as a
dependency.
Two different mechanisms, both driven by element names:
group(group_name, member_names) / move_group(group_name, dx, dy) —
moves several named elements together as a rigid unit. nudge(name, dx, dy)
and resize(name, dw, dh) do the same for a single element. Pins registered
on a chart before a move/resize are shifted along with it automatically.
link(group_name, member_names) -> int — ties elements across
different panels into one hover group: hovering any linked element (a
Chart, Rect, Text or Circle) glows/pulses all the others in the same
group. Returns how many of the given names were actually linkable (Line,
RawPath and other pure decoration types don't currently support it).
Pins are named anchor points registered inside a placed chart's coordinate
space, in canvas pixel coordinates. connect()/annotate_at() read pins to
draw a line or label between (or on top of) charts.
Method
Effect
pin(chart_ref, name, local_x, local_y)
Registers a pin at a chart-local pixel coordinate.
pin_frac(chart_ref, name, fx, fy)
Registers a pin at a fractional position (0..1) of the chart's native size.
Draws a curved connector between two pins, possibly on two different charts.
annotate_at(chart_ref, pin_name, text, ...)
Draws a leader-line label pointing at a pin.
Pins go stale when the geometry they were computed from changes.refill() on a chart clears its pins (so you don't silently connect to
coordinates that belonged to the old content) — re-pin after refilling if you
still need them. nudge/resize/move_group, on the other hand, do shift
existing pins automatically, since the underlying content hasn't changed.
skeleton = base_canvas.template() # strip Chart/Image elements, keep everything else
dashboard = skeleton.derive() # deep-clone a fresh instance to fill in
dashboard.fill("main", my_chart, name="panel")
template() returns a canvas with all place()d charts and image()s
removed but every decorative element (cards, gradients, titles, slots,
groups, custom CSS/JS) intact — the reusable "class". derive() deep-clones
any canvas (templated or not) into an independent instance — the
"constructor call". Build your branded skeleton once, derive() + fill()
it per dataset/variant instead of repeating layout code.
Serializes the full canvas state (elements, pins, groups, slots, custom CSS/JS) to JSON.
sp.canvas_load(path) -> Canvas
Rebuilds a canvas from a saved JSON file.
sp.canvas_save_named(cv, name) -> str
Saves under ~/.seraplot/canvas/{name}.json and updates an index.json manifest.
sp.canvas_load_named(name) -> Canvas
Loads back via that manifest.
to_json() -> str
The raw JSON string, if you want to manage storage yourself.
This is what lets a generated dashboard survive closing and reopening the
app: cv.save(...) once, sp.canvas_load(...) next session reconstructs an
identical canvas — positions, links, styling, everything.
Renders the canvas with a floating panel: drag any named element to move it,
drag the corner handle on charts/images to resize them, hover shows the
element's name and its linked group (if any). The panel's Copy Python
button generates the equivalent cv.nudge(...)/cv.resize(...) calls;
Download JSON exports the same deltas to a file that apply_deltas_json()
can replay headlessly (cv.apply_deltas_json(open(path).read())) — the
route from interactive tweaking to a reproducible script.
Canvas est la couche de composition de SeraPlot : une surface libre où l'on
place plusieurs Chart construits indépendamment, des images, des formes et
du texte, où on les relie entre eux, puis on exporte le tout comme un seul
Chart. C'est l'outil pour construire des dashboards, des histoires
annotées et des figures multi-panneaux qu'une seule fonction de chart ne
peut pas produire seule.
Chaque méthode de placement/dessin accepte un mot-clé name optionnel. Un
élément nommé reçoit un attribut data-sp-name="..." dans le HTML généré,
ce qui le rend adressable par toutes les autres méthodes ci-dessous
(nudge, resize, style, script, group, link, refill, le
glisser-déposer du mode dev, ...). Nommer est gratuit et sans coût
d'exécution — nommez tout ce que vous pourriez vouloir retoucher.
place(chart, x, y, w, h, rotation=0, opacity=1, clip="", group="", name="")
Place un Chart sur le canvas à (x, y) de taille w×h. Renvoie un chart_ref (int) utilisé par pin/connect/attach_*.
image(src, x, y, w, h, rotation=0, opacity=1, clip="", group="", name="")
Place une image PNG/JPEG/GIF/WebP/SVG. src est un chemin de fichier local (lu et encodé en base64), une URI data:, ou une URL http(s)://.
slot(name, x, y, w, h)
Réserve une région nommée sans encore rien y placer.
fill(slot_name, chart, ...)
Place un chart en utilisant la géométrie d'un slot déclaré au préalable.
grid(x, y, w, h, rows, cols, gap_x=0, gap_y=0)
Déclare une grille rows × cols de slots nommés cell_{row}_{col} et renvoie la liste des noms en ordre ligne par ligne — plus de calcul de coordonnées à la main pour un dashboard.
refill(name, chart) -> bool
Remplace le contenu d'un chart nommé et déjà placé, en conservant position, taille, rotation, style. À utiliser (au lieu de rappeler place/fill) chaque fois que vous rafraîchissez un panneau avec de nouvelles données.
clip accepte "circle", "diamond", "hex", "tri", "pentagon" pour
un cadrage non rectangulaire du chart/de l'image.
Rappeler place/fill avec un nom déjà pris par un chart placé met à
jour ce chart en place au lieu d'empiler un doublon — fill(slot, new_chart, name="panel") peut donc être rappelé sans risque.
text, line, curve, connector, circle, ring, rect, polygon,
path, arrow, annotate et gradient sont les primitives de dessin bas
niveau à partir desquelles tout le reste de Canvas (y compris voronoi(),
plus bas) est construit. Elles dessinent directement sur le SVG du canvas,
et partagent deux mots-clés :
layer="fg"|"bg" — "bg" s'affiche sous chaque chart/image place()é,
"fg" (par défaut) s'affiche au-dessus. Utilisez "bg" pour les fonds,
panneaux-cartes et décorations façon filigrane ; "fg" pour les
annotations, callouts et tout ce qui doit rester au-dessus des données.
name="" — rend l'élément adressable ensuite par nudge, resize,
style, script, group, link, et déplaçable au glisser-déposer en
mode dev(). Nommer est gratuit ; nommez tout ce que vous pourriez
vouloir retoucher.
Aucune de ces primitives ne nécessite un Chart — un canvas ne contenant
que ces primitives est une façon parfaitement valide de dessiner à la main
un diagramme pour lequel SeraPlot n'a pas de fonction de chart dédiée (voir
l'exemple de clôture de cette page).
stroke-dasharray SVG, ex. "6,4" pour un trait pointillé
opacity
float
1.0
0–1
cap
str
"round"
Style d'extrémité : "round", "butt", ou "square"
hover_group
str
""
Ajoute une zone de survol invisible plus large que le trait visible (souvent fin), pour que la ligne réagisse au survol en tant que membre d'un groupe link()
name
str
""
Nom adressable
La primitive ligne droite la plus simple — axes, séparateurs, lignes
guides, ou un segment d'un diagramme construit à la main.
Tension Catmull-Rom : 0 réduit la courbe à des segments droits entre les points, 1 donne une spline lissée standard, des valeurs plus hautes accentuent les bombements après chaque point
fill
str
"none"
Remplit la zone sous la courbe si défini — effet aire dessinée à la main
(color / width / opacity / layer / name — comme line)
Contrairement à connector (ci-dessous), qui relie toujours exactement
deux points, curve interpole à travers une polyligne arbitraire de
points de passage. À utiliser pour des lignes de tendance dessinées à la
main, des décorations façon sparkline, ou des traits organiques libres non
liés aux axes d'un Chart :
Fraction, le long de l'axe dominant (celui de dx/dy le plus grand), où se placent les points de contrôle de la bézier — 0.5 donne une courbe en S symétrique ; des valeurs vers 0 ou 1 décalent le milieu de la courbe vers une extrémité
(autres — comme line)
La primitive « fil de flowchart » : une seule bézier cubique qui produit
toujours un tracé propre en S ou en L entre deux points, quelle que soit
leur position relative. À utiliser pour relier deux panneaux place()és
ou deux éléments nommés sans calculer les points de contrôle à la main —
connect() (sous Connecter deux charts, plus bas) trace exactement la
même courbe, mais lit ses extrémités depuis des pins enregistrés plutôt
que des coordonnées brutes.
ring est un anneau (donut) : la région remplie strictement entre
inner_r et outer_r, construite à partir de deux arcs combinés avec
fill-rule="evenodd" plutôt qu'un <circle> plein. À utiliser pour des
anneaux de progression radiale, des cadres d'avatar, ou des halos de mise
en valeur — partout où le disque plein de circle masquerait ce qu'il y a
en dessous.
rx arrondit les coins ; rotation (en degrés) fait pivoter le rectangle
autour de son propre centre. Les deux ensemble couvrent les fonds de
carte, badges/puces, et échantillons de légende de catégorie.
Une forme fermée à travers une liste arbitraire list[[x, y]] de
sommets — la primitive à partir de laquelle voronoi() elle-même est
construite (chaque cellule qu'elle renvoie est un appel polygon() en
coulisses). À utiliser directement pour des marqueurs triangulaires/en
losange, des formes de badge personnalisées, ou toute région fermée que
rect/circle ne peuvent pas exprimer.
L'échappatoire : d est une chaîne de données de chemin SVG brute
("M ... L ... A ... Z") pour les formes qu'aucune autre primitive ne
couvre — logos, icônes, arcs avec un balayage spécifique, ou géométrie
calculée par votre propre code. C'est exactement ainsi que la variante
"radial" d'icicle() dessine ses secteurs
annulaires en interne : des chaînes M/A/L/Z construites à la main,
sans primitive d'arc séparée.
anchor est le text-anchor SVG ("start", "middle", "end") relatif à
(x, y) — "middle" centre un titre au-dessus d'un panneau, "end"
aligne une valeur à droite le long d'un axe.
Où se trouve le texte lui-même — où finit la ligne de rappel
text
str
requis
Supporte \n pour des étiquettes multi-lignes
line_dash
str
""
Motif de tirets pour la ligne de rappel, ex. "4,3"
bg
str
""
Couleur de fond derrière le texte ; ""/"none" ne dessine aucun cadre
Contrairement à une paire text() + line(), annotate() route
automatiquement un coude propre en deux segments entre (ax, ay) et
(tx, ty) (le point de coude choisi selon l'axe ayant le plus grand
écart) et dimensionne son propre cadre de fond pour s'ajuster au texte.
L'outil pour « ce point précis, étiqueté, avec une ligne d'appel » — le
pic d'une barre, un point aberrant sur un scatter. annotate_at() (sous
Connecter deux charts, plus bas) est la version « pin-aware » de cette
même primitive, pour étiqueter un point à l'intérieur d'un chart place()é
plutôt qu'une coordonnée canvas brute.
Enregistre une définition linearGradient SVG — x1/y1/x2/y2 vivent dans
l'espace objectBoundingBox 0..1, donc (0,0)→(1,0) va de gauche à
droite et (0,0)→(0,1) de haut en bas. Ne dessine rien par elle-même ;
appelez-la une fois, puis référencez fill=f"url(#{id})" sur n'importe
quel rect/circle/polygon/path suivant :
Aucune des primitives ci-dessus n'a besoin d'un Chart — un canvas
construit uniquement à partir d'elles est une façon parfaitement valide de
dessiner à la main un widget pour lequel SeraPlot n'a pas de fonction de
chart dédiée. ring ne dessine jamais qu'un anneau complet, donc un
cadran de progression à balayage partiel nécessite path avec un arc SVG
calculé à la main — exactement le rôle d'« échappatoire » décrit plus haut :
ring dessine la piste de fond statique, path dessine par-dessus l'arc
de progression réel (tracé avec le gradient défini juste avant), et
text centre le nombre — trois primitives issues de trois sections
différentes ci-dessus, une seule jauge autonome.
Le arc_path codé à la main ci-dessus, c'est exactement ce que font
arc/wedge en interne — utilisez-les en premier ; path reste
l'échappatoire pour les formes qu'aucun des deux ne couvre.
arc, wedge, ribbon, polar et radial_gradient partagent une
convention d'angle : degrés, 0° en haut, croissant dans le sens horaire
— la même convention que les charts pie/donut/gauge, pour que les
compositions radiales se lisent de la même façon.
Un segment d'anneau rempli ; r_inner=0 le réduit à une part de camembert. La brique de base des barres radiales — une wedge par barre, r_outer mappé sur la valeur.
Une bande courbe reliant deux plages d'arc sur le même cercle en passant par son centre — liens façon chord diagram entre catégories.
polar(cx, cy, r, deg) -> (x, y)
Du calcul de coordonnées pur, sans dessin — convertit une position radiale en (x, y) pour placer n'importe quelle autre primitive (text, circle, line, un Chart placé) à un angle calculé, sans refaire la trigonométrie à la main.
C'est la même construction que les pièces radiales de
visualcinnamon.com — un centre, des
wedges disposées tout autour, des labels placés avec polar, et des
ribbons qui traversent entre les plages. Rien ici n'est un type
« radial bar chart » dédié ; ce sont les cinq primitives ci-dessus
composées à la main, de la même façon que le reste de Canvas —
y compris en mélangeant un Chart placé à une position calculée par
polar si l'histoire le demande.
Trois formes de plus construites à partir des mêmes primitives — aucune
nouvelle API en dessous de cette ligne, juste polar, curve, wedge et
connector combinés différemment à chaque fois.
Une spirale — le rayon de chaque point grandit avec son index au lieu
de rester fixe, la technique derrière les pièces façon timeline-en-spirale
comme Searching for Birds :
cx, cy = 300, 300
n = 60
cv = sp.canvas(600, 600, "#0a0a12")
pts = []
for i in range(n):
deg = i * 12
r = 20 + i * 4.2
pts.append(list(cv.polar(cx, cy, r, deg)))
cv.curve(pts, color="#a78bfa", width=2, tension=0.8, name="spiral")
for i in range(0, n, 4):
x, y = pts[i]
cv.circle(x, y, 3 + (i / n) * 5, fill="#22d3ee", name=f"pt-{i}")
chart = cv.build()
Un sunburst — deux anneaux de wedge, les plages de l'anneau extérieur
calculées à partir des proportions de l'anneau intérieur plutôt qu'un
partage égal, pour une lecture hiérarchique « partie d'une partie d'un
tout » :
Un réseau radial — des nœuds placés sur un cercle avec polar, des
paires aléatoires reliées via le bend de connector pour une courbe
douce plutôt qu'une corde droite ; la même histoire de « relations entre
les choses » que raconte ribbon, dessinée nœuds-et-arêtes plutôt que
bandes-et-arcs :
import random
cx, cy = 300, 300
n = 14
cv = sp.canvas(600, 600, "#0a0a12")
cv.radial_gradient("net-glow", "#1e1b4b", "#0a0a12", r=0.85)
cv.circle(cx, cy, 280, fill="url(#net-glow)", name="bg")
nodes = [cv.polar(cx, cy, 220, i * 360 / n) for i in range(n)]
edges = set()
while len(edges) < 22:
a, b = random.sample(range(n), 2)
edges.add((min(a, b), max(a, b)))
for a, b in edges:
ax, ay = nodes[a]
bx, by = nodes[b]
cv.connector(ax, ay, bx, by, color="#4c1d95", width=1, opacity=0.5, bend=0.15, name=f"edge-{a}-{b}")
for i, (x, y) in enumerate(nodes):
cv.circle(x, y, 8, fill="#a78bfa", stroke="#0a0a12", stroke_width=2, name=f"node-{i}")
chart = cv.build()
RéciTAC (Nadieh Bremer)
est un diagramme réseau d3.js/Canvas qui organise les projets, les
personnes et les résultats d'un programme de recherche en un cluster
central flanqué de deux demi-cercles — les disciplines des chercheurs d'un
côté, les activités/résultats de l'autre — avec de petits anneaux donut
autour de chaque nœud projet encodant le mix disciplinaire de tous ceux qui
y sont connectés. Rien de tout cela ne nécessite un type "graphique réseau"
dédié : c'est polar pour chaque position de nœud, wedge empilés en un
donut par nœud, connector pour les arêtes, et link pour que survoler un
projet mette en valeur tout son voisinage — les mêmes primitives que dans
chaque section ci-dessus, composées autour d'une seule idée.
import seraplot as sp
DISCIPLINES = {"Climate": "#38bdf8", "Social": "#f472b6", "Engineering": "#a3e635", "Policy": "#fbbf24"}
RESEARCHERS = [("Amara", "Climate"), ("Lian", "Climate"), ("Devi", "Social"),
("Noah", "Social"), ("Jamal", "Engineering"), ("Elin", "Policy")]
ACTIVITIES = ["Workshops", "Sensors", "Policy Brief", "Dataset", "Exhibition"]
PROJECTS = [
("Coastal Lab", [0, 1, 3, 5], [0, 1]),
("Urban Heat", [1, 2, 4], [1, 3]),
("Youth Climate", [2, 3, 5], [2, 4]),
]
cx, cy = 450, 450
cv = sp.Canvas(900, 900)
cv.radial_gradient("bg", "#141b2e", "#05070d", cx=0.5, cy=0.46, r=0.75)
cv.rect(0, 0, 900, 900, fill="url(#bg)", layer="bg")
left = [cv.polar(cx, cy, 340, 210 + i * 25) for i in range(len(RESEARCHERS))]
right = [cv.polar(cx, cy, 340, 30 + i * 25) for i in range(len(ACTIVITIES))]
proj_pos = [cv.polar(cx, cy, 150, i * 360 / len(PROJECTS)) for i in range(len(PROJECTS))]
for i, (name, disc) in enumerate(RESEARCHERS):
x, y = left[i]
cv.circle(x, y, 8, fill=DISCIPLINES[disc], stroke="#0b0e18", stroke_width=2, name=f"res{i}")
for i, name in enumerate(ACTIVITIES):
x, y = right[i]
cv.rect(x - 6, y - 6, 12, 12, fill="#e2e8f0", rotation=45, name=f"act{i}")
for pi, (name, r_idx, a_idx) in enumerate(PROJECTS):
px, py = proj_pos[pi]
for ri in r_idx:
rx, ry = left[ri]
cv.connector(rx, ry, px, py, color=DISCIPLINES[RESEARCHERS[ri][1]], width=1.2, opacity=0.35, bend=0.2)
for ai in a_idx:
ax, ay = right[ai]
cv.connector(px, py, ax, ay, color="#64748b", width=1, opacity=0.3, bend=0.2)
counts = {}
for ri in r_idx:
counts[RESEARCHERS[ri][1]] = counts.get(RESEARCHERS[ri][1], 0) + 1
start = 0.0
for d, count in counts.items():
end = start + count / len(r_idx) * 360
cv.wedge(px, py, 34, 42, start, end, fill=DISCIPLINES[d])
start = end
cv.circle(px, py, 30, fill="#111827", stroke="#f8fafc", stroke_width=2, name=f"proj{pi}")
cv.text(name, px, py + 4, size=11, color="#f8fafc", weight="600", anchor="middle")
members = [f"proj{pi}"] + [f"res{ri}" for ri in r_idx] + [f"act{ai}" for ai in a_idx]
cv.link(f"cluster{pi}", members)
chart = cv.build()
Le jeu de données ci-dessus est synthétique — l'intérêt est le motif de
composition, pas un portage littéral des vraies données du programme de
recherche de Nadieh (issues d'une feuille Google Sheets privée). Tout
s'adapte aux données : ajouter une discipline au dict et chaque anneau
donut la prend en compte automatiquement ; ajouter un chercheur à la liste
d'un projet et un nouveau connecteur + une nouvelle part de donut
apparaissent au prochain build().
place() intègre un Chart complet — pas seulement une primitive — dans
un canvas, ce qui signifie que la composition canvas ne se limite pas aux
formes dessinées à la main : de vrais panneaux sp.line(), sp.bar(),
sp.gauge(), sp.area() peuvent être encadrés, connectés et annotés par
les mêmes primitives que partout ailleurs sur cette page. Ce qui fait
vraiment la différence entre "tableau de bord" et "quatre charts dans des
boîtes", c'est la même astuce que le réseau RéciTAC ci-dessus : un
centre partagé auquel chaque panneau se connecte. Un hub lumineux, 4
rayons colorés (connector + une ancre circle à chaque bout), chaque
rayon teinté pour correspondre à son panneau via les méthodes chainables
de niveau chart (palette(), gridlines(), width()/height() — voir
Méthodes de graphique) appliquées
avant place() :
Le hub lui-même est une petite jauge radiale déguisée — 4 parts de wedge
(une par panneau, dans sa couleur) à l'intérieur de deux pistes ring
pulsées, construites avec exactement les mêmes primitives que le cadran
"Composer les micro-outils" et les anneaux donut RéciTAC ci-dessus.
Réutiliser un seul langage visuel sur chaque exemple travaillé de cette
page est tout l'intérêt : les primitives ne savent pas et ne se soucient
pas de dessiner un cadran de progression, un anneau de discipline, ou un
hub de tableau de bord.
panel_frame() dessine une carte arrondie plus une fine bordure supérieure
teintée — cette couleur d'accent est la même utilisée pour le rayon de ce
panneau et son palette(), pour que l'œil relie "cette ligne est orange"
à "ce panneau est orange" à "ce rayon est orange" sans légende. Le détail
facile à manquer derrière tout ça : toute décoration censée se trouver
derrière un chart placé (fond, cadres de panneau) a besoin de
layer="bg" explicitement — le layer="fg" par défaut de rect() se
dessine au-dessus des charts placés par conception (pour que
connecteurs et annotations puissent les traverser), ce qui masquera
silencieusement le contenu d'un panneau si celui-ci est dessiné sur la
couche de premier plan.
voronoi(sites, x, y, w, h, fills=None, stroke=..., stroke_width=..., opacity=...)
calcule un diagramme de Voronoi borné — une cellule par site, chaque cellule
étant la région plus proche de ce site que de tout autre — et ajoute chaque
cellule au canvas comme un polygon() en un seul appel, en renvoyant leurs
indices d'éléments pour un adressage ultérieur (groupes de survol,
derive(), etc.). C'est la même technique de mise en page organique par
cellules derrière des pièces éditoriales de data-journalisme comme
Highly Hazardous Pesticides de Nadieh Bremer
— pas besoin d'un "type de chart voronoi" tout fait, juste la primitive.
import random
cv = sp.Canvas(900, 540)
sites = [[random.uniform(30, 870), random.uniform(30, 510)] for _ in range(22)]
palette = ["#6366f1", "#ec4899", "#22c55e", "#f59e0b", "#06b6d4", "#8b5cf6", "#ef4444"]
fills = [palette[i % len(palette)] for i in range(len(sites))]
cv.voronoi(sites, 0, 0, 900, 540, fills=fills, stroke="#0d1117", stroke_width=2, opacity=0.88)
La taille des cellules suit automatiquement la densité des sites — resserrer
des sites rétrécit leurs cellules, utile pour une mise en page façon treemap
("une cellule par enregistrement, colorée par catégorie, dimensionnée par
densité locale") sans algorithme de packing séparé. Implémenté nativement
(découpage itératif par demi-plans contre chaque autre site, aucune
dépendance de géométrie externe).
Deux mécanismes distincts, tous deux pilotés par les noms d'éléments :
group(group_name, member_names) / move_group(group_name, dx, dy) —
déplace plusieurs éléments nommés ensemble comme un bloc rigide. nudge(name, dx, dy) et resize(name, dw, dh) font pareil pour un seul élément. Les
pins enregistrés sur un chart avant un déplacement/redimensionnement sont
automatiquement décalés avec lui.
link(group_name, member_names) -> int — relie des éléments à
travers des panneaux différents en un seul groupe de survol : survoler
n'importe quel élément lié (Chart, Rect, Text ou Circle) fait
briller/pulser tous les autres du même groupe. Renvoie le nombre de noms
effectivement liables (Line, RawPath et autres types purement
décoratifs ne le supportent pas encore).
Les pins sont des points d'ancrage nommés enregistrés dans l'espace de
coordonnées d'un chart placé, en coordonnées pixel du canvas.
connect()/annotate_at() lisent les pins pour tracer une ligne ou une
étiquette entre (ou par-dessus) des charts.
Méthode
Effet
pin(chart_ref, name, local_x, local_y)
Enregistre un pin à une coordonnée pixel locale au chart.
pin_frac(chart_ref, name, fx, fy)
Enregistre un pin à une position fractionnaire (0..1) de la taille native du chart.
Trace un connecteur courbe entre deux pins, éventuellement sur deux charts différents.
annotate_at(chart_ref, pin_name, text, ...)
Trace une étiquette avec ligne de rappel pointant vers un pin.
Les pins deviennent obsolètes quand la géométrie qui les a produits
change.refill() sur un chart efface ses pins (pour ne pas connecter
silencieusement vers des coordonnées appartenant à l'ancien contenu) —
re-pinnez après un refill si vous en avez encore besoin. nudge/resize/
move_group, en revanche, décalent bien les pins existants automatiquement,
puisque le contenu sous-jacent n'a pas changé.
skeleton = base_canvas.template() # retire les Chart/Image, garde le reste
dashboard = skeleton.derive() # clone profond d'une instance prête à remplir
dashboard.fill("main", my_chart, name="panel")
template() renvoie un canvas dont tous les charts place()és et images
image()ées sont retirés, mais où chaque élément décoratif (cartes,
dégradés, titres, slots, groupes, CSS/JS custom) reste intact — la "classe"
réutilisable. derive() clone en profondeur n'importe quel canvas
(templatisé ou non) en une instance indépendante — "l'instanciation".
Construisez votre squelette de marque une fois, puis derive() + fill()
par jeu de données/variante au lieu de répéter le code de mise en page.
Sérialise tout l'état du canvas (éléments, pins, groupes, slots, CSS/JS custom) en JSON.
sp.canvas_load(path) -> Canvas
Reconstruit un canvas depuis un fichier JSON sauvegardé.
sp.canvas_save_named(cv, name) -> str
Sauvegarde sous ~/.seraplot/canvas/{name}.json et met à jour un manifeste index.json.
sp.canvas_load_named(name) -> Canvas
Recharge via ce manifeste.
to_json() -> str
La chaîne JSON brute, pour gérer soi-même le stockage.
C'est ce qui permet à un dashboard généré de survivre à la fermeture et à la
réouverture de l'application : cv.save(...) une fois, sp.canvas_load(...)
à la session suivante reconstruit un canvas identique — positions, liens,
style, tout.
Rend le canvas avec un panneau flottant : glissez n'importe quel élément
nommé pour le déplacer, glissez la poignée en coin des charts/images pour
les redimensionner, le survol affiche le nom de l'élément et son groupe lié
(le cas échéant). Le bouton Copy Python du panneau génère les appels
cv.nudge(...)/cv.resize(...) équivalents ; Download JSON exporte les
mêmes deltas dans un fichier que apply_deltas_json() peut rejouer sans
interface (cv.apply_deltas_json(open(path).read())) — le chemin entre
ajustement interactif et script reproductible.
sp.App is a small, dependency-free reactive dashboard server built directly
into the Rust core — no Flask, no Dash, no Node. .serve() spins up a
Tokio-based HTTP + WebSocket server (hand-rolled HTTP/1.1 parsing and
RFC 6455 framing, no external web framework) that pushes live UI updates
to the browser whenever a registered callback re-runs.
App(title) creates a single-page app state with an implicit "/" page.
.page(path, title=None) registers/switches to additional pages; every
component call after it attaches to that page until the next .page().
Component builders (dropdown, slider, button, text_input,
checkbox, chart) render server-side HTML for that widget, register its
initial value, and append it to the current page's layout. All of them
return self, so calls chain.
.add_callback(inputs, output, handler) wires a Python callable: whenever
any component whose id is in inputs changes, handler is invoked with
the current value of every input, typed to its component — float
for a slider, bool for a checkbox, str for everything else —
positionally, in the order given to inputs. Its return value becomes
the new HTML for output — either a raw string, or any object exposing
an .html attribute (a Chart works directly, no .html access needed
on the caller's side).
.interval(seconds, output, handler) wires a Python callable that fires
on a server-side timer instead of a client event — no arguments, same
return contract as a callback. .push(id, html) sets a component's HTML
and broadcasts it to every connected browser immediately, from outside
any callback (e.g. from a background thread). Both bypass the
request/response cycle: they reach the browser over the same open
WebSocket, unprompted.
Each browser tab that opens /ws gets its own session — input values
are tracked per connection, so two tabs moving the same-id slider don't
clobber each other's callback inputs.
.auth(username, password) gates every request (page loads and the /ws
upgrade) behind HTTP Basic Auth; omit it and the app stays open.
.serve(port=8787, host="127.0.0.1") blocks and starts the server. The
browser opens a WebSocket to /ws; every input interaction sends
{"type":"event","id":...,"value":...}, the server re-runs matching
callbacks and pushes back {"type":"update","id":...,"html":...} — the
same message an .interval() tick or a .push() call sends — and a
~15-line bootstrap script does document.getElementById(id).innerHTML = html — no virtual DOM, no client-side framework.
Creates the page on first call, switches the "current page" on every call
.dropdown(id, options, value=None)
(str, list[str], str | None)
Defaults to options[0] if value omitted
.slider(id, min, max, step=1.0, value=None)
(str, float, float, float, float | None)
Defaults to min if value omitted
.button(id, label)
(str, str)
Emits value "click" on press
.text_input(id, value="", placeholder="")
(str, str, str)
.checkbox(id, label, checked=False)
(str, str, bool)
Emits "true"/"false"
.chart(id, html="")
(str, str)
Registers an output slot; typically seeded with a Chart.html and refreshed via a callback or .push()
.add_callback(inputs, output, handler)
(list[str], str, Callable)
handler receives one positional argument per entry in inputs, typed to its component (float/bool/str)
.interval(seconds, output, handler)
(float, str, Callable)
handler takes no arguments; fires on a repeating server-side timer, independent of any client event
.push(id, html)
(str, str | Chart)
Sets id's HTML and broadcasts it to every open connection immediately
.auth(username, password)
(str, str)
Gates every request behind HTTP Basic Auth
.serve(port=8787, host="127.0.0.1")
(int, str)
Blocking call
sp.App est un petit serveur de tableau de bord réactif, sans dépendance,
intégré directement au cœur Rust — pas de Flask, pas de Dash, pas de Node.
.serve() démarre un serveur HTTP + WebSocket basé sur Tokio (parsing
HTTP/1.1 et trames RFC 6455 écrits à la main, sans framework web
externe) qui pousse les mises à jour de l'interface vers le navigateur à
chaque nouvelle exécution d'un callback enregistré.
Ouvrez http://127.0.0.1:8787/ — changer le menu déroulant relance
on_change côté serveur et pousse le nouveau HTML du graphique dans la page
sans rechargement.
App(title) crée un état d'application avec une page implicite "/".
.page(path, title=None) enregistre/bascule vers d'autres pages ; chaque
appel de composant suivant s'attache à cette page jusqu'au .page()
suivant.
Les constructeurs de composants (dropdown, slider, button,
text_input, checkbox, chart) génèrent le HTML côté serveur du
widget, enregistrent sa valeur initiale et l'ajoutent à la mise en page de
la page courante. Tous retournent self, donc les appels s'enchaînent.
.add_callback(inputs, output, handler) relie un callable Python : dès
qu'un composant dont l'id figure dans inputs change, handler est
appelé avec la valeur courante de chaque input, typée selon son
composant — float pour un slider, bool pour une checkbox, str
pour le reste — en positionnel, dans l'ordre de inputs. Sa valeur de
retour devient le nouveau HTML de output — une chaîne brute, ou tout
objet exposant un attribut .html (un Chart fonctionne directement,
sans accès .html côté appelant).
.interval(seconds, output, handler) relie un callable Python déclenché
par un minuteur côté serveur plutôt qu'un événement client — sans
argument, même contrat de retour qu'un callback. .push(id, html) fixe
le HTML d'un composant et le diffuse immédiatement à toutes les
connexions ouvertes, depuis l'extérieur de tout callback (par ex. depuis
un thread d'arrière-plan). Les deux contournent le cycle
requête/réponse : ils atteignent le navigateur sur le même WebSocket
ouvert, sans sollicitation préalable.
Chaque onglet de navigateur qui ouvre /ws obtient sa propre
session — les valeurs des inputs sont suivies par connexion, donc
deux onglets qui modifient un slider de même id ne s'écrasent pas
mutuellement dans les callbacks.
.auth(username, password) protège chaque requête (chargements de page
et upgrade /ws) derrière une authentification HTTP Basic ; omise,
l'application reste ouverte.
.serve(port=8787, host="127.0.0.1") bloque et démarre le serveur. Le
navigateur ouvre un WebSocket vers /ws ; chaque interaction envoie
{"type":"event","id":...,"value":...}, le serveur relance les callbacks
correspondants et repousse {"type":"update","id":...,"html":...} — le
même message qu'envoie un tick .interval() ou un appel .push() — et
un script d'amorçage d'une quinzaine de lignes fait
document.getElementById(id).innerHTML = html — pas de DOM virtuel, pas
de framework côté client.
import seraplot as sp
import random
random.seed(0)
pts = [(random.gauss(cx, 0.3), random.gauss(cy, 0.3))
for cx, cy in [(0,0),(3,0),(1.5,2.5)] for _ in range(500)]
x, y = zip(*pts)
model = sp.KMeans(k=3)
labels = model.fit_predict([[xi, yi] for xi, yi in zip(x, y)])
# Build chart with known labels
chart = sp.kmeans(
title="K-Means Result",
x_values=list(x),
y_values=list(y),
k=3,
)
chart.show()
print(f"Inertia: {model.inertia_:.4f}")
import seraplot as sp
data = [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [0.0, 0.0]]
model = sp.KMeans(k=2)
model.fit(data)
distances = model.transform(data)
for i, row in enumerate(distances):
print(f"Point {i}: distances to centroids = {[f'{d:.3f}' for d in row]}")
K-Means minimises the total inertia — the sum of squared distances from each point to
its assigned centroid:
$$J = \sum_{i=1}^{n} \|x_i - \mu_{c(x_i)}\|^2$$
K-Means++ initialisation
The first centroid $\mu_1$ is chosen uniformly at random. Each subsequent centroid
$\mu_j$ is sampled with probability proportional to $D(x)^2$ — the squared distance to
the nearest already-placed centroid. This reduces the expected inertia at convergence
to $O(\log k)$ of optimal.
Iterations run until inertia delta $< $ tol or max_iter is reached.
transform(x) returns the $n \times k$ matrix of Euclidean distances from each
sample to each centroid, useful for soft-assignment and feature engineering.
Classe K-Means haute performance pour données N-dimensionnelles, compatible avec l'API scikit-learn. Passe automatiquement en mode mini-batch pour n > 100 000.
K-Means minimise l'inertie totale — la somme des carrés des distances de chaque point
à son centroïde assigné :
$$J = \sum_{i=1}^{n} \|x_i - \mu_{c(x_i)}\|^2$$
Initialisation K-Means++
Le premier centroïde $\mu_1$ est choisi de façon uniforme aléatoire. Chaque centroïde
suivant $\mu_j$ est échantillonné avec une probabilité proportionnelle à $D(x)^2$ — la
distance au carré au centroïde le plus proche déjà placé. Cela réduit l'inertie attendue
à la convergence à $O(\log k)$ de l'optimal.
Mise à jour : $\mu_k = \dfrac{1}{|C_k|}\displaystyle\sum_{x_i \in C_k} x_i$
Les itérations tournent jusqu'à ce que le delta d'inertie passe sous tol ou que
max_iter soit atteint.
transform(x) retourne la matrice $n \times k$ des distances euclidiennes de chaque
échantillon à chaque centroïde, utile pour l'affectation douce et l'ingénierie de
caractéristiques.
Border point: reachable from a core point but not itself a core point
Noise point: not reachable from any core point — assigned label $-1$
The 3D variant operates identically in $\mathbb{R}^3$ — the KD-tree extends to three
dimensions with SIMD-accelerated Euclidean distance $|p - q| = \sqrt{\Delta x^2 + \Delta y^2 + \Delta z^2}$.
When normalize=True, each axis is scaled to $[0, 1]$ independently before clustering,
so that the scale of $z$ does not distort $\epsilon$.
DBSCAN regroupe les points situés dans des régions denses et marque les points
isolés comme du bruit. Il ne nécessite pas de spécifier le nombre de clusters à
l'avance.
Point cœur : $|N_\epsilon(p)| \geq \text{min_samples}$
Point frontière : accessible depuis un point cœur, mais pas lui-même un point cœur
Point bruit : non accessible depuis aucun point cœur — label $-1$
La variante 3D fonctionne identiquement dans $\mathbb{R}^3$ — le KD-tree s'étend à
trois dimensions avec un calcul de distance euclidienne $|p - q| = \sqrt{\Delta x^2 + \Delta y^2 + \Delta z^2}$ accéléré par SIMD.
Avec normalize=True, chaque axe est normalisé dans $[0, 1]$ indépendamment avant le
clustering, de façon à ce que l'échelle de $z$ ne distorde pas $\epsilon$.
Border point: not core, but within $\epsilon$ of a core point
Noise point: not reachable from any core point → label $-1$
Algorithm:
For each unvisited point $p$:
If $p$ is core, start a new cluster via BFS (expand through density-connected neighbors)
Otherwise, mark as noise (or leave unvisited)
Clusters are maximal sets of density-connected points.
Implementation:
SeraPlot uses KD-tree for $O(\log n)$ radius queries and parallel BFS with SIMD distance acceleration. n_clusters_ counts only true clusters; noise points are excluded.
Complexity: $O(n \log n)$ average time; $O(n^2)$ worst case.
Point cœur: $|N_\epsilon(p)| \geq \text{min_samples}$
Point frontière: non cœur, mais dans $\epsilon$ d'un point cœur
Point bruit: non accessible depuis aucun point cœur → label $-1$
Algorithme:
Pour chaque point $p$ non visité:
Si $p$ est cœur, démarrer cluster via BFS
Sinon, marquer comme bruit
Les clusters sont ensembles maximaux de points densément connexes.
Implémentation:
SeraPlot utilise KD-tree pour $O(\log n)$ requêtes de rayon et BFS parallèle avec accélération SIMD. n_clusters_ ne compte que vrais clusters; bruit exclu.
Complexité: $O(n \log n)$ en moyenne; $O(n^2)$ pire cas.
"yeo-johnson" (any sign) or "box-cox" (positive only)
Attribute: lambdas_ : list[float]. Lambda is found by grid-searching $[-2, 2]$ and minimising variance after transform.
QuantileTransformer
Parameter
Type
Default
Description
n_quantiles
int
1000
Quantile knots
output_distribution
str
"uniform"
"uniform" or "normal"
Attribute: quantiles_ : list[list[float]].
OneHotEncoder / OrdinalEncoder
fit / transform accept list[list[Any]] of strings or numbers; categories are deduced per column.
Attribute: categories_ : list[list[Any]]. OneHotEncoder exposes n_features_out_.
Enumerates all monomials $\prod_j x_j^{a_j}$ with $\sum_j a_j \leq d$. With interaction_only=True, every $a_j \in {0, 1}$ (no pure powers). With include_bias=True, the constant 1 column is prepended.
Maps each feature to a uniform $[0, 1]$ distribution via its empirical CDF, then optionally re-maps to $\mathcal{N}(0, 1)$ via the inverse normal CDF (Beasley–Springer–Moro approximation).
OneHotEncoder builds the union of observed categories per column and emits one indicator per category. OrdinalEncoder assigns each category an integer index in fit-time order.
Attribut : lambdas_ : list[float]. Lambda est trouvé par recherche sur grille $[-2, 2]$ minimisant la variance après transformation.
QuantileTransformer
Paramètre
Type
Défaut
Description
n_quantiles
int
1000
Nœuds de quantiles
output_distribution
str
"uniform"
"uniform" ou "normal"
Attribut : quantiles_ : list[list[float]].
OneHotEncoder / OrdinalEncoder
fit / transform acceptent list[list[Any]] de chaînes ou de nombres ; les catégories sont déduites par colonne.
Attribut : categories_ : list[list[Any]]. OneHotEncoder expose n_features_out_.
Pour chaque feature $j$, ajuste une statistique $\theta_j$ sur les valeurs non manquantes
($\mathrm{NaN}$, $+\infty$, $-\infty$ sont considérés comme manquants) :
Énumère tous les monômes $\prod_j x_j^{a_j}$ avec $\sum_j a_j \leq d$. Avec interaction_only=True, chaque $a_j \in {0, 1}$ (pas de puissances pures). Avec include_bias=True, la colonne constante 1 est ajoutée en tête.
Mappe chaque feature vers une distribution uniforme $[0, 1]$ via sa CDF empirique, puis optionnellement re-mappe vers $\mathcal{N}(0, 1)$ via la CDF normale inverse (approximation de Beasley–Springer–Moro).
OneHotEncoder construit l'union des catégories observées par colonne et émet un indicateur par catégorie. OrdinalEncoder attribue à chaque catégorie un index entier dans l'ordre du fit.
Export predictions and feature matrices directly to PowerBI Push dataset JSON or Tableau TDS/CSV — native format, no extra tooling. / Export les prédictions et matrices de features en JSON PowerBI ou TDS/CSV Tableau.
✓ PowerBI JSON✓ Tableau TDS + CSV
PowerBI formatPushdataset API compatible
Tableau formatTDS + CSVdata source + extract
ColumnsX + y + ŷfeature, target, pred
📊
export_powerbi
Returns a JSON string representing a PowerBI Push dataset. Paste into the PowerBI REST API or save to .json.
📋
export_tableau_tds
Returns a Tableau Data Source XML (.tds). Defines all columns — open in Tableau Desktop to connect instantly.
📁
export_tableau_csv
Returns a CSV string with a header row. Includes feature columns, optional target and optional predictions.
🔗
Combine with Registry
Export predictions from a registry-loaded model payload and share reproducible exports alongside versioned models.
Quick start
import seraplot as sp, numpy as np
X = np.random.randn(200, 3)
y = X[:, 0] * 2 + np.random.randn(200) * 0.1
model = sp.LinearRegression()
model.fit(X, y)
yhat = model.predict(X)
pbi_json = sp.export_powerbi(
name = "HousePrice",
table = "Predictions",
X = X,
y = list(y),
y_pred = list(yhat),
)
with open("powerbi_dataset.json", "w") as f:
f.write(pbi_json)
tds_xml = sp.export_tableau_tds(
name = "HousePrice",
X = X,
y = list(y),
y_pred = list(yhat),
)
with open("house_price.tds", "w") as f:
f.write(tds_xml)
csv_str = sp.export_tableau_csv(
name = "HousePrice",
X = X,
y = list(y),
y_pred = list(yhat),
)
with open("house_price.csv", "w") as f:
f.write(csv_str)
Column names are auto-generated: feat_0 … feat_{p-1}, target, prediction. All functions return strings — write them to disk or POST them to the relevant API.
Les colonnes sont nommées automatiquement : feat_0 … feat_{p-1}, target, prediction. Toutes les fonctions retournent des chaînes de caractères — écrivez-les sur disque ou envoyez-les à l'API concernée via HTTP POST.
Backend detection and selection for CUDA, Metal (Apple Silicon), ROCm and CPU. Auto-detection at runtime with zero configuration. / Détection et sélection du backend GPU/CPU — CUDA, Metal, ROCm détectés automatiquement.
✓ CUDA detected✓ CPU always available
Backends4CPU · CUDA · Metal · ROCm
DetectionAutoenv-var + path probing
ConfigZerono setup required
⚠️
EN — Current state: The default pip install seraplot wheel ships CPU-only and uses rayon multi-threading. Real GPU kernels are available through opt-in feature flags compiled from source: cargo build --features cuda, --features metal, --features rocm. The detection and selection API works in both builds. FR — État actuel : La wheel par défaut pip install seraplot est CPU only avec multi-threading rayon. Les vrais kernels GPU sont disponibles via des feature flags opt-in compilés depuis les sources : cargo build --features cuda, --features metal, --features rocm. L'API de détection et sélection fonctionne dans les deux builds.
🔍
gpu_devices
List all detected devices with backend, name, memory and availability flag.
⚡
gpu_set_backend
Explicitly select a backend. Pass None for auto-selection (picks the first available non-CPU backend).
📍
gpu_active_backend
Returns the currently active backend name as a string.
Setting a backend does not automatically enable GPU kernels in the current version. It records your preference so that when kernel dispatch ships in 2.5.x, your code is already correct and ready.
Dans la version actuelle, définir un backend ne déclenche pas encore les kernels GPU. Cela enregistre votre préférence : quand le dispatch de kernels arrivera en 2.5.x, votre code sera déjà correct et prêt.
Ray-style scatter/gather primitives backed by rayon + auto-scaling planner for datasets >1M rows. Zero infra, pure Python. / Primitives scatter/gather style Ray via rayon + planificateur auto-scaling pour jeux de données >1M lignes.
✓ WorkerPool✓ cloud_plan
Max workersauto0 = rayon thread count
Scatter / gather✓allreduce_mean / sum
Planner✓in_memory / chunked / streamed
🔀
WorkerPool
Create a pool, scatter rows into shards, train per shard in parallel, then allreduce results back.
📐
cloud_plan
Given n_rows × n_cols and a memory budget, produces the optimal strategy: in_memory, chunked or streamed.
📡
cloud_resources
Runtime snapshot: CPU threads, active backend, OS, architecture, registry path.
📂
cloud_count_rows
Streaming CSV row count — constant memory regardless of file size. Useful before calling cloud_plan.
Quick start — WorkerPool
import seraplot as sp, numpy as np
n_rows = 10_000
X = np.random.randn(n_rows, 5)
y = X[:, 0] * 2 + np.random.randn(n_rows) * 0.1
wp = sp.WorkerPool(n_workers=0)
print(f"Pool: {wp.n_workers} workers")
handle = wp.scatter(n_rows)
shards = wp.shards(handle)
coefs = []
for shard in shards:
Xi = X[shard["start"]:shard["end"]]
yi = y[shard["start"]:shard["end"]]
model = sp.LinearRegression()
model.fit(Xi, yi)
coefs.append(model.coef_)
mean_coef = wp.allreduce_mean(coefs)
print(f"Aggregated coef: {mean_coef}")
wp.release(handle)
Retourne → int — nombre de lignes de données (en-tête exclu si has_header=True)
Référence des stratégies (cloud_plan)
Stratégie
Condition
Description
in_memory
Dataset tient dans le budget
Chargement complet, entraînement parallèle total
chunked
Dataset > budget, <2× budget par chunk
Traitement en chunks, agrégation des résultats
streamed
Dataset >> budget
Streaming ligne par ligne, agrégation en ligne
💡
Appelez cloud_count_rows(chemin) sur votre CSV, puis passez le résultat à cloud_plan pour obtenir la stratégie optimale avant de charger la moindre donnée en mémoire.
ROC curve / AUC — sweep all thresholds of $s_i$, plotting FPR vs. TPR; AUC is the area under that curve, equal to the probability that a random positive scores higher than a random negative.
Precision-Recall curve / Average Precision — same sweep, plotting Precision vs. Recall; AP is computed as the step-area:
Courbe ROC / AUC — balayage de tous les seuils de $s_i$, traçant FPR vs. TPR ; l'AUC est l'aire sous la courbe, égale à la probabilité qu'un positif aléatoire ait un score supérieur à celui d'un négatif aléatoire.
Courbe Précision-Rappel / Average Precision — même balayage, traçant Précision vs. Rappel ; AP est l'aire en escalier :
Silhouette — pour chaque échantillon $i$, avec $a_i$ la distance moyenne aux points du même cluster et $b_i$ la distance moyenne au cluster voisin le plus proche :
puis en combinant et mélangeant tous les ensembles test/train par classe. Cela garantit que les classes rares ne sont pas accidentellement exclues de l'ensemble de test.
Divise le jeu de données en $k$ plis non-chevauchants tout en préservant les distributions de classes dans chaque pli.
Algorithme :
1. Pour chaque classe $c$, collecter ses indices $\mathcal{I}_c = {i : y_i = c}$.
2. Optionnellement mélanger $\mathcal{I}_c$ avec random_state.
3. Diviser $\mathcal{I}_c$ en $k$ sous-tableaux approximativement égaux de taille $\lfloor|\mathcal{I}_c|/k\rfloor$ ou $\lceil|\mathcal{I}_c|/k\rceil$.
4. Pour le pli $f \in {0,\ldots,k-1}$ : l'ensemble de test est $\bigcup_c \mathcal{I}_c[f]$ et l'ensemble d'entraînement est son complément.
L'estimation d'erreur du $f$-ième pli $\hat{e}_f$ donne le score de validation croisée :
SeraDFrame is a columnar, Rust-native dataframe — Vec<f64> / Vec<String>
/ Vec<bool> per column, no per-cell object boxing — covering the common
pandas surface: relational joins, group-by/aggregate, sorting, filtering,
dedup, describe/corr, and a builder pattern, plus native, lossless
conversion to and from pandas.DataFrame so it drops into an existing
pandas pipeline anywhere.
Every table below is generated at page load straight from the #[sera_doc(...)]
attributes on each method in v2/src/data/dframe/ — not hand-maintained, so
it cannot drift from what is actually implemented. SeraDFrame methods do
not currently carry aliases the way chart functions do — one canonical name
per method, matching the underlying pandas-shaped surface directly.
SeraDFrame est un dataframe colonnaire, natif Rust — Vec<f64> /
Vec<String> / Vec<bool> par colonne, sans boxing objet par cellule —
couvrant la surface pandas courante : jointures relationnelles,
group-by/agrégation, tri, filtrage, dédoublonnage, describe/corr, un
patron builder, plus une conversion native et sans perte vers et depuis
pandas.DataFrame, pour s'insérer n'importe où dans un pipeline pandas
existant.
Chaque tableau ci-dessous est généré au chargement de la page directement
depuis les attributs #[sera_doc(...)] de chaque méthode dans
v2/src/data/dframe/ — pas maintenu à la main, donc impossible de dériver
de ce qui est réellement implémenté. Les méthodes SeraDFrame n'ont
actuellement pas d'alias comme les fonctions de graphique — un seul nom
canonique par méthode, reflétant directement la surface pandas sous-jacente.
Table is a small, columnar data-shaping utility: relational joins,
group-by/aggregate, pivots, rolling windows and filters, all in Rust, with no
pandas dependency. Its purpose is narrow and deliberate — reshape one source
of truth into the exact inputs each chart function expects, so several
panels built from the same data stay consistent, instead of hand-rolling
loops per chart.
Joins two tables on a key column. how="left" keeps unmatched left rows with zero-filled right columns. Colliding column names from other are prefixed right_.
concat(other) -> Table
Vertical union of two tables; missing columns on either side are zero/empty-filled.
with_column(name, op, left, right) -> Table
Adds a computed column. op is "add"/"sub"/"mul"/"div". right is either another column's name or a constant.
to_grouped_bar(index_col, columns_col, values_col, agg="sum") pivots and
flattens in one call, returning a dict shaped exactly for
sp.grouped_bar(labels=, values=, series_names=) — the most common
table-to-chart handoff, done in one line instead of manual pivoting.
For any other chart, column_f64/column_str after a filter_*/sort_by/
groupby_agg chain gets you there just as directly.
Columns are auto-typed: numeric if every value in the column parses as a
float, string otherwise.
Table est un petit outil de mise en forme de données en colonnes :
jointures relationnelles, group-by/agrégation, pivots, fenêtres glissantes
et filtres, le tout en Rust, sans dépendance à pandas. Son rôle est étroit
et délibéré — remodeler une source de vérité unique vers les entrées exactes
qu'attend chaque fonction de chart, pour que plusieurs panneaux construits
depuis les mêmes données restent cohérents, au lieu d'écrire des boucles à
la main pour chacun.
Les colonnes se construisent depuis un dict[str, list] Python — chaque
valeur peut être int, float, str ou bool ; les colonnes de type
mixte sont converties en chaîne à la lecture.
Joint deux tables sur une colonne clé. how="left" garde les lignes gauches sans correspondance avec des colonnes droites à zéro. Les noms de colonnes de other en collision sont préfixés right_.
concat(other) -> Table
Union verticale de deux tables ; les colonnes manquantes d'un côté sont remplies à zéro/vide.
with_column(name, op, left, right) -> Table
Ajoute une colonne calculée. op vaut "add"/"sub"/"mul"/"div". right est soit le nom d'une autre colonne, soit une constante.
to_grouped_bar(index_col, columns_col, values_col, agg="sum") pivote et
aplatit en un seul appel, renvoyant un dict au format exact pour
sp.grouped_bar(labels=, values=, series_names=) — la passerelle
table-vers-chart la plus courante, faite en une ligne plutôt qu'un pivot
manuel.
Pour tout autre chart, column_f64/column_str après une chaîne
filter_*/sort_by/groupby_agg y mène tout aussi directement.
Set seraplot.pythonPath to your project venv, e.g.
${workspaceFolder}/.venv/Scripts/python.exe on Windows or
${workspaceFolder}/.venv/bin/python on macOS / Linux.
Pointer seraplot.pythonPath vers le venv du projet, par exemple
${workspaceFolder}/.venv/Scripts/python.exe sous Windows ou
${workspaceFolder}/.venv/bin/python sous macOS / Linux.
SeraPlot is built entirely on my own, on top of a day job. I regularly rework the core of the framework to keep improving it — not because I'm the best at low-level optimisation, but because I care about delivering a complete solution that originally answered my own needs and hopefully answers yours too.
If you'd like a specific mechanic, feature or chart type, don't hesitate to reach out — by email only. I can't promise I'll build everything, but I'll do as much as I can.
I work on SeraPlot for free, on top of my day job. If it saves you time, a donation is very welcome — but you absolutely don't need to donate to send me a feature request.
SeraPlot est entièrement réalisé seul, en plus d'un travail à plein temps. Je retravaille régulièrement le corps du framework pour continuer à l'améliorer — pas parce que je suis le meilleur en optimisation bas-niveau, mais parce que je tiens à offrir une solution complète qui répondait au départ à mes propres besoins et qui répond, j'espère, aussi aux vôtres.
Si vous voulez une mécanique, une fonctionnalité ou un type de graphique particulier, n'hésitez pas — par mail exclusivement. Je ne dis pas que je serai en capacité de tout réaliser, mais je ferai le plus possible.
Je travaille sur SeraPlot gratuitement, en plus de mon travail. Si cela vous fait gagner du temps, un don est le bienvenu — mais il n'y a aucun besoin de faire un don pour me faire une demande de fonctionnalité.