Skip to content
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

ENH: Expose symlog scaling in plotting API #24968

Merged
merged 14 commits into from
Apr 12, 2019
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v0.25.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ including other versions of pandas.

Other Enhancements
^^^^^^^^^^^^^^^^^^

- :func:`DataFrame.plot` keywords ``logy``, ``logx`` and ``loglog`` can now accept the value ``'sym'`` for symlog scaling. (:issue:`24867`)
- Indexing of ``DataFrame`` and ``Series`` now accepts zerodim ``np.ndarray`` (:issue:`24919`)
- :meth:`Timestamp.replace` now supports the ``fold`` argument to disambiguate DST transition times (:issue:`25017`)
- :meth:`DataFrame.at_time` and :meth:`Series.at_time` now support :meth:`datetime.time` objects with timezones (:issue:`24043`)
Expand Down
15 changes: 13 additions & 2 deletions pandas/plotting/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,10 +308,21 @@ def _setup_subplots(self):

axes = _flatten(axes)

if self.logx or self.loglog:
valid_log = {False, True, 'sym', None}
jreback marked this conversation as resolved.
Show resolved Hide resolved
input_log = {self.logx, self.logy, self.loglog}
if input_log - valid_log:
raise ValueError(f"Valid inputs are boolean, None and 'sym'"
Copy link
Contributor

Choose a reason for hiding this comment

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

We can't use f-strings yet, unfortunately.

Copy link
Member Author

Choose a reason for hiding this comment

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

sorry, changed! thanks! @TomAugspurger

Copy link
Contributor

Choose a reason for hiding this comment

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

May need to push these changes.

Copy link
Member Author

Choose a reason for hiding this comment

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

ahh, i am really really sorry... i thought i successfully pushed the change before rushing home @TomAugspurger

f", {i} is given.")

if self.logx is True or self.loglog is True:
[a.set_xscale('log') for a in axes]
if self.logy or self.loglog:
elif self.logx == 'sym' or self.loglog == 'sym':
[a.set_xscale('symlog') for a in axes]

if self.logy is True or self.loglog is True:
[a.set_yscale('log') for a in axes]
elif self.logy == 'sym' or self.loglog == 'sym':
[a.set_yscale('symlog') for a in axes]

self.fig = fig
self.axes = axes
Expand Down
28 changes: 28 additions & 0 deletions pandas/tests/plotting/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,42 @@ def test_plot_xy(self):
@pytest.mark.slow
def test_logscales(self):
df = DataFrame({'a': np.arange(100)}, index=np.arange(100))

ax = df.plot(logy=True)
self._check_ax_scales(ax, yaxis='log')
assert ax.get_yscale() == 'log'
Copy link
Member

Choose a reason for hiding this comment

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

Rather than these individual checks can you parametrize all of the combinations?

Copy link
Member Author

Choose a reason for hiding this comment

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

thanks! @WillAyd i parametrize the combinations.


ax = df.plot(logy='sym')
self._check_ax_scales(ax, yaxis='symlog')
assert ax.get_yscale() == 'symlog'

ax = df.plot(logx=True)
self._check_ax_scales(ax, xaxis='log')
assert ax.get_xscale() == 'log'

ax = df.plot(logx='sym')
self._check_ax_scales(ax, xaxis='symlog')
assert ax.get_xscale() == 'symlog'

ax = df.plot(loglog=True)
self._check_ax_scales(ax, xaxis='log', yaxis='log')
assert ax.get_xscale() == 'log'
assert ax.get_yscale() == 'log'

ax = df.plot(loglog='sym')
self._check_ax_scales(ax, xaxis='symlog', yaxis='symlog')
assert ax.get_xscale() == 'symlog'
assert ax.get_yscale() == 'symlog'

@pytest.mark.parametrize("wrong_input", ["sm", "symlog"])
@pytest.mark.parametrize("input_param", ["logx", "logy", "loglog"])
def test_invalid_logscale(self, wrong_input, input_param):
# GH: 24867
df = DataFrame({'a': np.arange(100)}, index=np.arange(100))

msg = "Valid inputs are boolean, None and 'sym'"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you update the match msg to ensure that the bad parameter is included. You can remove the "wrong_input" fixture I think, and just test one bad input.

Copy link
Member Author

Choose a reason for hiding this comment

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

updated!

with pytest.raises(ValueError, match=msg):
df.plot(**{input_param: wrong_input})

@pytest.mark.slow
def test_xcompat(self):
Expand Down