Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
3962 commits
Select commit Hold shift + click to select a range
cd606ab
fix: remove custom classifier for MPL (#62)
beckermr Feb 1, 2025
4a2f4a8
fix: correct chain of logic for publish workflow (#63)
beckermr Feb 1, 2025
a912237
fix: get pypi publish working (#64)
beckermr Feb 2, 2025
8d1836d
add case where cycler is already a cycle (#65)
cvanelteren Feb 2, 2025
32b49be
fix: make sure PRs do not mess with releases (#67)
beckermr Feb 2, 2025
277dfc8
fix: ensure pypi readme works ok (#70)
beckermr Feb 3, 2025
cbd7513
feat: deduplicate pypi publish workflow (#71)
beckermr Feb 3, 2025
35f7c0f
[pre-commit.ci] pre-commit autoupdate (#73)
pre-commit-ci[bot] Feb 3, 2025
4e1fbf3
use norm explicitly (#76)
cvanelteren Feb 14, 2025
a91b57e
added path explicitly on publish (#77)
cvanelteren Feb 15, 2025
bf6c8f6
fix: remove race condition for pushes of tags (#78)
beckermr Feb 15, 2025
ca07a1f
use seed for reproducibility (#79)
cvanelteren Feb 15, 2025
aa39093
Fix demo function not extracting colormaps (#83)
cvanelteren Feb 19, 2025
d7d1624
allows cycle to be a tuple (#87)
cvanelteren Feb 20, 2025
cd424df
Fixes heatmap not showing labels. (#91)
cvanelteren Feb 21, 2025
9ca858c
Remove autoapi (#92)
cvanelteren Feb 21, 2025
8d91014
explicitly override minor locator if given (#96)
cvanelteren Feb 23, 2025
f6b827a
filter out property if not set (#99)
cvanelteren Feb 25, 2025
79a7eff
Add texgyre to docs (#101)
cvanelteren Feb 27, 2025
ff8247f
Texgyre fix (#102)
cvanelteren Feb 27, 2025
498ec93
rm warnings when downloading data inside env (#104)
cvanelteren Feb 27, 2025
8b2cc1a
prod: only build ultraplot when ultraplot src changes (#106)
cvanelteren Feb 27, 2025
339f6d6
Gitignore baseline (#109)
cvanelteren Feb 28, 2025
2327071
add unittest and fix (#108)
cvanelteren Feb 28, 2025
e08df1f
Update dependencies and ensure docs build on latest ipython (#114)
cvanelteren Mar 2, 2025
7548756
rm ref semver (#112)
cvanelteren Mar 2, 2025
58ec33d
Only store failed tests mpl-pytest (#113)
cvanelteren Mar 2, 2025
a8e4761
fix: matrix test for no changes is running when it should not be (#115)
beckermr Mar 2, 2025
767cab5
prod: add me to maintainers (#117)
beckermr Mar 6, 2025
47300f2
prod: only use readthedocs for PR tests (#118)
beckermr Mar 6, 2025
e84f9e8
feat: enable test coverage with codecov (#121)
beckermr Mar 8, 2025
552ca33
feat: dynamically build what's new (#122)
cvanelteren Mar 9, 2025
27cebca
rm extra == line (#123)
cvanelteren Mar 9, 2025
fbd079a
bugfix for Axes.legend when certain keywords are set to str (#124)
syrte Mar 15, 2025
f38bc7b
reduce verbosity of extension (#127)
cvanelteren Mar 16, 2025
c1b955a
Add add ticks to GeoAxes (#126)
cvanelteren Mar 20, 2025
8670ed6
Update intersphinx links (#128)
cvanelteren Mar 21, 2025
92257bc
Update geo doc (#129)
cvanelteren Mar 21, 2025
a17be1c
Hotfix update geo doc (#130)
cvanelteren Mar 21, 2025
19dd08a
New site, who dis? (#132)
cvanelteren Mar 24, 2025
3af0bfa
Add about page (#133)
cvanelteren Mar 24, 2025
502202a
make it mobile friendly (#134)
cvanelteren Mar 24, 2025
b4a4cc3
Add gallery to github page (#140)
cvanelteren Apr 6, 2025
9d06e26
Fix readme (#142)
cvanelteren Apr 6, 2025
cbdfc68
Fix readme fixed sizes (#143)
cvanelteren Apr 6, 2025
8089edb
added page for errors (#141)
cvanelteren Apr 6, 2025
301e7ad
Bump dawidd6/action-download-artifact from 2 to 6 in /.github/workflo…
dependabot[bot] Apr 6, 2025
55d0cb1
Update archive-pytest-results.yml
beckermr Apr 6, 2025
2d33af8
fix: checkout from correct fork (#145)
beckermr Apr 6, 2025
29d4822
fix: allow forks
beckermr Apr 6, 2025
81c2c41
fix: directly use run id for workflow
beckermr Apr 6, 2025
1948c4b
fix: use force add due to gitignore
beckermr Apr 6, 2025
f888dde
Set seed prior to test to ensure fidelity (#148)
cvanelteren Apr 8, 2025
7518da8
Move warning inside pytest config (#151)
cvanelteren Apr 9, 2025
71bfd57
Fix scaler parsing (#153)
cvanelteren Apr 9, 2025
fe1183d
Update site logo (#154)
cvanelteren Apr 9, 2025
8852174
Fix minor grid showing on cbar (#150)
cvanelteren Apr 9, 2025
46efef6
Add option to place abc indicator outside the axis bbox (#139)
cvanelteren Apr 11, 2025
caa2391
Add unitests for ultraplot.internals.fonts (#156)
cvanelteren Apr 12, 2025
98ec600
Minor refactor of unittests (#157)
cvanelteren Apr 12, 2025
a626970
rm archive
cvanelteren Apr 13, 2025
670739e
make anchor_mode default
cvanelteren Apr 16, 2025
bf7211f
Revert "make anchor_mode default"
cvanelteren Apr 16, 2025
24a1eee
Make anchor_mode default (#161)
cvanelteren Apr 16, 2025
a2cb2bd
Gracefully exit context if cannot set key (#164)
cvanelteren Apr 18, 2025
025a4f7
allow subfigure formatting (#167)
cvanelteren Apr 22, 2025
bc53419
make cbar labelloc possible for all direction (#165)
cvanelteren Apr 22, 2025
dd5a951
add pyarrow to rm pandas error (#171)
cvanelteren Apr 22, 2025
64e2069
Update css (#170)
cvanelteren Apr 22, 2025
8d7f228
mv toc to left (#172)
cvanelteren Apr 24, 2025
f67ba1d
surpress warnings on action (#174)
cvanelteren Apr 25, 2025
59a46b9
surpress warnings on action
cvanelteren Apr 25, 2025
9d11df4
separate logger for ultraplot and matplotlib (#178)
cvanelteren Apr 25, 2025
838251b
Capture warning (#180)
cvanelteren Apr 25, 2025
6be152a
tmp turning of test (#183)
cvanelteren Apr 25, 2025
821eadc
Skip missing tests if added in PR (#175)
cvanelteren Apr 25, 2025
d0675cd
Revert "Skip missing tests if added in PR (#175)" (#184)
beckermr Apr 25, 2025
a86d6d4
Rm conftest from codecov (#187)
cvanelteren Apr 25, 2025
683fc0b
skip tests properly (#186)
cvanelteren Apr 26, 2025
48aaabc
Fix colorbar loc (#182)
cvanelteren Apr 26, 2025
9ebe9d4
Fix bar alpha (#192)
cvanelteren Apr 27, 2025
22cbfb4
Make import uplt to be consistent with rest of repo (#195)
cvanelteren Apr 27, 2025
860b2c6
Ensure that shared labels are consistently updated. (#177)
cvanelteren Apr 28, 2025
c6ee712
sensible defaults and unittest (#189)
cvanelteren Apr 28, 2025
d911cd4
Deprecation fix mpl 3.10 and beyond (#69)
cvanelteren Apr 28, 2025
f5510d5
Add network plotting to UltraPlot (#169)
cvanelteren Apr 29, 2025
9a9eb72
Hotfix test (#196)
cvanelteren Apr 29, 2025
89205de
Discrete colors for quiver (#198)
cvanelteren Apr 29, 2025
0b94056
correct url for basemap objects (#202)
cvanelteren Apr 30, 2025
4072c76
override logx/y/log with updated docstring (#203)
cvanelteren May 1, 2025
aa44035
Add lollipop graph (#194)
cvanelteren May 4, 2025
4f78a79
Fix intersphinx links and refs to docs for networkx (#205)
cvanelteren May 4, 2025
b18b5c4
Avoid getting edges and setting centers for some shaders (#208)
cvanelteren May 8, 2025
dd89675
Fix some references for inset docs (#209)
cvanelteren May 10, 2025
ce842a8
rm dep warning (#210)
cvanelteren May 11, 2025
8b22df2
[Feature add] Share Axes in GeoPlot + bug fixes (#159)
cvanelteren May 11, 2025
60f100d
fix: specify import exception type and add typing-extensions to deps …
beckermr May 12, 2025
618e1c5
perf: run comparison tests in parallel (#213)
beckermr May 13, 2025
8e147a1
fix cycler setting to 1 when only 1 column is parsed (#218)
cvanelteren May 16, 2025
7a0a4b7
Skip sharing logic when colorbar is added to GeoPlots. (#219)
cvanelteren May 16, 2025
9bbf9f1
Restore redirection for tricontourf for GeoPlotting (#222)
cvanelteren May 16, 2025
bfe47c5
fix docs visuals (#223)
cvanelteren May 17, 2025
d413ad4
Allow rasterization on GeoFeatures. (#220)
cvanelteren May 17, 2025
ce6754e
more fixes (#224)
cvanelteren May 19, 2025
93b1271
Colormap docs fixes
cvanelteren May 19, 2025
bbf2e3a
Cartesian docs links fixed (#226)
cvanelteren May 21, 2025
a4b7979
minor fix for mpl3.10 (#229)
cvanelteren May 22, 2025
5240fec
Adjust the ticks to center on 'nice' values (#228)
cvanelteren May 23, 2025
afae3d1
rm unnecessary show in test_center_labels_colormesh_data_type(#241)
cvanelteren May 30, 2025
103517d
Feat bar labels (#240)
cvanelteren May 30, 2025
91a084c
Fix links for 1d plots in docs (#242)
cvanelteren May 31, 2025
b9ff267
Deprecate basemap (#243)
cvanelteren Jun 1, 2025
1a05b0e
Hotfix get_border_axes (#236)
cvanelteren Jun 2, 2025
e5a8ee2
Hotfix panel (#238)
cvanelteren Jun 3, 2025
2520034
hotfix (#246)
cvanelteren Jun 4, 2025
ef66d20
Hotfix GeoAxes indicate zoom. (#249)
cvanelteren Jun 5, 2025
b5405b4
Feature: Beeswarm plot (#251)
cvanelteren Jun 10, 2025
8d1689d
GeoTicks not responsive (#253)
cvanelteren Jun 10, 2025
1b9e475
add top level ignores for local testing (#255)
cvanelteren Jun 12, 2025
abc11ba
Update .gitignore (#257)
cvanelteren Jun 12, 2025
7e49be9
Refactor beeswarm (#254)
cvanelteren Jun 12, 2025
b825849
Fix unused parameters being passed to pie chart (#260)
cvanelteren Jun 14, 2025
3db1449
Update return statements of tests to be compliant with pytest 8.4.0 (…
cvanelteren Jun 16, 2025
5a48107
Bump python to 3.13 (#264)
cvanelteren Jun 16, 2025
789bf3b
Update matplotlib to mpl 3.10 (#263)
cvanelteren Jun 16, 2025
54591c1
fix test_graph_edges_kw and test_quiver_discrete_colors (#267)
cvanelteren Jun 17, 2025
cdf82ef
set rng per test (#268)
cvanelteren Jun 17, 2025
6decf39
Add xdist to image compare (#266)
cvanelteren Jun 18, 2025
553ef6e
Fix issue where view is reset on setting ticklen (#272)
cvanelteren Jun 19, 2025
0215ace
Racing condition xdist fix (#273)
cvanelteren Jun 19, 2025
12f7b87
replace spring with forceatlas2 (#275)
cvanelteren Jun 19, 2025
3bb696d
Revert xdist addition (#277)
cvanelteren Jun 20, 2025
2fd2a70
fix: pass layout_kw in network test function (#278)
beckermr Jun 20, 2025
b6e96b4
fix: this one needs a seed too (#279)
beckermr Jun 20, 2025
1887673
rm paren decorator on network test(#280)
cvanelteren Jun 20, 2025
b56ca6a
Add citation metadata (CITATION.cff, .zenodo.json) to support scholar…
cvanelteren Jun 24, 2025
117f06b
Update README.rst
cvanelteren Jun 24, 2025
38d0dbe
Add doi badge to readme
cvanelteren Jun 24, 2025
2e4cdaa
add zenodo link to citation.cff
cvanelteren Jun 24, 2025
887c9e9
Update CITATON.cff (#286)
cvanelteren Jun 24, 2025
17ff712
rn file
cvanelteren Jun 24, 2025
8e7313b
rn file
cvanelteren Jun 24, 2025
1424358
Update README.rst (#287)
cvanelteren Jun 24, 2025
e0218f3
Mv dynamic function to the subplotgrid (#281)
cvanelteren Jun 25, 2025
c53b8ce
add downloads badge to README.rst (#290)
cvanelteren Jun 28, 2025
2ea0feb
replace color to orange (#291)
cvanelteren Jun 28, 2025
1d71642
Hotfix add all locations to colorbar label (#295)
cvanelteren Jun 29, 2025
b21c21e
Fix DMS not set on some projections (#293)
cvanelteren Jun 30, 2025
37c1432
Bump mamba-org/setup-micromamba in the github-actions group (#299)
dependabot[bot] Jul 1, 2025
b9a6289
fix late binding and proper reversal for funcs (#296)
cvanelteren Jul 2, 2025
2ca1e2b
Feat vert inset cbars (#301)
cvanelteren Jul 3, 2025
e43df1d
Refactor colorbar loc handling (#304)
cvanelteren Jul 30, 2025
2a3512b
update base to use plegend
cvanelteren Aug 1, 2025
4121a1e
Revert "update base to use plegend"
cvanelteren Aug 1, 2025
ff91841
Sync `_legend_dict` on legend location change (#310)
cvanelteren Aug 2, 2025
b79d322
fix color being parsed for none (#312)
cvanelteren Aug 3, 2025
4ed2539
Update axis sharing to allow for top and right sharing.
cvanelteren Aug 5, 2025
aaedff6
Fix edge case where vcenter is not properly set for diverging norms (…
cvanelteren Aug 7, 2025
e2a6a7f
update logic (#315)
cvanelteren Aug 8, 2025
147d68b
Handle non homogeneous arrays (#318)
cvanelteren Aug 11, 2025
f466757
Update Cartopy references (#322)
rcomer Aug 17, 2025
a5e48de
Fix inhomogeneous violin test (#323)
rcomer Aug 17, 2025
e6b7cbc
Fix issue where double decorator does not parse function name (#320)
cvanelteren Aug 18, 2025
ff93437
Fix edgecolor not set on scatter plots with single-row DataFrame data…
Copilot Aug 24, 2025
54b3163
Add suptitle_kw alignment support to UltraPlot (#327)
Copilot Aug 24, 2025
2dd9428
Update Documentation for `abc` Parameter in Subplots and Format Comma…
cvanelteren Aug 25, 2025
e685d52
Fix subplots docs (#330)
cvanelteren Aug 26, 2025
1f227e6
Add members to api (#332)
cvanelteren Aug 26, 2025
9a6baad
rm show from tests (#335)
cvanelteren Aug 27, 2025
29d7227
Revert "Fix edge case where vcenter is not properly set for diverging…
beckermr Aug 27, 2025
3bb4861
Bump the github-actions group with 2 updates (#339)
dependabot[bot] Sep 1, 2025
a619ffd
Extra tests for geobackends (#334)
cvanelteren Sep 5, 2025
fe6e5d4
Fix some links for docs (#341)
cvanelteren Sep 6, 2025
8001535
Fix links docs (#342)
cvanelteren Sep 6, 2025
2311a52
Lazy loading colormaps (#343)
cvanelteren Sep 9, 2025
1c79cfb
Set warning level for mpl to error (#350)
cvanelteren Sep 16, 2025
5fddd32
sanitize and add tests (#346)
cvanelteren Sep 19, 2025
0c06164
Fix order of label transfer (#353)
cvanelteren Sep 29, 2025
da81e1b
Bump the github-actions group with 2 updates (#354)
dependabot[bot] Oct 1, 2025
8f560d8
Add cftime support for non-standard calendars (#344)
cvanelteren Oct 5, 2025
5ab153e
rm debug prints
cvanelteren Oct 5, 2025
7827552
Add unittests for AutoFormatting (#360)
cvanelteren Oct 5, 2025
580b685
[pre-commit.ci] pre-commit autoupdate (#362)
pre-commit-ci[bot] Oct 6, 2025
9cc88a7
Fix: Corrects `SubplotGrid` indexing and sequential `GeoAxes` format…
cvanelteren Oct 8, 2025
d514aa1
Fix circular import rc setting (#365)
cvanelteren Oct 8, 2025
27c2883
Add `curved_quiver` — Curved Vector Field Arrows for 2D Plots (#361)
cvanelteren Oct 13, 2025
cb8da22
Add Colormap parsing to curved-quiver (#369)
cvanelteren Oct 13, 2025
aa58701
Allow non-rectangular grids to use side labels (#376)
cvanelteren Oct 23, 2025
40c3c6f
Test/update label sharing tests (#372)
cvanelteren Oct 25, 2025
e329bbb
Add version checker for UltraPlot (#377)
cvanelteren Oct 25, 2025
836cf41
Feature: allow cycle objects to be set on rc (#378)
cvanelteren Oct 25, 2025
571817a
Add unittest for demos (#386)
cvanelteren Oct 27, 2025
a65d1d9
rm debug comments
cvanelteren Oct 28, 2025
7409ad8
increase timeout limit (#388)
cvanelteren Oct 28, 2025
dd452c2
bump to 60 minutes (#389)
cvanelteren Oct 28, 2025
ca14892
skip test_demos on gha (#391)
cvanelteren Oct 29, 2025
230c9dd
Hotfix: minor update in sharing logic (#387)
cvanelteren Oct 29, 2025
58de687
Housekeeping for `ultraplot-build.yml` (#390)
cvanelteren Oct 29, 2025
2a97153
Feature: Allow multi-span colorbars (#394)
cvanelteren Oct 31, 2025
0146afd
Bump the github-actions group with 2 updates (#398)
dependabot[bot] Nov 1, 2025
02c4aaf
add s and unittest (#400)
cvanelteren Nov 2, 2025
fc156f0
redo with new ticker (#411)
cvanelteren Nov 16, 2025
08235b1
Hotfix: bar labels cause limit to reset for unaffected axis. (#413)
cvanelteren Nov 18, 2025
55d3d67
fix: change default `reduce_C_function` to `np.sum` for `hexbin` (#408)
beckermr Nov 19, 2025
63cf012
Add external context mode for axes (#406)
cvanelteren Nov 20, 2025
f20ce02
Bump actions/checkout from 5 to 6 in the github-actions group (#415)
dependabot[bot] Dec 1, 2025
e985cf9
[pre-commit.ci] pre-commit autoupdate (#416)
pre-commit-ci[bot] Dec 1, 2025
db97f5c
Add placement of legend to axes within a figure (#418)
cvanelteren Dec 7, 2025
4964ee7
There's a typo about zerotrim in doc. (#420)
gepcel Dec 8, 2025
c44ed69
Fix references in documentation for clarity (#421)
gepcel Dec 10, 2025
59ea30a
fix links to apply_norm (#423)
cvanelteren Dec 11, 2025
50f1cda
[Feature] add lon lat labelrotation (#426)
cvanelteren Dec 12, 2025
6b15deb
fix boundary check for ticks
cvanelteren Dec 13, 2025
c3bad07
Revert "fix boundary check for ticks"
cvanelteren Dec 13, 2025
e4e8471
Fix: Boundary labels now visible when setting lonlim/latlim (#429)
cvanelteren Dec 15, 2025
4af84bc
Add Copernicus Publications figure standard widths. (#433)
Holmgren825 Dec 16, 2025
06551f6
Fix unequal slicing for Gridspec (#435)
cvanelteren Dec 17, 2025
03848e4
Fix GeoAxes panel alignment with aspect-constrained projections (#432)
cvanelteren Dec 29, 2025
0443eac
Bump the github-actions group with 2 updates (#444)
dependabot[bot] Jan 1, 2026
13664bd
Fix dualx alignment on log axes (#443)
cvanelteren Jan 4, 2026
c88c98a
Subset label sharing and implicit slice labels for axis groups (#440)
cvanelteren Jan 4, 2026
2dd5065
Preserve log formatter when setting log scales (#437)
cvanelteren Jan 4, 2026
d60a25c
added inference of labels for spanning legends (#447)
cvanelteren Jan 4, 2026
1aea20a
[pre-commit.ci] pre-commit autoupdate (#449)
pre-commit-ci[bot] Jan 5, 2026
9380f4b
Avoid title overlap with abc labels (#442)
cvanelteren Jan 7, 2026
3da0f66
Guard abc/title width calc when text detached (#452)
cvanelteren Jan 9, 2026
9b301cf
Refactor GeoAxes gridliner handling and format flow; remove cartopy m…
cvanelteren Jan 11, 2026
299746b
Swap visual tests with hash comparison (#427)
cvanelteren Jan 13, 2026
e188495
Remove xdist from image compare (#462)
cvanelteren Jan 13, 2026
0361de6
Fix 'What's New' page formatting and generation (#464)
cvanelteren Jan 13, 2026
483e071
Fix SubplotGrid indexing and enhance legend placement with 'ref' argu…
cvanelteren Jan 14, 2026
5f02982
Add ridgeline plot feature (#451)
cvanelteren Jan 14, 2026
4006105
Fix/axes alpha doc formatting (#467)
cvanelteren Jan 15, 2026
c7818bd
Run image compare single thread
cvanelteren Jan 15, 2026
cafe414
Fix legend span inference with panels (#469)
cvanelteren Jan 15, 2026
f65e238
Hopefully this fixes the api indexing (#471)
cvanelteren Jan 15, 2026
919aa84
Introduce internal dataclass to restructure the craziest loop that ex…
cvanelteren Jan 15, 2026
c7eeec5
Fix share label group overrides (#473)
cvanelteren Jan 15, 2026
32e1f40
Lazy-load top-level imports and defer setup (#439)
cvanelteren Jan 15, 2026
1a4367e
add make.bat (#475)
gepcel Jan 16, 2026
65644d0
Add gallery (#465)
cvanelteren Jan 16, 2026
5a6adc8
Add layered Sankey renderer
cvanelteren Jan 16, 2026
292b2e9
Expose layered Sankey API and defaults
cvanelteren Jan 16, 2026
103d6e0
Add layered Sankey tests
cvanelteren Jan 16, 2026
6ed4f3b
Add Sankey example to plot types gallery
cvanelteren Jan 16, 2026
a1839ee
Empty readme.txt
cvanelteren Jan 16, 2026
65cce2e
Add type hints for sankey API
cvanelteren Jan 16, 2026
679dd5d
Convert tests to visual tests
cvanelteren Jan 16, 2026
c0ac4b5
Ignore local test.py
cvanelteren Jan 16, 2026
56daaae
Merge branch 'main' into sankey-labels
cvanelteren Jan 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@ dist

# Local docs builds
docs/api
docs/_build
docs/_build/*
docs/_static/ultraplotrc
docs/_static/rctable.rst
docs/_static/*
docs/gallery/*

# Development subfolders
dev
sources

# Python extras
*.ipynb
.ipynb_checkpoints
*.log
*.pyc
Expand Down
42 changes: 42 additions & 0 deletions docs/examples/plot_types/07_sankey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
Layered Sankey diagram
======================

An example of UltraPlot's layered Sankey renderer for publication-ready
flow diagrams.

Why UltraPlot here?
-------------------
``sankey`` in layered mode handles node ordering, flow styling, and
label placement without manual geometry.

Key function: :py:meth:`ultraplot.axes.PlotAxes.sankey`.

See also
--------
* :doc:`2D plot types </2dplots>`
"""

import ultraplot as uplt

nodes = ["Budget", "Operations", "R&D", "Marketing", "Support", "Infra"]
flows = [
("Budget", "Operations", 5.0, "Ops"),
("Budget", "R&D", 3.0, "R&D"),
("Budget", "Marketing", 2.0, "Mkt"),
("Operations", "Support", 1.5, "Support"),
("Operations", "Infra", 2.0, "Infra"),
]

fig, ax = uplt.subplots(refwidth=3.6)
ax.sankey(
nodes=nodes,
flows=flows,
style="budget",
flow_labels=True,
value_format="{:.1f}",
node_label_box=True,
flow_label_pos=0.5,
)
ax.format(title="Budget allocation")
fig.show()
242 changes: 241 additions & 1 deletion ultraplot/axes/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import sys
from collections.abc import Callable, Iterable
from numbers import Integral, Number
from typing import Any, Iterable, Optional, Union
from typing import Any, Iterable, Mapping, Optional, Sequence, Union

import matplotlib as mpl
import matplotlib.artist as martist
Expand Down Expand Up @@ -205,6 +205,83 @@
"""

docstring._snippet_manager["plot.curved_quiver"] = _curved_quiver_docstring

_sankey_docstring = """
Draw a Sankey diagram.

Parameters
----------
flows : sequence of float or flow tuples
If a numeric sequence, use Matplotlib's Sankey implementation.
Otherwise, expect flow tuples or dicts describing (source, target, value).
nodes : sequence or dict, optional
Node identifiers or dicts with ``id``/``label``/``color`` keys. If omitted,
nodes are inferred from flow sources/targets.
labels : sequence of str, optional
Labels for each flow in Matplotlib's Sankey mode.
orientations : sequence of int, optional
Flow orientations (-1: down, 0: right, 1: up) for Matplotlib's Sankey.
pathlengths : float or sequence of float, optional
Path lengths for each flow in Matplotlib's Sankey.
trunklength : float, optional
Length of the trunk between the input and output flows.
patchlabel : str, optional
Label for the main patch in Matplotlib's Sankey mode.
scale, unit, format, gap, radius, shoulder, offset, head_angle, margin, tolerance : optional
Passed to `matplotlib.sankey.Sankey`.
prior : int, optional
Index of a prior diagram to connect to.
connect : (int, int), optional
Flow indices for the prior and current diagram connection.
rotation : float, optional
Rotation angle in degrees.
node_kw, flow_kw, label_kw : dict-like, optional
Style dictionaries for the layered Sankey renderer.
node_label_kw, flow_label_kw : dict-like, optional
Label style dictionaries for node and flow labels in layered mode.
node_label_box : bool or dict-like, optional
If ``True``, draw a rounded box behind node labels. If dict-like, used as
the ``bbox`` argument for node label styling.
style : {'budget', 'pastel', 'mono'}, optional
Built-in styling presets for layered mode.
node_order : sequence, optional
Explicit node ordering for layered mode.
layer_order : sequence, optional
Explicit layer ordering for layered mode.
group_cycle : sequence, optional
Cycle for flow group colors (defaults to flow cycle).
flow_other : float, optional
Aggregate flows below this threshold into a single ``other_label``.
other_label : str, optional
Label for the aggregated flow target.
value_format : str or callable, optional
Formatter for flow labels when not explicitly provided.
node_label_outside : {'auto', True, False}, optional
Place node labels outside narrow nodes.
node_label_offset : float, optional
Offset for outside node labels (axes-relative units).
flow_sort : bool, optional
Whether to sort flows by target position to reduce crossings.
flow_label_pos : float, optional
Horizontal placement for single flow labels (0 to 1 along the ribbon).
When flow labels overlap, positions are redistributed between 0.25 and 0.75.
node_labels, flow_labels : bool, optional
Whether to draw node or flow labels in layered mode.
align : {'center', 'top', 'bottom'}, optional
Vertical alignment for nodes within each layer in layered mode.
layers : dict-like, optional
Manual layer assignments for nodes in layered mode.
**kwargs
Patch properties passed to `matplotlib.sankey.Sankey.add` in Matplotlib mode.

Returns
-------
matplotlib.sankey.Sankey or list or SankeyDiagram
The Sankey diagram instance, or a list for multi-diagram usage. For layered
mode, returns a `~ultraplot.axes.plot_types.sankey.SankeyDiagram`.
"""

docstring._snippet_manager["plot.sankey"] = _sankey_docstring
# Auto colorbar and legend docstring
_guide_docstring = """
colorbar : bool, int, or str, optional
Expand Down Expand Up @@ -1849,6 +1926,169 @@ def curved_quiver(
stream_container = CurvedQuiverSet(lc, ac)
return stream_container

@docstring._snippet_manager
def sankey(
self,
flows: Any,
labels: Sequence[str] | None = None,
orientations: Sequence[int] | None = None,
pathlengths: float | Sequence[float] = 0.25,
trunklength: float = 1.0,
patchlabel: str = "",
*,
nodes: Any = None,
links: Any = None,
node_kw: Mapping[str, Any] | None = None,
flow_kw: Mapping[str, Any] | None = None,
label_kw: Mapping[str, Any] | None = None,
node_label_kw: Mapping[str, Any] | None = None,
flow_label_kw: Mapping[str, Any] | None = None,
node_label_box: bool | Mapping[str, Any] | None = None,
style: str | None = None,
node_order: Sequence[Any] | None = None,
layer_order: Sequence[int] | None = None,
group_cycle: Sequence[Any] | None = None,
flow_other: float | None = None,
other_label: str = "Other",
value_format: str | Callable[[float], str] | None = None,
node_label_outside: bool | str = "auto",
node_label_offset: float = 0.01,
flow_sort: bool = True,
flow_label_pos: float = 0.5,
node_labels: bool = True,
flow_labels: bool = False,
align: str = "center",
layers: Mapping[Any, int] | None = None,
scale: float | None = None,
unit: str | None = None,
format: str | None = None,
gap: float | None = None,
radius: float | None = None,
shoulder: float | None = None,
offset: float | None = None,
head_angle: float | None = None,
margin: float | None = None,
tolerance: float | None = None,
prior: int | None = None,
connect: tuple[int, int] | None = (0, 0),
rotation: float = 0,
**kwargs: Any,
) -> Any:
"""
%(plot.sankey)s
"""

def _looks_like_links(values):
if values is None:
return False
if isinstance(values, np.ndarray) and values.ndim == 1:
return False
if isinstance(values, dict):
return True
if isinstance(values, (list, tuple)) and values:
first = values[0]
if isinstance(first, dict):
return True
if isinstance(first, (list, tuple)) and len(first) >= 3:
return True
return False

use_layered = nodes is not None or links is not None or _looks_like_links(flows)
if use_layered:
from .plot_types.sankey import sankey_diagram

node_kw = node_kw or {}
flow_kw = flow_kw or {}
label_kw = label_kw or {}
if links is None:
links = flows

cycle = rc["axes.prop_cycle"].by_key().get("color", [])
if not cycle:
cycle = [self._get_lines.get_next_color()]

return sankey_diagram(
self,
nodes=nodes,
flows=links,
layers=layers,
flow_cycle=cycle,
group_cycle=group_cycle,
node_order=node_order,
layer_order=layer_order,
style=style,
flow_other=flow_other,
other_label=other_label,
value_format=value_format,
node_kw=node_kw,
flow_kw=flow_kw,
label_kw=label_kw,
node_label_kw=node_label_kw,
flow_label_kw=flow_label_kw,
node_label_box=node_label_box,
node_label_outside=node_label_outside,
node_label_offset=node_label_offset,
flow_sort=flow_sort,
flow_label_pos=flow_label_pos,
node_labels=node_labels,
flow_labels=flow_labels,
align=align,
node_pad=rc["sankey.nodepad"],
node_width=rc["sankey.nodewidth"],
margin=rc["sankey.margin"],
flow_alpha=rc["sankey.flow.alpha"],
flow_curvature=rc["sankey.flow.curvature"],
node_facecolor=rc["sankey.node.facecolor"],
)

from matplotlib.sankey import Sankey

sankey_kw = {}
if scale is not None:
sankey_kw["scale"] = scale
if unit is not None:
sankey_kw["unit"] = unit
if format is not None:
sankey_kw["format"] = format
if gap is not None:
sankey_kw["gap"] = gap
if radius is not None:
sankey_kw["radius"] = radius
if shoulder is not None:
sankey_kw["shoulder"] = shoulder
if offset is not None:
sankey_kw["offset"] = offset
if head_angle is not None:
sankey_kw["head_angle"] = head_angle
if margin is not None:
sankey_kw["margin"] = margin
if tolerance is not None:
sankey_kw["tolerance"] = tolerance

if "facecolor" not in kwargs and "color" not in kwargs:
kwargs["facecolor"] = self._get_lines.get_next_color()

sankey = Sankey(ax=self, **sankey_kw)
add_kw = {
"flows": flows,
"trunklength": trunklength,
"patchlabel": patchlabel,
"rotation": rotation,
"pathlengths": pathlengths,
}
if labels is not None:
add_kw["labels"] = labels
if orientations is not None:
add_kw["orientations"] = orientations
if prior is not None:
add_kw["prior"] = prior
if connect is not None:
add_kw["connect"] = connect

sankey.add(**add_kw, **kwargs)
diagrams = sankey.finish()
return diagrams[0] if len(diagrams) == 1 else diagrams

def _call_native(self, name, *args, **kwargs):
"""
Call the plotting method and redirect internal calls to native methods.
Expand Down
Loading
Loading