From d7109e1ea8b2ae85271cef7e548c7d0c52a83a09 Mon Sep 17 00:00:00 2001 From: Dan Birken Date: Tue, 25 Jun 2013 09:59:46 -0700 Subject: [PATCH 1/2] ENH: Add ``layout`` keyword to DataFrame.hist() for customizable format GH4050 --- pandas/tests/test_graphics.py | 25 +++++++++++++++++++++++++ pandas/tools/plotting.py | 24 +++++++++++++++++------- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/pandas/tests/test_graphics.py b/pandas/tests/test_graphics.py index fe793275627e0..15389ef687951 100644 --- a/pandas/tests/test_graphics.py +++ b/pandas/tests/test_graphics.py @@ -611,6 +611,31 @@ def test_hist(self): # propagate attr exception from matplotlib.Axes.hist self.assertRaises(AttributeError, ser.hist, foo='bar') + @slow + def test_hist_layout(self): + import matplotlib.pyplot as plt + plt.close('all') + df = DataFrame(np.random.randn(100, 4)) + + layout_to_expected_size = ( + {'layout': None, 'expected_size': (2, 2)}, # default is 2x2 + {'layout': (2, 2), 'expected_size': (2, 2)}, + {'layout': (4, 1), 'expected_size': (4, 1)}, + {'layout': (1, 4), 'expected_size': (1, 4)}, + {'layout': (3, 3), 'expected_size': (3, 3)}, + ) + + for layout_test in layout_to_expected_size: + ax = df.hist(layout=layout_test['layout']) + self.assert_(len(ax) == layout_test['expected_size'][0]) + self.assert_(len(ax[0]) == layout_test['expected_size'][1]) + + # layout too small for all 4 plots + self.assertRaises(ValueError, df.hist, layout=(1, 1)) + + # invalid format for layout + self.assertRaises(ValueError, df.hist, layout=(1,)) + @slow def test_scatter(self): _skip_if_no_scipy() diff --git a/pandas/tools/plotting.py b/pandas/tools/plotting.py index 2ed9d2f607ea9..ef55319da185c 100644 --- a/pandas/tools/plotting.py +++ b/pandas/tools/plotting.py @@ -1890,7 +1890,7 @@ def plot_group(group, ax): def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, ax=None, sharex=False, - sharey=False, figsize=None, **kwds): + sharey=False, figsize=None, layout=None, **kwds): """ Draw Histogram the DataFrame's series using matplotlib / pylab. @@ -1916,6 +1916,7 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, sharey : bool, if True, the Y axis will be shared amongst all subplots. figsize : tuple The size of the figure to create in inches by default + layout: (optional) a tuple (rows, columns) for the layout of the histograms kwds : other plotting keyword arguments To be passed to hist function """ @@ -1943,12 +1944,21 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, import matplotlib.pyplot as plt n = len(data.columns) - rows, cols = 1, 1 - while rows * cols < n: - if cols > rows: - rows += 1 - else: - cols += 1 + + if layout is not None: + if not isinstance(layout, (tuple, list)) or len(layout) != 2: + raise ValueError('Layout must be a tuple of (rows, columns)') + + rows, cols = layout + if rows * cols < n: + raise ValueError('Layout of %sx%s is incompatible with %s columns' % (rows, cols, n)) + else: + rows, cols = 1, 1 + while rows * cols < n: + if cols > rows: + rows += 1 + else: + cols += 1 fig, axes = _subplots(nrows=rows, ncols=cols, ax=ax, squeeze=False, sharex=sharex, sharey=sharey, figsize=figsize) From 54074e4b465ab2249ec1da3ddf794f4878e2c9a0 Mon Sep 17 00:00:00 2001 From: Dan Birken Date: Wed, 26 Jun 2013 15:56:24 -0700 Subject: [PATCH 2/2] Add release notes for new ``layout`` keyword - GH4050 --- doc/source/release.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/release.rst b/doc/source/release.rst index 18d5939d909ed..513540698023f 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -100,6 +100,7 @@ pandas 0.12 (:issue:`3910`, :issue:`3914`) - ``read_csv`` will now throw a more informative error message when a file contains no columns, e.g., all newline characters + - Added ``layout`` keyword to DataFrame.hist() for more customizable layout (:issue:`4050`) **API Changes**