Irregular tile size in matplotlib hexbin
Asked Answered
J

1

10

When making a hexbin plot in matplotlib, I find that alternating rows of hexagon tiles have different sizes, sometimes significantly so. This demonstration code shows the effect:

from matplotlib import pyplot as plt
from matplotlib import cm as cm
from matplotlib import mlab as ml
import numpy as np

n = 1e5
x = y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z1 = ml.bivariate_normal(X, Y, 2, 2, 0, 0)
Z2 = ml.bivariate_normal(X, Y, 4, 1, 1, 1)
ZD = Z2 - Z1
x = X.ravel()
y = Y.ravel()
z = ZD.ravel()
plt.subplot(111)

plt.hexbin(x, y, C=z, gridsize=30, cmap=cm.jet, bins=None)
plt.axis([x.min(), x.max(), y.min(), y.max()])

cb = plt.colorbar()
cb.set_label('mean value')
plt.show()

In this image, with a gridsize of 30, you can see that alternate rows are squished a bit vertically:

The effect is not very significant, but in this magnified view of the same hexbin plot but with a gridsize of 80, the small rows are nearly half the size of the large rows. (The generated sample data starts to mis-align with the grid, too, but that's an unimportant artifact.)

The hexbin documentation reads:

gridsize: [ 100 | integer ]

The number of hexagons in the x-direction, default is 100. The corresponding number of hexagons in the y-direction is chosen such that the hexagons are approximately regular. Alternatively, gridsize can be a tuple with two elements specifying the number of hexagons in the x-direction and the y-direction.

It only guarantees that the hexagons be "approximately" regular, but it seems that, especially in cases like the 80-gridsize image above, the hexagons could be made a lot closer to regular by reducing the number of rows so small rows could be enlarged and made more regular. Or, the normal-sized rows could be shrunk vertically while the small ones are enlarged vertically, keeping all rows the same height, even if the tiles aren't regularly-shaped.

What is the cause of this irregularity, and can it be avoided?

Janeyjangle answered 27/6, 2013 at 22:11 Comment(0)
J
9

After some more experimentation, I think I've found the cause. The documentation for hexbin also mentions:

linewidths: [ None | scalar ]

If None, defaults to rc lines.linewidth. Note that this is a tuple, and if you set the linewidths argument you must set it as a sequence of floats, as required by RegularPolyCollection.

Other keyword arguments controlling the Collection properties:

edgecolors: [ None | 'none' | mpl color | color sequence ]

If 'none', draws the edges in the same color as the fill color. This is the default, as it avoids unsightly unpainted pixels between the hexagons.

If None, draws the outlines in the default color.

If a matplotlib color arg or sequence of rgba tuples, draws the outlines in the specified color.

By setting linewidths to (0,), I exposed the unsightly unpainted pixels, but my hexagons were all the same size. It seems that some rows of hexagons were in a higher layer and being drawn with overly-large borders protruding over neighboring rows. A linewidths value of (1,) gives the uneven results I was seeing before, so it may be the default. A linewidths between 0 and 1 (I'm using 0.25) covers the unpainted pixels without causing much overlap, producing a much nicer plot. However, this only happens when saving the image via savefig() at a higher DPI than default (I'm using dpi=300). Calling show() or saving with the default DPI produces plots with the unpainted pixels uncovered. So while effective, this workaround is somewhat limited.

Janeyjangle answered 28/6, 2013 at 20:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.