def outside_legend( fig: plt.Figure, ax: plt.Axes, legend_padding: float = 0.04, legend_height: float = 0.3, **kwargs, ) -> None: """Plots a legend immediately above the figure axes. Args: fig: The figure to plot the legend on. ax: The axes to plot the legend above. legend_padding: Padding between top of axes and bottom of legend, in inches. legend_height: Height of legend, in inches. **kwargs: Passed through to `fig.legend`. """ _width, height = fig.get_size_inches() pos = ax.get_position() legend_left = pos.x0 legend_right = pos.x0 + pos.width legend_width = legend_right - legend_left legend_bottom = pos.y0 + pos.height + legend_padding / height legend_height = legend_height / height bbox = (legend_left, legend_bottom, legend_width, legend_height) fig.legend( loc="lower left", bbox_to_anchor=bbox, bbox_transform=fig.transFigure, mode="expand", **kwargs, )
class Example(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.hsv_color = colorsys.rgb_to_hsv(0.0, 0.0, 1.0) self.hex_color = '#0000ff' self.color_list = ["red","blue","green","orange","purple"] self.parent = parent print "Loading model..." self.lux = Lux() fp = open(curdir+"/gauss_model.pkl"); self.gm = pickle.load(fp); fp.close() fp = open(curdir+"/memoized_binomial_data.pkl"); self.curve_data = pickle.load(fp); fp.close() fp = open(curdir+"/sampling_normalizer.pkl"); self.normalizer = pickle.load(fp); fp.close() print "Creating UI" self.initUI() self.update_output() self.replot() def update_output(self): (h, s, v) = self.hsv_color self.hsv_var.set("Hue: \t %2.1f \nSat:\t %2.1f \nValue:\t %2.1f" % (h*360,s*100,v*100)) items = self.lux.full_posterior((h * 360, s * 100, v * 100)) self.current_post = items desc = [ '{:<25} ({:.3f})\n'.format(items[i][0], items[i][1]) for i in range(25) ] self.display.config(state=NORMAL) self.display.delete(0, END) for i in range(25): self.display.insert(END, '{:<20} ({:.3f})'.format(items[i][0], items[i][1])) self.display.select_set(0, 0) def plot_lux_model(self, params,ax1,label,support,dim): cur_color='black' mu1,sh1,sc1,mu2,sh2,sc2 = params left_stach=gam_dist(sh1,scale=sc1); lbounds=left_stach.interval(0.99) right_stach=gam_dist(sh2,scale=sc2); rbounds=right_stach.interval(0.99) lx=np.linspace(mu1,-180); rx=np.linspace(mu2,360) s=3; ax1.plot(rx, [right_stach.sf(abs(y-mu2)) for y in rx],linewidth=s,c=cur_color); ax1.plot([1.01*mu1,0.99*mu2], [1.,1.], linewidth=s,c=cur_color) return ax1.plot(lx,[left_stach.sf(abs(y-mu1)) for y in lx],c=cur_color, linewidth=s); def plot_gm_model(self, params, ax, label, support): s=3 x = np.linspace(support[0],support[1],360) return ax.plot(x,norm.pdf(x,params[0],params[1]),c='red', linewidth=s), norm.pdf([params[0]],params[0],[params[1]])[0] def initUI(self): self.parent.title("Interactive LUX visualization") self.pack(fill=BOTH, expand=1) self.color_frame = Frame(self, border=1) self.color_frame.pack(side=LEFT) probe_title_var = StringVar(); probe_title_label = Label(self.color_frame, textvariable=probe_title_var, justify=CENTER, font = "Helvetica 16 bold italic") probe_title_var.set("Color Probe X"); probe_title_label.pack(side=TOP) self.hsv_var = StringVar() self.hsv_label = Label(self.color_frame, textvariable=self.hsv_var,justify=LEFT) h,s,v = self.hsv_color self.hsv_var.set("Hue: %2.1f \nSaturation: %2.1f \nValue: %2.1f" % (h*360,s*100,v*100)) self.hsv_label.pack(side=TOP) self.frame = Frame(self.color_frame, border=1, relief=SUNKEN, width=200, height=200) self.frame.pack(side=TOP) self.frame.config(bg=self.hex_color) self.frame.bind("<Button-1>",self.onChoose) self.btn = Button(self.color_frame, text="Select Color", command=self.onChoose) self.btn.pack(side=TOP) posterior_title_var = StringVar(); posterior_title_label = Label(self.color_frame, textvariable=posterior_title_var, justify=CENTER, font = "Helvetica 16 bold italic") posterior_title_var.set("\n\nLUX's Posterior"); posterior_title_label.pack(side=TOP) Label(self.color_frame, text="Double click to show details \n(Wait time dependent on computer)").pack(side=TOP) my_font = tkFont.Font(family="Courier", size=10) self.display = Listbox(self.color_frame, border=1, relief=SUNKEN, width=30, height=25, font=my_font) self.display.pack(side=TOP,fill=Y,expand=1) self.display.bind("<Double-Button-1>",self.onSelect) self.display_btn = Button(self.color_frame, text="Show details", command=self.onSelect) self.display_btn.pack(side=TOP) self.update_output() self.fig = Figure(figsize=(10,4), dpi=100) self.canvas = FigureCanvasTkAgg(self.fig, master=self) self.canvas.show() self.canvas.get_tk_widget().pack(side=LEFT, fill=BOTH, expand=1) self.canvas._tkcanvas.pack(side='top', fill='both', expand=1) def replot(self): def gb(x,i,t): #t is max value, i in number of bins, x is the thing to be binned if x==t: return i-1 elif x==0.0: return 0 return int(floor(float(x)*i/t)) hsv_title = [] j=self.display.curselection()[0] name = self.current_post[j][0] mult = lambda x: reduce(operator.mul, x) g_labels = []; lux_labels=[]; all_g_params=[] for i in range(3): def align_yaxis(ax1, v1, ax2, v2): """adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1""" _, y1 = ax1.transData.transform((0, v1)) _, y2 = ax2.transData.transform((0, v2)) inv = ax2.transData.inverted() _, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2)) miny, maxy = ax2.get_ylim() ax2.set_ylim(miny, maxy+dy) subplot = self.fig.add_subplot(4,1,i+1) dim_label = ["H", "S","V"][i] subplot.set_ylabel(r"$P(k^{true}_{%s}|x)$" % ["H", "S","V"][i] ) curve_data = self.curve_data[name][i] scale = lambda x,a=0.3,b=0.9: (b-a)*(x)+a p_x = lambda x: self.normalizer[i][gb(x,len(self.normalizer[i]),[360,100,100][i])] max_p_x = max(self.normalizer[i]) #1 is white, 0 is black. so we want highly probable thigns to be black.. if self.lux.get_adj(self.current_post[j][0]): support = [[-180,180], [0,100],[0,100]][i] pp = lambda x,i: x-360 if i==0 and x>180 else x hacky_solution = [360,100,100][i] w = 1.5 if i==0 else 1 conv = lambda x: x*support[1]/len(curve_data) bar_colors = ["%s" % (scale(1-p_x(conv(x))/max_p_x)) for x in range(len(curve_data))] bar1 = subplot.bar([pp(atan2(sin((x*hacky_solution/len(curve_data))*pi/180),cos((x*hacky_solution/len(curve_data))*pi/180))*180/pi,i) for x in range(len(curve_data))],[x/max(curve_data) for x in curve_data], label="%s data" % j,ec="black",width=w,linewidth=0,color=bar_colors) else: support = [[0,360], [0,100],[0,100]][i] w = 1.5 if i==0 else 1 conv = lambda x: x*support[1]/len(curve_data) bar_colors = ["%s" % (scale(1-p_x(conv(x))/max_p_x)) for x in range(len(curve_data))] bar1 = subplot.bar([x*support[1]/len(curve_data) for x in range(len(curve_data))],[x/max(curve_data) for x in curve_data], label="%s data" % name[0],ec="black",width=w,linewidth=0,color=bar_colors) pp = lambda x,*args: x point = pp(self.hsv_color[i]*[360,100,100][i],i) hsv_title.append(point) probeplot = subplot.plot([point,point], [0,1],linewidth=3,c='blue',label="Probe") #for j in range(5): lux_plot = self.plot_lux_model(self.lux.get_params(self.current_post[j][0])[i], subplot, self.current_post[j][0],support, i) subplot2 = subplot.twinx() gm_plot,gm_height = self.plot_gm_model([pp(g_param,i) for g_param in self.gm[self.current_post[j][0]][0][i]], subplot2, self.current_post[j][0], support) extra = Rectangle((0, 0), 1, 1, fc="w", fill=False, edgecolor='none', linewidth=0) subplot.legend([extra], [["Hue", "Saturation", "Value"][i]],loc=2,frameon=False) if i==0: legend_set=lux_plot+[extra,extra,extra]+gm_plot+[extra,extra,extra] lux_params = self.lux.get_params(self.current_post[j][0])[i] g_params = [pp(g_param,i) for g_param in self.gm[self.current_post[j][0]][0][i]] all_g_params.append(g_params) g_labels.append(r"$\mu^{%s}=$%2.2f, $\sigma^{%s}$=%2.2f" % (dim_label, g_params[0],dim_label,g_params[1])) #lux_labels.append(r"$\mu^{L,%s}=$%2.2f, $E[\tau^{L,%s}]$=%2.2f, $\alpha^{L,%s}$=%2.2f, $\beta^{L,%s}$=%2.2f, $\mu^{U,%s}=$%2.2f, $E[\tau^{L,%s}]$=%2.2f, $\alpha^{U,%s}$=%2.2f, $\beta^{U,%s}$=%2.2f" % (dim_label, lux_params[0],dim_label, (lux_params[0]-lux_params[1]*lux_params[2]),dim_label,lux_params[1],dim_label, lux_params[2],dim_label,lux_params[3],dim_label,(lux_params[3]+lux_params[4]*lux_params[5]),dim_label, lux_params[4],dim_label,lux_params[5])) lux_labels.append(r"$\mu^{L,%s}=$%2.2f, $\alpha^{L,%s}$=%2.2f, $\beta^{L,%s}$=%2.2f, $\mu^{U,%s}=$%2.2f, $\alpha^{U,%s}$=%2.2f, $\beta^{U,%s}$=%2.2f" % (dim_label, lux_params[0],dim_label, lux_params[1],dim_label, lux_params[2],dim_label,lux_params[3],dim_label,lux_params[4],dim_label,lux_params[5])) subplot.set_xlim(support[0],support[1]) subplot.set_ylim(0,1.05) subplot2.set_xlim(support[0],support[1]) subplot2.set_ylabel(r"$P(x|Gaussian_{%s})$" % ["H", "S","V"][i]) align_yaxis(subplot, 1., subplot2, gm_height) leg_loc =(0.9,0.2) datum = [x*[360,100,100][i] for i,x in enumerate(self.hsv_color)]; phi_value = self.lux.get_phi(datum,self.current_post[j][0]) #gauss_value = mult([norm.pdf(datum[i],all_g_params[i][0],all_g_params[i][1]) for i in range(3)]) leg=self.fig.legend(probeplot+legend_set, ["Probe X"]+ [r"$\mathbf{\phi}_{%s}(X)=\mathbf{%2.5f}$; $\mathbf{\alpha}=\mathbf{%2.4f}$" % (self.current_post[j][0],phi_value,self.lux.get_avail(self.current_post[j][0]))]+lux_labels+ [r"$Normal^{Hue}_{%s}$; $prior(%s)=%2.4f$" % (self.current_post[j][0],self.current_post[j][0], self.gm[self.current_post[j][0]][2])]+[g_labels[0]+"; "+g_labels[1]+"; "+g_labels[2]] , loc=8, handletextpad=4,labelspacing=0.1) self.fig.suptitle("%s" % name, size=30) print "done replotting" def onChoose(self, *args): try: ((red,green,blue), hx) = tkColorChooser.askcolor() except: print "I think you hit cancel" return self.hex_color = hx self.hsv_color = colorsys.rgb_to_hsv(red/255.0, green/255.0, blue/255.0) self.frame.config(bg=hx) self.update_output() self.fig.clear() self.replot() self.canvas.draw() def onSelect(self, *args): self.fig.clear() self.replot() self.canvas.draw()
def plot(self, pars : Parameters, data : 'Data' = None, channel : Channel = None, only : list = None, exclude : list = None, variations : list = None, residuals : bool = False, canvas : plt.Figure = None, labels : bool = True) : """Plot the expected event yields and optionally data as well The plot is performed for a single model, which must be of `binned_range` type. The event yields are plotted as a histogram, as a function of the channel observable. The `variations` arg allows to plot yield variations for selected NP values. The format is { ('par1', val1), ... } , which will plot the yields for the case where NP par1 is set to val1 (while other NPs remain at nominal), etc. Args: pars : parameter values for which to compute the expected yields data : observed dataset to plot alongside the expected yields channel : name of the channel to plot. If `None`, plot the first channel. exclude : list of sample names to exclude from the plot variations : list of NP variations to plot, as a list of (str, float) pairs providing the NP name and the value to set. residuals : if True, plot the data-model differences canvas : a matplotlib Figure on which to plot (if None, plt.gca() is used) labels : if True (default), add labels to the legend """ if canvas is None : canvas = plt.gca() if not isinstance(only, list) and only is not None : only = [ only ] if not isinstance(exclude, list) and exclude is not None : exclude = [ exclude ] if channel is None : channel = list(self.channels.values())[0] print("Plotting channel '%s'" % channel.name) else : if not channel in self.channels : raise KeyError('ERROR: Channel %s is not defined.' % channel) channel = self.channels[channel] if isinstance(channel, BinnedRangeChannel) : grid = [ b['lo_edge'] for b in channel.bins ] grid.append(channel.bins[-1]['hi_edge']) elif isinstance(channel, SingleBinChannel) : grid = [0,1] else : raise ValueError("Channel '%s' is o an unsupported type" % channel.name) xvals = [ (grid[i] + grid[i+1])/2 for i in range(0, len(grid) - 1) ] start = self.channel_offsets[channel.name] stop = start + channel.nbins() nexp = self.n_exp(pars)[:, start:stop] tot_exp = nexp.sum(axis=0) if only is not None : samples = [] for sample_name in only : if not sample_name in channel.samples : raise ValueError('Sample %s is not defined.' % sample_name) samples.append(list(channel.samples).index(sample_name)) subtract = nexp[samples,:].sum(axis=0) subtract = tot_exp - subtract line_style = '--' title = ','.join(only) elif exclude is not None : samples = [] for sample_name in exclude : if not sample_name in channel.samples : raise ValueError('Sample %s is not defined.' % sample_name) samples.append(list(channel.samples).index(sample_name)) subtract = nexp[samples,:].sum(axis=0) line_style = '--' title = 'Model excluding ' + ','.join(exclude) else : subtract = np.zeros(nexp.shape[1]) line_style = '-' title = 'Model' yvals = tot_exp - subtract if not residuals or data is None else tot_exp - subtract - counts canvas.hist(xvals, weights=yvals, bins=grid, histtype='step',color='b', linestyle=line_style, label=title if labels else None) if data is not None : counts = data.counts[start:stop] yerrs = [ math.sqrt(n) if n > 0 else 0 for n in counts ] yvals = counts if not residuals else np.zeros(channel.nbins()) canvas.errorbar(xvals, yvals, xerr=[0]*channel.nbins(), yerr=yerrs, fmt='ko', label='Data' if labels else None) canvas.set_xlim(grid[0], grid[-1]) if variations is not None : for v in variations : vpars = pars.clone() vpars.set(v[0], v[1]) col = 'r' if len(v) < 3 else v[2] nexp = self.n_exp(vpars)[:, start:stop] if only is None and exclude is None : subtract = np.zeros(nexp.shape[1]) else : subtract = nexp[samples,:].sum(axis=0) if only is not None : subtract = nexp.sum(axis=0) - subtract tot_exp = nexp.sum(axis=0) - subtract canvas.hist(xvals, weights=tot_exp, bins=grid, histtype='step',color=col, linestyle=line_style, label='%s=%+g' %(v[0], v[1]) if labels else None) if labels : canvas.legend() canvas.set_title(self.name) if isinstance(channel, BinnedRangeChannel) : canvas.set_xlabel('$' + channel.obs_name + '$' + ((' [' + channel.obs_unit + ']') if channel.obs_unit != '' else '')) canvas.set_ylabel('Events / bin') elif isinstance(channel, SingleBinChannel) : canvas.set_xlabel(channel.name) canvas.set_ylabel('Events')
class Example(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.hsv_color = colorsys.rgb_to_hsv(0.0, 0.0, 1.0) self.hex_color = '#0000ff' self.color_list = ["red","blue","green","orange","purple"] self.parent = parent print "Getting Model" self.lux = lux.LUX("lux.xml") print "Creating UI" self.initUI() self.update_output() def update_output(self): (h, s, v) = self.hsv_color items = self.lux.full_posterior((h * 360, s * 100, v * 100)) self.current_post = items desc = [ '{} ({:.3f})\n'.format(items[i][0], items[i][1]) for i in range(25) ] self.display.config(state=NORMAL) self.display.delete(1.0, END) self.display.insert(END, ''.join(desc)) self.display.config(state=DISABLED) def make_plotter(self, params,ax1,label,cur_color,support): mu1,sh1,sc1,mu2,sh2,sc2 = params left_stach=gam_dist(sh1,scale=sc1); lbounds=left_stach.interval(0.99) right_stach=gam_dist(sh2,scale=sc2); rbounds=right_stach.interval(0.99) lx=np.linspace(mu1,-180); rx=np.linspace(mu2,360) s=3; #cur_color='black' ax1.plot(rx, [1-right_stach.cdf(abs(y-mu2)) for y in rx],linewidth=s,c=cur_color); ax1.plot([1.01*mu1,0.99*mu2], [1,1], linewidth=s,c=cur_color) return ax1.plot(lx,[1-left_stach.cdf(abs(y-mu1)) for y in lx],c=cur_color, label=r"$\phi^{Hue}_{%s}$" % label,linewidth=s); def initUI(self): self.parent.title("Interactive LUX visualization") self.pack(fill=BOTH, expand=1) self.color_frame = Frame(self, border=1) self.color_frame.pack(side=LEFT) self.frame = Frame(self.color_frame, border=1, relief=SUNKEN, width=100, height=100) #self.frame.place(x=160, y=30) self.frame.pack(side=TOP) self.frame.config(bg=self.hex_color) self.btn = Button(self.color_frame, text="Select Color", command=self.onChoose) self.btn.pack(side=TOP) #self.btn.place(x=30, y=30) self.display = Text(self, border=1, relief=SUNKEN, width=30, height=5) #self.display.place(x=280, y=30) self.display.pack(side=LEFT,fill=Y,expand=1) self.update_output() self.fig = Figure(figsize=(10,4), dpi=100) self.replot() self.canvas = FigureCanvasTkAgg(self.fig, master=self) self.canvas.show() self.canvas.get_tk_widget().pack(side=LEFT, fill=BOTH, expand=1) self.canvas._tkcanvas.pack(side='top', fill='both', expand=1) def replot(self): hsv_title = [] for i in range(3): if self.lux.get_adj(self.current_post[0][0]): support = [[-180,180], [0,100],[0,100]][i] pp = lambda x,i: x-360 if i==0 and x>180 else x else: support = [[0,360], [0,100],[0,100]][i] pp = lambda x,*args: x subplot = self.fig.add_subplot(3,1,i+1) subplot.set_xlim(support[0],support[1]) subplot.set_ylabel("%s" % ["Hue", "Saturation","Value"][i]) point = pp(self.hsv_color[i]*[360,100,100][i],i) hsv_title.append(point) probeplot = subplot.plot([point,point], [0,1],linewidth=3,c='black',label="Probe") legend_set = [] for j in range(5): test = self.make_plotter(self.lux.get_params(self.current_post[j][0])[i], subplot, self.current_post[j][0],self.color_list[j],support) if type(test)==type([]): legend_set+=test else: legend_set.append(test) self.fig.legend(legend_set, [r"$\phi_{%s}$; $\alpha=%2.4f$" % (x[0],self.lux.get_avail(x[0])) for x in self.current_post[:5]], loc=1) self.fig.suptitle("HSV (%2.2f,%2.2f,%2.2f) top 5 Phi curves" % (hsv_title[0],hsv_title[1],hsv_title[2])) def onChoose(self): ((red,green,blue), hx) = tkColorChooser.askcolor() self.hex_color = hx self.hsv_color = colorsys.rgb_to_hsv(red/255.0, green/255.0, blue/255.0) self.frame.config(bg=hx) self.update_output() self.fig.clear() self.replot() self.canvas.draw()
def set_figure_properties(fig: plt.Figure, **kwargs) -> None: """ Ease the configuration of a :class:`matplotlib.figure.Figure`. Parameters ---------- fig : matplotlib.figure.Figure The figure. Keyword arguments ----------------- fontsize : int Font size to use for the plot titles, and axes ticks and labels. Defaults to 12. tight_layout : bool `True` to fit the whole subplots into the figure area, `False` otherwise. Defaults to `True`. tight_layout_rect : Sequence[float] A rectangle (left, bottom, right, top) in the normalized figure coordinate that the whole subplots area (including labels) will fit into. Defaults to (0, 0, 1, 1). suptitle : str The figure title. Defaults to an empty string. xlabel : str TODO ylabel : str TODO figlegend_on : bool TODO figlegend_ax : int TODO figlegend_loc : `str` or `Tuple[float, float]` TODO figlegend_framealpha : float TODO figlegend_ncol : int TODO subplots_adjust_hspace : float TODO subplots_adjust_vspace : float TODO """ fontsize = kwargs.get("fontsize", 12) tight_layout = kwargs.get("tight_layout", True) tight_layout_rect = kwargs.get("tight_layout_rect", (0, 0, 1, 1)) suptitle = kwargs.get("suptitle", "") x_label = kwargs.get("x_label", "") x_labelpad = kwargs.get("x_labelpad", 20) y_label = kwargs.get("y_label", "") y_labelpad = kwargs.get("y_labelpad", 20) figlegend_on = kwargs.get("figlegend_on", False) figlegend_ax = kwargs.get("figlegend_ax", 0) figlegend_loc = kwargs.get("figlegend_loc", "lower center") figlegend_framealpha = kwargs.get("figlegend_framealpha", 1.0) figlegend_ncol = kwargs.get("figlegend_ncol", 1) wspace = kwargs.get("subplots_adjust_wspace", None) hspace = kwargs.get("subplots_adjust_hspace", None) rcParams["font.size"] = fontsize if suptitle is not None and suptitle != "": fig.suptitle(suptitle, fontsize=fontsize + 1) if x_label != "" or y_label != "": ax = fig.add_subplot(111) ax.set_frame_on(False) ax.set_xticks([]) ax.set_xticklabels([], visible=False) ax.set_yticks([]) ax.set_yticklabels([], visible=False) if x_label != "": ax.set_xlabel(x_label, labelpad=x_labelpad) if y_label != "": ax.set_ylabel(y_label, labelpad=y_labelpad) if tight_layout: fig.tight_layout(rect=tight_layout_rect) if figlegend_on: handles, labels = fig.get_axes()[figlegend_ax].get_legend_handles_labels() fig.legend( handles, labels, loc=figlegend_loc, framealpha=figlegend_framealpha, ncol=figlegend_ncol, ) if wspace is not None: fig.subplots_adjust(wspace=wspace) if hspace is not None: fig.subplots_adjust(hspace=hspace)