# pylab is a nice interface to all that is matplotlib
import matplotlib.pyplot as plt
import numpy as np
# Should plots pop up as you type? Use interactive on/off plt.ion() or plt.ioff()
plt.ion()
# if you want the plots to appear in the notebook
%matplotlib inline
Basic components of the figure environment:
from IPython.display import Image
Image(url='http://matplotlib.org/_images/fig_map.png')
There are several ways to make a figure: (for documentation type e.g, plt.figure?)
# basic
fig = plt.figure()
ax = plt.axes()
# plt.figure?
# plt.axes?
# my favorite:
fig, ax = plt.subplots()
# plt.subplots?
# fig, ax = plt.subplots(ncols=2)
# fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(8,8))
# clear them:
plt.close() # close the most recent window
plt.close('all')
# define something to plot
y = np.sin(np.linspace(0, 2*np.pi, 20))
x = np.arange(20)
'-' solid line style
'--' dashed line style
'-.' dash-dot line style
':' dotted line style
'steps' | 'steps-pre' | 'steps-mid' | 'steps-post'
Try these:
fig, ax = plt.subplots(nrows=2, ncols=3, figsize=(14, 14))
# plt.plot?
ax[0, 0].plot(x, y)
ax[0, 1].plot(x, y, ':')
ax[0, 2].plot(x, y, linestyle=':')
ax[1, 0].plot(x, y, linestyle='steps')
ax[1, 1].plot(x, y, linestyle='steps-pre:')
ax[1, 2].plot(x, y, linestyle='steps-pre-')
ax[1, 2].plot(x, y, linestyle='steps-mid-')
ax[1, 2].plot(x, y, linestyle='steps-post-')
[<matplotlib.lines.Line2D at 0x10d089210>]
'.'
point marker
','
pixel marker
'o'
circle marker
'*'
star marker
'x'
x marker
'D'
diamond marker
'd'
thin_diamond marker
fig, ax = plt.subplots(ncols=2, figsize=(14, 4))
ax[0].plot(x, y, 'o')
ax[1].plot(x, y, 'o', ms=30, mec='white', alpha=0.3)
[<matplotlib.lines.Line2D at 0x107f69ad0>]
Scatter is useful because you can set the individual color or marker sizes as arrays.
# A scatter plot that uses colors picked by the y value (you must set the color map)
fig, ax = plt.subplots(ncols=2, figsize=(14,4))
ax[0].scatter(x, y, c=y, cmap=plt.cm.RdYlBu, s=150)
# Same as above, with the marker size set by the array z
z = np.random.random(20) * 1000
ax[1].scatter(x, y, c=y, cmap=plt.cm.RdYlBu, s=z)
<matplotlib.collections.PathCollection at 0x1095c8810>
xerr = np.sqrt(x)
yerr = 0.1
fig, ax = plt.subplots(ncols=3, figsize=(16, 4))
ax[0].errorbar(x, y, xerr=xerr, yerr=yerr)
# no error caps, color the error bars differently than the data:
ax[1].errorbar(x, y, xerr=xerr, yerr=yerr, capsize=0, ecolor='gray')
# don't plot values? Just error bars? set fmt='none'
ax[2].errorbar(x, y, xerr=xerr, yerr=yerr, capsize=0, ecolor='gray', fmt='none')
<Container object of 3 artists>
Matplotlib has a plt.hist function that can create histograms and plot them.
x = np.random.normal(size=10000)
y = np.random.normal(size=10000)
fig, ax = plt.subplots(ncols=2, figsize=(14, 4))
h, bins, p = ax[0].hist(x, bins=20)
h, xe, ye, p = ax[1].hist2d(x, y, bins=20, cmap=plt.cm.RdYlBu)
Personally, I prefer to use np.histogram with plt.plot using linestyle='steps-pre' since I'm usually using the binned data at some other point in the code.
fig, ax = plt.subplots()
h, bins = np.histogram(x, bins=20)
ax.plot(bins[1:], h, linestyle='steps-pre')
plt.draw()
Take any galaxy data file that ends with 'gst.fits'
http://archive.stsci.edu/prepds/angst/datalist.html
I'll be using this for this lesson: http://archive.stsci.edu/pub/hlsp/angst/acs/hlsp_angst_hst_acs-wfc_10605-ugc-5336_f555w-f814w_v1_gst.fits
from astropy.io import fits
# replace this with the file you downloaded
fitsfile = 'hlsp_angst_hst_acs-wfc_10605-ugc-5336_f555w-f814w_v1_gst.fits'
hdu = fits.open(fitsfile)
data = hdu[1].data
photsys = hdu[0].header['CAMERA'] # survey used ACS and WFPC2
# the magnitude fields in the fits file are named MAG[1 or 2]_[ACS or WFPC2]
mag1 = data['MAG1_%s' % photsys]
mag2 = data['MAG2_%s' % photsys]
mag1_err = data['MAG1_ERR']
mag2_err = data['MAG2_ERR']
color = mag1 - mag2
color_err = np.sqrt(data['MAG1_ERR'] ** 2 + data['MAG2_ERR'] ** 2)
# these fits file contains stars that are recovered in only one filter
# stars not recovered are given values >= 90. No need to plot them, messes up the autoscaling.
good, = np.nonzero((np.abs(color) < 30) & (np.abs(mag2) < 30))
fig, ax = plt.subplots(figsize=(8, 8))
ax.plot(color[good], mag2[good], '.', color='black', ms=3)
# CMDs have yaxis reversed, a simple way to reverse axis depend on autoscaling
ax.set_ylim(ax.get_ylim()[::-1])
(30.0, 18.0)
With fmt = 'none'
we can simply overplot error bars and no replot the data. This is a good option for a general CMD function with the option of overploting uncertainties
fig, ax = plt.subplots(figsize=(8, 8))
ax.plot(color[good], mag2[good], '.', color='black', ms=3)
# CMDs have yaxis reversed, a simple way to reverse axis depend on autoscaling
ax.set_ylim(ax.get_ylim()[::-1])
ax.errorbar(color[good], mag2[good], fmt='none', lw=1, xerr=color_err[good],
yerr=mag2_err[good], capsize=0, ecolor='gray')
<Container object of 3 artists>
binsize = 0.1
mbin = np.arange(mag2.min(), mag2.max(), binsize)
lf, bins = np.histogram(mag2, bins=mbin)
fig, ax = plt.subplots(ncols=2, figsize=(14, 4))
[ax[i].plot(bins[1:], lf, linestyle='steps-pre') for i in range(2)]
# maybe you want a log scale
for i, yscale in enumerate(['log', 'linear']):
ax[i].set_yscale(yscale)
ax[i].set_title(yscale)
cbin = np.arange(color.min(), color.max(), binsize)
hess, cbin, mbin = np.histogram2d(color, mag2, bins=[cbin, mbin])
# Set the extent of the image
extent = [np.min(cbin), np.max(cbin), np.max(mbin), np.min(mbin)]
fig, ax = plt.subplots(ncols=2, figsize=(14, 4))
im = ax[0].imshow(hess.T, cmap=plt.cm.gray, interpolation='nearest',
extent=extent, aspect='auto')
# Perhaps you want log bin counts
from matplotlib.colors import LogNorm
im = ax[1].imshow(hess.T, cmap=plt.cm.gray, interpolation='nearest',
extent=extent, aspect='auto', norm=LogNorm())
Matplotlib has a few ways of customizing the plotting environment.
# some code for the next sections
import os
def directory_check(directory):
"""check if a directory exists, if not print the mkdir command to create it"""
if not os.path.isdir(directory):
print 'Need to make the directory.'
print 'mkdir', directory
else:
print '%s exists' % directory
def plot_demo():
"""make a plot for a demo"""
fig, ax = plt.subplots()
ax.plot(np.sin(np.linspace(0, 2*np.pi)))
ax.plot(np.sin(np.linspace(0, 2*np.pi)), 'o')
return ax
To see what the current setup is, find the matplotlibrc file in your matplotlib data path: install_dir/python?.?/site-packages/matplotlib/
print 'matplotlib data path:', mpl.get_data_path()
mplrc = os.path.join(mpl.get_data_path(), 'matplotlibrc')
matplotlib data path: /Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/mpl-data
# In terminal use 'cat' to see the contents of the matplotlibrc file, or open it.
print '$ cat %s' % mplrc
$ cat /Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/mpl-data/matplotlibrc
To make changes to matplotlibrc, you will first have to copy it to your matplotlib user directory, or any changes will be overwritten with future matplotlib installs/upgrades.
# Quick check if you may need to create the directory .matplotlib
user_mpl_path = os.path.join(os.environ['HOME'], '.matplotlib')
directory_check(user_mpl_path)
/Users/rosenfield/.matplotlib exists
Copy the default matplotlibrc to your matplotlib user directory and then follow directions within the matplotlibrc file
print 'cp %s %s/matplotlibrc' % (mplrc, user_mpl_path)
cp /Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/mpl-data/matplotlibrc /Users/rosenfield/.matplotlib/matplotlibrc
To see all available options (uncomment):
#plt.rcParams.keys()
Say you want to plot a few different styles, one for your paper, another for your presenation. In that case, you don't need to make many changes to the matplotlibrc file. You can create your own style files that overide rcParams, either for a given plot command, or thoughout an entire script.
Some styles are available by default:
print plt.style.available
[u'grayscale', u'bmh', u'dark_background', u'ggplot', u'fivethirtyeight', u'presentation']
to set a default style for the entire session:
# default parameters
plot_demo()
# set all future plots to use bmh
plt.style.use('bmh')
plot_demo()
<matplotlib.axes._subplots.AxesSubplot at 0x10b354b10>
Use a style once, and go back to default style afterwards:
# plot each of the available styles
for style in plt.style.available:
with plt.style.context(style):
ax = plot_demo()
ax.set_title(style)
# back to bmh
plot_demo()
<matplotlib.axes._subplots.AxesSubplot at 0x10bf789d0>
Just like the matplotlibrc file, the easiest way to make your own style file is to copy an existing one, place it in your matplotlib user directory, and edit at will.
The style files are located in install_dir/python?.?/site-packages/matplotlib/mpl-data/stylelib
mpl defualt style files, see http://matplotlib.org/examples/style_sheets/index.html
stylelib_path = os.path.join(mpl.get_data_path(), 'stylelib')
print 'mpl style path:', stylelib_path
print 'mpl style files:', os.listdir(stylelib_path)
mpl style path: /Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/mpl-data/stylelib mpl style files: [u'bmh.mplstyle', u'dark_background.mplstyle', u'fivethirtyeight.mplstyle', u'ggplot.mplstyle', u'grayscale.mplstyle']
Open them, check them out, copy your favorite to the directory keeping the .mplstyle file extension
~/.matplotlib/stylelib/[YOUR_CHOSEN_NAME].mplstyle
NOTE: ~/.matplotlib/stylelib/ files overide those in mpl-data/stylelib of the same name
# Quick check if you may need to create the directory stylelib
user_stylelib_path = os.path.join(os.environ['HOME'], '.matplotlib', 'stylelib')
directory_check(user_stylelib_path)
/Users/rosenfield/.matplotlib/stylelib exists
In the terminal, copy one of these to your user directory
# for copy and paste ease, here are the full paths
for s in os.listdir(stylelib_path):
print os.path.join(stylelib_path, s)
/Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/mpl-data/stylelib/bmh.mplstyle /Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/mpl-data/stylelib/dark_background.mplstyle /Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle /Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/mpl-data/stylelib/ggplot.mplstyle /Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/mpl-data/stylelib/grayscale.mplstyle
%%file presentation.mplstyle
# Author: Phil Rosenfield
# Adapted from:
# Author: Cameron Davidson-Pilon, replicated styles from FiveThirtyEight.com
# See https://www.dataorigami.net/blogs/fivethirtyeight-mpl
lines.linewidth: 3
lines.solid_capstyle: butt
legend.frameon: False
legend.numpoints: 1
legend.fontsize: 16
axes.color_cycle: 000000, 30a2da, fc4f30, e5ae38, 6d904f, 8b8b8b
axes.axisbelow: true
axes.edgecolor: f0f0f0
axes.linewidth: 3.0
axes.titlesize: 24
axes.labelsize: 20
patch.edgecolor: f0f0f0
patch.linewidth: 0.5
font.size: 14.0
figure.figsize: 8, 8 # square figures (inches)
figure.subplot.left: 0.08
figure.subplot.right: 0.95
figure.subplot.bottom: 0.07
figure.facecolor: ffffff
xtick.labelsize : 16
ytick.labelsize : 16
text.usetex: True
text.latex.unicode: False
image.aspect: 1.0
image.cmap: Blues
Overwriting presentation.mplstyle
You can copy that to your directory (you may need to restart this kernel to have python find it):
print 'cp %s %s/' % (os.path.join(os.getcwd(), 'presentation.mplstyle'), user_stylelib_path)
cp /Users/rosenfield/Work/action/aas/scwkshop/2015-01-03-aas/intermediate/matplotlib/presentation.mplstyle /Users/rosenfield/.matplotlib/stylelib/
Here is a script that pulls together many topics discussed today and yesterday.
%%file data_plots.py
#!/usr/bin/env python
"""
Script to make CMD, LF, or Hess diagram from a binary fits table
Written by: Phil Rosenfield
"""
import argparse
from astropy.io import fits
import matplotlib.pyplot as plt
import numpy as np
import sys
def plot_cmd(color, mag, color_err=None, mag_err=None, ax=None):
'''
Plot a Color Magnitude diagram with uncertainties
Parameters
----------
color, mag : color and magnitude arrays
color_err, mag_err: uncertainties in color and mag
ax : axes instance
Returns
-------
ax : axes instance
'''
if ax is None:
fig, ax = plt.subplots(figsize=(8, 8))
ax.plot(color, mag, '.', ms=3)
if color_err is not None and mag_err is not None:
ax.errorbar(color, mag, fmt='none', lw=1, xerr=color_err,
yerr=mag_err, capsize=0, ecolor='gray')
# reverse yaxis
ax.set_ylim(ax.get_ylim()[::-1])
return ax
def plot_lf(mag, binsize, mbin=None, yscale='log'):
"""
Make a Luminosty function (binned magnitude) plot
Parameters
----------
mag : array
magnitude array to be binned
binsize : float
width of magnitude bins
mbin : array
right edges of magnitude bins
yscale : str
plt.set_yscale option
Returns
-------
ax : axes instance
"""
if mbin is None:
mbin = np.arange(mag.min(), mag.max(), binsize)
lf, bins = np.histogram(mag, bins=mbin)
fig, ax = plt.subplots()
ax.plot(bins[1:], lf, linestyle='steps-pre')
ax.set_yscale(yscale)
return ax
def make_hess(color, mag, binsize, cbinsize=None, mbin=None, cbin=None):
"""
Compute a hess diagram (surface-density CMD) on photometry data.
Parameters
---------
color : array
color values
mag : array
magnitude values
binsize, cbinsize: float, float
width of mag, color bins in magnitudes
cbin : array
the right edges of the color bins
mbin : array
the right edges of the magnitude bins
Returns
-------
cbin : array
the centers of the color bins
mbin : array
the centers of the magnitude bins
hess : 2d array
The Hess diagram values
"""
if mbin is None:
mbin = np.arange(mag.min(), mag.max(), binsize)
if cbin is None:
if cbinsize is None:
cbinsize = binsize
cbin = np.arange(color.min(), color.max(), cbinsize)
hess, cbin, mbin = np.histogram2d(color, mag, bins=[cbin, mbin])
return hess, cbin, mbin
def plot_hess(color, mag, binsize, ax=None, colorbar=False,
vmin=None, vmax=None, cbinsize=None, im_kwargs={}):
"""
Plot a hess diagram with imshow.
Parameters
----------
color : array
color array to be binned
mag : array
magnitude array to be binned
binsize, cbinsize : float, float
width of magnitude, color bins
colorbar : bool
option to also plot the colorbar
vmin, vmax : float, float or None, None
lower and upper limits sent to matplotlib.colors.LogNorm
im_kwargs : dict
dictionary passed to imshow by default:
defaults = {'norm': LogNorm(vmin=vmin, vmax=vmax),
'cmap': plt.cm.gray,
'interpolation': 'nearest',
'extent' [limits of mag and color]
'aspect': 'auto'}
Returns
-------
ax : axes instance
"""
from matplotlib.colors import LogNorm
if ax is None:
fig, ax = plt.subplots()
hess, cbin, mbin = make_hess(color, mag, binsize, cbinsize=cbinsize)
extent = [np.min(cbin), np.max(cbin), np.max(mbin), np.min(mbin)]
vmax = vmax or hess.max()
defaults = {'norm': LogNorm(vmin=vmin, vmax=vmax),
'cmap': plt.cm.gray,
'interpolation': 'nearest',
'extent': extent,
'aspect': 'auto'}
kwargs = dict(defaults.items() + im_kwargs.items())
im = ax.imshow(hess.T, **kwargs)
if colorbar is True:
plt.colorbar(im)
return ax
def load_data(fitsfile, yfilt='I'):
"""
Load color, magnitude and uncertainties from binary fits table
Parameters
----------
fitsfile : string or file object
path to binary fits table or object to be read by astropy.io.fits
yfilt : string
filter to use as mag (V or I)
Returns
-------
color, mag : arrays of color and magnitude
color_error, mag_err : arrays of summed quadriture uncertainies and magnitude uncertainties
"""
hdu = fits.open(fitsfile)
data = hdu[1].data
photsys = hdu[0].header['CAMERA']
# the magnitude fields in the fits file are named MAG{1,2}_[photsys]
mag1 = data['MAG1_%s' % photsys]
mag2 = data['MAG2_%s' % photsys]
color = mag1 - mag2
color_err = np.sqrt(data['MAG1_ERR'] ** 2 + data['MAG2_ERR'] ** 2)
# choose what gets the yaxis V or I
if yfilt.upper() == 'I':
mag = mag2
ymag = 'MAG2'
else:
mag = mag1
ymag = 'MAG1'
# the error fields in the fits file are named [MAG]_ERR
mag_err = data['%s_ERR' % ymag]
return color, mag, color_err, mag_err
def main(argv):
parser = argparse.ArgumentParser(description="Generate a plot of a fits file")
parser.add_argument('-p', '--plottype', type=str, default='cmd',
help='which plot to make: CMD, hess, or LF')
parser.add_argument('-f', '--filters', type=str, default=None,
help='comma separated V and I filter names for plot labels')
parser.add_argument('-y', '--yfilter', type=str, default='I',
help='plot V or I for LF or on y axis for cmd, Hess')
parser.add_argument('-m', '--binsize', type=float, default=0.05,
help='hess diagram or LF mag binsize')
parser.add_argument('-c', '--cbinsize', type=float, default=0.1,
help='hess diagram color binsize')
parser.add_argument('-xlim', '--xlim', type=list, default=None,
help='comma separated x axis min, max')
parser.add_argument('-ylim', '--ylim', type=list, default=None,
help='comma separated y axis min, max')
parser.add_argument('-yscale', '--yscale', type=str, default='log',
help='y axis scale for LF')
parser.add_argument('-x', '--colorbar', action='store_true',
help='add the hess diagram colorbar')
parser.add_argument('-outfile', '--outfile', type=str, default='data_plot.png',
help='the name of the output file')
parser.add_argument('-style', '--style', type=str, default='ggplot',
choices=plt.style.available,
help='the name of the matplotlib style')
parser.add_argument('file', type=argparse.FileType('r'),
help='the name of the fits file')
args = parser.parse_args(argv)
# set the plot style
plt.style.use(args.style)
if args.filters is not None:
filter1, filter2 = args.filters.split(',')
else:
print('warning: using V, I as default filter names')
filter1 = 'V'
filter2 = 'I'
yfilt = args.yfilter
color, mag, color_err, mag_err = load_data(args.file, yfilt=yfilt)
# the fits file contains stars that are recovered in only one filter
# stars not recovered are given values >= 90. No need to plot em.
good, = np.nonzero((np.abs(color) < 30) & (np.abs(mag) < 30))
if args.plottype.lower() == 'cmd':
ax = plot_cmd(color[good], mag[good], color_err=color_err[good], mag_err=mag_err[good])
if args.plottype.lower() == 'hess':
ax = plot_hess(color[good], mag[good], colorbar=args.colorbar,
binsize=args.binsize, cbinsize=args.cbinsize)
if args.plottype.lower() == 'lf':
ax = plot_lf(mag[good], args.binsize, yscale=args.yscale)
# make axis labels
ax.set_xlabel(r'$%s$' % yfilt)
ax.set_ylabel(r'$\#$')
else:
# make axis labels for cmd, hess
if args.filters is not None:
if yfilt == 'I':
yfilt = filter2
else:
yfilt = filter1
ax.set_ylabel(r'$%s$' % yfilt)
ax.set_xlabel(r'$%s-%s$' % (filter1, filter2))
if args.ylim is not None:
ylim = np.array(''.join(args.ylim).split(','), dtype=float)
ax.set_ylim(ylim)
if args.xlim is not None:
xlim = np.array(''.join(args.xlim).split(','), dtype=float)
ax.set_xlim(xlim)
plt.savefig(args.outfile)
print('wrote %s' % args.outfile)
if __name__ == "__main__":
main(sys.argv[1:])
Overwriting data_plots.py
! chmod u+x data_plots.py
!./data_plots.py -h
usage: data_plots.py [-h] [-p PLOTTYPE] [-f FILTERS] [-y YFILTER] [-m BINSIZE] [-c CBINSIZE] [-xlim XLIM] [-ylim YLIM] [-yscale YSCALE] [-x] [-outfile OUTFILE] [-style {grayscale,bmh,dark_background,ggplot,fivethirtyeight,presentation}] file Generate a plot of a fits file positional arguments: file the name of the fits file optional arguments: -h, --help show this help message and exit -p PLOTTYPE, --plottype PLOTTYPE which plot to make: CMD, hess, or LF -f FILTERS, --filters FILTERS comma separated V and I filter names for plot labels -y YFILTER, --yfilter YFILTER plot V or I for LF or on y axis for cmd, Hess -m BINSIZE, --binsize BINSIZE hess diagram or LF mag binsize -c CBINSIZE, --cbinsize CBINSIZE hess diagram color binsize -xlim XLIM, --xlim XLIM comma separated x axis min, max -ylim YLIM, --ylim YLIM comma separated y axis min, max -yscale YSCALE, --yscale YSCALE y axis scale for LF -x, --colorbar add the hess diagram colorbar -outfile OUTFILE, --outfile OUTFILE the name of the output file -style {grayscale,bmh,dark_background,ggplot,fivethirtyeight,presentation}, --style {grayscale,bmh,dark_background,ggplot,fivethirtyeight,presentation} the name of the matplotlib style
! ./data_plots.py -x -p hess -style presentation -f F555W,F814W -outfile hess_presentation.png hlsp_angst_hst_acs-wfc_10605-ugc-5336_f555w-f814w_v1_gst.fits
wrote hess_presentation.png
(IMO this is an advanced topic) Read more here
http://matplotlib.org/faq/usage_faq.html#what-is-a-backend
Real quickly, if you need to run a python script over ssh you might not want plots to be popping up, especially if you don't have X forwarding enabled. In these cases, specify the backend as "Agg".
You must put this statement above any other matplotlib import
import matplotlib as mpl
mpl.use('Agg')
/Users/rosenfield/anaconda/lib/python2.7/site-packages/matplotlib/__init__.py:1312: UserWarning: This call to matplotlib.use() has no effect because the backend has already been chosen; matplotlib.use() must be called *before* pylab, matplotlib.pyplot, or matplotlib.backends is imported for the first time. warnings.warn(_use_error_msg)