def plot_surface(spectra, dimension1, dimension2, fig_num=1, show_plot=True, **kwargs): """ Plot the two dimensions from spectra as a 2D histogram Args: spectra (:class:`echidna.core.spectra`): The spectra to plot. dimension1 (string): The name of the dimension you want to plot. dimension2 (string): The name of the dimension you want to plot. Returns: matplotlib.pyplot.figure: Plot of the surface of the two dimensions. """ fig = plt.figure(num=fig_num) axis = fig.add_subplot(111) index1 = spectra.get_config().get_index(dimension1) index2 = spectra.get_config().get_index(dimension2) if index1 < index2: x = _produce_axis(spectra, dimension2) y = _produce_axis(spectra, dimension1) else: x = _produce_axis(spectra, dimension1) y = _produce_axis(spectra, dimension2) data = spectra.surface(dimension1, dimension2) par1 = spectra.get_config().get_par(dimension1) par2 = spectra.get_config().get_par(dimension2) if index1 < index2: axis.set_xlabel("%s (%s)" % (dimension2, par2.get_unit())) axis.set_ylabel("%s (%s)" % (dimension1, par1.get_unit())) else: axis.set_xlabel("%s (%s)" % (dimension1, par1.get_unit())) axis.set_ylabel("%s (%s)" % (dimension2, par2.get_unit())) # `plot_surface` expects `x` and `y` data to be 2D X, Y = numpy.meshgrid(x, y) # Set sensible levels, pick the desired colormap and define normalization if kwargs.get("color_scheme") is None: color_scheme = "hot_r" # default else: color_scheme = kwargs.get("color_scheme") color_map = plt.get_cmap(color_scheme) linear = numpy.linspace(numpy.sqrt(data.min()), numpy.sqrt(data.max()), num=100) locator = FixedLocator(linear**2) levels = locator.tick_values(data.min(), data.max()) norm = BoundaryNorm(levels, ncolors=color_map.N) # Plot color map color_map = axis.pcolormesh(X, Y, data, cmap=color_map, norm=norm) color_bar = fig.colorbar(color_map) color_bar.set_label("Counts per bin") if show_plot: plt.show() return fig
def chi_squared_map(syst_analyser, fig_num=1, **kwargs): """ Plot chi squared surface for systematic vs. signal counts Args: syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic analyser object, created during limit setting. Can be used during limit setting setting or can load an instance from hdf5 fig_num (int): Fig number. When creating multiple plots in the same script, ensures matplotlib doesn't overwrite them. .. note:: Keyword arguments include: * contours (*bool*): if True produces a contour plot of chi squared surface. Default (*False*). * preferred_values (*bool*): if False "preferred values" curve is not overlayed on colour map. Default (*True*) * minima (*bool*): if False "minima" are not overlayed on colour map. Default (*True*) * save_as (*string*): supply file name to save image Default is to produce a colour map, with "preferred values" curve and "minima" overlayed. """ # Set kwargs defaults if kwargs.get("preferred_values") is None: kwargs["preferred_values"] = True if kwargs.get("minima") is None: kwargs["minima"] = True # Set x and y axes x = syst_analyser.get_actual_counts() y = syst_analyser.get_syst_values() # Set chi squared map values data = numpy.average(syst_analyser.get_chi_squareds(), axis=1) data = numpy.transpose(data) # transpose it so that axes are correct # Set preferred value values y_2 = numpy.average(syst_analyser.get_preferred_values(), axis=1) # Set minima values x_3 = syst_analyser.get_minima()[0] y_3 = syst_analyser.get_minima()[1] # Create meshgrid X, Y = numpy.meshgrid(x, y) # Set sensible levels, pick the desired colormap and define normalization color_map = plt.get_cmap('hot_r') linear = numpy.linspace(numpy.sqrt(data.min()), numpy.sqrt(data.max()), num=100) locator = FixedLocator(linear**2) levels = locator.tick_values(data.min(), data.max()) norm = BoundaryNorm(levels, ncolors=color_map.N) if kwargs.get("contours"): fig = plt.figure(fig_num, figsize=(16, 10)) # Fig. 2 fig.text(0.1, 0.9, syst_analyser._name, **BOLD_FONT) ax = Axes3D(fig) ax.view_init(elev=17.0, azim=-136.0) # set intial viewing position # Plot surface surf = ax.plot_surface(X, Y, data, rstride=1, cstride=1, cmap=color_map, norm=norm, linewidth=0, antialiased=False) ax.zaxis.set_minor_locator(locator) ax.ticklabel_format(style="scientific", scilimits=(3, 4)) # Set axis labels ax.set_xlabel("\nSignal counts", **BOLD_FONT) ax.set_ylabel("\nValue of systematic", **BOLD_FONT) for label in (ax.get_xticklabels() + ax.get_yticklabels() + ax.get_zticklabels()): label.set_fontsize(MAIN_FONT.get("size")) # tick label size ax.dist = 11 # Ensures tick labels are not cut off ax.margins(0.05, 0.05, 0.05) # Adjusts tick margins # Draw colorbar color_bar = fig.colorbar(surf, ax=ax, orientation="vertical", fraction=0.2, shrink=0.5, aspect=10) # kwargs here control axes that the colorbar is drawn in color_bar.set_label(r"$\chi^2$", size=MAIN_FONT.get("size")) color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size")) plt.show() if kwargs.get("save_as") is not None: fig.savefig(kwargs.get("save_as") + "_contour.png", dpi=300) else: fig = plt.figure(fig_num, figsize=(12, 10)) # Fig. 2 fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT) ax = fig.add_subplot(1, 1, 1) # Set labels ax.set_xlabel("Signal counts", **BOLD_FONT) ax.set_ylabel("Value of systematic", **BOLD_FONT) # Plot color map color_map = ax.pcolormesh(X, Y, data, cmap=color_map, norm=norm) color_bar = fig.colorbar(color_map) color_bar.set_label("$\chi^2$", size=MAIN_FONT.get("size")) color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size")) # tick label size # Set axes limits ax.set_xlim([X.min(), X.max()]) ax.set_ylim([Y.min(), Y.max()]) if kwargs.get("preferred_values"): ax.plot(x, y_2, "bo-", label="Preferred values") if kwargs.get("minima"): ax.plot(x_3, y_3, "ko", label="Minima") # Set axes tick label size for label in (ax.get_xticklabels() + ax.get_yticklabels()): label.set_fontsize(MAIN_FONT.get("size")) ax.legend(loc="upper left") if kwargs.get("save_as") is not None: fig.savefig(kwargs.get("save_as") + "_color_map.png", dpi=300) return fig
def push_pull(syst_analyser, fig=1, **kwargs): """ Plot penalty value - poisson likelihood chi squared. When does minimising chi squared, which wants to "pull" the away from the data/prior value dominate and when does the penalty term, which wants to "pull" towards the data/prior, constraining the fit dominate? Args: syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic analyser object, created during limit setting. Can be used during limit setting setting or can load an instance from hdf5 fig_num (int): Fig number. When creating multiple plots in the same script, ensures matplotlib doesn't overwrite them. .. note:: Keyword arguments include: * save_as (*string*): supply file name to save image """ # Set x and y axes x = syst_analyser.get_actual_counts() y = syst_analyser.get_syst_values() # Set chi squared map values data = numpy.average(syst_analyser.get_chi_squareds(), axis=1) data = numpy.transpose(data) # transpose it so that axes are correct # Create meshgrid X, Y = numpy.meshgrid(x, y) # Define an array penalty values penalty_values = syst_analyser._penalty_values[1, 0:len(y)] penalty_array = numpy.zeros(data.shape) # zeroed array the same size as data for y, penalty_value in enumerate(penalty_values): for x in range(len(penalty_array[y])): penalty_array[y][x] = penalty_value # Define the push pull array penalty term - chi_squared # --> push_pull > 0 when penalty_value > chi_squared # --> push_pull < 1 when penalty_value < chi_squared push_pull = (2.*penalty_array) - data # Set sensible levels, pick the desired colormap and define normalization color_map = plt.get_cmap('coolwarm') if push_pull.min() < 0.: negatives = numpy.linspace(push_pull.min(), 0., num=50, endpoint=False) else: negatives = numpy.zeros((50)) if push_pull.max() > 0.: positives = numpy.linspace(0., push_pull.max(), num=51) else: positives = numpy.zeros((51)) # Add the pull part to the push part full_scale = numpy.append(negatives, positives) locator = FixedLocator(full_scale) levels = locator.tick_values(push_pull.min(), push_pull.max()) norm = BoundaryNorm(levels, ncolors=color_map.N) fig = plt.figure(fig, figsize=(12, 10)) # Fig. 4 fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT) ax = fig.add_subplot(1, 1, 1) # Set labels ax.set_xlabel("Signal counts", **BOLD_FONT) ax.set_ylabel("Value of systematic", **BOLD_FONT) # Plot color map color_map = ax.pcolormesh(X, Y, push_pull, cmap=color_map, norm=norm) color_bar = fig.colorbar(color_map) color_bar.set_label("$s-\chi^{2}_{\lambda,p}$", size=MAIN_FONT.get("size")) color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size")) # tick label size # Set axes limits ax.set_xlim([X.min(), X.max()]) ax.set_ylim([Y.min(), Y.max()]) # Set axes tick label size for label in (ax.get_xticklabels() + ax.get_yticklabels()): label.set_fontsize(MAIN_FONT.get("size")) ax.legend(loc="upper left") if kwargs.get("save_as") is not None: fig.savefig(kwargs.get("save_as") + "_push_pull.png", dpi=300) return fig
def turn_on(syst_analyser, signal_config, fig=1, **kwargs): """ Plot deviation from chi-squared with no floated systematics. When does the effect of floating the systematic "turn on"? Args: syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic analyser object, created during limit setting. Can be used during limit setting setting or can load an instance from hdf5 signal_config (:class:`echidna.limit.limit_config.LimitConfig`): Signal config class, where chi squareds have been stored. fig_num (int): Fig number. When creating multiple plots in the same script, ensures matplotlib doesn't overwrite them. .. note:: Keyword arguments include: * save_as (*string*): supply file name to save image """ # Set x and y axes x = syst_analyser.get_actual_counts() y = syst_analyser.get_syst_values() # Set chi squared map values data = numpy.average(syst_analyser.get_chi_squareds(), axis=1) data = numpy.transpose(data) # transpose it so that axes are correct # Create meshgrid X, Y = numpy.meshgrid(x, y) # Define an array of \chi_0 values - chi squared without floating systematics chi_squareds = signal_config.get_chi_squareds()[0] data_np = numpy.zeros(data.shape) # zeroed array the same size as data for y in range(len(data_np)): for x, chi_squared in enumerate(chi_squareds): data_np[y][x] = chi_squared #if numpy.any((numpy.average(data_np, axis=0) != chi_squareds)): # raise AssertionError("Incorrect chi squareds (no floating) array.") # Make an array of the offsets offsets = data - data_np # Set sensible levels, pick the desired colormap and define normalization color_map = plt.get_cmap('coolwarm') positives = numpy.linspace(numpy.log10(offsets.max())*-1., numpy.log10(offsets.max()), num=50) # linear array in log space if offsets.min() < 0.: negatives = numpy.linspace(offsets.min(), 0.0, num=51) else: negatives = numpy.zeros((51)) # Add the positive part to the negative part full_scale = numpy.append(negatives, numpy.power(10, positives)) locator = FixedLocator(full_scale) levels = locator.tick_values(offsets.min(), offsets.max()) norm = BoundaryNorm(levels, ncolors=color_map.N) fig = plt.figure(fig, figsize=(12, 10)) # Fig. 4 fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT) ax = fig.add_subplot(1, 1, 1) # Set labels ax.set_xlabel("Signal counts", **BOLD_FONT) ax.set_ylabel("Value of systematic", **BOLD_FONT) # Plot color map color_map = ax.pcolormesh(X, Y, offsets, cmap=color_map, norm=norm) color_bar = fig.colorbar(color_map) color_bar.set_label("$\chi^2 - \chi_0^2$", size=MAIN_FONT.get("size")) color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size")) # tick label size # Set axes limits ax.set_xlim([X.min(), X.max()]) ax.set_ylim([Y.min(), Y.max()]) # Set axes tick label size for label in (ax.get_xticklabels() + ax.get_yticklabels()): label.set_fontsize(MAIN_FONT.get("size")) ax.legend(loc="upper left") if kwargs.get("save_as") is not None: fig.savefig(kwargs.get("save_as") + "_turn_on.png", dpi=300) return fig
def push_pull(syst_analyser, fig=1, **kwargs): """ Plot penalty value - poisson likelihood chi squared. When does minimising chi squared, which wants to "pull" the away from the data/prior value dominate and when does the penalty term, which wants to "pull" towards the data/prior, constraining the fit dominate? Args: syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic analyser object, created during limit setting. Can be used during limit setting setting or can load an instance from hdf5 fig_num (int): Fig number. When creating multiple plots in the same script, ensures matplotlib doesn't overwrite them. .. note:: Keyword arguments include: * save_as (*string*): supply file name to save image """ # Set x and y axes x = syst_analyser.get_actual_counts() y = syst_analyser.get_syst_values() # Set chi squared map values data = numpy.average(syst_analyser.get_chi_squareds(), axis=1) data = numpy.transpose(data) # transpose it so that axes are correct # Create meshgrid X, Y = numpy.meshgrid(x, y) # Define an array penalty values penalty_values = syst_analyser._penalty_values[1, 0:len(y)] penalty_array = numpy.zeros( data.shape) # zeroed array the same size as data for y, penalty_value in enumerate(penalty_values): for x in range(len(penalty_array[y])): penalty_array[y][x] = penalty_value # Define the push pull array penalty term - chi_squared # --> push_pull > 0 when penalty_value > chi_squared # --> push_pull < 1 when penalty_value < chi_squared push_pull = (2. * penalty_array) - data # Set sensible levels, pick the desired colormap and define normalization color_map = plt.get_cmap('coolwarm') if push_pull.min() < 0.: negatives = numpy.linspace(push_pull.min(), 0., num=50, endpoint=False) else: negatives = numpy.zeros((50)) if push_pull.max() > 0.: positives = numpy.linspace(0., push_pull.max(), num=51) else: positives = numpy.zeros((51)) # Add the pull part to the push part full_scale = numpy.append(negatives, positives) locator = FixedLocator(full_scale) levels = locator.tick_values(push_pull.min(), push_pull.max()) norm = BoundaryNorm(levels, ncolors=color_map.N) fig = plt.figure(fig, figsize=(12, 10)) # Fig. 4 fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT) ax = fig.add_subplot(1, 1, 1) # Set labels ax.set_xlabel("Signal counts", **BOLD_FONT) ax.set_ylabel("Value of systematic", **BOLD_FONT) # Plot color map color_map = ax.pcolormesh(X, Y, push_pull, cmap=color_map, norm=norm) color_bar = fig.colorbar(color_map) color_bar.set_label("$s-\chi^{2}_{\lambda,p}$", size=MAIN_FONT.get("size")) color_bar.ax.tick_params( labelsize=MAIN_FONT.get("size")) # tick label size # Set axes limits ax.set_xlim([X.min(), X.max()]) ax.set_ylim([Y.min(), Y.max()]) # Set axes tick label size for label in (ax.get_xticklabels() + ax.get_yticklabels()): label.set_fontsize(MAIN_FONT.get("size")) ax.legend(loc="upper left") if kwargs.get("save_as") is not None: fig.savefig(kwargs.get("save_as") + "_push_pull.png", dpi=300) return fig
def turn_on(syst_analyser, signal_config, fig=1, **kwargs): """ Plot deviation from chi-squared with no floated systematics. When does the effect of floating the systematic "turn on"? Args: syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic analyser object, created during limit setting. Can be used during limit setting setting or can load an instance from hdf5 signal_config (:class:`echidna.limit.limit_config.LimitConfig`): Signal config class, where chi squareds have been stored. fig_num (int): Fig number. When creating multiple plots in the same script, ensures matplotlib doesn't overwrite them. .. note:: Keyword arguments include: * save_as (*string*): supply file name to save image """ # Set x and y axes x = syst_analyser.get_actual_counts() y = syst_analyser.get_syst_values() # Set chi squared map values data = numpy.average(syst_analyser.get_chi_squareds(), axis=1) data = numpy.transpose(data) # transpose it so that axes are correct # Create meshgrid X, Y = numpy.meshgrid(x, y) # Define an array of \chi_0 values - chi squared without floating systematics chi_squareds = signal_config.get_chi_squareds()[0] data_np = numpy.zeros(data.shape) # zeroed array the same size as data for y in range(len(data_np)): for x, chi_squared in enumerate(chi_squareds): data_np[y][x] = chi_squared #if numpy.any((numpy.average(data_np, axis=0) != chi_squareds)): # raise AssertionError("Incorrect chi squareds (no floating) array.") # Make an array of the offsets offsets = data - data_np # Set sensible levels, pick the desired colormap and define normalization color_map = plt.get_cmap('coolwarm') positives = numpy.linspace(numpy.log10(offsets.max()) * -1., numpy.log10(offsets.max()), num=50) # linear array in log space if offsets.min() < 0.: negatives = numpy.linspace(offsets.min(), 0.0, num=51) else: negatives = numpy.zeros((51)) # Add the positive part to the negative part full_scale = numpy.append(negatives, numpy.power(10, positives)) locator = FixedLocator(full_scale) levels = locator.tick_values(offsets.min(), offsets.max()) norm = BoundaryNorm(levels, ncolors=color_map.N) fig = plt.figure(fig, figsize=(12, 10)) # Fig. 4 fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT) ax = fig.add_subplot(1, 1, 1) # Set labels ax.set_xlabel("Signal counts", **BOLD_FONT) ax.set_ylabel("Value of systematic", **BOLD_FONT) # Plot color map color_map = ax.pcolormesh(X, Y, offsets, cmap=color_map, norm=norm) color_bar = fig.colorbar(color_map) color_bar.set_label("$\chi^2 - \chi_0^2$", size=MAIN_FONT.get("size")) color_bar.ax.tick_params( labelsize=MAIN_FONT.get("size")) # tick label size # Set axes limits ax.set_xlim([X.min(), X.max()]) ax.set_ylim([Y.min(), Y.max()]) # Set axes tick label size for label in (ax.get_xticklabels() + ax.get_yticklabels()): label.set_fontsize(MAIN_FONT.get("size")) ax.legend(loc="upper left") if kwargs.get("save_as") is not None: fig.savefig(kwargs.get("save_as") + "_turn_on.png", dpi=300) return fig
def chi_squared_map(syst_analyser, fig_num=1, **kwargs): """ Plot chi squared surface for systematic vs. signal counts Args: syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic analyser object, created during limit setting. Can be used during limit setting setting or can load an instance from hdf5 fig_num (int): Fig number. When creating multiple plots in the same script, ensures matplotlib doesn't overwrite them. .. note:: Keyword arguments include: * contours (*bool*): if True produces a contour plot of chi squared surface. Default (*False*). * preferred_values (*bool*): if False "preferred values" curve is not overlayed on colour map. Default (*True*) * minima (*bool*): if False "minima" are not overlayed on colour map. Default (*True*) * save_as (*string*): supply file name to save image Default is to produce a colour map, with "preferred values" curve and "minima" overlayed. """ # Set kwargs defaults if kwargs.get("preferred_values") is None: kwargs["preferred_values"] = True if kwargs.get("minima") is None: kwargs["minima"] = True # Set x and y axes x = syst_analyser.get_actual_counts() y = syst_analyser.get_syst_values() # Set chi squared map values data = numpy.average(syst_analyser.get_chi_squareds(), axis=1) data = numpy.transpose(data) # transpose it so that axes are correct # Set preferred value values y_2 = numpy.average(syst_analyser.get_preferred_values(), axis=1) # Set minima values x_3 = syst_analyser.get_minima()[0] y_3 = syst_analyser.get_minima()[1] # Create meshgrid X, Y = numpy.meshgrid(x, y) # Set sensible levels, pick the desired colormap and define normalization color_map = plt.get_cmap('hot_r') linear = numpy.linspace(numpy.sqrt(data.min()), numpy.sqrt(data.max()), num=100) locator = FixedLocator(linear**2) levels = locator.tick_values(data.min(), data.max()) norm = BoundaryNorm(levels, ncolors=color_map.N) if kwargs.get("contours"): fig = plt.figure(fig_num, figsize=(16, 10)) # Fig. 2 fig.text(0.1, 0.9, syst_analyser._name, **BOLD_FONT) ax = Axes3D(fig) ax.view_init(elev=17.0, azim=-136.0) # set intial viewing position # Plot surface surf = ax.plot_surface(X, Y, data, rstride=1, cstride=1, cmap=color_map, norm=norm, linewidth=0, antialiased=False) ax.zaxis.set_minor_locator(locator) ax.ticklabel_format(style="scientific", scilimits=(3, 4)) # Set axis labels ax.set_xlabel("\nSignal counts", **BOLD_FONT) ax.set_ylabel("\nValue of systematic", **BOLD_FONT) for label in (ax.get_xticklabels() + ax.get_yticklabels() + ax.get_zticklabels()): label.set_fontsize(MAIN_FONT.get("size")) # tick label size ax.dist = 11 # Ensures tick labels are not cut off ax.margins(0.05, 0.05, 0.05) # Adjusts tick margins # Draw colorbar color_bar = fig.colorbar(surf, ax=ax, orientation="vertical", fraction=0.2, shrink=0.5, aspect=10) # kwargs here control axes that the colorbar is drawn in color_bar.set_label(r"$\chi^2$", size=MAIN_FONT.get("size")) color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size")) plt.show() if kwargs.get("save_as") is not None: fig.savefig(kwargs.get("save_as") + "_contour.png", dpi=300) else: fig = plt.figure(fig_num, figsize=(12, 10)) # Fig. 2 fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT) ax = fig.add_subplot(1, 1, 1) # Set labels ax.set_xlabel("Signal counts", **BOLD_FONT) ax.set_ylabel("Value of systematic", **BOLD_FONT) # Plot color map color_map = ax.pcolormesh(X, Y, data, cmap=color_map, norm=norm) color_bar = fig.colorbar(color_map) color_bar.set_label("$\chi^2$", size=MAIN_FONT.get("size")) color_bar.ax.tick_params( labelsize=MAIN_FONT.get("size")) # tick label size # Set axes limits ax.set_xlim([X.min(), X.max()]) ax.set_ylim([Y.min(), Y.max()]) if kwargs.get("preferred_values"): ax.plot(x, y_2, "bo-", label="Preferred values") if kwargs.get("minima"): ax.plot(x_3, y_3, "ko", label="Minima") # Set axes tick label size for label in (ax.get_xticklabels() + ax.get_yticklabels()): label.set_fontsize(MAIN_FONT.get("size")) ax.legend(loc="upper left") if kwargs.get("save_as") is not None: fig.savefig(kwargs.get("save_as") + "_color_map.png", dpi=300) return fig