plotnine 0.15.3__py3-none-any.whl → 0.16.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plotnine/_mpl/gridspec.py +50 -6
- plotnine/_mpl/layout_manager/__init__.py +2 -5
- plotnine/_mpl/layout_manager/_composition_layout_items.py +98 -0
- plotnine/_mpl/layout_manager/_composition_side_space.py +461 -0
- plotnine/_mpl/layout_manager/_engine.py +19 -58
- plotnine/_mpl/layout_manager/_grid.py +94 -0
- plotnine/_mpl/layout_manager/_layout_tree.py +402 -817
- plotnine/_mpl/layout_manager/{_layout_items.py → _plot_layout_items.py} +55 -278
- plotnine/_mpl/layout_manager/{_spaces.py → _plot_side_space.py} +111 -291
- plotnine/_mpl/layout_manager/_side_space.py +176 -0
- plotnine/_mpl/utils.py +259 -1
- plotnine/_utils/__init__.py +23 -3
- plotnine/_utils/context.py +9 -13
- plotnine/_utils/dataclasses.py +24 -0
- plotnine/animation.py +13 -12
- plotnine/composition/__init__.py +6 -0
- plotnine/composition/_beside.py +13 -11
- plotnine/composition/_compose.py +263 -99
- plotnine/composition/_plot_annotation.py +75 -0
- plotnine/composition/_plot_layout.py +143 -0
- plotnine/composition/_plot_spacer.py +1 -1
- plotnine/composition/_stack.py +13 -11
- plotnine/composition/_types.py +28 -0
- plotnine/composition/_wrap.py +60 -0
- plotnine/facets/facet.py +9 -12
- plotnine/facets/facet_grid.py +2 -2
- plotnine/facets/facet_wrap.py +1 -1
- plotnine/geoms/geom.py +2 -2
- plotnine/geoms/geom_map.py +4 -5
- plotnine/geoms/geom_path.py +8 -7
- plotnine/geoms/geom_rug.py +6 -10
- plotnine/geoms/geom_text.py +5 -5
- plotnine/ggplot.py +63 -9
- plotnine/guides/guide.py +24 -6
- plotnine/guides/guide_colorbar.py +88 -46
- plotnine/guides/guide_legend.py +47 -20
- plotnine/guides/guides.py +2 -2
- plotnine/iapi.py +17 -1
- plotnine/scales/scale.py +1 -1
- plotnine/stats/binning.py +15 -43
- plotnine/stats/smoothers.py +7 -3
- plotnine/stats/stat.py +2 -2
- plotnine/stats/stat_density_2d.py +10 -6
- plotnine/stats/stat_pointdensity.py +8 -1
- plotnine/stats/stat_qq.py +5 -5
- plotnine/stats/stat_qq_line.py +6 -1
- plotnine/stats/stat_sina.py +19 -20
- plotnine/stats/stat_summary.py +4 -2
- plotnine/stats/stat_summary_bin.py +7 -1
- plotnine/themes/elements/element_line.py +2 -0
- plotnine/themes/elements/element_text.py +12 -1
- plotnine/themes/theme.py +18 -24
- plotnine/themes/themeable.py +17 -3
- plotnine/typing.py +6 -1
- {plotnine-0.15.3.dist-info → plotnine-0.16.0a1.dist-info}/METADATA +3 -3
- {plotnine-0.15.3.dist-info → plotnine-0.16.0a1.dist-info}/RECORD +59 -51
- {plotnine-0.15.3.dist-info → plotnine-0.16.0a1.dist-info}/WHEEL +1 -1
- plotnine/composition/_plotspec.py +0 -50
- {plotnine-0.15.3.dist-info → plotnine-0.16.0a1.dist-info}/licenses/LICENSE +0 -0
- {plotnine-0.15.3.dist-info → plotnine-0.16.0a1.dist-info}/top_level.txt +0 -0
|
@@ -11,116 +11,32 @@ such cases as when left or right margin are affected by xlabel.
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
-
from
|
|
15
|
-
from dataclasses import dataclass, field, fields
|
|
14
|
+
from copy import copy
|
|
16
15
|
from functools import cached_property
|
|
17
|
-
from typing import TYPE_CHECKING
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
18
17
|
|
|
19
18
|
from plotnine.exceptions import PlotnineError
|
|
20
19
|
from plotnine.facets import facet_grid, facet_null, facet_wrap
|
|
21
20
|
|
|
22
|
-
from .
|
|
21
|
+
from ._plot_layout_items import PlotLayoutItems
|
|
22
|
+
from ._side_space import GridSpecParams, _side_space
|
|
23
23
|
|
|
24
24
|
if TYPE_CHECKING:
|
|
25
|
-
from dataclasses import Field
|
|
26
|
-
from typing import Generator
|
|
27
|
-
|
|
28
25
|
from plotnine import ggplot
|
|
29
26
|
from plotnine._mpl.gridspec import p9GridSpec
|
|
30
27
|
from plotnine.iapi import outside_legend
|
|
31
|
-
from plotnine.typing import Side
|
|
32
|
-
|
|
33
|
-
# Note
|
|
34
|
-
# Margins around the plot are specified in figure coordinates
|
|
35
|
-
# We interpret that value to be a fraction of the width. So along
|
|
36
|
-
# the vertical direction we multiply by W/H to get equal space
|
|
37
|
-
# in both directions
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
@dataclass
|
|
41
|
-
class GridSpecParams:
|
|
42
|
-
"""
|
|
43
|
-
Gridspec Parameters
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
left: float
|
|
47
|
-
right: float
|
|
48
|
-
top: float
|
|
49
|
-
bottom: float
|
|
50
|
-
wspace: float
|
|
51
|
-
hspace: float
|
|
52
|
-
|
|
53
|
-
@property
|
|
54
|
-
def valid(self) -> bool:
|
|
55
|
-
"""
|
|
56
|
-
Return True if the params will create a non-empty area
|
|
57
|
-
"""
|
|
58
|
-
return self.top - self.bottom > 0 and self.right - self.left > 0
|
|
59
28
|
|
|
60
29
|
|
|
61
|
-
|
|
62
|
-
class _side_spaces(ABC):
|
|
30
|
+
class _plot_side_space(_side_space):
|
|
63
31
|
"""
|
|
64
|
-
Base class
|
|
65
|
-
|
|
66
|
-
A *_space class does the book keeping for all the artists that may
|
|
67
|
-
fall on that side of the panels. The same name may appear in multiple
|
|
68
|
-
side classes (e.g. legend).
|
|
69
|
-
|
|
70
|
-
The amount of space for each artist is computed in figure coordinates.
|
|
32
|
+
Base class for the side space around a plot
|
|
71
33
|
"""
|
|
72
34
|
|
|
73
|
-
items:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
self.side: Side = cast("Side", self.__class__.__name__[:-7])
|
|
77
|
-
"""
|
|
78
|
-
Side of the panel(s) that this class applies to
|
|
79
|
-
"""
|
|
35
|
+
def __init__(self, items: PlotLayoutItems):
|
|
36
|
+
self.items = items
|
|
37
|
+
self.gridspec = items.plot._gridspec
|
|
80
38
|
self._calculate()
|
|
81
39
|
|
|
82
|
-
def _calculate(self):
|
|
83
|
-
"""
|
|
84
|
-
Calculate the space taken up by each artist
|
|
85
|
-
"""
|
|
86
|
-
|
|
87
|
-
@property
|
|
88
|
-
def total(self) -> float:
|
|
89
|
-
"""
|
|
90
|
-
Total space
|
|
91
|
-
"""
|
|
92
|
-
return sum(getattr(self, f.name) for f in fields(self)[1:])
|
|
93
|
-
|
|
94
|
-
def sum_upto(self, item: str) -> float:
|
|
95
|
-
"""
|
|
96
|
-
Sum of space upto but not including item
|
|
97
|
-
|
|
98
|
-
Sums from the edge of the figure i.e. the "plot_margin".
|
|
99
|
-
"""
|
|
100
|
-
|
|
101
|
-
def _fields_upto(item: str) -> Generator[Field, None, None]:
|
|
102
|
-
for f in fields(self)[1:]:
|
|
103
|
-
if f.name == item:
|
|
104
|
-
break
|
|
105
|
-
yield f
|
|
106
|
-
|
|
107
|
-
return sum(getattr(self, f.name) for f in _fields_upto(item))
|
|
108
|
-
|
|
109
|
-
def sum_incl(self, item: str) -> float:
|
|
110
|
-
"""
|
|
111
|
-
Sum of space upto and including the item
|
|
112
|
-
|
|
113
|
-
Sums from the edge of the figure i.e. the "plot_margin".
|
|
114
|
-
"""
|
|
115
|
-
|
|
116
|
-
def _fields_upto(item: str) -> Generator[Field, None, None]:
|
|
117
|
-
for f in fields(self)[1:]:
|
|
118
|
-
yield f
|
|
119
|
-
if f.name == item:
|
|
120
|
-
break
|
|
121
|
-
|
|
122
|
-
return sum(getattr(self, f.name) for f in _fields_upto(item))
|
|
123
|
-
|
|
124
40
|
@cached_property
|
|
125
41
|
def _legend_size(self) -> tuple[float, float]:
|
|
126
42
|
"""
|
|
@@ -134,7 +50,7 @@ class _side_spaces(ABC):
|
|
|
134
50
|
return (0, 0)
|
|
135
51
|
|
|
136
52
|
ol: outside_legend = getattr(self.items.legends, self.side)
|
|
137
|
-
return self.items.
|
|
53
|
+
return self.items.geometry.size(ol.box)
|
|
138
54
|
|
|
139
55
|
@cached_property
|
|
140
56
|
def legend_width(self) -> float:
|
|
@@ -150,62 +66,6 @@ class _side_spaces(ABC):
|
|
|
150
66
|
"""
|
|
151
67
|
return self._legend_size[1]
|
|
152
68
|
|
|
153
|
-
@cached_property
|
|
154
|
-
def gs(self) -> p9GridSpec:
|
|
155
|
-
"""
|
|
156
|
-
The gridspec of the plot
|
|
157
|
-
"""
|
|
158
|
-
return self.items.plot._gridspec
|
|
159
|
-
|
|
160
|
-
@property
|
|
161
|
-
def offset(self) -> float:
|
|
162
|
-
"""
|
|
163
|
-
Distance in figure dimensions from the edge of the figure
|
|
164
|
-
|
|
165
|
-
Derived classes should override this method
|
|
166
|
-
|
|
167
|
-
The space/margin and size consumed by artists is in figure dimensions
|
|
168
|
-
but the exact position is relative to the position of the GridSpec
|
|
169
|
-
within the figure. The offset accounts for the position of the
|
|
170
|
-
GridSpec and allows us to accurately place artists using figure
|
|
171
|
-
coordinates.
|
|
172
|
-
|
|
173
|
-
Example of an offset
|
|
174
|
-
|
|
175
|
-
Figure
|
|
176
|
-
----------------------------------------
|
|
177
|
-
| |
|
|
178
|
-
| Plot GridSpec |
|
|
179
|
-
| -------------------------- |
|
|
180
|
-
| offset | | |
|
|
181
|
-
|<------->| X | |
|
|
182
|
-
| | Panels GridSpec | |
|
|
183
|
-
| | -------------------- | |
|
|
184
|
-
| | | | | |
|
|
185
|
-
| | | | | |
|
|
186
|
-
| | | | | |
|
|
187
|
-
| | | | | |
|
|
188
|
-
| | -------------------- | |
|
|
189
|
-
| | | |
|
|
190
|
-
| -------------------------- |
|
|
191
|
-
| |
|
|
192
|
-
----------------------------------------
|
|
193
|
-
"""
|
|
194
|
-
return 0
|
|
195
|
-
|
|
196
|
-
def to_figure_space(self, rel_value: float) -> float:
|
|
197
|
-
"""
|
|
198
|
-
Convert value relative to the gridspec to one in figure space
|
|
199
|
-
|
|
200
|
-
The result is meant to be used with transFigure transforms.
|
|
201
|
-
|
|
202
|
-
Parameters
|
|
203
|
-
----------
|
|
204
|
-
rel_value :
|
|
205
|
-
Position relative to the position of the gridspec
|
|
206
|
-
"""
|
|
207
|
-
return self.offset + rel_value
|
|
208
|
-
|
|
209
69
|
@property
|
|
210
70
|
def has_tag(self) -> bool:
|
|
211
71
|
"""
|
|
@@ -282,8 +142,7 @@ class _side_spaces(ABC):
|
|
|
282
142
|
raise PlotnineError("Side has no axis title") from err
|
|
283
143
|
|
|
284
144
|
|
|
285
|
-
|
|
286
|
-
class left_spaces(_side_spaces):
|
|
145
|
+
class left_space(_plot_side_space):
|
|
287
146
|
"""
|
|
288
147
|
Space in the figure for artists on the left of the panel area
|
|
289
148
|
|
|
@@ -355,7 +214,7 @@ class left_spaces(_side_spaces):
|
|
|
355
214
|
|
|
356
215
|
def _calculate(self):
|
|
357
216
|
theme = self.items.plot.theme
|
|
358
|
-
|
|
217
|
+
geometry = self.items.geometry
|
|
359
218
|
items = self.items
|
|
360
219
|
|
|
361
220
|
self.plot_margin = theme.getp("plot_margin_left")
|
|
@@ -363,7 +222,7 @@ class left_spaces(_side_spaces):
|
|
|
363
222
|
if self.has_tag and items.plot_tag:
|
|
364
223
|
m = theme.get_margin("plot_tag").fig
|
|
365
224
|
self.plot_tag_margin_left = m.l
|
|
366
|
-
self.plot_tag =
|
|
225
|
+
self.plot_tag = geometry.width(items.plot_tag)
|
|
367
226
|
self.plot_tag_margin_right = m.r
|
|
368
227
|
|
|
369
228
|
if items.legends and items.legends.left:
|
|
@@ -373,7 +232,7 @@ class left_spaces(_side_spaces):
|
|
|
373
232
|
if items.axis_title_y:
|
|
374
233
|
m = theme.get_margin("axis_title_y").fig
|
|
375
234
|
self.axis_title_y_margin_left = m.l
|
|
376
|
-
self.axis_title_y =
|
|
235
|
+
self.axis_title_y = geometry.width(items.axis_title_y)
|
|
377
236
|
self.axis_title_y_margin_right = m.r
|
|
378
237
|
|
|
379
238
|
# Account for the space consumed by the axis
|
|
@@ -407,7 +266,7 @@ class left_spaces(_side_spaces):
|
|
|
407
266
|
(0, 0)----------------
|
|
408
267
|
|
|
409
268
|
"""
|
|
410
|
-
return self.
|
|
269
|
+
return self.gridspec.bbox_relative.x0
|
|
411
270
|
|
|
412
271
|
def x1(self, item: str) -> float:
|
|
413
272
|
"""
|
|
@@ -454,8 +313,7 @@ class left_spaces(_side_spaces):
|
|
|
454
313
|
)
|
|
455
314
|
|
|
456
315
|
|
|
457
|
-
|
|
458
|
-
class right_spaces(_side_spaces):
|
|
316
|
+
class right_space(_plot_side_space):
|
|
459
317
|
"""
|
|
460
318
|
Space in the figure for artists on the right of the panel area
|
|
461
319
|
|
|
@@ -475,14 +333,14 @@ class right_spaces(_side_spaces):
|
|
|
475
333
|
def _calculate(self):
|
|
476
334
|
items = self.items
|
|
477
335
|
theme = self.items.plot.theme
|
|
478
|
-
|
|
336
|
+
geometry = self.items.geometry
|
|
479
337
|
|
|
480
338
|
self.plot_margin = theme.getp("plot_margin_right")
|
|
481
339
|
|
|
482
340
|
if self.has_tag and items.plot_tag:
|
|
483
341
|
m = theme.get_margin("plot_tag").fig
|
|
484
342
|
self.plot_tag_margin_right = m.r
|
|
485
|
-
self.plot_tag =
|
|
343
|
+
self.plot_tag = geometry.width(items.plot_tag)
|
|
486
344
|
self.plot_tag_margin_left = m.l
|
|
487
345
|
|
|
488
346
|
if items.legends and items.legends.right:
|
|
@@ -513,7 +371,7 @@ class right_spaces(_side_spaces):
|
|
|
513
371
|
(0, 0)---------------
|
|
514
372
|
|
|
515
373
|
"""
|
|
516
|
-
return self.
|
|
374
|
+
return self.gridspec.bbox_relative.x1 - 1
|
|
517
375
|
|
|
518
376
|
def x1(self, item: str) -> float:
|
|
519
377
|
"""
|
|
@@ -560,8 +418,7 @@ class right_spaces(_side_spaces):
|
|
|
560
418
|
)
|
|
561
419
|
|
|
562
420
|
|
|
563
|
-
|
|
564
|
-
class top_spaces(_side_spaces):
|
|
421
|
+
class top_space(_plot_side_space):
|
|
565
422
|
"""
|
|
566
423
|
Space in the figure for artists above the panel area
|
|
567
424
|
|
|
@@ -587,7 +444,7 @@ class top_spaces(_side_spaces):
|
|
|
587
444
|
def _calculate(self):
|
|
588
445
|
items = self.items
|
|
589
446
|
theme = self.items.plot.theme
|
|
590
|
-
|
|
447
|
+
geometry = self.items.geometry
|
|
591
448
|
W, H = theme.getp("figure_size")
|
|
592
449
|
F = W / H
|
|
593
450
|
|
|
@@ -596,19 +453,19 @@ class top_spaces(_side_spaces):
|
|
|
596
453
|
if self.has_tag and items.plot_tag:
|
|
597
454
|
m = theme.get_margin("plot_tag").fig
|
|
598
455
|
self.plot_tag_margin_top = m.t
|
|
599
|
-
self.plot_tag =
|
|
456
|
+
self.plot_tag = geometry.height(items.plot_tag)
|
|
600
457
|
self.plot_tag_margin_bottom = m.b
|
|
601
458
|
|
|
602
459
|
if items.plot_title:
|
|
603
460
|
m = theme.get_margin("plot_title").fig
|
|
604
461
|
self.plot_title_margin_top = m.t * F
|
|
605
|
-
self.plot_title =
|
|
462
|
+
self.plot_title = geometry.height(items.plot_title)
|
|
606
463
|
self.plot_title_margin_bottom = m.b * F
|
|
607
464
|
|
|
608
465
|
if items.plot_subtitle:
|
|
609
466
|
m = theme.get_margin("plot_subtitle").fig
|
|
610
467
|
self.plot_subtitle_margin_top = m.t * F
|
|
611
|
-
self.plot_subtitle =
|
|
468
|
+
self.plot_subtitle = geometry.height(items.plot_subtitle)
|
|
612
469
|
self.plot_subtitle_margin_bottom = m.b * F
|
|
613
470
|
|
|
614
471
|
if items.legends and items.legends.top:
|
|
@@ -642,7 +499,7 @@ class top_spaces(_side_spaces):
|
|
|
642
499
|
| |
|
|
643
500
|
(0, 0)----------------
|
|
644
501
|
"""
|
|
645
|
-
return self.
|
|
502
|
+
return self.gridspec.bbox_relative.y1 - 1
|
|
646
503
|
|
|
647
504
|
def y1(self, item: str) -> float:
|
|
648
505
|
"""
|
|
@@ -689,8 +546,7 @@ class top_spaces(_side_spaces):
|
|
|
689
546
|
)
|
|
690
547
|
|
|
691
548
|
|
|
692
|
-
|
|
693
|
-
class bottom_spaces(_side_spaces):
|
|
549
|
+
class bottom_space(_plot_side_space):
|
|
694
550
|
"""
|
|
695
551
|
Space in the figure for artists below the panel area
|
|
696
552
|
|
|
@@ -728,7 +584,7 @@ class bottom_spaces(_side_spaces):
|
|
|
728
584
|
def _calculate(self):
|
|
729
585
|
items = self.items
|
|
730
586
|
theme = self.items.plot.theme
|
|
731
|
-
|
|
587
|
+
geometry = self.items.geometry
|
|
732
588
|
W, H = theme.getp("figure_size")
|
|
733
589
|
F = W / H
|
|
734
590
|
|
|
@@ -737,13 +593,13 @@ class bottom_spaces(_side_spaces):
|
|
|
737
593
|
if self.has_tag and items.plot_tag:
|
|
738
594
|
m = theme.get_margin("plot_tag").fig
|
|
739
595
|
self.plot_tag_margin_bottom = m.b
|
|
740
|
-
self.plot_tag =
|
|
596
|
+
self.plot_tag = geometry.height(items.plot_tag)
|
|
741
597
|
self.plot_tag_margin_top = m.t
|
|
742
598
|
|
|
743
599
|
if items.plot_caption:
|
|
744
600
|
m = theme.get_margin("plot_caption").fig
|
|
745
601
|
self.plot_caption_margin_bottom = m.b * F
|
|
746
|
-
self.plot_caption =
|
|
602
|
+
self.plot_caption = geometry.height(items.plot_caption)
|
|
747
603
|
self.plot_caption_margin_top = m.t * F
|
|
748
604
|
|
|
749
605
|
if items.legends and items.legends.bottom:
|
|
@@ -753,7 +609,7 @@ class bottom_spaces(_side_spaces):
|
|
|
753
609
|
if items.axis_title_x:
|
|
754
610
|
m = theme.get_margin("axis_title_x").fig
|
|
755
611
|
self.axis_title_x_margin_bottom = m.b * F
|
|
756
|
-
self.axis_title_x =
|
|
612
|
+
self.axis_title_x = geometry.height(items.axis_title_x)
|
|
757
613
|
self.axis_title_x_margin_top = m.t * F
|
|
758
614
|
|
|
759
615
|
# Account for the space consumed by the axis
|
|
@@ -789,7 +645,7 @@ class bottom_spaces(_side_spaces):
|
|
|
789
645
|
| v |
|
|
790
646
|
(0, 0)----------------
|
|
791
647
|
"""
|
|
792
|
-
return self.
|
|
648
|
+
return self.gridspec.bbox_relative.y0
|
|
793
649
|
|
|
794
650
|
def y1(self, item: str) -> float:
|
|
795
651
|
"""
|
|
@@ -836,8 +692,7 @@ class bottom_spaces(_side_spaces):
|
|
|
836
692
|
)
|
|
837
693
|
|
|
838
694
|
|
|
839
|
-
|
|
840
|
-
class LayoutSpaces:
|
|
695
|
+
class PlotSideSpaces:
|
|
841
696
|
"""
|
|
842
697
|
Compute the all the spaces required in the layout
|
|
843
698
|
|
|
@@ -853,56 +708,67 @@ class LayoutSpaces:
|
|
|
853
708
|
them in their final positions.
|
|
854
709
|
"""
|
|
855
710
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
l: left_spaces = field(init=False)
|
|
859
|
-
"""All subspaces to the left of the panels"""
|
|
860
|
-
|
|
861
|
-
r: right_spaces = field(init=False)
|
|
862
|
-
"""All subspaces to the right of the panels"""
|
|
863
|
-
|
|
864
|
-
t: top_spaces = field(init=False)
|
|
865
|
-
"""All subspaces above the top of the panels"""
|
|
866
|
-
|
|
867
|
-
b: bottom_spaces = field(init=False)
|
|
868
|
-
"""All subspaces below the bottom of the panels"""
|
|
869
|
-
|
|
870
|
-
W: float = field(init=False, default=0)
|
|
711
|
+
W: float
|
|
871
712
|
"""Figure Width [inches]"""
|
|
872
713
|
|
|
873
|
-
H: float
|
|
714
|
+
H: float
|
|
874
715
|
"""Figure Height [inches]"""
|
|
875
716
|
|
|
876
|
-
w: float
|
|
717
|
+
w: float
|
|
877
718
|
"""Axes width w.r.t figure in [0, 1]"""
|
|
878
719
|
|
|
879
|
-
h: float
|
|
720
|
+
h: float
|
|
880
721
|
"""Axes height w.r.t figure in [0, 1]"""
|
|
881
722
|
|
|
882
|
-
sh: float
|
|
723
|
+
sh: float
|
|
883
724
|
"""horizontal spacing btn panels w.r.t figure"""
|
|
884
725
|
|
|
885
|
-
sw: float
|
|
726
|
+
sw: float
|
|
886
727
|
"""vertical spacing btn panels w.r.t figure"""
|
|
887
728
|
|
|
888
|
-
|
|
889
|
-
|
|
729
|
+
def __init__(self, plot: ggplot):
|
|
730
|
+
self.plot = plot
|
|
731
|
+
self.gridspec = plot._gridspec
|
|
732
|
+
self.sub_gridspec = plot._sub_gridspec
|
|
733
|
+
self.items = PlotLayoutItems(plot)
|
|
734
|
+
|
|
735
|
+
self.l = left_space(self.items)
|
|
736
|
+
"""All subspaces to the left of the panels"""
|
|
737
|
+
|
|
738
|
+
self.r = right_space(self.items)
|
|
739
|
+
"""All subspaces to the right of the panels"""
|
|
890
740
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
self.W, self.H = self.plot.theme.getp("figure_size")
|
|
741
|
+
self.t = top_space(self.items)
|
|
742
|
+
"""All subspaces above the top of the panels"""
|
|
894
743
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
self.l = left_spaces(self.items)
|
|
898
|
-
self.r = right_spaces(self.items)
|
|
899
|
-
self.t = top_spaces(self.items)
|
|
900
|
-
self.b = bottom_spaces(self.items)
|
|
744
|
+
self.b = bottom_space(self.items)
|
|
745
|
+
"""All subspaces below the bottom of the panels"""
|
|
901
746
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
747
|
+
self.W, self.H = plot.theme.getp("figure_size")
|
|
748
|
+
|
|
749
|
+
def arrange(self):
|
|
750
|
+
"""
|
|
751
|
+
Resize plot and place artists in final positions around the panels
|
|
752
|
+
"""
|
|
753
|
+
self.resize_gridspec()
|
|
754
|
+
self.items._move_artists(self)
|
|
755
|
+
|
|
756
|
+
def resize_gridspec(self):
|
|
757
|
+
"""
|
|
758
|
+
Apply the space calculations to the sub_gridspec
|
|
759
|
+
|
|
760
|
+
After calling this method, the sub_gridspec will be appropriately
|
|
761
|
+
sized to accomodate the artists around the panels.
|
|
762
|
+
"""
|
|
763
|
+
gsparams = self.calculate_gridspec_params()
|
|
764
|
+
gsparams.validate()
|
|
765
|
+
self.sub_gridspec.update_params_and_artists(gsparams)
|
|
766
|
+
|
|
767
|
+
def calculate_gridspec_params(self) -> GridSpecParams:
|
|
768
|
+
"""
|
|
769
|
+
Grid spacing between panels w.r.t figure
|
|
770
|
+
"""
|
|
771
|
+
gsparams = self._calculate_panel_spacing()
|
|
906
772
|
|
|
907
773
|
# Adjust the spacing parameters for the desired aspect ratio
|
|
908
774
|
# It is simpler to adjust for the aspect ratio than to calculate
|
|
@@ -912,26 +778,26 @@ class LayoutSpaces:
|
|
|
912
778
|
current_ratio = self.aspect_ratio
|
|
913
779
|
if ratio > current_ratio:
|
|
914
780
|
# Increase aspect ratio, taller panels
|
|
915
|
-
self._reduce_width(ratio)
|
|
781
|
+
gsparams = self._reduce_width(gsparams, ratio)
|
|
916
782
|
elif ratio < current_ratio:
|
|
917
783
|
# Increase aspect ratio, wider panels
|
|
918
|
-
self._reduce_height(ratio)
|
|
784
|
+
gsparams = self._reduce_height(gsparams, ratio)
|
|
919
785
|
|
|
920
|
-
return
|
|
786
|
+
return gsparams
|
|
921
787
|
|
|
922
788
|
@property
|
|
923
789
|
def plot_width(self) -> float:
|
|
924
790
|
"""
|
|
925
791
|
Width [figure dimensions] of the whole plot
|
|
926
792
|
"""
|
|
927
|
-
return self.
|
|
793
|
+
return float(self.gridspec.width)
|
|
928
794
|
|
|
929
795
|
@property
|
|
930
796
|
def plot_height(self) -> float:
|
|
931
797
|
"""
|
|
932
798
|
Height [figure dimensions] of the whole plot
|
|
933
799
|
"""
|
|
934
|
-
return self.
|
|
800
|
+
return float(self.gridspec.height)
|
|
935
801
|
|
|
936
802
|
@property
|
|
937
803
|
def panel_width(self) -> float:
|
|
@@ -948,66 +814,20 @@ class LayoutSpaces:
|
|
|
948
814
|
return self.t.panel_top - self.b.panel_bottom
|
|
949
815
|
|
|
950
816
|
@property
|
|
951
|
-
def
|
|
952
|
-
"""
|
|
953
|
-
Width [figure dimensions] of space taken up by the tag
|
|
954
|
-
"""
|
|
955
|
-
# Atleast one of these is zero
|
|
956
|
-
return max(self.l.tag_width, self.r.tag_width)
|
|
957
|
-
|
|
958
|
-
@property
|
|
959
|
-
def tag_height(self) -> float:
|
|
817
|
+
def horizontal_space(self) -> float:
|
|
960
818
|
"""
|
|
961
|
-
|
|
819
|
+
Horizontal non-panel space [figure dimensions]
|
|
962
820
|
"""
|
|
963
|
-
#
|
|
964
|
-
return
|
|
821
|
+
# The same as plot_width - panel_width
|
|
822
|
+
return self.l.total + self.r.total
|
|
965
823
|
|
|
966
824
|
@property
|
|
967
|
-
def
|
|
825
|
+
def vertical_space(self) -> float:
|
|
968
826
|
"""
|
|
969
|
-
|
|
827
|
+
Vertical non-panel space [figure dimensions]
|
|
970
828
|
"""
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
@property
|
|
974
|
-
def right_tag_width(self) -> float:
|
|
975
|
-
"""
|
|
976
|
-
Width [figure dimensions] of space taken up by a right tag
|
|
977
|
-
"""
|
|
978
|
-
return self.r.tag_width
|
|
979
|
-
|
|
980
|
-
@property
|
|
981
|
-
def top_tag_height(self) -> float:
|
|
982
|
-
"""
|
|
983
|
-
Width [figure dimensions] of space taken up by a top tag
|
|
984
|
-
"""
|
|
985
|
-
return self.t.tag_height
|
|
986
|
-
|
|
987
|
-
@property
|
|
988
|
-
def bottom_tag_height(self) -> float:
|
|
989
|
-
"""
|
|
990
|
-
Height [figure dimensions] of space taken up by a bottom tag
|
|
991
|
-
"""
|
|
992
|
-
return self.b.tag_height
|
|
993
|
-
|
|
994
|
-
@property
|
|
995
|
-
def left_axis_title_clearance(self) -> float:
|
|
996
|
-
"""
|
|
997
|
-
Distance between the left y-axis title and the panel
|
|
998
|
-
|
|
999
|
-
In figure dimensions.
|
|
1000
|
-
"""
|
|
1001
|
-
return self.l.axis_title_clearance
|
|
1002
|
-
|
|
1003
|
-
@property
|
|
1004
|
-
def bottom_axis_title_clearance(self) -> float:
|
|
1005
|
-
"""
|
|
1006
|
-
Distance between the bottom x-axis title and the panel
|
|
1007
|
-
|
|
1008
|
-
In figure dimensions.
|
|
1009
|
-
"""
|
|
1010
|
-
return self.b.axis_title_clearance
|
|
829
|
+
# The same as plot_height - panel_height
|
|
830
|
+
return self.t.total + self.b.total
|
|
1011
831
|
|
|
1012
832
|
def increase_horizontal_plot_margin(self, dw: float):
|
|
1013
833
|
"""
|
|
@@ -1084,9 +904,6 @@ class LayoutSpaces:
|
|
|
1084
904
|
ncol = self.plot.facet.ncol
|
|
1085
905
|
nrow = self.plot.facet.nrow
|
|
1086
906
|
|
|
1087
|
-
left, right = self.l.panel_left, self.r.panel_right
|
|
1088
|
-
top, bottom = self.t.panel_top, self.b.panel_bottom
|
|
1089
|
-
|
|
1090
907
|
# Both spacings are specified as fractions of the figure width
|
|
1091
908
|
# Multiply the vertical by (W/H) so that the gullies along both
|
|
1092
909
|
# directions are equally spaced.
|
|
@@ -1094,8 +911,8 @@ class LayoutSpaces:
|
|
|
1094
911
|
self.sh = theme.getp("panel_spacing_y") * self.W / self.H
|
|
1095
912
|
|
|
1096
913
|
# width and height of axes as fraction of figure width & height
|
|
1097
|
-
self.w = (
|
|
1098
|
-
self.h = (
|
|
914
|
+
self.w = (self.panel_width - self.sw * (ncol - 1)) / ncol
|
|
915
|
+
self.h = (self.panel_height - self.sh * (nrow - 1)) / nrow
|
|
1099
916
|
|
|
1100
917
|
# Spacing as fraction of axes width & height
|
|
1101
918
|
wspace = self.sw / self.w
|
|
@@ -1112,9 +929,6 @@ class LayoutSpaces:
|
|
|
1112
929
|
ncol = facet.ncol
|
|
1113
930
|
nrow = facet.nrow
|
|
1114
931
|
|
|
1115
|
-
left, right = self.l.panel_left, self.r.panel_right
|
|
1116
|
-
top, bottom = self.t.panel_top, self.b.panel_bottom
|
|
1117
|
-
|
|
1118
932
|
# Both spacings are specified as fractions of the figure width
|
|
1119
933
|
self.sw = theme.getp("panel_spacing_x")
|
|
1120
934
|
self.sh = theme.getp("panel_spacing_y") * self.W / self.H
|
|
@@ -1143,8 +957,8 @@ class LayoutSpaces:
|
|
|
1143
957
|
) + self.items.axis_ticks_y_max_width_at("all")
|
|
1144
958
|
|
|
1145
959
|
# width and height of axes as fraction of figure width & height
|
|
1146
|
-
self.w = (
|
|
1147
|
-
self.h = (
|
|
960
|
+
self.w = (self.panel_width - self.sw * (ncol - 1)) / ncol
|
|
961
|
+
self.h = (self.panel_height - self.sh * (nrow - 1)) / nrow
|
|
1148
962
|
|
|
1149
963
|
# Spacing as fraction of axes width & height
|
|
1150
964
|
wspace = self.sw / self.w
|
|
@@ -1155,16 +969,18 @@ class LayoutSpaces:
|
|
|
1155
969
|
"""
|
|
1156
970
|
Calculate spacing parts for facet_null
|
|
1157
971
|
"""
|
|
1158
|
-
self.w = self.
|
|
1159
|
-
self.h = self.
|
|
972
|
+
self.w = self.panel_width
|
|
973
|
+
self.h = self.panel_height
|
|
1160
974
|
self.sw = 0
|
|
1161
975
|
self.sh = 0
|
|
1162
976
|
return 0, 0
|
|
1163
977
|
|
|
1164
|
-
def _reduce_height(self, ratio: float):
|
|
978
|
+
def _reduce_height(self, gsparams: GridSpecParams, ratio: float):
|
|
1165
979
|
"""
|
|
1166
980
|
Reduce the height of axes to get the aspect ratio
|
|
1167
981
|
"""
|
|
982
|
+
gsparams = copy(gsparams)
|
|
983
|
+
|
|
1168
984
|
# New height w.r.t figure height
|
|
1169
985
|
h1 = ratio * self.w * (self.W / self.H)
|
|
1170
986
|
|
|
@@ -1172,17 +988,20 @@ class LayoutSpaces:
|
|
|
1172
988
|
dh = (self.h - h1) * self.plot.facet.nrow / 2
|
|
1173
989
|
|
|
1174
990
|
# Reduce plot area height
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
991
|
+
gsparams.top -= dh
|
|
992
|
+
gsparams.bottom += dh
|
|
993
|
+
gsparams.hspace = self.sh / h1
|
|
1178
994
|
|
|
1179
995
|
# Add more vertical plot margin
|
|
1180
996
|
self.increase_vertical_plot_margin(dh)
|
|
997
|
+
return gsparams
|
|
1181
998
|
|
|
1182
|
-
def _reduce_width(self, ratio: float):
|
|
999
|
+
def _reduce_width(self, gsparams: GridSpecParams, ratio: float):
|
|
1183
1000
|
"""
|
|
1184
1001
|
Reduce the width of axes to get the aspect ratio
|
|
1185
1002
|
"""
|
|
1003
|
+
gsparams = copy(gsparams)
|
|
1004
|
+
|
|
1186
1005
|
# New width w.r.t figure width
|
|
1187
1006
|
w1 = (self.h * self.H) / (ratio * self.W)
|
|
1188
1007
|
|
|
@@ -1190,12 +1009,13 @@ class LayoutSpaces:
|
|
|
1190
1009
|
dw = (self.w - w1) * self.plot.facet.ncol / 2
|
|
1191
1010
|
|
|
1192
1011
|
# Reduce width
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1012
|
+
gsparams.left += dw
|
|
1013
|
+
gsparams.right -= dw
|
|
1014
|
+
gsparams.wspace = self.sw / w1
|
|
1196
1015
|
|
|
1197
1016
|
# Add more horizontal margin
|
|
1198
1017
|
self.increase_horizontal_plot_margin(dw)
|
|
1018
|
+
return gsparams
|
|
1199
1019
|
|
|
1200
1020
|
@property
|
|
1201
1021
|
def aspect_ratio(self) -> float:
|