Skip to content

BUG: Handle overlapping line and bar on the same plot #61173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Apr 18, 2025

Conversation

MartinBraquet
Copy link
Contributor

@MartinBraquet MartinBraquet commented Mar 24, 2025

This code

import pandas as pd
import matplotlib.pyplot as plt

index = pd.period_range('2023', periods=3, freq='Y')
df = pd.DataFrame({
    'col1': [10, 20, 30],
    'col2': [40, 25, 10]
}, index=index)

fig, ax = plt.subplots()
df['col1'].plot(kind='bar', ax=ax)
ax2 = ax.twinx()
df['col2'].plot(kind='line', ax=ax2, color = 'r')
plt.show()

from the linked issue gives this plot:
image

And swapping the lines (line before bar)

df['col2'].plot(kind='line', ax=ax2, color = 'r')
df['col1'].plot(kind='bar', ax=ax)

gives
image

I believe the difference of results stems from the fact that the figure is always cropped to match the x-range of the last plot. We can certainly adjust the PR to make the first figure behave like the second one if desired.

Please provide feedback on the overall approach. Once confirmed, I'll expand and cover with tests.

@MartinBraquet MartinBraquet changed the title BUG: Handle line and box on the same plot BUG: Handle line and bar on the same plot Mar 24, 2025
Copy link
Member

@rhshadrach rhshadrach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! Can you add a test in pandas/tests/plotting/test_series.py. Make the plots and check that the x-axis is as expected.

Introduce `convert_from_freq` method and streamline the conversion process by passing `freq` directly instead of using `axis.freq`. This improves modularity and ensures clearer separation of concerns for frequency handling in Period conversion.
Simplified `tick_pos` calculation by reusing helper methods and added a decorator to register pandas Matplotlib converters in the `_plot` method. This improves clarity and ensures proper integration with the pandas Matplotlib ecosystem.
This test ensures that bar and line plots with identical x values are correctly superposed on the same axes. It verifies that the x-tick positions remain consistent across plot types.
@@ -971,3 +971,17 @@ def test_secondary_y_subplot_axis_labels(self):
s1.plot(ax=ax2)
assert len(ax.xaxis.get_minor_ticks()) == 0
assert len(ax.get_xticklabels()) > 0

def test_bar_line_plot(self):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fails on main

@MartinBraquet MartinBraquet changed the title BUG: Handle line and bar on the same plot BUG: Handle overlapping line and bar on the same plot Apr 7, 2025
Resolved a bug that prevented a line and bar from aligning on the same plot in `Series.plot`. This addresses issue pandas-dev#61161 and improves plot consistency when combining these chart types.
Move `x_compat` logic and time series helper methods from `LinePlot` to `MPLPlot` for better reusability and maintainability. This simplifies the `LinePlot` class and centralizes common functionality.
return PeriodConverter.convert_from_freq(values, axis.freq)

@staticmethod
def convert_from_freq(values, freq):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convert only uses the freq attribute of axis, so one should allow the user to pass freq without axis object.

Comment on lines 294 to 308
from pandas.plotting import plot_params

self.x_compat = plot_params["x_compat"]
if "x_compat" in self.kwds:
self.x_compat = bool(self.kwds.pop("x_compat"))

@final
def _is_ts_plot(self) -> bool:
# this is slightly deceptive
return not self.x_compat and self.use_index and self._use_dynamic_x()

@final
def _use_dynamic_x(self) -> bool:
return use_dynamic_x(self._get_ax(0), self.data)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those routines are not exclusive to LinePlot, as they seem related to any type of time series. _is_ts_plot is used in this PR to check if a boxplot is a time series.

@MartinBraquet
Copy link
Contributor Author

@rhshadrach I covered the change with a test. I believe the PR is now ready for review and then merge.

@MartinBraquet MartinBraquet requested a review from rhshadrach April 8, 2025 04:29
# Conflicts:
#	doc/source/whatsnew/v3.0.0.rst
#	pandas/plotting/_matplotlib/core.py
Ensure bar and line plots share consistent x-axis tick labels and verify that x-axis limits are adjusted to make all plotted elements visible in `test_bar_line_plot` test. These changes improve the robustness of visual alignment and boundary checks.
Add an assertion to verify the length of `bar_xticks` aligns with the length of the index. This improves the test's robustness by ensuring the data and ticks remain consistent.
Comment on lines +993 to +994
assert len(bar_xticks) == len(index)
assert bar_xticks == line_xticks
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is checking that

  • the three index years are in the two plots
  • the x-tick positions for those years are the same in the two plots

Comment on lines +996 to +997
assert x_limits[0] <= bar_xticks[0].get_position()[0]
assert x_limits[1] >= bar_xticks[-1].get_position()[0]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that both left-most and right-most data points are shown in the current view

Copy link
Member

@rhshadrach rhshadrach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@rhshadrach rhshadrach added this to the 3.0 milestone Apr 16, 2025
@MartinBraquet
Copy link
Contributor Author

Only a maintainer is allowed to merge a PR, right?

@mroeschke mroeschke merged commit 5f354ca into pandas-dev:main Apr 18, 2025
42 checks passed
@mroeschke
Copy link
Member

Thanks @MartinBraquet

@MartinBraquet MartinBraquet deleted the plot branch April 19, 2025 01:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BUG: fails to plot 2 plots with secondary y axis when index type is PeriodIndex and plot kinds are different
3 participants