Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Fix ValueError being raised when plotting hist and hexbin on empty dataset (Fix #3886) #4119

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

Merged
merged 7 commits into from
Mar 30, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix hist to return correct patches on empty input. Add test for hist …
…step on empty input.
  • Loading branch information
Umair Idris committed Mar 17, 2015
commit a567658a790cb5b9d5e310b9bcc766ecaf98ed37
27 changes: 18 additions & 9 deletions 27 lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5394,7 +5394,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
Compute and draw the histogram of *x*. The return value is a
tuple (*n*, *bins*, *patches*) or ([*n0*, *n1*, ...], *bins*,
[*patches0*, *patches1*,...]) if the input contains multiple
data or ([], [], []) if input is an empty array.
data.

Multiple data can be provided via *x* as a list of datasets
of potentially different length ([*x0*, *x1*, ...]), or as
Expand Down Expand Up @@ -5605,12 +5605,14 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,

# basic input validation
flat = np.ravel(x)
if len(flat) == 0:
return [], [], cbook.silent_list('No Patches')

input_empty = len(flat) == 0

# Massage 'x' for processing.
# NOTE: Be sure any changes here is also done below to 'weights'
if isinstance(x, np.ndarray) or not iterable(x[0]):
if input_empty:
x = np.asarray([[]])
elif isinstance(x, np.ndarray) or not iterable(x[0]):
# TODO: support masked arrays;
x = np.asarray(x)
if x.ndim == 2:
Expand Down Expand Up @@ -5677,7 +5679,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
#hist_kwargs = dict(range=range, normed=bool(normed))
# We will handle the normed kwarg within mpl until we
# get to the point of requiring numpy >= 1.5.
hist_kwargs = dict(range=bin_range)
hist_kwargs = dict(range=bin_range) if not input_empty else dict()
Copy link
Member

Choose a reason for hiding this comment

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

Why is this change needed?

Copy link
Author

Choose a reason for hiding this comment

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

The bin_range was (np.inf, -np.inf) when binsgiven was false due to the block above this line; I have changed that block to not be entered if input is empty and reverted this change via 22c6c7f.

Thanks!


n = []
mlast = None
Expand Down Expand Up @@ -5760,6 +5762,8 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
for m, c in zip(n, color):
if bottom is None:
bottom = np.zeros(len(m), np.float)
if input_empty:
width = 0
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this?

Copy link
Author

Choose a reason for hiding this comment

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

As per @efiring 's comment: "I think we should follow that logic, which implies that we should be drawing zero-height (or width) patches."

Without setting width to 0, the patches for bar have size 0.1 and thus an empty graph (as desired) is not drawn.

Before (width 0.1, non empty graph):
bar_width_nonzero

After (width 0, empty graph):
bar_width_zero

However, currently the code still calculates the original width of 0.1 and then overwrites it, I will send a commit which moves it into a more appropriate place (where width, dw and boffset are originally calculated) in a future commit if this is the desired result.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Author

Choose a reason for hiding this comment

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

Done via 40cf945

if stacked:
height = m - bottom
else:
Expand Down Expand Up @@ -5842,6 +5846,8 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
xvals.append(x.copy())
yvals.append(y.copy())

fill_kwargs = dict() if not input_empty else dict(linewidth=0)
Copy link
Member

Choose a reason for hiding this comment

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

Again. do we need this?

Copy link
Author

Choose a reason for hiding this comment

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

As above, it draws one patch with linewidth 1 if it is not manually set to 0 and thus is not an empty graph.

Before (linewidth 1, non empty graph):
step_linewidth_nonzero

After (linewidth 0, empty graph):
step_linewidth_zero

Copy link
Member

Choose a reason for hiding this comment

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

Again. I think showing the line is the correct thing to do. My main motivation is that if some one now wants to update the histogram artists, this will cause a great deal of trouble.

Copy link
Member

Choose a reason for hiding this comment

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

Just making sure, is autoscaling in the x still working even with
zero-height elements on the edges?

On Fri, Mar 13, 2015 at 7:31 PM, Thomas A Caswell notifications@github.com
wrote:

In lib/matplotlib/axes/_axes.py
#4119 (comment):

@@ -5843,6 +5846,8 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
xvals.append(x.copy())
yvals.append(y.copy())

  •        fill_kwargs = dict() if not input_empty else dict(linewidth=0)
    

Again. I think showing the line is the correct thing to do. My main
motivation is that if some one now wants to update the histogram artists,
this will cause a great deal of trouble.


Reply to this email directly or view it on GitHub
https://github.com/matplotlib/matplotlib/pull/4119/files#r26430425.

Copy link
Author

Choose a reason for hiding this comment

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

Done via 40cf945


if fill:
# add patches in reverse order so that when stacking,
# items lower in the stack are plottted on top of
Expand All @@ -5850,14 +5856,16 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
patches.append(self.fill(
x, y,
closed=True,
facecolor=c))
facecolor=c,
**fill_kwargs))
else:
for x, y, c in reversed(list(zip(xvals, yvals, color))):
split = 2 * len(bins)
patches.append(self.fill(
x[:split], y[:split],
closed=False, edgecolor=c,
fill=False))
fill=False,
**fill_kwargs))

# we return patches, so put it back in the expected order
patches.reverse()
Expand All @@ -5870,17 +5878,18 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
if np.sum(m) > 0: # make sure there are counts
xmin = np.amin(m[m != 0])
# filter out the 0 height bins
xmin = max(xmin*0.9, minimum)
xmin = max(xmin*0.9, minimum) if not input_empty else minimum
Copy link
Member

Choose a reason for hiding this comment

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

This discards the _saved_bounds information. I am not strictly sure that this is the best place to do this, but this bit of code is convoluted enough....

Copy link
Member

Choose a reason for hiding this comment

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

Yup, never mind my last comment.

xmin = min(xmin0, xmin)
self.dataLim.intervalx = (xmin, xmax)
elif orientation == 'vertical':
ymin0 = max(_saved_bounds[1]*0.9, minimum)
ymax = self.dataLim.intervaly[1]

for m in n:
if np.sum(m) > 0: # make sure there are counts
ymin = np.amin(m[m != 0])
# filter out the 0 height bins
ymin = max(ymin*0.9, minimum)
ymin = max(ymin*0.9, minimum) if not input_empty else minimum
ymin = min(ymin0, ymin)
self.dataLim.intervaly = (ymin, ymax)

Expand Down
31 changes: 29 additions & 2 deletions 31 lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1007,10 +1007,37 @@ def test_hist_log():
ax.hist(data, fill=False, log=True)

@cleanup
def test_hist_empty():
def test_hist_bar_empty():
# From #3886: creating hist from empty dataset raises ValueError
ax = plt.gca()
ax.hist([])

n, bins, patches = ax.hist([], histtype='bar')

# empty hist should match empty numpy histogram
n1, bins1 = np.histogram([])
assert_array_equal(n, n1)
assert_array_equal(bins, bins1)

# check zero size patches
assert_true(len(patches) == 10)
for patch in patches:
assert_false(patch._height)
assert_false(patch._width)

@cleanup
def test_hist_step_empty():
# From #3886: creating hist from empty dataset raises ValueError
ax = plt.gca()

n, bins, patches = ax.hist([], histtype='step')

# empty hist should match empty numpy histogram
n1, bins1 = np.histogram([])
assert_array_equal(n, n1)
assert_array_equal(bins, bins1)

# check zero size patches
assert_true(len(patches) == 1 and not patches[0]._linewidth)

@image_comparison(baseline_images=['hist_steplog'], remove_text=True)
def test_hist_steplog():
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.