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:

[sourcecode language=”python”]

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

[/sourcecode]

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 $latex x_i, y_i, z_i$ (lower left corner of bar), as well as $latex 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.

[sourcecode language=”python”] 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’$3cdot10^2$’, r’$10^3$’, r’$3cdot10^3$’, r’$10^4$’)
ax2.w_yaxis.set_major_formatter(ticker.FixedFormatter(yformatter))

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

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.