-
-
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?
Changes from all commits
3caf8e6
97d0440
5fb6172
ee183d7
e5b4e6f
c5a6554
218871d
16762b6
bb2fdd7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,7 +59,10 @@ | |
|
||
from pandas.io.formats.printing import pprint_thing | ||
from pandas.plotting._matplotlib import tools | ||
from pandas.plotting._matplotlib.converter import register_pandas_matplotlib_converters | ||
from pandas.plotting._matplotlib.converter import ( | ||
PeriodConverter, | ||
register_pandas_matplotlib_converters, | ||
) | ||
from pandas.plotting._matplotlib.groupby import reconstruct_data_with_by | ||
from pandas.plotting._matplotlib.misc import unpack_single_str_list | ||
from pandas.plotting._matplotlib.style import get_standard_colors | ||
|
@@ -288,6 +291,21 @@ def __init__( | |
|
||
self.data = self._ensure_frame(self.data) | ||
|
||
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) | ||
|
||
Comment on lines
+294
to
+308
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
@final | ||
@staticmethod | ||
def _validate_sharex(sharex: bool | None, ax, by) -> bool: | ||
|
@@ -1520,23 +1538,9 @@ def _kind(self) -> Literal["line", "area", "hist", "kde", "box"]: | |
return "line" | ||
|
||
def __init__(self, data, **kwargs) -> None: | ||
from pandas.plotting import plot_params | ||
|
||
MPLPlot.__init__(self, data, **kwargs) | ||
if self.stacked: | ||
self.data = self.data.fillna(value=0) | ||
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) | ||
|
||
def _make_plot(self, fig: Figure) -> None: | ||
if self._is_ts_plot(): | ||
|
@@ -1855,7 +1859,6 @@ def __init__( | |
self.bar_width = width | ||
self._align = align | ||
self._position = position | ||
self.tick_pos = np.arange(len(data)) | ||
|
||
if is_list_like(bottom): | ||
bottom = np.array(bottom) | ||
|
@@ -1868,6 +1871,16 @@ def __init__( | |
|
||
MPLPlot.__init__(self, data, **kwargs) | ||
|
||
if self._is_ts_plot(): | ||
self.tick_pos = np.array( | ||
PeriodConverter.convert_from_freq( | ||
self._get_xticks(), | ||
data.index.freq, | ||
) | ||
) | ||
else: | ||
self.tick_pos = np.arange(len(data)) | ||
|
||
@cache_readonly | ||
def ax_pos(self) -> np.ndarray: | ||
return self.tick_pos - self.tickoffset | ||
|
@@ -1897,6 +1910,7 @@ def lim_offset(self): | |
|
||
# error: Signature of "_plot" incompatible with supertype "MPLPlot" | ||
@classmethod | ||
@register_pandas_matplotlib_converters | ||
def _plot( # type: ignore[override] | ||
cls, | ||
ax: Axes, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 commentThe reason will be displayed to describe this comment to others. Learn more. Fails on main |
||
""" | ||
Test that bar and line plots with the same x values are superposed | ||
""" | ||
# GH61161 | ||
index = period_range("2023", periods=3, freq="Y") | ||
s = Series([1, 2, 3], index=index) | ||
ax = plt.subplot() | ||
s.plot(kind="bar", ax=ax) | ||
bar_xticks = ax.get_xticks().tolist() | ||
s.plot(kind="line", ax=ax, color="r") | ||
line_xticks = ax.get_xticks()[: len(s)].tolist() | ||
assert line_xticks == bar_xticks |
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.