Skip to content

Commit 0d89bf6

Browse files
authored
Merge pull request #21039 from dstansby/hexbin-marginal
Fix `hexbin` marginals and log scaling
2 parents d0b5bdc + 7c800f9 commit 0d89bf6

File tree

4 files changed

+51
-47
lines changed

4 files changed

+51
-47
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
hexbin with a log norm
2+
----------------------
3+
`~.axes.Axes.hexbin` no longer (incorrectly) adds 1 to every bin value if a
4+
log norm is being used.

lib/matplotlib/axes/_axes.py

Lines changed: 45 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4589,6 +4589,11 @@ def reduce_C_function(C: array) -> float
45894589
# Count the number of data in each hexagon
45904590
x = np.array(x, float)
45914591
y = np.array(y, float)
4592+
4593+
if marginals:
4594+
xorig = x.copy()
4595+
yorig = y.copy()
4596+
45924597
if xscale == 'log':
45934598
if np.any(x <= 0.0):
45944599
raise ValueError("x contains non-positive values, so can not"
@@ -4617,10 +4622,6 @@ def reduce_C_function(C: array) -> float
46174622
sx = (xmax - xmin) / nx
46184623
sy = (ymax - ymin) / ny
46194624

4620-
if marginals:
4621-
xorig = x.copy()
4622-
yorig = y.copy()
4623-
46244625
x = (x - xmin) / sx
46254626
y = (y - ymin) / sy
46264627
ix1 = np.round(x).astype(int)
@@ -4746,11 +4747,6 @@ def reduce_C_function(C: array) -> float
47464747
vmin = vmax = None
47474748
bins = None
47484749

4749-
if isinstance(norm, mcolors.LogNorm):
4750-
if (accum == 0).any():
4751-
# make sure we have no zeros
4752-
accum += 1
4753-
47544750
# autoscale the norm with current accum values if it hasn't
47554751
# been set
47564752
if norm is not None:
@@ -4781,40 +4777,42 @@ def reduce_C_function(C: array) -> float
47814777
if not marginals:
47824778
return collection
47834779

4780+
# Process marginals
47844781
if C is None:
47854782
C = np.ones(len(x))
47864783

4787-
def coarse_bin(x, y, coarse):
4788-
ind = coarse.searchsorted(x).clip(0, len(coarse) - 1)
4789-
mus = np.zeros(len(coarse))
4790-
for i in range(len(coarse)):
4791-
yi = y[ind == i]
4784+
def coarse_bin(x, y, bin_edges):
4785+
"""
4786+
Sort x-values into bins defined by *bin_edges*, then for all the
4787+
corresponding y-values in each bin use *reduce_c_function* to
4788+
compute the bin value.
4789+
"""
4790+
nbins = len(bin_edges) - 1
4791+
# Sort x-values into bins
4792+
bin_idxs = np.searchsorted(bin_edges, x) - 1
4793+
mus = np.zeros(nbins) * np.nan
4794+
for i in range(nbins):
4795+
# Get y-values for each bin
4796+
yi = y[bin_idxs == i]
47924797
if len(yi) > 0:
4793-
mu = reduce_C_function(yi)
4794-
else:
4795-
mu = np.nan
4796-
mus[i] = mu
4798+
mus[i] = reduce_C_function(yi)
47974799
return mus
47984800

4799-
coarse = np.linspace(xmin, xmax, gridsize)
4801+
if xscale == 'log':
4802+
bin_edges = np.geomspace(xmin, xmax, nx + 1)
4803+
else:
4804+
bin_edges = np.linspace(xmin, xmax, nx + 1)
4805+
xcoarse = coarse_bin(xorig, C, bin_edges)
48004806

4801-
xcoarse = coarse_bin(xorig, C, coarse)
4802-
valid = ~np.isnan(xcoarse)
48034807
verts, values = [], []
4804-
for i, val in enumerate(xcoarse):
4805-
thismin = coarse[i]
4806-
if i < len(coarse) - 1:
4807-
thismax = coarse[i + 1]
4808-
else:
4809-
thismax = thismin + np.diff(coarse)[-1]
4810-
4811-
if not valid[i]:
4808+
for bin_left, bin_right, val in zip(
4809+
bin_edges[:-1], bin_edges[1:], xcoarse):
4810+
if np.isnan(val):
48124811
continue
4813-
4814-
verts.append([(thismin, 0),
4815-
(thismin, 0.05),
4816-
(thismax, 0.05),
4817-
(thismax, 0)])
4812+
verts.append([(bin_left, 0),
4813+
(bin_left, 0.05),
4814+
(bin_right, 0.05),
4815+
(bin_right, 0)])
48184816
values.append(val)
48194817

48204818
values = np.array(values)
@@ -4829,20 +4827,21 @@ def coarse_bin(x, y, coarse):
48294827
hbar.update(kwargs)
48304828
self.add_collection(hbar, autolim=False)
48314829

4832-
coarse = np.linspace(ymin, ymax, gridsize)
4833-
ycoarse = coarse_bin(yorig, C, coarse)
4834-
valid = ~np.isnan(ycoarse)
4830+
if yscale == 'log':
4831+
bin_edges = np.geomspace(ymin, ymax, 2 * ny + 1)
4832+
else:
4833+
bin_edges = np.linspace(ymin, ymax, 2 * ny + 1)
4834+
ycoarse = coarse_bin(yorig, C, bin_edges)
4835+
48354836
verts, values = [], []
4836-
for i, val in enumerate(ycoarse):
4837-
thismin = coarse[i]
4838-
if i < len(coarse) - 1:
4839-
thismax = coarse[i + 1]
4840-
else:
4841-
thismax = thismin + np.diff(coarse)[-1]
4842-
if not valid[i]:
4837+
for bin_bottom, bin_top, val in zip(
4838+
bin_edges[:-1], bin_edges[1:], ycoarse):
4839+
if np.isnan(val):
48434840
continue
4844-
verts.append([(0, thismin), (0.0, thismax),
4845-
(0.05, thismax), (0.05, thismin)])
4841+
verts.append([(0, bin_bottom),
4842+
(0, bin_top),
4843+
(0.05, bin_top),
4844+
(0.05, bin_bottom)])
48464845
values.append(val)
48474846

48484847
values = np.array(values)
14.5 KB
Loading

lib/matplotlib/tests/test_axes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,8 @@ def test_hexbin_log():
781781
y = np.power(2, y * 0.5)
782782

783783
fig, ax = plt.subplots()
784-
h = ax.hexbin(x, y, yscale='log', bins='log')
784+
h = ax.hexbin(x, y, yscale='log', bins='log',
785+
marginals=True, reduce_C_function=np.sum)
785786
plt.colorbar(h)
786787

787788

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy