3D Histogram in Python (2)

Earlier last week I wrote about a hack to simulate bars with matplotlib in python.  By coincidence, I later read up on the mplot3d.axes3d API, and found a real bar 🙂

.

.

.

The description is sparse:


bar3d(x, y, z, dx, dy, dz, color='b')

This enables us to create histograms of this type:

It has occasional problems with clipping: a plane in the back comes to the front erroneously.  It also does not accept alpha, or other fancy features.

Note that the API is different from the regular bar.  It takes in a numpy array for each of x_i, y_i, z_i (lower left corner of bar), as well as dx, dy, dz:

For a histogram of the type shown above, z is zero (in the xy plane), and dx, dy are unity.  x, y controls which grid the bar is plotted in, and dz the height of the bar.  Together with the axes formatting, the code looks like the following.  Included is a converter for changing a matrix input into the correct numpy arrays.

import matplotlib.pyplot as plot
import mpl_toolkits.mplot3d
import numpy
import matplotlib.ticker as ticker

blankdata = [ [0,0,0,0,0,0],
 [0,0,0,0,0,0],
 [0,0,0,0,0,0],
 [0,0,0,0,0,0],
 [0,0,0,0,0,0],
 [0,0,0,0,0,0],
 [0,0,0,0,0,0],
 [0,0,0,0,0,0] ]

def convert_grid_to_array(data):
    """ This converts the explicit square grid into the x,y,dz positional arrays required for plot_matrix core """
    xpos = [-100]
    currentx = 0
    ypos = [-100]
    currenty = 0
    dz = [0.05]
    adata = numpy.array(data)
    #print adata
    #print adata.transpose()

    arr_data_inverse = adata.transpose()
    for i in arr_data_inverse:
        for j in i:
            zdata = arr_data_inverse[currentx][currenty]
            if zdata != 0:
                xpos.append(currentx)
                ypos.append(currenty)
                dz.append(zdata)
            currenty=currenty+1
            #print xpos, ypos, dz
       currenty=0
       currentx=currentx+1
    return xpos, ypos, dz

def plot_matrix(data, color, filename):
    fig = plot.figure()
    ax2 = mpl_toolkits.mplot3d.Axes3D(fig)

    xpos, ypos, dz = convert_grid_to_array(data)

    zpos = numpy.zeros_like(xpos)
    dx = 1 * numpy.ones_like(zpos)
    dy = numpy.ones_like(zpos)
    ax2.bar3d(xpos, ypos, zpos, dx, dy, dz, color=color)

    ax2.set_xlim3d(0,6)
    ax2.set_ylim3d(0,8)
    ax2.set_zlim3d(0,15)

    ax2.set_xlabel('$Duration / ms$')
    ax2.set_ylabel('$Conductance / pS$')
    ax2.set_zlabel('$Occurrences$')

    xformatter = (r'$10$', r'$10^2$', r'$10^3$', r'$10^4$', r'$10^5$', r'$10^6$')
    ax2.w_xaxis.set_major_formatter(ticker.FixedFormatter(xformatter))

    yformatter = ("", r'$3$', r'$10$', r'$30$', r'$10^2$', r'$3\cdot10^2$', r'$10^3$', r'$3\cdot10^3$', r'$10^4$')
    ax2.w_yaxis.set_major_formatter(ticker.FixedFormatter(yformatter))

    plot.savefig("".join([filename, ".pdf"]))

There is a funny (xpos, ypos, dz) == (-100, -100, 0.05) line in the converter function.  What this does is to validate empty input matrixes; the numpy functions array.ones_like() choke on empty arrays.

With this input, I think stacked 3D-histograms are also possible.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s