From d4668e79536fba98ea8804a13a372af1b3ba7b71 Mon Sep 17 00:00:00 2001 From: Hassan Kibirige Date: Tue, 12 Nov 2024 18:44:12 +0300 Subject: [PATCH] Add plot_title/caption_position themeables closes #838 --- doc/_quartodoc.yml | 2 + doc/changelog.qmd | 21 ++++ plotnine/_mpl/layout_manager/_layout_items.py | 58 +++++++--- plotnine/_mpl/layout_manager/_spaces.py | 103 ++++++++++++------ plotnine/themes/theme.py | 2 + plotnine/themes/theme_gray.py | 2 + plotnine/themes/theme_matplotlib.py | 2 + plotnine/themes/theme_seaborn.py | 2 + plotnine/themes/theme_void.py | 2 + plotnine/themes/themeable.py | 26 +++++ .../plot_titles_and_caption_positioning.png | Bin 0 -> 27860 bytes tests/test_layout.py | 7 ++ 12 files changed, 177 insertions(+), 50 deletions(-) create mode 100644 tests/baseline_images/test_layout/plot_titles_and_caption_positioning.png diff --git a/doc/_quartodoc.yml b/doc/_quartodoc.yml index 0ea9a17f0..3ad6ccadd 100644 --- a/doc/_quartodoc.yml +++ b/doc/_quartodoc.yml @@ -458,6 +458,7 @@ quartodoc: - panel_spacing_y - plot_background - plot_caption + - plot_caption_position - plot_margin - plot_margin_bottom - plot_margin_left @@ -465,6 +466,7 @@ quartodoc: - plot_margin_top - plot_subtitle - plot_title + - plot_title_position - rect - strip_align - strip_align_x diff --git a/doc/changelog.qmd b/doc/changelog.qmd index fbaac6afe..680533a2e 100644 --- a/doc/changelog.qmd +++ b/doc/changelog.qmd @@ -1,6 +1,27 @@ --- title: Changelog --- +--- +## v0.15.0 +(not-yet-released) + +### New Features + +- Using + [](:class:`~plotnine.themes.themeables.plot_title_position`) and + [](:class:`~plotnine.themes.themeables.plot_caption_position`) e.g. + + ```python + theme( + plot_title_position="plot", + plot_caption_position="plot", + ) + ``` + + You can now position the `plot_title`, `plot_subtitle` and `plot_caption` + by alignment them with respect to the plot. ({{< issue 838 >}}) + + ## v0.14.1 (2024-11-05) diff --git a/plotnine/_mpl/layout_manager/_layout_items.py b/plotnine/_mpl/layout_manager/_layout_items.py index 23ef4a18e..ef8fad88a 100644 --- a/plotnine/_mpl/layout_manager/_layout_items.py +++ b/plotnine/_mpl/layout_manager/_layout_items.py @@ -450,31 +450,39 @@ def _adjust_positions(self, spaces: LayoutSpaces): Set the x,y position of the artists around the panels """ theme = self.plot.theme + plot_title_position = theme.getp("plot_title_position", "panel") + plot_caption_position = theme.getp("plot_caption_position", "panel") if self.plot_title: ha = theme.getp(("plot_title", "ha")) self.plot_title.set_y(spaces.t.edge("plot_title")) - horizontally_align_text_with_panels(self.plot_title, ha, spaces) + horizontally_align_text( + self.plot_title, ha, spaces, plot_title_position + ) if self.plot_subtitle: ha = theme.getp(("plot_subtitle", "ha")) self.plot_subtitle.set_y(spaces.t.edge("plot_subtitle")) - horizontally_align_text_with_panels(self.plot_subtitle, ha, spaces) + horizontally_align_text( + self.plot_subtitle, ha, spaces, plot_title_position + ) if self.plot_caption: ha = theme.getp(("plot_caption", "ha"), "right") self.plot_caption.set_y(spaces.b.edge("plot_caption")) - horizontally_align_text_with_panels(self.plot_caption, ha, spaces) + horizontally_align_text( + self.plot_caption, ha, spaces, plot_caption_position + ) if self.axis_title_x: ha = theme.getp(("axis_title_x", "ha"), "center") self.axis_title_x.set_y(spaces.b.edge("axis_title_x")) - horizontally_align_text_with_panels(self.axis_title_x, ha, spaces) + horizontally_align_text(self.axis_title_x, ha, spaces) if self.axis_title_y: va = theme.getp(("axis_title_y", "va"), "center") self.axis_title_y.set_x(spaces.l.edge("axis_title_y")) - vertically_align_text_with_panels(self.axis_title_y, va, spaces) + vertically_align_text(self.axis_title_y, va, spaces) if self.legends: set_legends_position(self.legends, spaces) @@ -487,13 +495,17 @@ def _text_is_visible(text: Text) -> bool: return text.get_visible() and text._text # type: ignore -def horizontally_align_text_with_panels( - text: Text, ha: str | float, spaces: LayoutSpaces +def horizontally_align_text( + text: Text, + ha: str | float, + spaces: LayoutSpaces, + how: Literal["panel", "plot"] = "panel", ): """ Horizontal justification - Reinterpret horizontal alignment to be justification about the panels. + Reinterpret horizontal alignment to be justification about the panels or + the plot (depending on the how parameter) """ if isinstance(ha, str): lookup = { @@ -505,20 +517,30 @@ def horizontally_align_text_with_panels( else: f = ha - params = spaces.gsparams + if how == "panel": + left = spaces.l.left + right = spaces.r.right + else: + left = spaces.l.plot_left + right = spaces.r.plot_right + width = spaces.items.calc.width(text) - x = params.left * (1 - f) + (params.right - width) * f + x = left * (1 - f) + (right - width) * f text.set_x(x) text.set_horizontalalignment("left") -def vertically_align_text_with_panels( - text: Text, va: str | float, spaces: LayoutSpaces +def vertically_align_text( + text: Text, + va: str | float, + spaces: LayoutSpaces, + how: Literal["panel", "plot"] = "panel", ): """ Vertical justification - Reinterpret vertical alignment to be justification about the panels. + Reinterpret vertical alignment to be justification about the panels or + the plot (depending on the how parameter). """ if isinstance(va, str): lookup = { @@ -532,9 +554,15 @@ def vertically_align_text_with_panels( else: f = va - params = spaces.gsparams + if how == "panel": + top = spaces.t.top + bottom = spaces.b.bottom + else: + top = spaces.t.plot_top + bottom = spaces.b.plot_bottom + height = spaces.items.calc.height(text) - y = params.bottom * (1 - f) + (params.top - height) * f + y = bottom * (1 - f) + (top - height) * f text.set_y(y) text.set_verticalalignment("bottom") diff --git a/plotnine/_mpl/layout_manager/_spaces.py b/plotnine/_mpl/layout_manager/_spaces.py index bed7a7d65..c9d18e773 100644 --- a/plotnine/_mpl/layout_manager/_spaces.py +++ b/plotnine/_mpl/layout_manager/_spaces.py @@ -173,6 +173,20 @@ def edge(self, item: str) -> float: """ return self.sum_upto(item) + @property + def left(self): + """ + Left of the panels in figure space + """ + return self.total + + @property + def plot_left(self): + """ + Distance in figure space from left edge upto where artists start + """ + return self.edge("legend") + @dataclass class right_spaces(_side_spaces): @@ -219,6 +233,20 @@ def edge(self, item: str) -> float: """ return 1 - self.sum_upto(item) + @property + def right(self): + """ + Right of the panels in figure space + """ + return 1 - self.total + + @property + def plot_right(self): + """ + Distance in figure space from right edge upto where artists start + """ + return self.edge("legend") + @dataclass class top_spaces(_side_spaces): @@ -284,6 +312,20 @@ def edge(self, item: str) -> float: """ return 1 - self.sum_upto(item) + @property + def top(self): + """ + Top of the panels in figure space + """ + return 1 - self.total + + @property + def plot_top(self): + """ + Distance in figure space from top edge upto where artists start + """ + return self.edge("legend") + @dataclass class bottom_spaces(_side_spaces): @@ -353,6 +395,20 @@ def edge(self, item: str) -> float: """ return self.sum_upto(item) + @property + def bottom(self): + """ + Bottom of the panels in figure space + """ + return self.total + + @property + def plot_bottom(self): + """ + Distance in figure space from bottom edge upto where artists start + """ + return self.edge("legend") + @dataclass class LayoutSpaces: @@ -434,34 +490,6 @@ def __post_init__(self): # Increase aspect ratio, wider panels self._reduce_height(ratio) - @property - def left(self): - """ - Left of the panels in figure space - """ - return self.l.total - - @property - def right(self): - """ - Right of the panels in figure space - """ - return 1 - self.r.total - - @property - def top(self): - """ - Top of the panels in figure space - """ - return 1 - self.t.total - - @property - def bottom(self): - """ - Bottom of the panels in figure space - """ - return self.b.total - def increase_horizontal_plot_margin(self, dw: float): """ Increase the plot_margin to the right & left of the panels @@ -494,7 +522,12 @@ def _calculate_panel_spacing(self) -> GridSpecParams: raise TypeError(f"Unknown type of facet: {type(self.plot.facet)}") return GridSpecParams( - self.left, self.right, self.top, self.bottom, wspace, hspace + self.l.left, + self.r.right, + self.t.top, + self.b.bottom, + wspace, + hspace, ) def _calculate_panel_spacing_facet_grid(self) -> tuple[float, float]: @@ -513,8 +546,8 @@ def _calculate_panel_spacing_facet_grid(self) -> tuple[float, float]: self.sh = theme.getp("panel_spacing_y") * self.W / self.H # width and height of axes as fraction of figure width & height - self.w = ((self.right - self.left) - self.sw * (ncol - 1)) / ncol - self.h = ((self.top - self.bottom) - self.sh * (nrow - 1)) / nrow + self.w = ((self.r.right - self.l.left) - self.sw * (ncol - 1)) / ncol + self.h = ((self.t.top - self.b.bottom) - self.sh * (nrow - 1)) / nrow # Spacing as fraction of axes width & height wspace = self.sw / self.w @@ -559,8 +592,8 @@ def _calculate_panel_spacing_facet_wrap(self) -> tuple[float, float]: ) + self.items.axis_ticks_y_max_width("all") # width and height of axes as fraction of figure width & height - self.w = ((self.right - self.left) - self.sw * (ncol - 1)) / ncol - self.h = ((self.top - self.bottom) - self.sh * (nrow - 1)) / nrow + self.w = ((self.r.right - self.l.left) - self.sw * (ncol - 1)) / ncol + self.h = ((self.t.top - self.b.bottom) - self.sh * (nrow - 1)) / nrow # Spacing as fraction of axes width & height wspace = self.sw / self.w @@ -571,8 +604,8 @@ def _calculate_panel_spacing_facet_null(self) -> tuple[float, float]: """ Calculate spacing parts for facet_null """ - self.w = self.right - self.left - self.h = self.top - self.bottom + self.w = self.r.right - self.l.left + self.h = self.t.top - self.b.bottom self.sw = 0 self.sh = 0 return 0, 0 diff --git a/plotnine/themes/theme.py b/plotnine/themes/theme.py index 2c8e9c095..39dd40388 100644 --- a/plotnine/themes/theme.py +++ b/plotnine/themes/theme.py @@ -122,6 +122,8 @@ def __init__( plot_title=None, plot_subtitle=None, plot_caption=None, + plot_title_position=None, + plot_caption_position=None, strip_text_x=None, strip_text_y=None, strip_text=None, diff --git a/plotnine/themes/theme_gray.py b/plotnine/themes/theme_gray.py index 74cdc9cfa..189d6e073 100644 --- a/plotnine/themes/theme_gray.py +++ b/plotnine/themes/theme_gray.py @@ -126,6 +126,8 @@ def __init__(self, base_size=11, base_family=None): ma="left", margin={"b": m, "units": "fig"}, ), + plot_title_position="panel", + plot_caption_position="panel", strip_align=0, strip_background=element_rect(color="none", fill="#D9D9D9"), strip_background_x=element_rect(width=1), diff --git a/plotnine/themes/theme_matplotlib.py b/plotnine/themes/theme_matplotlib.py index 476c89592..4a56d591f 100644 --- a/plotnine/themes/theme_matplotlib.py +++ b/plotnine/themes/theme_matplotlib.py @@ -104,6 +104,8 @@ def __init__(self, rc=None, fname=None, use_defaults=True): ma="left", margin={"b": m, "units": "fig"}, ), + plot_title_position="panel", + plot_caption_position="panel", strip_align=0, strip_background=element_rect( fill="#D9D9D9", color="black", size=linewidth diff --git a/plotnine/themes/theme_seaborn.py b/plotnine/themes/theme_seaborn.py index ef2dff00a..c1220096f 100644 --- a/plotnine/themes/theme_seaborn.py +++ b/plotnine/themes/theme_seaborn.py @@ -116,6 +116,8 @@ def __init__( ma="left", margin={"b": m, "units": "fig"}, ), + plot_title_position="panel", + plot_caption_position="panel", strip_align=0, strip_background=element_rect(color="none", fill="#D1CDDF"), strip_text=element_text( diff --git a/plotnine/themes/theme_void.py b/plotnine/themes/theme_void.py index 0c43eb487..28c3a9527 100644 --- a/plotnine/themes/theme_void.py +++ b/plotnine/themes/theme_void.py @@ -93,6 +93,8 @@ def __init__(self, base_size=11, base_family=None): ma="left", margin={"b": m, "units": "fig"}, ), + plot_title_position="panel", + plot_caption_position="panel", strip_align=0, strip_text=element_text( color="#1A1A1A", diff --git a/plotnine/themes/themeable.py b/plotnine/themes/themeable.py index a20a489cb..89a41fccc 100644 --- a/plotnine/themes/themeable.py +++ b/plotnine/themes/themeable.py @@ -742,6 +742,32 @@ def blank_figure(self, figure: Figure, targets: ThemeTargets): text.set_visible(False) +class plot_title_position(themeable): + """ + How to align the plot title and plot subtitle + + Parameters + ---------- + theme_element : Literal["panel", "plot"], default = "panel" + If "panel", the title / subtitle are aligned with respect + to the panels. If "plot", they are aligned with the plot, + excluding the margin space + """ + + +class plot_caption_position(themeable): + """ + How to align the plot caption + + Parameters + ---------- + theme_element : Literal["panel", "plot"], default = "panel" + If "panel", the caption is aligned with respect to the + panels. If "plot", it is aligned with the plot, excluding + the margin space. + """ + + class strip_text_y(MixinSequenceOfValues): """ Facet labels along the vertical axis diff --git a/tests/baseline_images/test_layout/plot_titles_and_caption_positioning.png b/tests/baseline_images/test_layout/plot_titles_and_caption_positioning.png new file mode 100644 index 0000000000000000000000000000000000000000..115731389a367cd610af1f3b5e8c4b41f14d3135 GIT binary patch literal 27860 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