2

I have a function that generates a heterogeneous mesh and then plots the patches. It specifies the lower and upper x and y edge for every bin. For example, a single bin is defined by the vector [x0, x1, y0, y1]. These coordinates translate to a bin:

    y1|---------|   
      |         |  
      |   bin   | 
      |         |
    y0|---------|
     x0         x1   

I have an (Nx4) mesh that contains N bins with [x0, x1, y0, y1] columns. To plot the data, I do the following:

z_plot  = z_stat / (dx * dy)     # ``z_stat`` is a calculated z-value 
z_plot  = z_plot / z_plot.max()  # for any given bin.

colors = mpl.cm.jet(z_plot)                   # Let fill data be white.
colors[z_stat == fill] = (1.0, 1.0, 1.0, 1.0) # fill=-9999.0, typically.

dx = mesh[:, 1] - mesh[:, 0]  # x1-x0
dy = mesh[:, 3] - mesh[:, 2]  # y1-y0.

xy = zip(mesh[:, 0], mesh[:, 2])  # (x,y) coordinates of each
                                  # bin's lower left corner.

patches = [mpl.patches.Rectangle(xy[i], dx[i], dy[i],         # I dont want
                                 ec=None, lw=0, fc=colors[i]) # visible edges.
            for i in range(mesh.shape[0])
          ]

patches = mpl.collections.PatchCollection(patches, match_original=True)
ax.add_collection(patches)

if z_stat is not None:

    kwargs = {'orientation': 'vertical'}
    cax, kw = _mpl.colorbar.make_axes_gridspec(plot_ax, **kwargs)

    cbar = mpl.colorbar.ColorbarBase(cax, cmap=_mpl.cm.jet)

This is the result:

The <code>x</code> and <code>y</code> data is converted to a standard 2D histogram in <code>y vs. x</code>. The spiral mesh is generated. <code>Binned Data</code> is the number of data points in each bin. <code>Bin Mean</code> is the mean value in the bin. It would be very useful to log scale this.

This question does something similar, but without the logscale colors. I don't know how to get the colors to log scale. Simply passing something like mpl.colors.LogNorm() to mpl.colorbar.ColorbarBase() did not work for me.

EDIT 1: Generating the mesh.

I have a function that generates a heterogeneous mesh and then plots the patches. It starts with a 2D array:

mesh = [[x00, x10, y00, y01], 
        [x10, x11, y10, y11], 
        ..., 
        [xN0, xN1, yN0, yN1]] 

I read through the mesh and divide each bin in four:

#    y1|----|----|          x0, x1, y0, y1 = mesh[i, :]
#      | p4 | p3 |          xh = [x0 + .5*(x1-x0)]
#      |----|----| <- yh    yh = [y0 + .5 *(y1-y0)]
#      | p1 | p2 |
#    y0|----|----|
#     x0    ^-xh x1       

If each of [p1, p2, p3, p4] have more than the minimum number of data points (e.g. 50), I replace row [x0, x1, y0, y1] with this array:

        new_mesh = _np.array([[x0, xh, xh, x0],  # Define the 16 edges of  
                              [xh, x1, x1, xh],  # the 4 new bins that are  
                              [y0, y0, yh, yh],  # going to replace the bin 
                              [yh, yh, y1, y1]]  # originally defined by 
                            ).T                  # [x0, x1, y0, y1].

        if i == 0:  # 0th edge is a special case for indexing.

            mesh_h = _np.concatenate([new_mesh, mesh[1:]])

        else:

            mesh_h = _np.concatenate([mesh[:i], new_mesh, mesh[i+1:]])         


        mesh = mesh_h  # Set the new edges.
Community
  • 1
  • 1
blalterman
  • 565
  • 7
  • 17
  • You need to pass the `LogNorm` into `PatchCollection(... , norm=LogNorm())` – tacaswell Oct 04 '15 at 17:55
  • @tcaswell I have tried that and nothing changes. – blalterman Oct 04 '15 at 19:23
  • Ah, you are setting the colors by hand, to get this to work as you expect you need to let mpl do the normalization and color mapping. `patches.set_array` will set the values to be used for the mapping. – tacaswell Oct 04 '15 at 20:18
  • @tcaswell Do I need to pass anything additional to `ColorbarBase()`? – blalterman Oct 04 '15 at 20:34
  • You should also be using `figure.colorbar(patches, cax=cax)`. You have re-done a lot of work that the library will do for you if you find the right levers to pull. I may or may not have time to write a complete example later today, hopefully my terse comments are at least pointing you in the right direction! – tacaswell Oct 04 '15 at 20:35
  • @tcaswell Thank you. When I tried ``figure.colorbar()``, they were not populating on the subplots. – blalterman Oct 04 '15 at 20:42
  • @tcaswell Should I be doing this with a QuadMesh instead of generating individual patches? From what I've read, that seems rather unlikely. – blalterman Oct 04 '15 at 20:59
  • Depends on if you need the assume that rectangles are all abutted or not. If they are, then `pcolormesh` might be a much easier path. If there can be gaps, then doing this as you are is the right path. – tacaswell Oct 04 '15 at 21:03
  • @tcaswell It is possible that I will have empty bins where there is no data, but the mesh contains all four edges for every grid element in the `Spiral Mesh`. To generate the mesh, I create the following array: mesh = [[x00, x10, y00, y01], [x10, x11, y10, y11], ..., [xN0, xN1, yN0, yN1]] – blalterman Oct 04 '15 at 21:29
  • ooh, sorry, Yes, if you are using the spiral mesh then you have to do it with rectangles as you are doing it. – tacaswell Oct 04 '15 at 21:35
  • @tcaswell **Edit 1** outlines how I generate the mesh. Each bin is specified by a row in the `mesh` – blalterman Oct 04 '15 at 21:45

1 Answers1

2

Although I can't test your exact case as you've not provided an independently runnable example you should (if my understanding of your desired behaviour is correct) be able to accomplish what you want as follows.

Firstly edit this line to remove the manual setting of the colour and edge information:

patches = [mpl.patches.Rectangle(xy[i], dx[i], dy[i],         # I dont want
                                 ec=None, lw=0, fc=colors[i]) # visible edges.
            for i in range(mesh.shape[0])
          ]

It should look something like this:

patches = [mpl.patches.Rectangle(xy[i], dx[i], dy[i]) for i in range(mesh.shape[0])]

Then pass LogNorm, jet and your edge parameter to PatchCollection. This is because we want matplotlib to handle as much as possible by itself so it can sort out the colours for you.

patch_collection = mpl.collections.PatchCollection(patches,cmap=matplotlib.cm.jet, norm=matplotlib.colors.LogNorm(), lw=0)

Then use set_array to give the PatchCollection the z information:

patch_collection.set_array(z_plot)

Finally add the collection to the plot, create the colorbar and show the figure:

ax.add_collection(patch_collection)
plt.colorbar(patch_collection)

plt.show()

This answer is heavily based on the example given here which may be useful.

or1426
  • 929
  • 4
  • 7