op({"color": {"legend": True},
"grid": False,
"marks": [Plot.dot(data, {"x": "x", "y": "y", "fill": "type", "r": 5})],
})
Usage
For a quick usage introduction, see getting started.
Plot specification
Converting a plot specification from JavaScript to Python should be straightforward most of the time:
- all dictionary keys must be quoted (so
x:
becomes"x":
) - JavaScript
true
andfalse
must be replaced byTrue
andFalse
- JavaScript
null
must be replaced byNone
So the following JavaScript code:
.plot(
Plot
{color: {legend: true},
grid: false,
marks: [Plot.dot(data, {x: "x", y: "y", fill: "type", r: 5})]
} )
Becomes:
It is possible to replace JavaScript methods from the Plot
, d3
and Math
modules with Python methods, but you must first import the corresponding classes.
from pyobsplot import Plot, d3
Plot.plot({"x": {
"axis": None
},"marks": [
0], {"stroke": "steelblue"}),
Plot.ruleY(["length": 100 }, d3.randomNormal()))
Plot.lineY(d3.cumsum({
] })
If your specification includes JavaScript code (such as anonymous functions), you can pass it as a string by using the js
method (after importing it):
from pyobsplot import Plot, d3, js
import polars as pl
= pl.DataFrame({
data "x": [1, 5, 2, 4, 6, 2, 4],
"y": [2, 1, 3, 4, 5, 1, 2],
"type": ["T1", "T2", "T1", "T2", "T1", "T1", "T2"],
})
Plot.plot({"grid": True,
"marks": [
Plot.dot(data, {"x": "x", "y": "y", "r": 5,
"stroke": "black", "fill": "steelblue",
"fillOpacity": js("d => d.type == 'T1' ? 0.7 : 0.1")
})
] })
Alternative syntaxes
For the simplest cases, you can also pass a mark method directly as plot specification. The JavaScript plot()
method will be called automatically to display the plot:
import random
= [random.gauss(0,1) for i in range(1000)]
v
Plot.plot(
Plot.tickX(
v, "stroke": "steelblue", "opacity": 0.2}
{
) )
You can call also call the plot()
method directly on a Plot mark method:
Plot.tickX(
v, "stroke": "steelblue", "opacity": 0.2}
{ ).plot()
Output formats
pyobsplot
allows to output plots as Jupyter widgets, but also as static HTML, SVG or PNG.
The output format is determined by the format
argument passed to a plot generator object or during plot creation.
Format value | Output type | Renderer |
---|---|---|
“widget” (default) | Jupyter Widget | widget |
“html” | Static HTML | jsdom |
“svg” | SVG image | jsdom or jsdom+typst |
“png” | PNG image | jsdom+typst |
The following table lists the differences between the widget output and the other static formats (HTML, SVG and PNG).
Widget output | Other outputs | |
---|---|---|
Output | Jupyter Widget | Static HTML, SVG, PNG. PDF is available when saving to a file. |
Additional installation |
None |
Needs a working node.js installation and an additional npm package
|
Quarto | Supported only in HTML format | HTML output supported in HTML format, SVG and PNG outputs supported in other formats |
Output size | Big : includes the data and code needed to generate the plot | Moderate : size of the output file |
Plot interactions (tooltips…) |
Supported | Not supported. Only static plots are produced. |
Save plot to file | Plots can be saved as embeddable HTML file | Plots can be saved to static HTML, SVG, PNG or PDF files. |
Jupyter interactivity |
Basic | None |
Persistence between sessions |
Widget state is not saved between sessions (for now) | Output is saved between sessions |
To use a static output format like HTML, SVG or PNG, you need a working node.js installation and you must install the npm pyobsplot
package globally or locally:
# Install locally
npm install pyobsplot
# Install globally
npm install -g pyobsplot
After that, you can specifiy a format when creating the plot by adding a format
argument:
# Specify format to Plot.plot()
"x": "flipper_length_mm"}), format="png") Plot.plot(Plot.auto(penguins, {
Saving plots to file
Plots can be saved to a file. To do this, just add a path
argument to your generator or Plot.plot
call:
1,2,3,2]), path="path_to/file.svg") Plot.plot(Plot.lineY([
Widget format
When using the widget
format, plots can only be saved to HTML files. These files retain interactive features such as tooltips.
1,2,3,2]), path="path_to/file.html") Plot.plot(Plot.lineY([
To embed widgets into an HTML website or document, Quarto documents can be more practical.
Other output formats
Plots can also be saved as SVG, PNG, PDF or static HTML files. The output format is determined by the path
file extension.
1,2,3,2]), path="path_to/file.png") Plot.plot(Plot.lineY([
PDF format is only available when saving to a file, as PDF output cannot be embedded and displayed in a Jupyter notebook.
When saving a plot to an HTML file, the result will depend on the format
value. If format="widget"
(the default), the HTML file will be a Jupyter widget, but if format="html"
, then a static HTML version will be saved.
Plot generates charts as SVG, but if a legend, title, subtitle or caption is present, the SVG is wrapped in a <figure>
HTML tag. In this case, when saving to an SVG file, the plot will be converted using typst
.
Themes
When using a plot generator object, it is possible to specify one of three color themes:
light
theme (default) creates plots with a white background and a black foreground colordark
theme creates plots with a black background and a white foregroundcurrent
theme creates plots with a transparent background and acurrentColor
foreground
You can specify a mode when creating the plot generator object by using the theme
argument:
1,2,3,2]), theme="dark") Plot.plot(Plot.lineY([
You can see output examples in the themes gallery
The current
theme is not available when exporting to PNG, PDF, or SVG via typst.
Plot generator
Calling Plot.plot()
is the fastest way to generate a plot with the default settings, but for further customization you can import the Obsplot
class and create a plot generator:
from pyobsplot import Obsplot, Plot
= Obsplot() op
By default plot generators output plots as widget format, but you can specify another one:
= Obsplot(format="png") op
You can then create plots by calling this generator with your plot specification:
op(
{"grid": True,
"marks": [Plot.dot(data, {"x": "x", "y": "y", "fill": "type", "r": 5})],
} )
For the simplest cases, you can also create your plot directly by passing a Plot
mark method to the generator:
= pl.read_csv("data/penguins.csv")
penguins
"x": "flipper_length_mm"})) op(Plot.auto(penguins, {
You can also override the default format output or save the plot to a file by adding a format
or path
argument when calling the generator:
# Switch to SVG output for this plot
"x": "flipper_length_mm"}), format="svg")
op(Plot.auto(penguins, {# Save widget to HTML file
"x": "flipper_length_mm"}), path="plot.html") op(Plot.auto(penguins, {
Default specification values
When creating a plot generator, it is possible to specify default specification values that will be applied to every plot created with this generator.
Only the top-level layout options can be specified as defaults. This can be useful to specify a default width
, colors, margins or even style
.
The complete list of available default attributes is :
['marginTop', 'marginRight', 'marginBottom', 'marginLeft', 'margin', 'width', 'height', 'grid', 'aspectRatio', 'style']
So to create a plot generator object that creates by default 400px wide plots with a 50px margin and a dark theme with blue color, you could use :
= Obsplot(
op_colors format = "html",
= "dark",
theme ={
default"width": 400,
"margin": 50,
"style": {"color": "#54A4C4"}
} )
op_colors("x": "x", "y": "y"})
Plot.dot(data, { )
Data handling
DataFrames and Series
Pandas and polars DataFrames can be passed directly in a plot specification. They will be converted to JavaScript objects via Arrow IPC serialization, to ensure speed and data types conversion.
import polars as pl
from datetime import date
= pl.DataFrame({
df "Date": [date(2023, 1, 1), date(2023, 1, 2), date(2023, 1, 3), date(2023, 1, 4)],
"Value": [4.2, 3.8, 4.5, 4.7]
})
Plot.plot({"x": {"grid": True},
"y": {"domain": [0, 5]},
"marks": [Plot.lineY(df, {"x": "Date", "y": "Value", "stroke": "steelblue"})]
})
If you pass a pandas or polars Series object, it will be automatically converted to a DataFrame with one column:
= df.get_column("Value")
value
Plot.plot("x": "Value", "stroke": "darkviolet"})
Plot.tickX(value, { )
pyobsplot
implements a simple caching mechanism for some data objects (it currently works for DataFrames and for GeoJson data). Sometimes the same data object is used several times in a plot specification:
= pl.read_csv("data/penguins.csv")
penguins
Plot.plot({"height": 600,
"grid": True,
"facet": {
"marginRight": 80
},"marks": [
"facet": False}),
Plot.frame({
Plot.dot(penguins, {"x": "culmen_depth_mm",
"y": "culmen_length_mm",
"r": 1.5,
"fill": "#ccc",
"facet": "exclude",
"fx": "sex",
"fy": "species",
}),
Plot.dot(penguins, {"x": "culmen_depth_mm",
"y": "culmen_length_mm",
"fx": "sex",
"fy": "species",
})
] })
In this case, caching ensures that the penguins
DataFrame is only serialized and transmitted once instead of twice.
datetime objects
datetime.date
and datetime.datetime
Python objects are automatically serialized and converted to JavaScript Date
objects.
That makes the following two specifications equivalent:
Plot.plot({"x": {"domain": [js("new Date('2021-01-01')"), js("new Date('2022-01-01')")]},
"grid": True
})
from datetime import date
Plot.plot({"x": {"domain": [date(2021,1,1), date(2022,1,1)]},
"grid": True
})
As well as the two following ones, using datetime
:
Plot.plot({"x": {"domain": [js("new Date('2021-01-01T07:00:00')"), js("new Date('2021-01-01T08:00:00')")]},
"grid": True
})
from datetime import datetime
Plot.plot({"x": {"domain": [datetime(2021,1,1,7,0,0), datetime(2021,1,1,8,0,0)]},
"grid": True
})
Quarto
pyobsplot
plots are compatible with quarto HTML formats. If you use static output formats such as SVG, PNG or PDF, they may work in PDF or docx documents as well.
If your source document is a jupyter notebook (and not a .qmd
file), then you have to use the --execute
argument to force plot computation and to make them visible in the output:
quarto render test.ipynb --execute --to html
Jupyter Interactivity
When using the default widget
format, the fact that plots are generated as Jupyter widgets allow for basic interactivity. More specifically, you can set the spec
attribute of an existing Obsplot
to another plot specification and it will update it.
This allows to do things like the following, where a plot is updated depending on the value of a Jupyter IntSlider
widget:
def generate_plot_spec(opacity):
return {
"grid": True,
"marks": [
"y": "count"}, {"x": "body_mass_g", "fill": "steelblue", "fillOpacity": opacity})),
Plot.rectY(penguins, Plot.binX({0])
Plot.ruleY([
]
}
= Plot.plot(generate_plot_spec(1))
plot
def update_plot(change):
= change['new']
new = generate_plot_spec(new / 100)
plot.spec
= IntSlider(value = 100, min = 0, max = 100)
w ='value')
w.observe(update_plot, names
display(w) display(plot)
You can see a live version of this example in the following Colab notebook: