Search code examples
pythonmatplotlibhexagonal-tiles

How to get hexagon in matplotlib.hexbin flat side up


Consider this simple example:

import numpy as np
import matplotlib.pyplot as plt

x = np.random.rand(1000)*2-1
y = np.random.rand(1000)*2-1

plt.hexbin(x,y,gridsize=10)
plt.show()

Which will produce the following plot:

enter image description here

The hexagons in this plot have their pointy side up, i.e. along the y-direction. Is there any way to rotate the hexagons 90 degrees so that the flat side is up?


Solution

  • The hexbin is represented as a PolyCollection. Calling get_paths() on that collection gives the path(s) of the base form (a hexagon). You can change the form of that hexagon by replacing its vertices, for example, exchanging their x and y coordinates. This rotates the hexagons, but is only approximate for the counts into each.

    To get a more precise result, the hexbins can be calculated with the x and y exchanged, and afterwards also exchanging the hexagon grid positions.

    Here is some example code:

    import numpy as np
    from matplotlib import pyplot as plt
    
    x = 2 + np.random.rand(200) * 2
    y = np.random.rand(200) * 2
    
    fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(14, 4))
    
    hexb = ax1.hexbin(x, y, gridsize=10, cmap='Reds')
    ax1.scatter(x, y, color='blue', alpha=0.5)
    for (xi, yi), val in zip(hexb.get_offsets(), hexb.get_array()):
        if val > 0:
            ax1.text(xi, yi, f'{val:.0f}', color='lime', ha='center', va='center')
    
    hexb = ax2.hexbin(y, x, gridsize=10, cmap='Reds')  # exchange x and y
    xlim = ax2.get_xlim()
    ylim = ax2.get_ylim()
    hexagon = hexb.get_paths()[0]
    hexagon.vertices = hexagon.vertices[:, ::-1]  # exchange the x and y coordinates of the basic hexagon
    offsets = hexb.get_offsets()
    hexb.set_offsets(offsets[:, ::-1])
    ax2.scatter(x, y, color='blue', alpha=0.5)
    ax2.set_ylim(xlim)  # apply the original ylim to xlim
    ax2.set_xlim(ylim)
    for (xi, yi), val in zip(hexb.get_offsets(), hexb.get_array()):
        if val > 0:
            ax2.text(xi, yi, f'{val:.0f}', color='lime', ha='center', va='center')
    plt.show()
    

    hexbin with rotated bins