In [21]:
%matplotlib inline
import copy
import pylab
import pyfits
import numpy as np
# from pysurvey import plot as plt
# reload(plt)
# from pysurvey.plot import setup, icolorbar, density, box, text, hist, legend, scontour
import matplotlib.gridspec as gridspec
# from pyclustering import plot
def fhist(x, bins, **kwargs):
v,l = np.histogram(x, bins, )
return pylab.bar(left=l[:-1], height=v,
width=np.diff(l), **kwargs)
In [28]:
# from pysurvey.plot
def _crange(xmin, xmax, nbin):
if xmin > xmax: xmin, xmax = xmax, xmin
xmin, xmax = embiggen([xmin,xmax], 0.1)
bins = np.linspace(xmin, xmax, nbin+1)
delta = (bins[1] - bins[0])/2.0
return bins,delta
def embiggen(r, p=0.05, mode='both'):
'''Returns a larger range from an input range (r) and percentage p.
p=[0.05] -- 5 percent increase in size
mode=['both'] -- increase both sides of the range ('both','upper','lower')
'''
xmin, xmax = r
if isinstance(p,(float,int,np.float, np.int)):
if mode == 'both':
p = [p, p]
elif mode == 'upper':
p = [0, p]
elif mode == 'lower':
p = [p, 0]
d = np.array(p) * np.array([-1,1]) * (xmax-xmin)
return [sum(x) for x in zip(r,d)]
def setup(subplt=None, figsize=None, ax=None,
xr=None, xmin=None, xmax=None,
yr=None, ymin=None, ymax=None,
xlog=False, ylog=False,
xoffset=None, yoffset=None,
xlabel=None, ylabel=None,
xtickv=None, xticknames=None, xtickrotate=None,
ytickv=None, yticknames=None, ytickrotate=None,
halfxlog=False,
suptitle=None, suptitle_prop=None,
subtitle=None, subtitle_prop=None, subtitleloc=1,
title=None,
xticks=None, yticks=None, autoticks=False,
embiggenx=None, embiggeny=None,
secondx=False, secondx_prop=None,
secondy=False, secondy_prop=None,
grid=True, tickmarks=True, font=True,
adjust=True, hspace=0.1, wspace=0.1, aspect=None,
rasterized=False,
):
'''Setup some nice defaults so that we are all fancy like
xr,yr -- xrange and yrange. by setting this it turns off the autoscale feature in that axis
xlog,ylog -- T/F I do love me my log log plots
xlabel,ylabel,title -- Set some nice text for the different axis
xticks, yticks -- set to False if you want to hide the tickmarks and labels
grid -- Turn on the grid in a nice way
tickmarks -- make me some nick minor and major ticks
you can pass in a gridspec
'''
# if notebook:
# # http://matplotlib.org/users/customizing.html
# # http://damon-is-a-geek.com/publication-ready-the-first-time-beautiful-reproducible-plots-with-matplotlib.html
# matplotlib.rcParams['savefig.dpi'] = 144
# matplotlib.rcParams.update({'font.size': 12})
# # matplotlib.rcParams['font.family'] = 'serif'
# # matplotlib.rcParams['font.serif'] = ['Computer Modern Roman']
# # matplotlib.rcParams['text.usetex'] = True
if figsize is not None:
fig = pylab.figure(figsize=figsize)
## Handle subplot being an int `223`, tuple `(2,2,3)` or gridspec
try:
if subplt is None:
if ax is None:
ax = pylab.gca()
elif isinstance(subplt, (int, gridspec.GridSpec, gridspec.SubplotSpec) ):
ax = pylab.subplot(subplt)
else:
ax = pylab.subplot(*subplt)
except Exception as e:
raise ValueError('Failed to setup subplt:[{}] -- probably indexing error'.format(subplt))
# Ranges -- Setting either xr,yr stops the auto ranging
if xr is not None:
ax.set_xlim(xr)
pylab.autoscale(False, 'x', True)
if yr is not None:
ax.set_ylim(yr)
pylab.autoscale(False, 'y', True)
# Log stuff -- do this afterwards to ensure the minor tick marks are updated
# can set the specific ticks using subsx, subsy --
# ax.set_xscale('log', subsx=[2, 3, 4, 5, 6, 7, 8, 9])
# Look here: http://www.ianhuston.net/2011/02/minor-tick-labels-in-matplotlib
# clip ensures that lines that go off the edge of the plot are shown but clipped
if xlog:
ax.set_xscale('log', nonposx='clip')
if ylog:
ax.set_yscale('log', nonposy='clip')
# Labels
if xlabel is not None:
pylab.xlabel(xlabel)
if ylabel is not None:
pylab.ylabel(ylabel)
if title is not None:
pylab.title(title)
if suptitle is not None:
if suptitle_prop is None:
suptitle_prop = {}
pylab.suptitle(suptitle, **suptitle_prop)
if subtitle is not None:
prop = dict(transform=ax.transAxes)
if subtitleloc == 1:
prop.update({'location':(0.95,0.95),
'horizontalalignment':'right',
'verticalalignment':'top'})
elif subtitleloc == 3:
prop.update({'location':(0.05,0.05),
'horizontalalignment':'left',
'verticalalignment':'bottom'})
else:
raise NotImplementedError('Get to work adding the following subtitle location: %d'%(subtitleloc))
if subtitle_prop is not None:
prop.update(subtitle_prop)
loc = prop.pop('location')
outline = prop.pop('outline',True)
outlinewidth = prop.pop('linewidth',3.5)
txt = pylab.text(loc[0], loc[1], subtitle, **prop)
if outline:
# pylab.setp(txt,path_effects=[PathEffects.Stroke(linewidth=outlinewidth, foreground="w")]) # Broken
txt.set_path_effects(
[PathEffects.Stroke(linewidth=outlinewidth, foreground="w"),
PathEffects.Normal()])
# raise ValueError()
if xtickv is not None:
ax.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(xtickv))
if xticknames is not None:
ax.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(xticknames))
if xtickrotate is not None:
if xtickrotate == 'vertical':
pylab.xticks(xtickv, xticknames, rotation='vertical')
else:
tmp = dict(rotation=30, ha='right')
if isinstance(xtickrotate, dict):
tmp.update(xtickrotate)
else:
tmp['rotation'] = xtickrotate
pylab.setp(pylab.xticks()[1], **tmp)
if ytickv is not None:
ax.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(ytickv))
if yticknames is not None:
ax.yaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(yticknames))
if ytickrotate is not None:
pylab.setp(pylab.yticks()[1], rotation=ytickrotate, ha='right')
# Axis hiding
if autoticks:
if not ( ( (isinstance(subplt, tuple) and len(subplt) == 3) ) or
( (isinstance(subplt, gridspec.SubplotSpec)) ) ):
splog('Cannot setup auto ticks without a proper subplot')
else:
if isinstance(subplt, gridspec.SubplotSpec):
rows,cols,i, _ = subplt.get_geometry()
i += 1 # i is 0 indexed.
else:
rows,cols,i = subplt
if ( (i%cols) != 1 ) and (not yticks):
yticks = False
# if ( i < (cols*(rows-1) + 1) ) and (not xticks):
# xticks = False
xticks = not ( ( i < (cols*(rows-1) + 1) ) and (not xticks) )
# Tickmark hiding -- used by autoticks as well.
if xticks is False:
# ax.set_xticklabels([])
ax.set_xlabel('')
pylab.setp(ax.get_xticklabels(), visible=False)
if yticks is False:
# ax.set_yticklabels([])
ax.set_ylabel('')
pylab.setp(ax.get_yticklabels(), visible=False)
# some nice defaults
if grid is True:
pylab.grid(b=True, which='major', linestyle='solid', color='0.3', alpha=0.5)
ax.set_axisbelow(True)
if grid is False:
pylab.grid('off')
if tickmarks:
ax.tick_params('both', which='major', length=5, width=2)
ax.tick_params('both', which='minor', length=3, width=1)
# ax.tick_params('both', which='major', length=majorticklen, width=2)
# ax.tick_params('both', which='minor', length=minorticklen, width=1)
ax.minorticks_on()
else:
ax.minorticks_off()
pylab.tick_params(axis='both',which='both',
bottom='off', top='off',
left='off', right='off')
if adjust:
pylab.subplots_adjust(hspace=hspace, wspace=wspace)
if font:
# this in theory should work, but fails becuase of the stupidify known as `_`
# pylab.rc('font', **{'family':'sans-serif', 'sans-serif':['Helvetica']})
# prop = {'family' : 'normal', 'weight' : 'bold', 'size' : 22}
# pylab.rc('font', **{'family':'serif', 'serif':['Computer Modern Roman']})
# pylab.rc('text', usetex=True)
pass
if aspect is not None:
# 'auto', 'equal'
ax.set_aspect(aspect)
if embiggenx:
setup(xr=embiggen(ax.axis()[:2],embiggenx))
if embiggeny:
setup(yr=embiggen(ax.axis()[2:],embiggeny))
if (xticks) and (halfxlog):
xax = ax.xaxis
xax.set_minor_formatter(matplotlib.ticker.FormatStrFormatter('%g'))
tmp = (10.0**(np.arange(-1,5,1)))*5.0
for x,label in zip(xax.get_minorticklocs(), xax.get_minorticklabels()):
if x in tmp:
label.set_fontsize(8)
else:
label.set_fontsize(0)
pylab.setp(label, visible=False)
if secondx:
# second x axis
tmp = dict(xlabel=None,
fcn=lambda arr: arr,
fmt='{}',
xr=ax.axis()[:2],
xlog=('log' in ax.get_xscale()),
# yr=ax.axis()[2:]
xtickv=ax.get_xticks(),
) # defaults
if secondx_prop:
tmp.update(secondx_prop)
# adjust some nice things
f = tmp.pop('fcn')
fcn = lambda arr: [f(x) for x in arr]
fmt = tmp.pop('fmt')
if isinstance(fmt, str): fmt = (fmt,fmt)
if 'xtickv' in tmp:
tmp['xtickv'] = np.array(tmp['xtickv'])
if 'xticknames' not in tmp:
arr = np.array(fcn(tmp['xtickv']))
tmp['xticknames'] = np.array([fmt[0].format(x) for x in arr])
if tmp['xlog'] and tmp['nicelog']:
ii = np.where((arr >= 1e1)&
(arr < 1e4) )[0]
tmp['xticknames'][ii] = [fmt[1].format(x) for x in arr[ii]]
ii = np.where(arr >= 1e4)
tmp['xticknames'][ii] = [fmt[2].format(np.log10(x)) for x in arr[ii]]
ax2 = ax.twiny() # I have never understood this
ax2.set_xlabel(tmp['xlabel'])
ax2.set_xlim(tmp['xr'])
if tmp['xlog']:
ax2.set_xscale('log', nonposx='clip')
if 'xtickv' in tmp:
ax2.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(tmp['xtickv']))
if 'xticknames' in tmp:
ax2.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(tmp['xticknames']))
ax.secondx = ax2
pylab.sca(ax)
if secondy:
# second x axis
tmp = dict(ylabel=None,
fcn=lambda arr: arr,
fmt='{}',
yr=ax.axis()[2:],
ylog=('log' in ax.get_yscale()),
ytickv=ax.get_yticks(), )
if secondy_prop:
tmp.update(secondy_prop)
# adjust some nice things
f = tmp.pop('fcn')
fcn = lambda arr: [f(x) for x in arr]
fmt = tmp.pop('fmt')
if isinstance(fmt, str): fmt = (fmt,fmt)
if 'ytickv' in tmp:
tmp['ytickv'] = np.array(tmp['ytickv'])
if 'yticknames' not in tmp:
arr = np.array(fcn(tmp['ytickv']))
tmp['yticknames'] = np.array([fmt[0].format(x) for x in arr])
if tmp['ylog'] and tmp['nicelog']:
ii = np.where((arr >= 1e1)&
(arr < 1e4) )[0]
tmp['yticknames'][ii] = [fmt[1].format(x) for x in arr[ii]]
ii = np.where(arr >= 1e4)
tmp['yticknames'][ii] = [fmt[2].format(np.log10(x)) for x in arr[ii]]
print(tmp)
ax2 = ax.twinx() # I have never understood this
ax2.set_ylabel(tmp['ylabel'])
ax2.set_ylim(tmp['yr'])
if tmp['ylog']:
ax2.set_yscale('log', nonposx='clip')
if 'ytickv' in tmp:
ax2.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(tmp['ytickv']))
if 'yticknames' in tmp:
ax2.yaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(tmp['yticknames']))
ax.secondy = ax2
pylab.sca(ax)
if xoffset is False:
tmp = matplotlib.ticker.ScalarFormatter(useOffset=False)
tmp.set_scientific(False)
ax.xaxis.set_major_formatter(tmp)
if yoffset is False:
tmp = matplotlib.ticker.ScalarFormatter(useOffset=False)
tmp.set_scientific(False)
ax.yaxis.set_major_formatter(tmp)
if rasterized:
ax.set_rasterized(True)
# temp
return ax
def scontour(x,y, levels=None, nbin=20,
frac_contour=False,
fill_contour=True,
add_bar=False,
smooth=False, smoothlen=None,
**kwargs):
'''contour a scatter plot'''
'''Contour the data for the bulk bits
returns the outermost polygon
'''
tmp = {
'color': '0.6',
'alpha': 0.8,
'cmap': pylab.cm.gray_r,
}
tmp.update(kwargs)
# make sure that we have good bounds for the 2d contouring
xmin,xmax,ymin,ymax = pylab.axis()
xbin, xdelta = _crange(xmin, xmax, nbin)
ybin, ydelta = _crange(ymin, ymax, nbin)
# find the height map for the points
H, _, _ = np.histogram2d(x, y, bins=(xbin,ybin))
# sort by the cumulative number for each point in the Height map
if frac_contour:
if levels is None:
levels = np.linspace(0.1,1.0, 5)
t = np.reshape(H,-1)
ii = np.argsort(t)
t = np.cumsum(t[ii]) / np.sum(t)
H[np.unravel_index(ii,H.shape)] = t
if levels is None:
# levels = np.logspace(np.log10(0.2),np.log10(1.0), 5)
levels = np.linspace(0.3*np.nanmax(H), np.nanmax(H)*1.05, 6)
# plot the resulting contours X,Y are centers rather than edges
X, Y = np.meshgrid(xbin[:-1]+xdelta, ybin[1:]-ydelta)
if smooth:
X,Y,H = _smooth(X,Y,H, smoothlen)
if fill_contour:
con = pylab.contourf(X,Y,H.T,levels, **tmp)
conl = pylab.contour(X,Y,H.T,levels, colors='0.8', linewidths=1.1)
else:
con = pylab.contour(X,Y,H.T,levels, **tmp)
conl = None
if add_bar:
label = kwargs.get('label', None)
if frac_contour:
# I hope you dont want to use levels below here becuase I am adjusting it
levels = levels[1:-1]
labels = ['%0.1f'%(1-t) for t in levels]
if label is None:
label = 'Fraction of Sample'
cb = colorbar(con, conl, clabel=label, levels=levels, levellabels=labels)
if frac_contour:
cb.ax.invert_yaxis()
else:
cb = None
return con, cb
def legend(handles=None, labels=None,
textsize=9, zorder=None, box=None,
alpha=None,
reverse=False, ax=None, **kwargs):
'''Set a better legend
zorder=int -- layer ordering
box = T/F -- draw the box or not
http://matplotlib.org/users/legend_guide.html
http://matplotlib.org/users/recipes.html
'''
kwargs.setdefault('fancybox', True)
kwargs.setdefault('numpoints',1)
kwargs.setdefault('prop',{'size':textsize})
if ax is None:
ax = pylab.gca()
args = []
if handles is not None:
args.append(handles)
if labels is not None:
args.append(labels)
l = ax.legend(*args, **kwargs)
if reverse:
handles, labels = ax.get_legend_handles_labels()
return legend(handles[::-1], labels[::-1], zorder=zorder, box=box, **kwargs)
if l is None:
return l
if zorder is not None:
l.set_zorder(zorder)
if box is not None:
l.draw_frame(box)
if alpha is not None:
l.get_frame().set_alpha(alpha)
return l
def box(x=None, y=None, normal=False, fill=True, ax=None,**kwargs):
oline = kwargs.pop('outline',False)
outline_prop = kwargs.pop('outline_prop',{})
tmp = dict(color='0.2', alpha=0.4, linewidth=2,)
if ax is None:
ax = pylab.gca()
axt = ax.axis()
if normal:
tmp['transform'] = ax.transAxes
else:
tmp['transform'] = ax.transData
# if x is not None and percent: x = np.diff(axt[:2])*np.array(x) + axt[0]
# if y is not None and percent: y = np.diff(axt[2:])*np.array(y) + axt[2]
if x is None: x = axt[:2]
if y is None: y = axt[2:]
dx = np.diff(x)[0]
dy = np.diff(y)[0]
# use date2num
# if isinstance(x[0], datetime): dx = dx.days
# if isinstance(y[0], datetime): dy = dy.total_seconds()
if oline or (not fill):
tmp['facecolor'] = 'none'
tmp['edgecolor'] = kwargs.pop('color', tmp.pop('color'))
tmp.update(kwargs)
patch = pylab.Rectangle((x[0],y[0]),dx,dy, **tmp)
if oline:
outline(patch, **outline_prop)
ax.add_patch(patch)
# return patch
In [31]:
# from pyclustering.plot
def getmass(cat):
return cat.mass_gal
def getsfr(cat):
return cat.sfr_gal
def getplotinfo(name, cat=None):
'''x, xbin, xrange, xlabel '''
if cat is None:
cat = NoCat()
if isinstance(cat, str):
raise ValueError('You got it backwards')
if name == 'z':
return cat.z, 0.055, [0.0,1.4], 'Redshift'
elif name == 'rz':
return cat.rz, 100, [0,3400], 'r_z'
elif name == 'mag':
return cat.bessell_absmag[:,1], 1.0, [-15.01, -23.5], r'$M_B$ [AB Magnitude]'
# return cat.bessell_absmag2[:,1], [-15.01,-23.5], r'$M_B$ [AB Magnitude]'
elif name == 'magg':
return cat.sdss_absmag[:,1], 1.0, [-15.01, -23.5], r'$M_g$ [AB Magnitude]'
elif name == 'magr':
return cat.bessell_absmag[:,3], 1.0, [-17.01, -25.5], r'$M_R$ [AB Magnitude]'
elif 'filter_' in name:
return (getmag(cat, name.replace('filter_','')), 1.0, [0,30],
'Mag {}'.format(name.replace('filter_','')) )
elif name == 'obsmag':
return cat.bessell_mag[:,3], 1.0, [25, 18], r'$m_R$ [AB Magnitude]'
elif name =='color':
if name in cat.names:
x = x[name]
else:
x = cat.bessell_absmag[:,0] - cat.bessell_absmag[:,1]
return x,0.1, [0,1.5], r'(U-B) Color'
elif name == 'relcolor':
if name in cat.names:
x = cat[name]
else:
x = ( (cat.bessell_absmag[:,0]-cat.bessell_absmag[:,1]) -
(selection.colormagcut(cat.bessell_absmag[:,1])) )
return x, 0.1, [-1., 0.5], r'Relative (U-B) Color'
elif name == 'color2':
# return (cat.sdss_absmag2[:,0]-cat.sdss_absmag2[:,1],
return (cat.sdss_absmag[:,0]-cat.sdss_absmag[:,1],
0.1, [0.2,2.1], r'(u-g) Color')
elif name == 'mass':
return getmass(cat), 0.25, [7.9, 12.2], r'Log( $M_{*}$ / [$M_\odot$])'
elif name == 'sfr':
return getsfr(cat), 0.5, [-2.5, 2.0], r'Log( SFR / [$M_\odot$ yr$^{-1}$])'
elif name == 'ssfr':
if cat is None:
ssfr = None
else:
ssfr = getsfr(cat) - getmass(cat)
return ssfr, 0.5, [-12.5, -7.9], r'Log( sSFR / [yr$^{-1}$])'
elif name == 'enssfr':
ssfr = None if (cat is None) else getsfr(cat) - getmass(cat)
enssfr = None if (cat is None) else ssfr - 0.3*cat.z + 9.62
return enssfr, 0.5, [-3, 2], r'Log( ENsSFR )'
elif name == 'relsfr':
mass = getmass(cat)
sfr = getsfr(cat)
return (sfr-selection.sfrcut(mass, z=cat.z)), 0.5, [-2.1, 2], r'RelSFR'
else:
raise NotImplementedError('Failed to load data: %s'%(name))
def setup_niceplot(cat, xname, yname, **kwargs):
''' Do the setup dance and setup some thing'''
x, xbin, xr, xlabel = getplotinfo(xname, cat)
y, ybin, yr, ylabel = getplotinfo(yname, cat)
tmp = {'xr':xr, 'xlabel':xlabel,
'yr':yr, 'ylabel':ylabel }
tmp.update(kwargs)
setup(**tmp)
return copy.copy(x),copy.copy(y)
def niceplot(x, y, index=None,
contour=False, add_bar=True, contour_prop=None,
points=True,
**kwargs):
'''some nice defaults for scatter plots and the sort
TODO fix the _ to just words
'''
if index is None:
index = np.arange(len(x))
# plot parameters
tmp = {
'marker':'.',
'color':'0.7',
'rasterized':True,
'alpha':0.5,
'lw':0,
'markersize':5,
'markeredgewidth':0,
}
tmp2 = {
'frac_contour':True,
'add_bar':add_bar,
'nbin':25
}
if contour_prop is not None:
tmp2.update(contour_prop)
if contour:
tmp['zorder'] = tmp.get('zorder', 0.9)
tmp.update(kwargs)
if points:
pylab.plot(x[index], y[index], **tmp)
if contour:
scontour(x[index], y[index], **tmp2)
In [15]:
In [16]:
cat = pyfits.getdata('/home/ajmendez/research/data/clustering/data/full_data.fits.gz')
sfr = cat.sfr_gal
mass = cat.mass_gal
ssfr = sfr-mass
In [32]:
zbins = [0.2,0.7,1.2]
xr = [8,12]
yr=[-13, -8]
setup(figsize=(12,6))
for k, (zmin, zmax) in enumerate(zip(zbins, zbins[1:])):
ii = np.where( (cat.z > zmin) & (cat.z < zmax) & cat.ismass)[0]
x,y = setup_niceplot(cat[ii], 'mass','enssfr',
xr=[8,12], yr=[-3,2],
subplt=(1,2,k+1), #autoticks=True,
title='{:0.1f}<z<{:0.1f}'.format(zmin,zmax) )
niceplot(x,y, color='k', contour=True, add_bar=False)
pylab.tight_layout()
In [33]:
zbins = [0.2,0.7,1.2]
xr = [8,12]
yr=[-13, -8]
setup(figsize=(12,6))
for k, (zmin, zmax) in enumerate(zip(zbins, zbins[1:])):
ii = np.where( (cat.z > zmin) & (cat.z < zmax) & cat.ismass)[0]
x,y = setup_niceplot(cat[ii], 'mass','ssfr',
xr=[8,12], yr=[-13,-8],
subplt=(1,2,k+1), #autoticks=True,
title='{:0.1f}<z<{:0.1f}'.format(zmin,zmax) )
x = mass[ii]
y = ssfr[ii]
niceplot(x,y, color='k', contour=True, add_bar=False)
pylab.tight_layout()
In [34]:
mbins = dict(highz1 = [ [11.0, 12.0],
[10.5, 11.0],
[10.0, 10.5]],
highz2 = [[ 9.5, 12.0]],
lowz1 = [[11.0, 12.0],
[10.5, 11.0],
[10.0, 10.5],
[ 9.5, 10.0],
[ 9.0, 9.5]],
lowz2 = [[ 8.5, 12.0]], )
sbins = dict(highz1 = [ [-10.65, 99.0],
[-10.65, 99.0],
[-10.65, 99.0]],
highz2 = [[-10.65, 99.0]],
lowz1 = [[-10.65, 99.0],
[-10.65, 99.0],
[-10.65, 99.0],
[-10.65, 99.0],
[-10.65, 99.0]],
lowz2 = [[-10.65, 99.0]], )
zbins = dict(highz1 = [0.7, 1.2],
highz2 = [0.7, 1.0],
lowz1 = [0.2, 0.7],
lowz2 = [0.2, 0.4],)
items = ['lowz1', 'highz1', 'lowz2', 'highz2']
setup(figsize=(12,12))
for k, item in enumerate(items):
zmin, zmax = zbins[item]
ii = np.where( (cat.z > zmin) & (cat.z < zmax) & cat.ismass)[0]
x,y = setup_niceplot(cat[ii], 'mass','ssfr',
xr=[8,12], yr=[-13,-8],
subplt=(2,2,k+1), #autoticks=True,
title='{} : {:0.1f}<z<{:0.1f}'.format(item, zmin,zmax) )
niceplot(x,y, color='k', contour=True, add_bar=False)
for mr, sr in zip(mbins[item], sbins[item]):
box(mr, sr, fill=True, color='b', alpha=0.4)
box(mr, [-99, sr[0]], fill=True, color='r', alpha=0.4)
jj = np.where( (x > mr[0]) &
(x < mr[1]) &
(y > sr[0]) &
(y < sr[1]) )[0]
text(np.median(x[jj]), np.median(y[jj]),
'+ {}'.format(len(jj)),
ha='left', va='bottom',
fontsize=12, fontweight='bold', color='b',
outline=True)
jj = np.where( (x > mr[0]) &
(x < mr[1]) &
(y > -99) &
(y < sr[0]) )[0]
text(np.median(x[jj]), np.median(y[jj]),
'+ {}'.format(len(jj)),
ha='left', va='bottom',
fontsize=12, fontweight='bold', color='r',
outline=True)
pylab.tight_layout()