Skip to content

Commit 07403ca

Browse files
committed
Merge pull request #4031 from danbirken/add_layout_to_frame_histogram
Add layout keyword to dataframe.hist()
2 parents 3b08632 + 54074e4 commit 07403ca

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

doc/source/release.rst

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ pandas 0.12
100100
(:issue:`3910`, :issue:`3914`)
101101
- ``read_csv`` will now throw a more informative error message when a file
102102
contains no columns, e.g., all newline characters
103+
- Added ``layout`` keyword to DataFrame.hist() for more customizable layout (:issue:`4050`)
103104

104105
**API Changes**
105106

pandas/tests/test_graphics.py

+25
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,31 @@ def test_hist(self):
611611
# propagate attr exception from matplotlib.Axes.hist
612612
self.assertRaises(AttributeError, ser.hist, foo='bar')
613613

614+
@slow
615+
def test_hist_layout(self):
616+
import matplotlib.pyplot as plt
617+
plt.close('all')
618+
df = DataFrame(np.random.randn(100, 4))
619+
620+
layout_to_expected_size = (
621+
{'layout': None, 'expected_size': (2, 2)}, # default is 2x2
622+
{'layout': (2, 2), 'expected_size': (2, 2)},
623+
{'layout': (4, 1), 'expected_size': (4, 1)},
624+
{'layout': (1, 4), 'expected_size': (1, 4)},
625+
{'layout': (3, 3), 'expected_size': (3, 3)},
626+
)
627+
628+
for layout_test in layout_to_expected_size:
629+
ax = df.hist(layout=layout_test['layout'])
630+
self.assert_(len(ax) == layout_test['expected_size'][0])
631+
self.assert_(len(ax[0]) == layout_test['expected_size'][1])
632+
633+
# layout too small for all 4 plots
634+
self.assertRaises(ValueError, df.hist, layout=(1, 1))
635+
636+
# invalid format for layout
637+
self.assertRaises(ValueError, df.hist, layout=(1,))
638+
614639
@slow
615640
def test_scatter(self):
616641
_skip_if_no_scipy()

pandas/tools/plotting.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -1890,7 +1890,7 @@ def plot_group(group, ax):
18901890

18911891
def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None,
18921892
xrot=None, ylabelsize=None, yrot=None, ax=None, sharex=False,
1893-
sharey=False, figsize=None, **kwds):
1893+
sharey=False, figsize=None, layout=None, **kwds):
18941894
"""
18951895
Draw Histogram the DataFrame's series using matplotlib / pylab.
18961896
@@ -1916,6 +1916,7 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None,
19161916
sharey : bool, if True, the Y axis will be shared amongst all subplots.
19171917
figsize : tuple
19181918
The size of the figure to create in inches by default
1919+
layout: (optional) a tuple (rows, columns) for the layout of the histograms
19191920
kwds : other plotting keyword arguments
19201921
To be passed to hist function
19211922
"""
@@ -1943,12 +1944,21 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None,
19431944

19441945
import matplotlib.pyplot as plt
19451946
n = len(data.columns)
1946-
rows, cols = 1, 1
1947-
while rows * cols < n:
1948-
if cols > rows:
1949-
rows += 1
1950-
else:
1951-
cols += 1
1947+
1948+
if layout is not None:
1949+
if not isinstance(layout, (tuple, list)) or len(layout) != 2:
1950+
raise ValueError('Layout must be a tuple of (rows, columns)')
1951+
1952+
rows, cols = layout
1953+
if rows * cols < n:
1954+
raise ValueError('Layout of %sx%s is incompatible with %s columns' % (rows, cols, n))
1955+
else:
1956+
rows, cols = 1, 1
1957+
while rows * cols < n:
1958+
if cols > rows:
1959+
rows += 1
1960+
else:
1961+
cols += 1
19521962
fig, axes = _subplots(nrows=rows, ncols=cols, ax=ax, squeeze=False,
19531963
sharex=sharex, sharey=sharey, figsize=figsize)
19541964

0 commit comments

Comments
 (0)