import numpy as np
import numpy.random as npr
import matplotlib.pyplot as mpl
import matplotlib.ticker as mpt
import sys
#==============================================================================
# Create Mock Data
#==============================================================================
def generate_correlated_random_xydata(npoints=2500):
x = np.linspace(0,3,npoints)+npr.normal(size=npoints)
y = x+npr.normal(size=npoints)
return x,y
#==============================================================================
# Utility Functions
#==============================================================================
def find_level(histogram,level,nslices=1000):
"""
This return the histogram value that contain level% of the data inside.
Useful to construct isocontours for 2D data based on data counts.
"""
HTOT = np.sum(histogram)
HMAX = np.amax(histogram)
HMIN = np.amin(histogram)
slices = np.linspace(HMAX,HMIN,nslices)
for i in range(nslices):
if np.sum(histogram[histogram>slices[i]]) > level*HTOT:
break
else:
continue
return slices[i]
#==============================================================================
# Double Hist Figure
#==============================================================================
histCOLOR = 'GoldenRod'
histCMAP = 'YlOrRd'
contourCMAP = 'YlGnBu_r'
pointColor = 'LimeGreen'
fig = mpl.figure(figsize=(12.5,14)) ## Create an empty figure, figsize allows to control extra space for added axes
## Add axes to the figure (where you will plot the data)
## [x_lowerCorner,y_lowerCorner,width,height]
## The full figure canvas goes from [0,1] both in x and y
## The use of sharex=axname will make the two axes linked in matplotlib interactive mode
## The same can be done with sharey=axname
axMain = fig.add_axes([0.1,0.1,0.65,0.65])
axHistX = fig.add_axes([0.1,0.75,0.65,0.20],sharex=axMain)
axHistY = fig.add_axes([0.75,0.1,0.20,0.65],sharey=axMain)
## Adjust label parameters for an axis to show (or not) the number labels
axHistX.tick_params(labelbottom='off')
axHistY.tick_params(labelleft='off')
## Generate random set of data
varx,vary = generate_correlated_random_xydata()
## Create a 2D histogram from data
binx = np.linspace(-3,7,21)
biny= np.linspace(-3,7,21)
Histogram2D,Xedges,Yedges = np.histogram2d(varx,vary,bins=[binx,biny])
## Now we want to mask out all histogram bins where we have less than 10 data poin ts
masked_Histogram=np.ma.masked_where(Histogram2D<=10,Histogram2D,copy=False)
## Compute levels to draw contours at 90,60,40 and 20% levels
levels = [0.90,0.60,0.40,0.20]
levels_contour = np.array([find_level(Histogram2D,l) for l in levels])
## Plot both histograms in the variables x and y
hx,ex,px=axHistX.hist(varx,bins=binx,color=histCOLOR,histtype='stepfilled')
hy,ey,py=axHistY.hist(vary,bins=biny,orientation='horizontal',color=histCOLOR,histtype='stepfilled')
## Adjust axis limits semi-automatically
axHistX.set_ylim(0,1.1*np.amax(hx))
axHistY.set_xlim(0,1.1*np.amax(hy))
## Plot data points, 2D histogram and contour levels on the main axes
## the picker keyword allows for added interactive actions to your plot by selecting
## closer point to your mouse click and have accessible data to play with.
## The zorder keyword allows you to control which elements of the plot apper on top.
## The higher the value of zorder the more upfront the element will be.
## This is all done in relative terms.
z=0
axMain.plot(varx,vary,'.',alpha=0.5,color=pointColor,zorder=z,picker=2)
axMain.imshow(masked_Histogram.T,cmap=histCMAP,extent=(min(Xedges),max(Xedges),min(Yedges),max(Yedges)),aspect='auto',zorder=z+1,origin="lower",interpolation="nearest")
axMain.contour(Histogram2D.T,levels=levels_contour,extent=(min(Xedges),max(Xedges),min(Yedges),max(Yedges)),cmap=contourCMAP,origin='lower',zorder=z+10)
## This limits the number of main ticks on each axis which helps in reducing
## the confusing in cluttered axes
axHistY.xaxis.set_major_locator(mpt.MaxNLocator(3))
axHistX.yaxis.set_major_locator(mpt.MaxNLocator(3))
axMain.xaxis.set_major_locator(mpt.MaxNLocator(5))
## This is not useful here, but can be useful in future scenarios since we use loglog plots often
## This will make your axis have the number labels in scalar format and not in power law format
axMain.xaxis.set_major_formatter(mpt.ScalarFormatter(useOffset=False))
axMain.yaxis.set_major_formatter(mpt.ScalarFormatter(useOffset=False))
## Turn on the minorticks in the plot to look more 'scientific'. Helpful to read values off plots in an easier way
for eixo in [axMain,axHistX,axHistY]:
eixo.minorticks_on()
## Set your plot labels using mathtext, same as you would do in LaTex
axMain.set_xlabel(r'$var_x$')
axMain.set_ylabel(r'$var_y$')
## Connect your figure with keyboard action. In this case, pressing a key while
## the matplotlib window is active will execute the function exit_code
## This in turn wil either close the plot or exit the program if you click
## 'q' or 'escape' respectively
def exit_code(event):
if event.key=='escape':
sys.exit()
if event.key=='q':
mpl.close('all')
fig.canvas.mpl_connect('key_press_event',exit_code)
## Connect yout figure to a picking event. This will allow you to select the
## closest element (data point, line, etc.) and allow you to access the data
## This is good for exploratory plots where you need specific information to
## add to that shown in the plot
def on_pick(event):
thisline = event.artist
xdata, ydata = thisline.get_data()
ind = event.ind
print('You clicked at x=%.2f, y=%.2f'%(xdata[ind], ydata[ind]))
cid = fig.canvas.mpl_connect('pick_event', on_pick)
## Make the Figure pop up
mpl.show()