Visualization Fun with Python

The below plot is my favorite data visualization I created for my thesis. It is a 2D density plot with histograms projected along each axis.

Density Plot

I based the above plot on code from here, however this plot also includes a 2D temperature/density plot in the middle, and 1/2/3 sigma contour lines.  Below is the code I used to generate this plot in python. There are quite a few tricks in the below code including:

  • Adding multiple plots to a single figure
  • Making 2D temperature/density plots
  • Making 1D histogram plots
  • Adding contour lines to plots
  • Adjusting size and font of labels and tick-marks
  • Adding text to a plot
  • Adjusting the limits of the plot
  • Removing tick-labels

Here is the code:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import NullFormatter, MaxNLocator
from numpy import linspace
plt.ion()
 
# Define a function to make the ellipses
def ellipse(ra,rb,ang,x0,y0,Nb=100):
    xpos,ypos=x0,y0
    radm,radn=ra,rb
    an=ang
    co,si=np.cos(an),np.sin(an)
    the=linspace(0,2*np.pi,Nb)
    X=radm*np.cos(the)*co-si*radn*np.sin(the)+xpos
    Y=radm*np.cos(the)*si+co*radn*np.sin(the)+ypos
    return X,Y
 
# Define the x and y data 
# For example just using random numbers
x = np.random.randn(10000)
y = np.random.randn(10000)
 
# Set up default x and y limits
xlims = [min(x),max(x)]
ylims = [min(y),max(y)]
 
# Set up your x and y labels
xlabel = '$\mathrm{Your\\ X\\ Label}$'
ylabel = '$\mathrm{Your\\ Y\\ Label}$'
 
# Define the locations for the axes
left, width = 0.12, 0.55
bottom, height = 0.12, 0.55
bottom_h = left_h = left+width+0.02
 
# Set up the geometry of the three plots
rect_temperature = [left, bottom, width, height] # dimensions of temp plot
rect_histx = [left, bottom_h, width, 0.25] # dimensions of x-histogram
rect_histy = [left_h, bottom, 0.25, height] # dimensions of y-histogram
 
# Set up the size of the figure
fig = plt.figure(1, figsize=(9.5,9))
 
# Make the three plots
axTemperature = plt.axes(rect_temperature) # temperature plot
axHistx = plt.axes(rect_histx) # x histogram
axHisty = plt.axes(rect_histy) # y histogram
 
# Remove the inner axes numbers of the histograms
nullfmt = NullFormatter()
axHistx.xaxis.set_major_formatter(nullfmt)
axHisty.yaxis.set_major_formatter(nullfmt)
 
# Find the min/max of the data
xmin = min(xlims)
xmax = max(xlims)
ymin = min(ylims)
ymax = max(y)
 
# Make the 'main' temperature plot
# Define the number of bins
nxbins = 50
nybins = 50
nbins = 100
 
xbins = linspace(start = xmin, stop = xmax, num = nxbins)
ybins = linspace(start = ymin, stop = ymax, num = nybins)
xcenter = (xbins[0:-1]+xbins[1:])/2.0
ycenter = (ybins[0:-1]+ybins[1:])/2.0
aspectratio = 1.0*(xmax - 0)/(1.0*ymax - 0)
 
H, xedges,yedges = np.histogram2d(y,x,bins=(ybins,xbins))
X = xcenter
Y = ycenter
Z = H
 
# Plot the temperature data
cax = (axTemperature.imshow(H, extent=[xmin,xmax,ymin,ymax],
       interpolation='nearest', origin='lower',aspect=aspectratio))
 
# Plot the temperature plot contours
contourcolor = 'white'
xcenter = np.mean(x)
ycenter = np.mean(y)
ra = np.std(x)
rb = np.std(y)
ang = 0
 
X,Y=ellipse(ra,rb,ang,xcenter,ycenter)
axTemperature.plot(X,Y,"k:",ms=1,linewidth=2.0)
axTemperature.annotate('$1\\sigma$', xy=(X[15], Y[15]), xycoords='data',xytext=(10, 10),
                       textcoords='offset points', horizontalalignment='right',
                       verticalalignment='bottom',fontsize=25)
 
X,Y=ellipse(2*ra,2*rb,ang,xcenter,ycenter)
axTemperature.plot(X,Y,"k:",color = contourcolor,ms=1,linewidth=2.0)
axTemperature.annotate('$2\\sigma$', xy=(X[15], Y[15]), xycoords='data',xytext=(10, 10),
                       textcoords='offset points',horizontalalignment='right',
                       verticalalignment='bottom',fontsize=25, color = contourcolor)
 
X,Y=ellipse(3*ra,3*rb,ang,xcenter,ycenter)
axTemperature.plot(X,Y,"k:",color = contourcolor, ms=1,linewidth=2.0)
axTemperature.annotate('$3\\sigma$', xy=(X[15], Y[15]), xycoords='data',xytext=(10, 10),
                       textcoords='offset points',horizontalalignment='right',
                       verticalalignment='bottom',fontsize=25, color = contourcolor)
 
#Plot the axes labels
axTemperature.set_xlabel(xlabel,fontsize=25)
axTemperature.set_ylabel(ylabel,fontsize=25)
 
#Make the tickmarks pretty
ticklabels = axTemperature.get_xticklabels()
for label in ticklabels:
    label.set_fontsize(18)
    label.set_family('serif')
 
ticklabels = axTemperature.get_yticklabels()
for label in ticklabels:
    label.set_fontsize(18)
    label.set_family('serif')
 
#Set up the plot limits
axTemperature.set_xlim(xlims)
axTemperature.set_ylim(ylims)
 
#Set up the histogram bins
xbins = np.arange(xmin, xmax, (xmax-xmin)/nbins)
ybins = np.arange(ymin, ymax, (ymax-ymin)/nbins)
 
#Plot the histograms
axHistx.hist(x, bins=xbins, color = 'blue')
axHisty.hist(y, bins=ybins, orientation='horizontal', color = 'red')
 
#Set up the histogram limits
axHistx.set_xlim( min(x), max(x) )
axHisty.set_ylim( min(y), max(y) )
 
#Make the tickmarks pretty
ticklabels = axHistx.get_yticklabels()
for label in ticklabels:
    label.set_fontsize(12)
    label.set_family('serif')
 
#Make the tickmarks pretty
ticklabels = axHisty.get_xticklabels()
for label in ticklabels:
    label.set_fontsize(12)
    label.set_family('serif')
 
#Cool trick that changes the number of tickmarks for the histogram axes
axHisty.xaxis.set_major_locator(MaxNLocator(4))
axHistx.yaxis.set_major_locator(MaxNLocator(4))
 
#Show the plot
plt.draw()
 
# Save to a File
filename = 'myplot'
plt.savefig(filename + '.pdf',format = 'pdf', transparent=True)
10 comments… add one
  • Anne Feb 10, 2014 @ 12:22

    Nice! This is a very useful kind of plot. But the rainbow color map is a bad idea: http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=4118486&tag=1 Fortunately it’s just a matter of using plt.hot() (or your other favourite non-problematic color map).

  • Adam Ginsburg Feb 10, 2014 @ 15:16

    This would be nice as a submission to the matplotlib gallery; it combines elements of a few examples already there:
    http://matplotlib.org/examples/pylab_examples/scatter_hist.html
    http://matplotlib.org/examples/axes_grid/scatter_hist.html
    http://matplotlib.org/examples/pylab_examples/hist2d_demo.html

    Also, while I often do as you did with ticks & loops, there are convenience functions to replace things like:
    ticklabels = axHistx.get_yticklabels()
    for label in ticklabels:
    label.set_fontsize(12)
    label.set_family(‘serif’)
    with:
    axHistx.yaxis.set_tick_params(labelsize=12)
    though apparently there’s no way to set the font family with this approach… that surprises me.

    • Coleman Krawczyk Feb 13, 2014 @ 23:59

      You can always set the global font family in one line by doing:

      plt.rcParams[‘font.family’] = ‘serif’

  • Bo Milvang-Jensen Feb 10, 2014 @ 16:16

    Thanks for sharing Jess! I’m new to Python and I’m looking forward to getting to know it.

    Why is the top panel wider than the middle panel?

  • adrian p-w Feb 10, 2014 @ 18:20

    Try using histtype=’step’ or histtype=’stepfilled’ in the .hist() call — all those vertical black lines in the histogram just become noise to the reader

  • Peter I. P. Feb 11, 2014 @ 3:50

    Using the http://matplotlib.org/users/gridspec.html package, you can have an even tidier arrangement of your subplots (as now they are not perfectly well lined up with the central plot).

  • Pablo Marchant Feb 11, 2014 @ 7:46

    Just reposting this link from the astronomers facebook page, a small read that summarizes the problems with the rainbow color map:

    http://www.jwave.vt.edu/~rkriz/Projects/create_color_table/color_07.pdf

    And that doesn’t even deal with issues with color blind people. Very specific to matplotlib, this stackoverflow page contains detailed answers on available colormaps, and their luminance from end to end:

    http://stackoverflow.com/questions/13968520/color-selection-for-matplotlib-that-prints-well

    A mononously changing luminance is usually paramount for publications, in order for plots to be usable in b/w.

    Other than that, its a nice example 😀

  • Gabby Apr 20, 2015 @ 20:17

    I have two 1d arrays (one with temp and the other with arcmin). I’m having issues figuring out how to use what I have to produce a plot similar in image to this one (temp declination as function of arcmin). Any helpful suggestions would be greatly appreciated!!!!

    Also this is an awesome website.

  • Viral May 18, 2015 @ 6:38

    Thanks for this script. Is there any one know how can I make fits file from this plot? In the above script H will give 2D array from which one can generate fits file, but if I want to overlay contours of the source then wcs information is not handling.

  • coline Jan 5, 2024 @ 10:42

    Thanks a lot, great exemple. I just would like to add a colorbar, on the left side of the main plot without change the size of the main plot. Do you have an idea how to do that?

    Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *