-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
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
BUG: Handle overlapping line and bar on the same plot #61173
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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.
pandas/plotting/_matplotlib/core.py
Outdated
@@ -1868,6 +1870,14 @@ def __init__( | |||
|
|||
MPLPlot.__init__(self, data, **kwargs) | |||
|
|||
if isinstance(data.index, ABCPeriodIndex): | |||
self.ax.freq = data.index.freq |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the modification of freq necessary? I worry that we don't own ax
, and that we shouldn't be mutating it. But I'm not too familiar with the plotting code; do we do this anywhere else? I don't see any instances at a glance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only reason is to pass the freq to PeriodConverter, which currently only accepts ax as input arg, not freq. It can obviously be made cleaner. I meant to modify PeriodConverter or find a cleaner way than hacking around ax.
I'd just like to get a thorough review of the overall approach, i.e. shifting the tick positions, as it would apply to all box plots with PeriodIndex, and hence is a potential major change. Not sure how well it is covered by tests currently; I don't want to insert regressions or breaking changes. It would be good to have it first checked by a maintainer well-versed with matplotlib, as I'm not so familiar with all the idiosyncrasies of the ax object.
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): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fails on main
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): |
There was a problem hiding this comment.
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.
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) | ||
|
There was a problem hiding this comment.
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.
@rhshadrach I covered the change with a test. I believe the PR is now ready for review and then merge. |
doc/source/whatsnew/vX.X.X.rst
file if fixing a bug or adding a new feature.This code
from the linked issue gives this plot:

And swapping the lines (line before bar)
gives

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.