8000 [Bug]: format_image_data on an image of only zeros produces a large number of zeros · Issue #28648 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

[Bug]: format_image_data on an image of only zeros produces a large number of zeros #28648

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

Closed
trygvrad opened this issue Aug 2, 2024 · 1 comment · Fixed by #28649
Closed

Comments

@trygvrad
Copy link
Contributor
trygvrad commented Aug 2, 2024

Bug summary

If you use imshow, and then mouseover the image, the value is listed at the bottom of the plot.
However, if the image contains only zeros, the result is a comical number of zeros.

Code for reproduction

import matplotlib
import numpy as np
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
im = ax.imshow(np.zeros((4, 4)))
fig.show()

Actual outcome

When you mouse-over the image it shows a large number of zeros (see bottom of image).
image

Expected outcome

0.0 should be at the bottom of the page and the window should retain its original size:
image

Additional information

test_image.py→test_image_cursor_formatting() should test this perscise functionality, but there is a bug in this test where

    data = np.ma.masked_array([0], mask=[False])
    assert im.format_cursor_data(data) == '[0]'

should be

    data = np.ma.masked_array(0, mask=[False])
    assert im.format_cursor_data(data) == '[0]'

And this causes im.format_cursor_data to fallback and '0' is returned.
This test should be updated as part of a PR to fix this bug.

Operating system

No response

Matplotlib Version

matplotlib_dev, 3.8.2

Matplotlib Backend

TkAgg

Python version

No response

Jupyter version

No response

Installation

git checkout

@tacaswell tacaswell added this to the v3.9.2 milestone Aug 2, 2024
@tacaswell
Copy link
Member

The issue is singular norms. We do a bunch of extra logic it

if np.ndim(data) == 0 and isinstance(self, ScalarMappable):
# This block logically belongs to ScalarMappable, but can't be
# implemented in it because most ScalarMappable subclasses inherit
# from Artist first and from ScalarMappable second, so
# Artist.format_cursor_data would always have precedence over
# ScalarMappable.format_cursor_data.
n = self.cmap.N
if np.ma.getmask(data):
return "[]"
normed = self.norm(data)
if np.isfinite(normed):
if isinstance(self.norm, BoundaryNorm):
# not an invertible normalization mapping
cur_idx = np.argmin(np.abs(self.norm.boundaries - data))
neigh_idx = max(0, cur_idx - 1)
# use max diff to prevent delta == 0
delta = np.diff(
self.norm.boundaries[neigh_idx:cur_idx + 2]
).max()
else:
# Midpoints of neighboring color intervals.
neighbors = self.norm.inverse(
(int(normed * n) + np.array([0, 1])) / n)
delta = abs(neighbors - data).max()
g_sig_digits = cbook._g_sig_digits(data, delta)
else:
g_sig_digits = 3 # Consistent with default below.
return f"[{data:-#.{g_sig_digits}g}]"
to estimate a "good" number of digits via
def _g_sig_digits(value, delta):
"""
Return the number of significant digits to %g-format *value*, assuming that
it is known with an error of *delta*.
"""
if delta == 0:
# delta = 0 may occur when trying to format values over a tiny range;
# in that case, replace it by the distance to the closest float.
delta = abs(np.spacing(value))
# If e.g. value = 45.67 and delta = 0.02, then we want to round to 2 digits
# after the decimal point (floor(log10(0.02)) = -2); 45.67 contributes 2
# digits before the decimal point (floor(log10(45.67)) + 1 = 2): the total
# is 4 significant digits. A value of 0 contributes 1 "digit" before the
# decimal point.
# For inf or nan, the precision doesn't matter.
return max(
0,
(math.floor(math.log10(abs(value))) + 1 if value else 1)
- math.floor(math.log10(delta))) if math.isfinite(value) else 0

In [10]: import matplotlib.cbook as mc

In [11]: mc._g_sig_digits(0, 0)
Out[13]: 325

In [14]: mc._g_sig_digits(0, .1)
Out[14]: 2

In [15]: mc._g_sig_digits(0, 0)
Out[15]: 325

In [16]: mc._g_sig_digits(0, .00001)
Out[16]: 6

In [17]: mc._g_sig_digits(0, .000001)
Out[17]: 7

In [18]: mc._g_sig_digits(0, .0001)
Out[18]: 5

In [19]: mc._g_sig_digits(.1, 0)
Out[19]: 17

I have some ideas, will open a PR shortly.

tacaswell added a commit to tacaswell/matplotlib that referenced this issue Aug 2, 2024
@QuLogic QuLogic changed the title [Bug]: format_image_data on an image of only zeros produses a large number of zeros [Bug]: format_image_data on an image of only zeros produces a large number of zeros Aug 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants
0