Search code examples
pythonmatplotlibpixel

How to plot on grid with refinements?


Related question

My data comes in the form of a (3 × N) array

[[x_0, ..., x_N-1],
 [y_0, ..., y_N-1],
 [z_0, ..., z_N-1]]

I want to plot it such that the first two lines code the X, Y position of a pixel and the third line sets the pixel's color.

However, I do not want any interpolation to take place. Rather, the space is tiled by the fact that all points lie on a grid, with lower divisions being refinements of the original grid. Here is some dummy data

[[4, 12, 24,  4, 12, 20, 28,  8, 18, 22, 28, 17, 19, 22, 17, 19],  # X
 [4,  4,  8, 12, 12, 20, 20, 24, 26, 26, 28, 29, 29, 30, 31, 31],  # Y
 [1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16]]  # Z (color)

These pixels have size

D = [8,  8, 16,  8,  8,  8,  8, 16,  4,  4,  8,  2,  2,  4,  2,  2]

Illustrated here is the desired position and spatial extent for the pixels corresponding to the dummy data above.

enter image description here

Now, I could interpolate my data to match the finest grid points, but that will be inefficient and not very elegant. Some areas of my grid may be much more refined than others.

Is there a way to make this kind of plot in matplotlib?

EDIT To clarify, refining a pixel in position (x, y) of size (d×d) gives 4 pixels in positions (x - d/4, y - d/4), (x + d/4, y - d/4), (x - d/4, y + d/4),(x + d/4, y + d/4), each of size (d/2 × d/2). Positions always refer to the center of a pixel.


Solution

  • There is no inbuilt function that would allow to plot an irregular grid like the one specified in the question. The solution would be to define a Collection of "pixels" with the respective edges.

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.collections import PolyCollection
    from matplotlib.ticker import MultipleLocator
    
    x =  np.array([4, 12, 24,  4, 12, 20, 28,  8, 18, 22, 28, 17, 19, 22, 17, 19])  # X
    y =  np.array([4,  4,  8, 12, 12, 20, 20, 24, 26, 26, 28, 29, 29, 30, 31, 31])  # Y
    z =  np.array([1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16])  # Z (color)
    D =  np.array([8,  8, 16,  8,  8,  8,  8, 16,  4,  4,  8,  2,  2,  4,  2,  2])
    
    
    def irregularmesh(x, y, s, c, ax=None, **kwargs):
        xedge = np.c_[-s, s, s, -s]/2. + np.atleast_2d(x).T
        yedge = np.c_[-s, -s, s, s]/2. + np.atleast_2d(y).T
        xy = np.stack((xedge,yedge), axis=2)
    
        # Create collection of rectangles.
        pc = PolyCollection(xy, closed=True, **kwargs)
        pc.set_array(c)
        ax = ax or plt.gca()
        ax.add_collection(pc)
        return pc
    
    ######## Plotting ################
    fig, ax = plt.subplots()
    
    pc = irregularmesh(x, y, D, z, ax=ax, linewidth=0, cmap="inferno")
    fig.colorbar(pc, ax=ax)
    
    ax.margins(0)
    ax.autoscale()
    
    for axis in [ax.xaxis, ax.yaxis]:
        axis.set_major_locator(MultipleLocator(4))
    plt.show()
    

    enter image description here