Python bindings for the myIO d3.js chart library
Project description
pymyIO
Python bindings for myIO — the d3.js-based interactive chart library originally shipped as an R package.
pymyIO is feature-equivalent to the R package: every R export is reachable
from Python, every chart type renders identically, and the JSON config the
Python builder produces matches what R emits, byte for byte where possible.
Both packages drive the same d3 engine (myIOapi.js), wired in via a git
submodule so there is one canonical source of truth — no duplicated JS to
drift.
- Docs: https://mortonanalytics.github.io/pymyIO/
- Live gallery: https://pymyio.morton-analytics.com/ (Shiny for Python, source in
app/app.py)
Status: alpha (0.1.0). API is settled and matches R's
setMargin/setBrush/ etc. surface. All 19 registry transforms are implemented, includingloess,smooth,density,survfit,fit_distribution, andpairwise_test(scipy-backed, Python-native — numeric output may differ from the R package'sstats-based versions).
Installation
pip install pymyio # once published to PyPI
For development:
git clone --recurse-submodules https://github.com/mortonanalytics/pymyIO
cd pymyIO
pip install -e ".[dev]"
pytest
If you cloned without --recurse-submodules, fetch the engine afterwards:
git submodule update --init --recursive
Quickstart
import pandas as pd
from pymyio import MyIO
mtcars = pd.DataFrame({
"wt": [2.62, 2.875, 2.32, 3.215, 3.44, 3.46],
"mpg": [21.0, 21.0, 22.8, 21.4, 18.7, 18.1],
})
(
MyIO(data=mtcars)
.add_layer(type="point", label="points",
mapping={"x_var": "wt", "y_var": "mpg"}, color="#E69F00")
.add_layer(type="line", label="trend", transform="lm",
mapping={"x_var": "wt", "y_var": "mpg"}, color="red")
.set_axis_format(x_label="Weight (1000 lbs)", y_label="MPG")
)
In a Jupyter cell, the trailing expression renders as an interactive widget.
Outside notebooks, call .render() to get a MyIOWidget, or .to_config()
for the underlying JSON spec.
Where pymyIO runs
| Host | Tier | Render idiom |
|---|---|---|
| JupyterLab | 1 | trailing expression in a cell |
| VS Code (Jupyter extension) | 1 | trailing expression in a cell |
| Shiny for Python | 1 | from pymyio.shiny import render_myio, output_myio |
| Classic Notebook 7.x | 2 | trailing expression |
| Google Colab | 2 | trailing expression |
| marimo | 2 | mo.ui.anywidget(MyIO(...).render()) |
| Panel | 2 | pn.pane.IPyWidget(MyIO(...).render()) |
| Solara | 2 | solara.display(MyIO(...).render()) |
| Quarto (HTML) | 2 | interactive HTML only; PDF/docx not supported |
| static HTML / email / Quarto PDF workaround | — | pymyio.to_standalone_html(chart) |
Tier 1 hosts are covered by CI and release-block on regressions. Tier 2 hosts are documented best-effort and verified on the pre-release smoke checklist.
Shiny for Python
from shiny import App, ui
from pymyio.shiny import render_myio, output_myio, reactive_brush, example_app
# Copy-paste the whole app:
app = example_app()
The pymyio.shiny submodule ships thin aliases over
shinywidgets so R-myIO
users get renderMyIO/myIOOutput muscle memory, plus
reactive_brush/reactive_annotated/reactive_rollover helpers that wrap
shinywidgets.reactive_read(widget, trait_name).
Install with pip install 'pymyio[shiny]' (pulls shinywidgets >= 0.8.0
and shiny >= 1.0). Don't import shinywidgets directly in vanilla
Jupyter notebooks — it installs a process-wide callback that breaks widget
construction outside a Shiny session. pymyio's top-level never touches
shinywidgets; the submodule is opt-in for exactly this reason.
Static HTML export (Quarto, nbconvert, email embeds)
from pymyio import to_standalone_html
html = to_standalone_html(MyIO(data=df).add_layer(...))
open("chart.html", "w").write(html)
include_assets="inline" (default) produces one self-contained HTML
string; include_assets="bundled" returns (html_str, assets_dict) for
publishing pipelines that prefer sidecar assets. Interactive-only features
(set_brush, set_annotation, drag_points) emit a
MyIOStaticWarning — the chart renders, but round-trip callbacks need a
live Python kernel.
Supported chart types (34 total)
line, point, bar, groupedBar, area, histogram, heatmap,
hexbin, treemap, gauge, donut, candlestick, waterfall, sankey,
boxplot, violin, ridgeline, rangeBar, text, regression,
bracket, comparison, qq, lollipop, dumbbell, waffle, beeswarm,
bump, radar, funnel, parallel, survfit, histogram_fit,
calendarHeatmap.
R → Python function map
| R export | Python equivalent |
|---|---|
myIO() |
MyIO() |
addIoLayer() |
MyIO.add_layer() |
setMargin() |
MyIO.set_margin() |
setAxisFormat() |
MyIO.set_axis_format() |
setAxisLimits() |
MyIO.set_axis_limits() |
setColorScheme() |
MyIO.set_color_scheme() |
setReferenceLines() |
MyIO.set_reference_lines() |
setTheme() |
MyIO.set_theme() |
setTransitionSpeed() |
MyIO.set_transition_speed() |
setToolTipOptions() |
MyIO.set_tooltip_options() |
defineCategoricalAxis() |
MyIO.define_categorical_axis() |
flipAxis() |
MyIO.flip_axis() |
suppressLegend() |
MyIO.suppress_legend() |
suppressAxis() |
MyIO.suppress_axis() |
setBrush() |
MyIO.set_brush() |
setAnnotation() |
MyIO.set_annotation() |
setExportOptions() |
MyIO.set_export_options() |
setFacet() |
MyIO.set_facet() |
setLayerOpacity() |
MyIO.set_layer_opacity() |
setSlider() |
MyIO.set_slider() |
setToggle() |
MyIO.set_toggle() |
setLinkedCursor() |
MyIO.set_linked_cursor() |
dragPoints() |
MyIO.drag_points() |
linkCharts() |
pymyio.link_charts() (module-level) |
setLinked() |
n/a — Crosstalk-specific; use link_charts() |
myIO_last_error() |
MyIOWidget.last_error traitlet |
myIOOutput/renderMyIO |
n/a — Shiny-specific |
Reading interactions back into Python
chart = MyIO(data=mtcars).add_layer(...).set_brush().render()
chart # display in a cell
chart.brushed # last brush selection (dict, syncs from JS)
chart.annotated # last annotation event
chart.last_error # most recent JS render error, if any
chart.observe(handler, names=["brushed"]) # react to selections
Architecture: one engine, two wrappers
mortonanalytics/myIO (R package)
└── inst/htmlwidgets/myIO/ ← canonical engine source
├── myIOapi.js
├── style.css
└── lib/d3*.js
mortonanalytics/pymyIO (this repo)
├── vendor/myIO/ ← git submodule pinned to a myIO commit
└── src/pymyio/static/ ← symlinks pointing into vendor/myIO/
Wheels built by python -m build follow the symlinks and ship real files,
so end-users pip-install a self-contained package. Developers and CI work
against the submodule directly. To pull in upstream chart fixes:
git submodule update --remote vendor/myIO
git add vendor/myIO && git commit -m "bump myIO engine to <sha>"
Roadmap
| ID | Item | Disposition |
|---|---|---|
| PYMYIO-DOC | Sphinx docs site | Out of scope for 0.1.0 |
License
MIT. See LICENSE. The vendored myIO engine is also MIT-licensed
(see vendor/myIO/LICENSE).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pymyio-0.1.0.tar.gz.
File metadata
- Download URL: pymyio-0.1.0.tar.gz
- Upload date:
- Size: 440.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89bbce6cd9cffb52ade303c4fa0a1603710145773bfe73177e827d547d0a6b63
|
|
| MD5 |
14f813fc3a8f44b58aa5e89a182efebd
|
|
| BLAKE2b-256 |
b71c59c5377ce7b410d7b683f908ba4bfb64dcf84e89c5a34ef240d2e2ba0bac
|
Provenance
The following attestation bundles were made for pymyio-0.1.0.tar.gz:
Publisher:
release.yaml on mortonanalytics/pymyIO
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymyio-0.1.0.tar.gz -
Subject digest:
89bbce6cd9cffb52ade303c4fa0a1603710145773bfe73177e827d547d0a6b63 - Sigstore transparency entry: 1439043251
- Sigstore integration time:
-
Permalink:
mortonanalytics/pymyIO@f5236a5a01416f2bb85b0fecf535c45e40f59125 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mortonanalytics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@f5236a5a01416f2bb85b0fecf535c45e40f59125 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pymyio-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pymyio-0.1.0-py3-none-any.whl
- Upload date:
- Size: 188.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
702f1cdd4a52557fa835e20487872d73aafce3df8577ac3ab9e75827efff9339
|
|
| MD5 |
8d98c49bd5e0e5d863c571d7c8a7f303
|
|
| BLAKE2b-256 |
d07ef0b9fd5c7287ed7a700310642853bd6e301956cd90bd9040bd3f9babf64a
|
Provenance
The following attestation bundles were made for pymyio-0.1.0-py3-none-any.whl:
Publisher:
release.yaml on mortonanalytics/pymyIO
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymyio-0.1.0-py3-none-any.whl -
Subject digest:
702f1cdd4a52557fa835e20487872d73aafce3df8577ac3ab9e75827efff9339 - Sigstore transparency entry: 1439043264
- Sigstore integration time:
-
Permalink:
mortonanalytics/pymyIO@f5236a5a01416f2bb85b0fecf535c45e40f59125 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mortonanalytics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@f5236a5a01416f2bb85b0fecf535c45e40f59125 -
Trigger Event:
push
-
Statement type: