Skip to content

Commit 49a19f5

Browse files
committed
Add byrow parameter to plot_layout
1 parent 6ad74b1 commit 49a19f5

File tree

7 files changed

+74
-16
lines changed

7 files changed

+74
-16
lines changed

plotnine/_mpl/gridspec.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111
from matplotlib.figure import SubplotParams
1212
from matplotlib.gridspec import GridSpecBase
1313

14+
from matplotlib.gridspec import SubplotSpec
1415
from matplotlib.transforms import Bbox, BboxTransformTo, TransformedBbox
1516

1617
if TYPE_CHECKING:
1718
from matplotlib.figure import Figure
18-
from matplotlib.gridspec import SubplotSpec
1919
from matplotlib.patches import Rectangle
2020
from matplotlib.transforms import Transform
2121

2222
from plotnine._mpl.layout_manager._spaces import GridSpecParams
23+
from plotnine.composition._plot_layout import plot_layout
2324

2425

2526
class p9GridSpec(GridSpecBase):
@@ -60,10 +61,12 @@ def __init__(
6061
*,
6162
width_ratios=None,
6263
height_ratios=None,
64+
byrow: bool = True,
6365
nest_into: SubplotSpec | None = None,
6466
):
6567
self.figure = figure
6668
self._nested_gridspecs = []
69+
self.byrow = byrow
6770

6871
super().__init__(
6972
nrows,
@@ -91,6 +94,34 @@ def __init__(
9194
hspace=0,
9295
)
9396

97+
@staticmethod
98+
def from_layout(
99+
layout: plot_layout,
100+
figure: Figure,
101+
*,
102+
nest_into: SubplotSpec | None = None,
103+
) -> p9GridSpec:
104+
"""
105+
Create gridspec from a plot_layout instance
106+
"""
107+
return p9GridSpec(
108+
layout.nrow,
109+
layout.ncol,
110+
figure,
111+
byrow=True if layout.byrow is None else layout.byrow,
112+
nest_into=nest_into,
113+
)
114+
115+
def __iter__(self):
116+
from itertools import product
117+
118+
if self.byrow:
119+
for r, c in product(range(self.nrows), range(self.ncols)):
120+
yield SubplotSpec(self, r * self.ncols + c)
121+
else:
122+
for c, r in product(range(self.ncols), range(self.nrows)):
123+
yield SubplotSpec(self, r * self.ncols + c)
124+
94125
@property
95126
def patch(self) -> Rectangle:
96127
"""

plotnine/_mpl/layout_manager/_grid.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ def __post_init__(
3939
self[r, c] = item
4040
c += 1
4141
if c >= ncol:
42-
c, r = 0, r + 1
42+
r, c = r + 1, 0
4343
else:
4444
for item in items:
4545
self[r, c] = item
4646
r += 1
4747
if r >= nrow:
48-
c, r = r + 1, 0
48+
r, c = 0, c + 1
4949

5050
@overload
5151
def __getitem__(self, index: tuple[int, int]) -> T | None: ...

plotnine/_mpl/layout_manager/_layout_tree.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,12 @@ class LayoutTree:
127127

128128
def __post_init__(self):
129129
self.gridspec = self.cmp.gridspec
130-
self.grid = Grid["Node"](self.nrow, self.ncol, self.nodes)
130+
self.grid = Grid["Node"](
131+
self.nrow,
132+
self.ncol,
133+
self.nodes,
134+
order="row_major" if self.cmp.layout.byrow else "col_major",
135+
)
131136

132137
@property
133138
def ncol(self) -> int:

plotnine/composition/_compose.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,8 @@ def _create_gridspec(self, figure, nest_into):
365365
from plotnine._mpl.gridspec import p9GridSpec
366366

367367
self.layout._setup(self)
368-
369-
self.gridspec = p9GridSpec(
370-
self.nrow, self.ncol, figure, nest_into=nest_into
368+
self.gridspec = p9GridSpec.from_layout(
369+
self.layout, figure=figure, nest_into=nest_into
371370
)
372371

373372
def _setup(self) -> Figure:
@@ -404,7 +403,7 @@ def _make_plotspecs(
404403
# "subplot" in the grid. The SubplotSpec is the handle that
405404
# allows us to set it up for a plot or to nest another gridspec
406405
# in it.
407-
for item, subplot_spec in zip(cmp, cmp.gridspec): # pyright: ignore[reportArgumentType]
406+
for item, subplot_spec in zip(cmp, cmp.gridspec):
408407
if isinstance(item, ggplot):
409408
yield plotspec(
410409
item,

plotnine/composition/_plot_layout.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,31 @@ class plot_layout(ComposeAddable):
1616
Customise the layout of plots in a composition
1717
"""
1818

19-
widths: Sequence[float] | None = None
19+
nrow: int | None = None
2020
"""
21-
Relative widths of each column
21+
Number of rows
2222
"""
2323

24-
heights: Sequence[float] | None = None
24+
ncol: int | None = None
2525
"""
26-
Relative heights of each column
26+
Number of columns
2727
"""
2828

29-
nrow: int | None = None
29+
byrow: bool | None = None
3030
"""
31-
Number of rows
31+
How to place plots into the grid.
32+
If None or True, they are placed row by row, left to right.
33+
If False, they are placed column by column, top to bottom.
3234
"""
3335

34-
ncol: int | None = None
36+
widths: Sequence[float] | None = None
3537
"""
36-
Number of columns
38+
Relative widths of each column
39+
"""
40+
41+
heights: Sequence[float] | None = None
42+
"""
43+
Relative heights of each column
3744
"""
3845

3946
_cmp: Compose = field(init=False, repr=False)
@@ -84,6 +91,10 @@ def _setup(self, cmp: Compose):
8491

8592
nrow, ncol = self.nrow, self.ncol
8693

94+
# byrow
95+
if self.byrow is None:
96+
self.byrow = True
97+
8798
# setup widths & heights
8899
ws, hs = self.widths, self.heights
89100
if ws is None:
@@ -111,6 +122,8 @@ def update(self, other: plot_layout):
111122
self.ncol = other.ncol
112123
if other.nrow:
113124
self.nrow = other.nrow
125+
if other.byrow is not None:
126+
self.byrow = other.byrow
114127

115128

116129
def repeat(seq: Sequence[float], n: int) -> list[float]:
9.49 KB
Loading

tests/test_plot_composition.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,13 @@ def test_add_into_stack_error():
318318
(c1 + p3).draw()
319319

320320
assert "more items than the layout rows" in str(ve.value)
321+
322+
323+
def test_plot_layout_byrow():
324+
p1 = plot.red
325+
p2 = plot.green
326+
p3 = plot.blue
327+
p4 = plot.yellow
328+
329+
p = (p1 + p2 + p3 + p4) + plot_layout(nrow=3, byrow=False)
330+
assert p == "plot_layout_byrow"

0 commit comments

Comments
 (0)