3D Bar Histograms in Python

— by

Attempts to prepare 3D (XYZ) histograms in python using the mplot3d package (part of matplotlib).


Plotting a XYZ-histogram turned out to be less trivial than anticipated.  The premise: I have datasets which are scattered with over a 2-dimensional grid, and I needed the (3D) histograms.  This is an exploratory effort, and we would like like to generate >150 of these histogram.  This favours a programmatic approach over manually doing this with spreadsheets.  (Incidentally, this is one of the few things that Excel can do that Origin can’t!)

The default 3D bar plots from mplot3d (part of matplotlib) looks like:

The view presented is the best view of these graphs; at shallower angles, the infinitely thin nature of the bars becomes painfully apparent (the worse case scenario occurs when viewed directly from the Y-axis).  The solution to “bulking it up” turns out to be simple.

Each of the normal bars are plotted with:


fig = pylab.figure()
 
ax = Axes3D(fig)
 
ax.bar(xs, ys, zs=z, zdir='y', color=c, alpha=0.8)

The simple workaround to giving a solid depth to the bars is by bracketing multiple bars in close succession:

barband = range(-45, 45, 5)
 for modifier in barband:
    ax.bar(current, occurrence, zs=duration+(float(modifier)/100), zdir='y', color=barcolor, alpha=0.6)

This generates output that looks like the following:

This generates unit blocks.  The parameters here are quite carefully tuned for this view to give a solid sense while keeping the shadows light.  When lines are closer (decrease spacing between steps) the shadow side appears pitch black; with further lines (or closer zoom) the individual scales become visible.

The parameter alpha is interesting.  Higher values give the same blackness as increased numbers of slices, but lower values (alpha <= 0.2) give fuzzy slices, as the edges blend substantially into the white in the background.

It is useful to wrap the above into a function, and mine is shown here with the data input:

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
 
data = [ [0,0,0,3,0,0],
 [0,0,0,0,0,0],
 [0,3,5,0,0,0],
 [0,0,0,7,0,0],
 [0,0,0,0,0,0],
 [0,0,0,0,0,2],
 [0,0,0,0,0,0],
 [0,0,0,0,2,0] ]
 
fig = plt.figure()
 ax = Axes3D(fig)
 
def plot_xyz_histogram(data, ax, barcolor, filename):
    """ Takes in a matrix (see structure above) and generate a pseudo-3D histogram by overlaying close, semitransparent bars. """
    for duration, occurrence in zip(range(len(data)), data):
        current = range(len(occurrence))
        barband = range(-45, 45, 5)
        for modifier in barband:
            ax.bar(current, occurrence, zs=duration+(float(modifier)/100), zdir='y', color=barcolor, alpha=0.6)
 
    ax.set_xlabel('Current')
    ax.set_ylabel('Duration')
    ax.set_zlabel('Occurrances')

My attempts to overlay these bar charts together to programmatically generate (4D?) histograms of this type is unsuccessful.  I tried to do this by summing the overlay layer with the base data, so they would “poke through”.  This did not work; transparency of the base bars resulted in optical mixing.  A multi-series 3D-histogram remain unique Illustrator artifacts.

Addendum: mplot3d have API for real 3D bars.  See this post: here.

Newsletter


Leave a Reply