From c1d1204de975908614b14dbbf14374e24a234e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20S=C3=A4ilynoja?= Date: Tue, 30 Sep 2025 12:24:09 +0300 Subject: [PATCH 1/2] Ensure y=0 are aligned between the two axes --- pymc_marketing/mmm/plot.py | 39 +++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/pymc_marketing/mmm/plot.py b/pymc_marketing/mmm/plot.py index b7a2ddae4..fd808ad73 100644 --- a/pymc_marketing/mmm/plot.py +++ b/pymc_marketing/mmm/plot.py @@ -243,6 +243,40 @@ def _build_subplot_title( return ", ".join(title_parts) return fallback_title + def _align_y_axes(self, ax, ax2, include_zero=False): + """Align y=0 of primary and secondary y-axis.""" + if ax.axes.get_ylim()[0] < 0 or ax2.axes.get_ylim()[0] < 0: + ylims1 = ax.axes.get_ylim() + ylims2 = ax2.axes.get_ylim() + # Find the ratio of negative vs. positive part of the axes. + if ylims1[1]: + ax1_yratio = ylims1[0] / ylims1[1] + else: + # Fully negative axis. + ax1_yratio = -1 + + if ylims2[1]: + ax2_yratio = ylims2[0] / ylims2[1] + else: + # Fully negative axis, may need to reflect the other + ax2_yratio = -1 + + # Make axis adjustments. If both axes fully negative, no adjustment. + if ax1_yratio < ax2_yratio: + ax2.set_ylim(bottom = ylims2[1]*ax1_yratio) + if ax1_yratio == -1: + # if the axis is fully negative, center zero. + ax.set_ylim(top=-ylims1[0]) + elif ax2_yratio < ax1_yratio: + ax.set_ylim(bottom = ylims1[1]*ax2_yratio) + if ax2_yratio == -1: + # if the axis is fully negative, center zero. + ax2.set_ylim(top=-ylims2[0]) + elif include_zero: + # Ensure both axes start at zero + ax.set_ylim(bottom=0) + ax2.set_ylim(bottom=0) + def _get_additional_dim_combinations( self, data: xr.Dataset, @@ -1179,7 +1213,7 @@ def _plot_budget_allocation_bars( alpha=opacity, label="Channel Contribution", ) - + # Labels and formatting ax.set_xlabel("Channels") ax.set_ylabel("Allocated Spend", color="C0", labelpad=10) @@ -1190,6 +1224,9 @@ def _plot_budget_allocation_bars( ax.set_xticklabels(channels) ax.tick_params(axis="x", rotation=90) + # Ensure that y=0 are aligned between ax and ax2. + self._align_y_axes(ax, ax2, include_zero=True) + # Turn off grid and add legend ax.grid(False) ax2.grid(False) From 6da79ecbd1e5c4af97cb94ced2b31ee71f505799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20S=C3=A4ilynoja?= Date: Tue, 30 Sep 2025 12:45:03 +0300 Subject: [PATCH 2/2] run pre-commit formatting --- pymc_marketing/mmm/plot.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pymc_marketing/mmm/plot.py b/pymc_marketing/mmm/plot.py index fd808ad73..e461b765a 100644 --- a/pymc_marketing/mmm/plot.py +++ b/pymc_marketing/mmm/plot.py @@ -260,15 +260,15 @@ def _align_y_axes(self, ax, ax2, include_zero=False): else: # Fully negative axis, may need to reflect the other ax2_yratio = -1 - + # Make axis adjustments. If both axes fully negative, no adjustment. if ax1_yratio < ax2_yratio: - ax2.set_ylim(bottom = ylims2[1]*ax1_yratio) + ax2.set_ylim(bottom=ylims2[1] * ax1_yratio) if ax1_yratio == -1: # if the axis is fully negative, center zero. ax.set_ylim(top=-ylims1[0]) elif ax2_yratio < ax1_yratio: - ax.set_ylim(bottom = ylims1[1]*ax2_yratio) + ax.set_ylim(bottom=ylims1[1] * ax2_yratio) if ax2_yratio == -1: # if the axis is fully negative, center zero. ax2.set_ylim(top=-ylims2[0]) @@ -276,7 +276,7 @@ def _align_y_axes(self, ax, ax2, include_zero=False): # Ensure both axes start at zero ax.set_ylim(bottom=0) ax2.set_ylim(bottom=0) - + def _get_additional_dim_combinations( self, data: xr.Dataset, @@ -1213,7 +1213,7 @@ def _plot_budget_allocation_bars( alpha=opacity, label="Channel Contribution", ) - + # Labels and formatting ax.set_xlabel("Channels") ax.set_ylabel("Allocated Spend", color="C0", labelpad=10)