Skip to content

Commit 69c58da

Browse files
charlesdong1991TomAugspurger
authored andcommitted
PLT: plot('line') or plot('area') produces wrong xlim in xaxis in 0.25.0 (#27993)
* Fix issue 27686
1 parent e118b1d commit 69c58da

File tree

6 files changed

+84
-33
lines changed

6 files changed

+84
-33
lines changed

doc/source/whatsnew/v1.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ Plotting
168168
-
169169
- Bug in :meth:`DataFrame.plot` producing incorrect legend markers when plotting multiple series on the same axis (:issue:`18222`)
170170
- Bug in :meth:`DataFrame.plot` when ``kind='box'`` and data contains datetime or timedelta data. These types are now automatically dropped (:issue:`22799`)
171+
- Bug in :meth:`DataFrame.plot.line` and :meth:`DataFrame.plot.area` produce wrong xlim in x-axis (:issue:`27686`, :issue:`25160`, :issue:`24784`)
171172

172173
Groupby/resample/rolling
173174
^^^^^^^^^^^^^^^^^^^^^^^^

pandas/plotting/_matplotlib/core.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
from pandas.plotting._matplotlib.style import _get_standard_colors
3434
from pandas.plotting._matplotlib.tools import (
3535
_flatten,
36-
_get_all_lines,
37-
_get_xlim,
3836
_handle_shared_axes,
3937
_subplots,
4038
format_date_labels,
@@ -1101,9 +1099,8 @@ def _make_plot(self):
11011099
)
11021100
self._add_legend_handle(newlines[0], label, index=i)
11031101

1104-
lines = _get_all_lines(ax)
1105-
left, right = _get_xlim(lines)
1106-
ax.set_xlim(left, right)
1102+
# GH27686 set_xlim will truncate xaxis to fixed space
1103+
ax.relim()
11071104

11081105
@classmethod
11091106
def _plot(cls, ax, x, y, style=None, column_num=None, stacking_id=None, **kwds):

pandas/plotting/_matplotlib/tools.py

-21
Original file line numberDiff line numberDiff line change
@@ -343,27 +343,6 @@ def _flatten(axes):
343343
return np.array(axes)
344344

345345

346-
def _get_all_lines(ax):
347-
lines = ax.get_lines()
348-
349-
if hasattr(ax, "right_ax"):
350-
lines += ax.right_ax.get_lines()
351-
352-
if hasattr(ax, "left_ax"):
353-
lines += ax.left_ax.get_lines()
354-
355-
return lines
356-
357-
358-
def _get_xlim(lines):
359-
left, right = np.inf, -np.inf
360-
for l in lines:
361-
x = l.get_xdata(orig=False)
362-
left = min(np.nanmin(x), left)
363-
right = max(np.nanmax(x), right)
364-
return left, right
365-
366-
367346
def _set_ticks_props(axes, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None):
368347
import matplotlib.pyplot as plt
369348

pandas/tests/plotting/test_datetimelike.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ def test_get_finder(self):
419419
assert conv.get_finder("A") == conv._annual_finder
420420
assert conv.get_finder("W") == conv._daily_finder
421421

422+
# TODO: The finder should be retested due to wrong xlim values on x-axis
423+
@pytest.mark.xfail(reason="TODO: check details in GH28021")
422424
@pytest.mark.slow
423425
def test_finder_daily(self):
424426
day_lst = [10, 40, 252, 400, 950, 2750, 10000]
@@ -442,6 +444,8 @@ def test_finder_daily(self):
442444
assert rs1 == xpl1
443445
assert rs2 == xpl2
444446

447+
# TODO: The finder should be retested due to wrong xlim values on x-axis
448+
@pytest.mark.xfail(reason="TODO: check details in GH28021")
445449
@pytest.mark.slow
446450
def test_finder_quarterly(self):
447451
yrs = [3.5, 11]
@@ -465,6 +469,8 @@ def test_finder_quarterly(self):
465469
assert rs1 == xpl1
466470
assert rs2 == xpl2
467471

472+
# TODO: The finder should be retested due to wrong xlim values on x-axis
473+
@pytest.mark.xfail(reason="TODO: check details in GH28021")
468474
@pytest.mark.slow
469475
def test_finder_monthly(self):
470476
yrs = [1.15, 2.5, 4, 11]
@@ -498,6 +504,8 @@ def test_finder_monthly_long(self):
498504
xp = Period("1989Q1", "M").ordinal
499505
assert rs == xp
500506

507+
# TODO: The finder should be retested due to wrong xlim values on x-axis
508+
@pytest.mark.xfail(reason="TODO: check details in GH28021")
501509
@pytest.mark.slow
502510
def test_finder_annual(self):
503511
xp = [1987, 1988, 1990, 1990, 1995, 2020, 2070, 2170]
@@ -522,7 +530,7 @@ def test_finder_minutely(self):
522530
_, ax = self.plt.subplots()
523531
ser.plot(ax=ax)
524532
xaxis = ax.get_xaxis()
525-
rs = xaxis.get_majorticklocs()[0]
533+
rs = xaxis.get_majorticklocs()[1]
526534
xp = Period("1/1/1999", freq="Min").ordinal
527535

528536
assert rs == xp
@@ -534,7 +542,7 @@ def test_finder_hourly(self):
534542
_, ax = self.plt.subplots()
535543
ser.plot(ax=ax)
536544
xaxis = ax.get_xaxis()
537-
rs = xaxis.get_majorticklocs()[0]
545+
rs = xaxis.get_majorticklocs()[1]
538546
xp = Period("1/1/1999", freq="H").ordinal
539547

540548
assert rs == xp
@@ -1410,7 +1418,9 @@ def test_plot_outofbounds_datetime(self):
14101418

14111419
def test_format_timedelta_ticks_narrow(self):
14121420

1413-
expected_labels = ["00:00:00.0000000{:0>2d}".format(i) for i in range(10)]
1421+
expected_labels = [
1422+
"00:00:00.0000000{:0>2d}".format(i) for i in np.arange(0, 10, 2)
1423+
]
14141424

14151425
rng = timedelta_range("0", periods=10, freq="ns")
14161426
df = DataFrame(np.random.randn(len(rng), 3), rng)
@@ -1420,8 +1430,8 @@ def test_format_timedelta_ticks_narrow(self):
14201430
labels = ax.get_xticklabels()
14211431

14221432
result_labels = [x.get_text() for x in labels]
1423-
assert len(result_labels) == len(expected_labels)
1424-
assert result_labels == expected_labels
1433+
assert (len(result_labels) - 2) == len(expected_labels)
1434+
assert result_labels[1:-1] == expected_labels
14251435

14261436
def test_format_timedelta_ticks_wide(self):
14271437
expected_labels = [
@@ -1444,8 +1454,8 @@ def test_format_timedelta_ticks_wide(self):
14441454
labels = ax.get_xticklabels()
14451455

14461456
result_labels = [x.get_text() for x in labels]
1447-
assert len(result_labels) == len(expected_labels)
1448-
assert result_labels == expected_labels
1457+
assert (len(result_labels) - 2) == len(expected_labels)
1458+
assert result_labels[1:-1] == expected_labels
14491459

14501460
def test_timedelta_plot(self):
14511461
# test issue #8711

pandas/tests/plotting/test_frame.py

+52
Original file line numberDiff line numberDiff line change
@@ -3177,6 +3177,58 @@ def test_x_multiindex_values_ticks(self):
31773177
assert labels_position["(2013, 1)"] == 2.0
31783178
assert labels_position["(2013, 2)"] == 3.0
31793179

3180+
@pytest.mark.parametrize("kind", ["line", "area"])
3181+
def test_xlim_plot_line(self, kind):
3182+
# test if xlim is set correctly in plot.line and plot.area
3183+
# GH 27686
3184+
df = pd.DataFrame([2, 4], index=[1, 2])
3185+
ax = df.plot(kind=kind)
3186+
xlims = ax.get_xlim()
3187+
assert xlims[0] < 1
3188+
assert xlims[1] > 2
3189+
3190+
def test_xlim_plot_line_correctly_in_mixed_plot_type(self):
3191+
# test if xlim is set correctly when ax contains multiple different kinds
3192+
# of plots, GH 27686
3193+
fig, ax = self.plt.subplots()
3194+
3195+
indexes = ["k1", "k2", "k3", "k4"]
3196+
df = pd.DataFrame(
3197+
{
3198+
"s1": [1000, 2000, 1500, 2000],
3199+
"s2": [900, 1400, 2000, 3000],
3200+
"s3": [1500, 1500, 1600, 1200],
3201+
"secondary_y": [1, 3, 4, 3],
3202+
},
3203+
index=indexes,
3204+
)
3205+
df[["s1", "s2", "s3"]].plot.bar(ax=ax, stacked=False)
3206+
df[["secondary_y"]].plot(ax=ax, secondary_y=True)
3207+
3208+
xlims = ax.get_xlim()
3209+
assert xlims[0] < 0
3210+
assert xlims[1] > 3
3211+
3212+
# make sure axis labels are plotted correctly as well
3213+
xticklabels = [t.get_text() for t in ax.get_xticklabels()]
3214+
assert xticklabels == indexes
3215+
3216+
def test_subplots_sharex_false(self):
3217+
# test when sharex is set to False, two plots should have different
3218+
# labels, GH 25160
3219+
df = pd.DataFrame(np.random.rand(10, 2))
3220+
df.iloc[5:, 1] = np.nan
3221+
df.iloc[:5, 0] = np.nan
3222+
3223+
figs, axs = self.plt.subplots(2, 1)
3224+
df.plot.line(ax=axs, subplots=True, sharex=False)
3225+
3226+
expected_ax1 = np.arange(4.5, 10, 0.5)
3227+
expected_ax2 = np.arange(-0.5, 5, 0.5)
3228+
3229+
tm.assert_numpy_array_equal(axs[0].get_xticks(), expected_ax1)
3230+
tm.assert_numpy_array_equal(axs[1].get_xticks(), expected_ax2)
3231+
31803232

31813233
def _generate_4_axes_via_gridspec():
31823234
import matplotlib.pyplot as plt

pandas/tests/plotting/test_series.py

+12
Original file line numberDiff line numberDiff line change
@@ -897,3 +897,15 @@ def test_plot_accessor_updates_on_inplace(self):
897897
_, ax = self.plt.subplots()
898898
after = ax.xaxis.get_ticklocs()
899899
tm.assert_numpy_array_equal(before, after)
900+
901+
@pytest.mark.parametrize("kind", ["line", "area"])
902+
def test_plot_xlim_for_series(self, kind):
903+
# test if xlim is also correctly plotted in Series for line and area
904+
# GH 27686
905+
s = Series([2, 3])
906+
_, ax = self.plt.subplots()
907+
s.plot(kind=kind, ax=ax)
908+
xlims = ax.get_xlim()
909+
910+
assert xlims[0] < 0
911+
assert xlims[1] > 1

0 commit comments

Comments
 (0)