Skip to content

Commit 23900d5

Browse files
authored
Merge branch 'main' into py-314
2 parents e1ed92a + aad9d57 commit 23900d5

18 files changed

Lines changed: 3191 additions & 572 deletions

.github/workflows/build-ultraplot.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ jobs:
136136
with:
137137
path: ./ultraplot/tests/baseline # The directory to cache
138138
# Key is based on OS, Python/Matplotlib versions, and the base commit SHA
139-
key: ${{ runner.os }}-baseline-base-v4-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}
139+
key: ${{ runner.os }}-baseline-base-v5-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}
140140
restore-keys: |
141-
${{ runner.os }}-baseline-base-v4-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}-
141+
${{ runner.os }}-baseline-base-v5-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}-
142142
143143
# Conditional Baseline Generation (Only runs on cache miss)
144144
- name: Generate baseline from main
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""
2+
Top-aligned ribbon flow
3+
=======================
4+
5+
Fixed-row ribbon flows for category transitions across adjacent periods.
6+
7+
Why UltraPlot here?
8+
-------------------
9+
This is a distinct flow layout from Sankey: topic rows are fixed globally and
10+
flows are stacked from each row top, so vertical position is semantically stable.
11+
12+
Key function: :py:meth:`ultraplot.axes.PlotAxes.ribbon`.
13+
14+
See also
15+
--------
16+
* :doc:`2D plot types </2dplots>`
17+
* :doc:`Layered Sankey diagram <07_sankey>`
18+
"""
19+
20+
import numpy as np
21+
import pandas as pd
22+
23+
import ultraplot as uplt
24+
25+
GROUP_COLORS = {
26+
"Group A": "#2E7D32",
27+
"Group B": "#6A1B9A",
28+
"Group C": "#5D4037",
29+
"Group D": "#0277BD",
30+
"Group E": "#F57C00",
31+
"Group F": "#C62828",
32+
"Group G": "#D84315",
33+
}
34+
35+
TOPIC_TO_GROUP = {
36+
"Topic 01": "Group A",
37+
"Topic 02": "Group A",
38+
"Topic 03": "Group B",
39+
"Topic 04": "Group B",
40+
"Topic 05": "Group C",
41+
"Topic 06": "Group C",
42+
"Topic 07": "Group D",
43+
"Topic 08": "Group D",
44+
"Topic 09": "Group E",
45+
"Topic 10": "Group E",
46+
"Topic 11": "Group F",
47+
"Topic 12": "Group F",
48+
"Topic 13": "Group G",
49+
"Topic 14": "Group G",
50+
}
51+
52+
53+
def build_assignments():
54+
"""Synthetic entity-category assignments by period."""
55+
state = np.random.RandomState(51423)
56+
countries = [f"Entity {i:02d}" for i in range(1, 41)]
57+
periods = ["1990-1999", "2000-2009", "2010-2019", "2020-2029"]
58+
topics = list(TOPIC_TO_GROUP.keys())
59+
60+
rows = []
61+
for country in countries:
62+
topic = state.choice(topics)
63+
rows.append((country, periods[0], topic))
64+
for period in periods[1:]:
65+
if state.rand() < 0.68:
66+
next_topic = topic
67+
else:
68+
group = TOPIC_TO_GROUP[topic]
69+
same_group = [
70+
t for t in topics if TOPIC_TO_GROUP[t] == group and t != topic
71+
]
72+
next_topic = state.choice(
73+
same_group if same_group and state.rand() < 0.6 else topics
74+
)
75+
topic = next_topic
76+
rows.append((country, period, topic))
77+
return pd.DataFrame(rows, columns=["country", "period", "topic"]), periods
78+
79+
80+
df, periods = build_assignments()
81+
82+
group_order = list(GROUP_COLORS)
83+
topic_order = []
84+
for group in group_order:
85+
topic_order.extend(sorted([t for t, g in TOPIC_TO_GROUP.items() if g == group]))
86+
87+
fig, ax = uplt.subplots(refwidth=6.3)
88+
ax.ribbon(
89+
df,
90+
id_col="country",
91+
period_col="period",
92+
topic_col="topic",
93+
period_order=periods,
94+
topic_order=topic_order,
95+
group_map=TOPIC_TO_GROUP,
96+
group_order=group_order,
97+
group_colors=GROUP_COLORS,
98+
)
99+
100+
ax.format(title="Category transitions with fixed top-aligned rows")
101+
fig.format(suptitle="Top-aligned ribbon flow by period")
102+
fig.show()

docs/subplots.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,9 @@
373373
# `~matplotlib.figure.Figure.supxlabel` and `~matplotlib.figure.Figure.supylabel`,
374374
# these labels are aligned between gridspec edges rather than figure edges.
375375
# #. Supporting five sharing "levels". These values can be passed to `sharex`,
376-
# `sharey`, or `share`, or assigned to :rcraw:`subplots.share`. The levels
377-
# are defined as follows:
376+
# `sharey`, or `share`, or assigned to :rcraw:`subplots.share`.
377+
# UltraPlot supports five explicit sharing levels plus ``'auto'``.
378+
# The levels are defined as follows:
378379
#
379380
# * ``False`` or ``0``: Axis sharing is disabled.
380381
# * ``'labels'``, ``'labs'``, or ``1``: Axis labels are shared, but nothing else.
@@ -384,6 +385,14 @@
384385
# in the same row or column of the :class:`~ultraplot.gridspec.GridSpec`; a space
385386
# or empty plot will add the labels, but not break the limit sharing. See below
386387
# for a more complex example.
388+
# * ``'limits'``, ``'lims'``, or ``2``: As above, plus share limits/scales/ticks.
389+
# * ``True`` or ``3``: As above, plus hide inner tick labels.
390+
# * ``'all'`` or ``4``: As above, plus share limits across the full subplot grid.
391+
# * ``'auto'`` (default): Start from level ``3`` and only share compatible axes.
392+
# This suppresses warnings for mixed axis families (e.g., cartesian + polar).
393+
#
394+
# Explicit sharing levels still force sharing attempts and may warn when
395+
# incompatible axes are encountered.
387396
#
388397
# The below examples demonstrate the effect of various axis and label sharing
389398
# settings on the appearance of several subplot grids.
@@ -422,6 +431,20 @@
422431
import ultraplot as uplt
423432
import numpy as np
424433

434+
# The default `share='auto'` keeps incompatible axis families unshared.
435+
fig, axs = uplt.subplots(ncols=2, proj=("cart", "polar"))
436+
x = np.linspace(0, 2 * np.pi, 100)
437+
axs[0].plot(x, np.sin(x))
438+
axs[1].plot(x, np.abs(np.sin(2 * x)))
439+
axs.format(
440+
suptitle="Auto sharing with mixed cartesian and polar axes",
441+
title=("cartesian", "polar"),
442+
)
443+
444+
# %%
445+
import ultraplot as uplt
446+
import numpy as np
447+
425448
state = np.random.RandomState(51423)
426449

427450
# Plots with minimum and maximum sharing settings

0 commit comments

Comments
 (0)