Skip to content

Commit 9563141

Browse files
committed
Merge pull request #4414 from cpcloud/fix-hist-subplots
BUG/VIS: fix Series.hist so that users can create hist subplots without the mpl API
2 parents aeabda1 + 06cf4fa commit 9563141

File tree

5 files changed

+66
-34
lines changed

5 files changed

+66
-34
lines changed

doc/source/release.rst

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pandas 0.13
4444
- Text parser now treats anything that reads like inf ("inf", "Inf", "-Inf",
4545
"iNf", etc.) to infinity. (:issue:`4220`, :issue:`4219`), affecting
4646
``read_table``, ``read_csv``, etc.
47+
- Added a more informative error message when plot arguments contain
48+
overlapping color and style arguments (:issue:`4402`)
4749

4850
**API Changes**
4951

@@ -98,6 +100,8 @@ pandas 0.13
98100
with the usecols parameter (:issue: `3192`)
99101
- Fix an issue in merging blocks where the resulting DataFrame had partially
100102
set _ref_locs (:issue:`4403`)
103+
- Fixed an issue where hist subplots were being overwritten when they were
104+
called using the top level matplotlib API (:issue:`4408`)
101105

102106
pandas 0.12
103107
===========

doc/source/v0.13.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ Enhancements
3838
``ValueError`` (:issue:`4303`, :issue:`4305`)
3939
- Added a test for ``read_clipboard()`` and ``to_clipboard()`` (:issue:`4282`)
4040
- Clipboard functionality now works with PySide (:issue:`4282`)
41+
- Added a more informative error message when plot arguments contain
42+
overlapping color and style arguments (:issue:`4402`)
4143

4244
Bug Fixes
4345
~~~~~~~~~

pandas/tests/test_graphics.py

+47-28
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
from datetime import datetime, date
77

88
from pandas import Series, DataFrame, MultiIndex, PeriodIndex, date_range
9-
from pandas.compat import range, lrange, StringIO, lmap, lzip, u, map, zip
9+
from pandas.compat import range, lrange, StringIO, lmap, lzip, u, zip
1010
import pandas.util.testing as tm
1111
from pandas.util.testing import ensure_clean
1212
from pandas.core.config import set_option
1313

1414

1515
import numpy as np
1616
from numpy import random
17+
from numpy.random import randn
1718

1819
from numpy.testing import assert_array_equal
1920
from numpy.testing.decorators import slow
@@ -64,7 +65,7 @@ def test_plot(self):
6465
_check_plot_works(self.series[:5].plot, kind='barh')
6566
_check_plot_works(self.series[:10].plot, kind='barh')
6667

67-
Series(np.random.randn(10)).plot(kind='bar', color='black')
68+
Series(randn(10)).plot(kind='bar', color='black')
6869

6970
# figsize and title
7071
import matplotlib.pyplot as plt
@@ -84,7 +85,7 @@ def test_bar_colors(self):
8485
custom_colors = 'rgcby'
8586

8687
plt.close('all')
87-
df = DataFrame(np.random.randn(5, 5))
88+
df = DataFrame(randn(5, 5))
8889
ax = df.plot(kind='bar')
8990

9091
rects = ax.patches
@@ -141,7 +142,7 @@ def test_bar_colors(self):
141142

142143
@slow
143144
def test_bar_linewidth(self):
144-
df = DataFrame(np.random.randn(5, 5))
145+
df = DataFrame(randn(5, 5))
145146

146147
# regular
147148
ax = df.plot(kind='bar', linewidth=2)
@@ -160,15 +161,15 @@ def test_bar_linewidth(self):
160161
self.assert_(r.get_linewidth() == 2)
161162

162163
def test_rotation(self):
163-
df = DataFrame(np.random.randn(5, 5))
164+
df = DataFrame(randn(5, 5))
164165
ax = df.plot(rot=30)
165166
for l in ax.get_xticklabels():
166167
self.assert_(l.get_rotation() == 30)
167168

168169
def test_irregular_datetime(self):
169170
rng = date_range('1/1/2000', '3/1/2000')
170171
rng = rng[[0, 1, 2, 3, 5, 9, 10, 11, 12]]
171-
ser = Series(np.random.randn(len(rng)), rng)
172+
ser = Series(randn(len(rng)), rng)
172173
ax = ser.plot()
173174
xp = datetime(1999, 1, 1).toordinal()
174175
ax.set_xlim('1/1/1999', '1/1/2001')
@@ -224,6 +225,25 @@ def test_hist_layout_with_by(self):
224225
_check_plot_works(df.weight.hist, by=df.category, layout=(4, 1))
225226
plt.close('all')
226227

228+
@slow
229+
def test_hist_no_overlap(self):
230+
from matplotlib.pyplot import subplot, gcf, close
231+
x = Series(randn(2))
232+
y = Series(randn(2))
233+
subplot(121)
234+
x.hist()
235+
subplot(122)
236+
y.hist()
237+
fig = gcf()
238+
axes = fig.get_axes()
239+
self.assertEqual(len(axes), 2)
240+
close('all')
241+
242+
@slow
243+
def test_plot_fails_with_dupe_color_and_style(self):
244+
x = Series(randn(2))
245+
self.assertRaises(ValueError, x.plot, style='k--', color='k')
246+
227247
def test_plot_fails_when_ax_differs_from_figure(self):
228248
from pylab import figure
229249
fig1 = figure()
@@ -362,7 +382,7 @@ def test_nonnumeric_exclude(self):
362382
def test_label(self):
363383
import matplotlib.pyplot as plt
364384
plt.close('all')
365-
df = DataFrame(np.random.randn(10, 3), columns=['a', 'b', 'c'])
385+
df = DataFrame(randn(10, 3), columns=['a', 'b', 'c'])
366386
ax = df.plot(x='a', y='b')
367387
self.assert_(ax.xaxis.get_label().get_text() == 'a')
368388

@@ -487,7 +507,7 @@ def test_subplots(self):
487507

488508
@slow
489509
def test_plot_bar(self):
490-
df = DataFrame(np.random.randn(6, 4),
510+
df = DataFrame(randn(6, 4),
491511
index=list(string.ascii_letters[:6]),
492512
columns=['one', 'two', 'three', 'four'])
493513

@@ -496,7 +516,7 @@ def test_plot_bar(self):
496516
_check_plot_works(df.plot, kind='bar', subplots=True)
497517
_check_plot_works(df.plot, kind='bar', stacked=True)
498518

499-
df = DataFrame(np.random.randn(10, 15),
519+
df = DataFrame(randn(10, 15),
500520
index=list(string.ascii_letters[:10]),
501521
columns=lrange(15))
502522
_check_plot_works(df.plot, kind='bar')
@@ -537,7 +557,7 @@ def test_bar_log(self):
537557

538558
@slow
539559
def test_boxplot(self):
540-
df = DataFrame(np.random.randn(6, 4),
560+
df = DataFrame(randn(6, 4),
541561
index=list(string.ascii_letters[:6]),
542562
columns=['one', 'two', 'three', 'four'])
543563
df['indic'] = ['foo', 'bar'] * 3
@@ -563,7 +583,7 @@ def test_boxplot(self):
563583
@slow
564584
def test_kde(self):
565585
_skip_if_no_scipy()
566-
df = DataFrame(np.random.randn(100, 4))
586+
df = DataFrame(randn(100, 4))
567587
_check_plot_works(df.plot, kind='kde')
568588
_check_plot_works(df.plot, kind='kde', subplots=True)
569589
ax = df.plot(kind='kde')
@@ -575,21 +595,21 @@ def test_kde(self):
575595
@slow
576596
def test_hist(self):
577597
import matplotlib.pyplot as plt
578-
df = DataFrame(np.random.randn(100, 4))
598+
df = DataFrame(randn(100, 4))
579599
_check_plot_works(df.hist)
580600
_check_plot_works(df.hist, grid=False)
581601

582602
# make sure layout is handled
583-
df = DataFrame(np.random.randn(100, 3))
603+
df = DataFrame(randn(100, 3))
584604
_check_plot_works(df.hist)
585605
axes = df.hist(grid=False)
586606
self.assert_(not axes[1, 1].get_visible())
587607

588-
df = DataFrame(np.random.randn(100, 1))
608+
df = DataFrame(randn(100, 1))
589609
_check_plot_works(df.hist)
590610

591611
# make sure layout is handled
592-
df = DataFrame(np.random.randn(100, 6))
612+
df = DataFrame(randn(100, 6))
593613
_check_plot_works(df.hist)
594614

595615
# make sure sharex, sharey is handled
@@ -641,7 +661,7 @@ def test_hist(self):
641661
def test_hist_layout(self):
642662
import matplotlib.pyplot as plt
643663
plt.close('all')
644-
df = DataFrame(np.random.randn(100, 4))
664+
df = DataFrame(randn(100, 4))
645665

646666
layout_to_expected_size = (
647667
{'layout': None, 'expected_size': (2, 2)}, # default is 2x2
@@ -666,8 +686,7 @@ def test_hist_layout(self):
666686
def test_scatter(self):
667687
_skip_if_no_scipy()
668688

669-
df = DataFrame(np.random.randn(100, 4))
670-
df = DataFrame(np.random.randn(100, 2))
689+
df = DataFrame(randn(100, 2))
671690
import pandas.tools.plotting as plt
672691

673692
def scat(**kwds):
@@ -730,11 +749,11 @@ def test_radviz(self):
730749

731750
@slow
732751
def test_plot_int_columns(self):
733-
df = DataFrame(np.random.randn(100, 4)).cumsum()
752+
df = DataFrame(randn(100, 4)).cumsum()
734753
_check_plot_works(df.plot, legend=True)
735754

736755
def test_legend_name(self):
737-
multi = DataFrame(np.random.randn(4, 4),
756+
multi = DataFrame(randn(4, 4),
738757
columns=[np.array(['a', 'a', 'b', 'b']),
739758
np.array(['x', 'y', 'x', 'y'])])
740759
multi.columns.names = ['group', 'individual']
@@ -751,7 +770,7 @@ def test_style_by_column(self):
751770
import matplotlib.pyplot as plt
752771
fig = plt.gcf()
753772

754-
df = DataFrame(np.random.randn(100, 3))
773+
df = DataFrame(randn(100, 3))
755774
for markers in [{0: '^', 1: '+', 2: 'o'},
756775
{0: '^', 1: '+'},
757776
['^', '+', 'o'],
@@ -771,7 +790,7 @@ def test_line_colors(self):
771790
custom_colors = 'rgcby'
772791

773792
plt.close('all')
774-
df = DataFrame(np.random.randn(5, 5))
793+
df = DataFrame(randn(5, 5))
775794

776795
ax = df.plot(color=custom_colors)
777796

@@ -826,7 +845,7 @@ def test_default_color_cycle(self):
826845
plt.rcParams['axes.color_cycle'] = list('rgbk')
827846

828847
plt.close('all')
829-
df = DataFrame(np.random.randn(5, 3))
848+
df = DataFrame(randn(5, 3))
830849
ax = df.plot()
831850

832851
lines = ax.get_lines()
@@ -856,13 +875,13 @@ def test_all_invalid_plot_data(self):
856875
@slow
857876
def test_partially_invalid_plot_data(self):
858877
kinds = 'line', 'bar', 'barh', 'kde', 'density'
859-
df = DataFrame(np.random.randn(10, 2), dtype=object)
878+
df = DataFrame(randn(10, 2), dtype=object)
860879
df[np.random.rand(df.shape[0]) > 0.5] = 'a'
861880
for kind in kinds:
862881
self.assertRaises(TypeError, df.plot, kind=kind)
863882

864883
def test_invalid_kind(self):
865-
df = DataFrame(np.random.randn(10, 2))
884+
df = DataFrame(randn(10, 2))
866885
self.assertRaises(ValueError, df.plot, kind='aasdf')
867886

868887

@@ -919,7 +938,7 @@ def test_time_series_plot_color_kwargs(self):
919938
def test_time_series_plot_color_with_empty_kwargs(self):
920939
import matplotlib as mpl
921940
import matplotlib.pyplot as plt
922-
941+
923942
def_colors = mpl.rcParams['axes.color_cycle']
924943

925944
plt.close('all')
@@ -933,7 +952,7 @@ def test_time_series_plot_color_with_empty_kwargs(self):
933952
@slow
934953
def test_grouped_hist(self):
935954
import matplotlib.pyplot as plt
936-
df = DataFrame(np.random.randn(500, 2), columns=['A', 'B'])
955+
df = DataFrame(randn(500, 2), columns=['A', 'B'])
937956
df['C'] = np.random.randint(0, 4, 500)
938957
axes = plotting.grouped_hist(df.A, by=df.C)
939958
self.assert_(len(axes.ravel()) == 4)
@@ -1036,7 +1055,7 @@ def test_option_mpl_style(self):
10361055
pass
10371056

10381057
def test_invalid_colormap(self):
1039-
df = DataFrame(np.random.randn(3, 2), columns=['A', 'B'])
1058+
df = DataFrame(randn(3, 2), columns=['A', 'B'])
10401059
self.assertRaises(ValueError, df.plot, colormap='invalid_colormap')
10411060

10421061

pandas/tools/plotting.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,14 @@ def _validate_color_args(self):
821821
warnings.warn("'color' and 'colormap' cannot be used "
822822
"simultaneously. Using 'color'")
823823

824+
if 'color' in self.kwds and self.style is not None:
825+
# need only a single match
826+
if re.match('^[a-z]+?', self.style) is not None:
827+
raise ValueError("Cannot pass 'style' string with a color "
828+
"symbol and 'color' keyword argument. Please"
829+
" use one or the other or pass 'style' "
830+
"without a color symbol")
831+
824832
def _iter_data(self):
825833
from pandas.core.frame import DataFrame
826834
if isinstance(self.data, (Series, np.ndarray)):
@@ -2026,7 +2034,7 @@ def hist_series(self, by=None, ax=None, grid=True, xlabelsize=None,
20262034
"""
20272035
import matplotlib.pyplot as plt
20282036

2029-
fig = kwds.get('figure', plt.gcf()
2037+
fig = kwds.get('figure', _gcf()
20302038
if plt.get_fignums() else plt.figure(figsize=figsize))
20312039
if figsize is not None and tuple(figsize) != tuple(fig.get_size_inches()):
20322040
fig.set_size_inches(*figsize, forward=True)
@@ -2036,8 +2044,8 @@ def hist_series(self, by=None, ax=None, grid=True, xlabelsize=None,
20362044
raise ValueError("The 'layout' keyword is not supported when "
20372045
"'by' is None")
20382046
if ax is None:
2039-
ax = fig.add_subplot(111)
2040-
if ax.get_figure() != fig:
2047+
ax = fig.gca()
2048+
elif ax.get_figure() != fig:
20412049
raise AssertionError('passed axis not bound to passed figure')
20422050
values = self.dropna().values
20432051

pandas/tseries/tests/test_plotting.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,10 @@ def test_both_style_and_color(self):
127127
plt.close('all')
128128

129129
ts = tm.makeTimeSeries()
130-
ts.plot(style='b-', color='#000099') # works
130+
self.assertRaises(ValueError, ts.plot, style='b-', color='#000099')
131131

132-
plt.close('all')
133132
s = ts.reset_index(drop=True)
134-
s.plot(style='b-', color='#000099') # non-tsplot
133+
self.assertRaises(ValueError, s.plot, style='b-', color='#000099')
135134

136135
@slow
137136
def test_high_freq(self):

0 commit comments

Comments
 (0)