Skip to content

Commit 47ac726

Browse files
committed
Added hexbin axes method and pyplot function by Michiel de Hoon.
Tracker 1952339. svn path=/trunk/matplotlib/; revision=5084
1 parent e0a7249 commit 47ac726

File tree

5 files changed

+252
-0
lines changed

5 files changed

+252
-0
lines changed

API_CHANGES

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
New axes method and pyplot function, hexbin, is an alternative
2+
to scatter for large datasets. It makes something like a
3+
pcolor of a 2-D histogram, but uses hexagonal bins.
4+
15
New kwarg, "symmetric", in MaxNLocator
26
allows one require an axis to be centered on zero.
37

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2008-04-27 Applied patch by Michiel de Hoon to add hexbin
2+
axes method and pyplot function - EF
3+
14
2008-04-25 Enforce python >= 2.4; remove subprocess build - EF
25

36
2008-04-25 Enforce the numpy requirement at build time - JDH

boilerplate.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def %(func)s(*args, **kwargs):
6363
'csd',
6464
'errorbar',
6565
'fill',
66+
'hexbin',
6667
'hist',
6768
'hlines',
6869
'imshow',

lib/matplotlib/axes.py

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4417,6 +4417,229 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None,
44174417

44184418
scatter.__doc__ = cbook.dedent(scatter.__doc__) % martist.kwdocd
44194419

4420+
def hexbin(self, x, y, gridsize = 100, bins = None,
4421+
xscale = 'linear', yscale = 'linear',
4422+
cmap=None, norm=None, vmin=None, vmax=None,
4423+
alpha=1.0, linewidths=None, edgecolors='none',
4424+
**kwargs):
4425+
"""
4426+
HEXBIN(x, y, gridsize = 100, bins = None,
4427+
xscale = 'linear', yscale = 'linear',
4428+
cmap=None, norm=None, vmin=None, vmax=None,
4429+
alpha=1.0, linewidths=None, edgecolors='none'
4430+
**kwargs)
4431+
4432+
Make a hexagonal binning plot of x versus y, where x, y are 1-D
4433+
sequences of the same length, N.
4434+
4435+
Either or both of x and y may be masked arrays, in which case all
4436+
masks will be combined and only unmasked points will be plotted.
4437+
4438+
* gridsize=100 : The number of hexagons in the x-direction. The
4439+
corresponding number of hexagons in the
4440+
y-direction is chosen such that the hexagons are
4441+
approximately regular.
4442+
Alternatively, gridsize can be a tuple with two
4443+
elements specifying the number of hexagons in
4444+
the x-direction and the y-direction.
4445+
4446+
* bins=None : If None, no binning is applied; the color of
4447+
each hexagon directly corresponds to its count
4448+
value.
4449+
bins='log' : Use a logarithmic scale for the color map.
4450+
Internally, log(count+1) is used to determine
4451+
the hexagon color.
4452+
bins=<integer> : Divide the counts in the specified number of
4453+
bins, and color the hexagons accordingly
4454+
bins=<a sequence of values> :
4455+
The values of the lower bound of the bins
4456+
to be used.
4457+
4458+
* xscale = 'linear' | 'log':
4459+
Use a logarithmic scale on the horizontal axis.
4460+
4461+
* yscale = 'linear' | 'log':
4462+
Use a logarithmic scale on the vertical axis.
4463+
4464+
Other keyword args; the color mapping and normalization arguments.
4465+
4466+
* cmap = cm.jet : a colors.Colormap instance from cm.
4467+
defaults to rc image.cmap
4468+
4469+
* norm = colors.Normalize() : colors.Normalize instance
4470+
is used to scale luminance data to 0,1.
4471+
4472+
* vmin=None and vmax=None : vmin and vmax are used in conjunction
4473+
with norm to normalize luminance data. If either are None, the
4474+
min and max of the color array C is used. Note if you pass a norm
4475+
instance, your settings for vmin and vmax will be ignored
4476+
4477+
* alpha =1.0 : the alpha value for the patches
4478+
4479+
* linewidths, if None, defaults to (lines.linewidth,). Note
4480+
that this is a tuple, and if you set the linewidths
4481+
argument you must set it as a sequence of floats, as
4482+
required by RegularPolyCollection -- see
4483+
collections.RegularPolyCollection for details
4484+
4485+
Optional kwargs control the Collection properties; in
4486+
particular:
4487+
4488+
edgecolors='none' : Draw the edges in the same color
4489+
as the fill color. This is the default, as
4490+
it avoids unsightly unpainted pixels
4491+
between the hexagons.
4492+
edgecolors=None : Draw the outlines in the default color.
4493+
edgecolors=<a matplotlib color arg or sequence of rgba tuples>
4494+
: Draw the outlines in the specified color.
4495+
4496+
Here are the standard descriptions of all the Collection kwargs:
4497+
%(Collection)s
4498+
4499+
The return value is a PolyCollection instance; use get_array() on
4500+
this PolyCollection to get the counts in each hexagon.
4501+
"""
4502+
4503+
if not self._hold: self.cla()
4504+
4505+
self._process_unit_info(xdata=x, ydata=y, kwargs=kwargs)
4506+
4507+
x, y = delete_masked_points(x, y)
4508+
4509+
# Set the size of the hexagon grid
4510+
if iterable(gridsize):
4511+
nx, ny = gridsize
4512+
else:
4513+
nx = gridsize
4514+
ny = int(nx/math.sqrt(3))
4515+
# Count the number of data in each hexagon
4516+
x = npy.array(x, float)
4517+
y = npy.array(y, float)
4518+
if xscale=='log':
4519+
x = npy.log(x)
4520+
if yscale=='log':
4521+
y = npy.log(y)
4522+
xmin = min(x)
4523+
xmax = max(x)
4524+
ymin = min(y)
4525+
ymax = max(y)
4526+
# In the x-direction, the hexagons exactly cover the region from
4527+
# xmin to xmax. Need some padding to avoid roundoff errors.
4528+
width = xmax - xmin
4529+
padding = 1.e-9 * width
4530+
xmin -= padding
4531+
xmax += padding
4532+
sx = (xmax-xmin) / nx
4533+
sy = (ymax-ymin) / ny
4534+
x = (x-xmin)/sx
4535+
y = (y-ymin)/sy
4536+
ix1 = npy.round(x)
4537+
iy1 = npy.round(y)
4538+
ix2 = npy.floor(x)
4539+
iy2 = npy.floor(y)
4540+
4541+
nx1 = nx + 1
4542+
ny1 = ny + 1
4543+
nx2 = nx
4544+
ny2 = ny
4545+
n = nx1*ny1+nx2*ny2
4546+
counts = npy.zeros(n)
4547+
lattice1 = counts[:nx1*ny1]
4548+
lattice2 = counts[nx1*ny1:]
4549+
lattice1.shape = (nx1,ny1)
4550+
lattice2.shape = (nx2,ny2)
4551+
4552+
d1 = (x-ix1)**2 + 3.0 * (y-iy1)**2
4553+
d2 = (x-ix2-0.5)**2 + 3.0 * (y-iy2-0.5)**2
4554+
4555+
for i in xrange(len(x)):
4556+
if d1[i] < d2[i]:
4557+
lattice1[ix1[i], iy1[i]]+=1
4558+
else:
4559+
lattice2[ix2[i], iy2[i]]+=1
4560+
4561+
px = xmin + sx * npy.array([ 0.5, 0.5, 0.0, -0.5, -0.5, 0.0])
4562+
py = ymin + sy * npy.array([-0.5, 0.5 ,1.0, 0.5, -0.5, -1.0]) / 3.0
4563+
4564+
polygons = npy.zeros((6, n, 2), float)
4565+
polygons[:,:nx1*ny1,0] = npy.repeat(npy.arange(nx1), ny1)
4566+
polygons[:,:nx1*ny1,1] = npy.array(range(ny1) * nx1)
4567+
polygons[:,nx1*ny1:,0] = npy.repeat(npy.arange(nx2) + 0.5, ny2)
4568+
polygons[:,nx1*ny1:,1] = npy.array(range(ny2) * nx2) + 0.5
4569+
4570+
polygons = npy.transpose(polygons, axes=[1,0,2])
4571+
polygons[:,:,0] *= sx
4572+
polygons[:,:,1] *= sy
4573+
polygons[:,:,0] += px
4574+
polygons[:,:,1] += py
4575+
4576+
if xscale=='log':
4577+
polygons[:,:,0] = npy.exp(polygons[:,:,0])
4578+
xmin = math.exp(xmin)
4579+
xmax = math.exp(xmax)
4580+
self.set_xscale('log')
4581+
if yscale=='log':
4582+
polygons[:,:,1] = npy.exp(polygons[:,:,1])
4583+
ymin = math.exp(ymin)
4584+
ymax = math.exp(ymax)
4585+
self.set_yscale('log')
4586+
4587+
class HexagonBinCollection(mcoll.PolyCollection):
4588+
"""A HexagonBinCollection is a PolyCollection where the edge
4589+
colors are always kept equal to the fill colors"""
4590+
def update_scalarmappable(self):
4591+
mcoll.PolyCollection.update_scalarmappable(self)
4592+
self._edgecolors = self._facecolors
4593+
4594+
if edgecolors=='none':
4595+
collection = HexagonBinCollection(
4596+
polygons,
4597+
linewidths = linewidths,
4598+
transOffset = self.transData,
4599+
)
4600+
else:
4601+
collection = mcoll.PolyCollection(
4602+
polygons,
4603+
edgecolors = edgecolors,
4604+
linewidths = linewidths,
4605+
transOffset = self.transData,
4606+
)
4607+
4608+
# Transform the counts if needed
4609+
if bins=='log':
4610+
counts = npy.log(counts+1)
4611+
elif bins!=None:
4612+
if not iterable(bins):
4613+
minimum, maximum = min(counts), max(counts)
4614+
bins-=1 # one less edge than bins
4615+
bins = minimum + (maximum-minimum)*npy.arange(bins)/bins
4616+
bins = npy.sort(bins)
4617+
counts = bins.searchsorted(counts)
4618+
4619+
if norm is not None: assert(isinstance(norm, mcolors.Normalize))
4620+
if cmap is not None: assert(isinstance(cmap, mcolors.Colormap))
4621+
collection.set_array(counts)
4622+
collection.set_cmap(cmap)
4623+
collection.set_norm(norm)
4624+
collection.set_alpha(alpha)
4625+
collection.update(kwargs)
4626+
4627+
if vmin is not None or vmax is not None:
4628+
collection.set_clim(vmin, vmax)
4629+
else:
4630+
collection.autoscale_None()
4631+
4632+
corners = ((xmin, ymin), (xmax, ymax))
4633+
self.update_datalim( corners)
4634+
self.autoscale_view()
4635+
4636+
# add the collection last
4637+
self.add_collection(collection)
4638+
return collection
4639+
4640+
hexbin.__doc__ = cbook.dedent(hexbin.__doc__) % martist.kwdocd
4641+
4642+
44204643
def arrow(self, x, y, dx, dy, **kwargs):
44214644
"""
44224645
Draws arrow on specified axis from (x,y) to (x+dx,y+dy).

lib/matplotlib/pyplot.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1951,6 +1951,27 @@ def scatter(*args, **kwargs):
19511951
scatter.__doc__ = dedent(Axes.scatter.__doc__) + """
19521952
Additional kwargs: hold = [True|False] overrides default hold state"""
19531953

1954+
# This function was autogenerated by boilerplate.py. Do not edit as
1955+
# changes will be lost
1956+
def hexbin(*args, **kwargs):
1957+
# allow callers to override the hold state by passing hold=True|False
1958+
b = ishold()
1959+
h = kwargs.pop('hold', None)
1960+
if h is not None:
1961+
hold(h)
1962+
try:
1963+
ret = gca().hexbin(*args, **kwargs)
1964+
draw_if_interactive()
1965+
except:
1966+
hold(b)
1967+
raise
1968+
gci._current = ret
1969+
hold(b)
1970+
return ret
1971+
if Axes.hexbin.__doc__ is not None:
1972+
hexbin.__doc__ = dedent(Axes.hexbin.__doc__) + """
1973+
Additional kwargs: hold = [True|False] overrides default hold state"""
1974+
19541975
# This function was autogenerated by boilerplate.py. Do not edit as
19551976
# changes will be lost
19561977
def semilogx(*args, **kwargs):

0 commit comments

Comments
 (0)