def common_plotting_settings(plot: plt.Axes, plot_def: PlotDef, xaxis_title: str, yaxis_title: str) -> Optional[mpl.legend.Legend]: """Common settings for plots. Args: plot: a pyplot plot object plot_def: a `PlotDef` that defines properties of the plot xaxis_title: label for x-axis yaxis_title: label for y-axis """ plot.set_xlabel(xaxis_title) plot.set_ylabel(yaxis_title) if plot_def.title: plot.set_title(plot_def.title) plot.grid(True) # get handles and labels for the legend handles, labels = plot.get_legend_handles_labels() # remove the errorbars from the legend if they are there handles = [(h[0] if isinstance(h, tuple) else h) for h in handles] if plot_def.legend_pos == "inside": return plot.legend(handles, labels) if plot_def.legend_pos == "outside": return plot.legend(handles, labels, loc="upper left", bbox_to_anchor=(1, plot_def.legend_yanchor)) return None
def plot_2E(ax: plt.Axes, bin_edges_human: np.ndarray = np.array( [-1000, -90, -60, -30, -4, -2, 0, 2, 4, 30, 60, 1000]), bin_edges_model: np.ndarray = np.linspace(-160, 100, 40)): Δ, p_human, p_model = [], [], [] for pid in DataExp1.pids: data = DataExp1(pid) model = data.build_model(models.BayesianIdealObserver) df = model.predict(model.fit()) δ = np.stack([ np.log(df[s]) - np.log(np.sum(df.loc[:, df.columns != s], axis=1)) for s in data.structures ]) Δ += list(δ.T.flatten()) p_model += list( data.cross_validate(models.ChoiceModel4Param).to_numpy().flatten()) p_human += list( np.array([data.df['choice'] == s for s in Exp1.structures]).T.flatten()) df = pd.DataFrame({'Δ': Δ, 'p_human': p_human, 'p_model': p_model}) x_human, y_human, yerr_human, x_model, y_model, yerr_model = [], [], [], [], [], [] df['bin'] = pd.cut(df['Δ'], bin_edges_human, labels=False) for i in range(len(bin_edges_human) - 1): _df = df[df['bin'] == i] x_human.append(_df['Δ'].mean()) y_human.append(_df['p_human'].mean()) yerr_human.append(_df['p_human'].sem()) df['bin'] = pd.cut(df['Δ'], bin_edges_model, labels=False) for i in range(len(bin_edges_model) - 1): _df = df[df['bin'] == i] x_model.append(_df['Δ'].mean()) y_model.append(_df['p_model'].mean()) yerr_model.append(_df['p_model'].sem()) ax.errorbar(x_human, y_human, yerr_human, label='Human ± sem', color=colors['decision_human'], fmt='.', capsize=2, ms=2, capthick=0.5, zorder=1) ax.plot(x_model, y_model, color=colors['decision_model'], label='Model', ms=1, zorder=0) ax.set_xlabel(r'logit( $P_\mathregular{ideal}$($S\,|\,\bf{X}$) )') ax.set_ylabel(r'$P($choice=$S\,|\,\bf{X}$)') ax.set_ylim(0, 1) handles, labels = ax.get_legend_handles_labels() ax.legend(handles[::-1], labels[::-1], loc='upper left', handler_map={ErrorbarContainer: HandlerErrorbar(yerr_size=0.25)}) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) plt.tight_layout()
def plot_stacked_activities(dict_states: Dict[str, np.ndarray], time_axis: np.ndarray = None, ax: plt.Axes = None, step: str = 'post', **kwargs) -> None: """Plot the stacked household states during time. Attributes: dict_states: A dictionary where keys are the names of the activities or states and the values are ndarray of size = n_steps and values are the number of residents in this states/activity time_axis: The datetime axis to plot ax: an ax on which to plot the activities kwargs: any other keywork argument from `ax.stackplot <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.stackplot.html#matplotlib.pyplot.stackplot>`_ """ # Creates a time axis if time_axis is None: # Creates an ax of the length of states time_axis = np.arange(len(dict_states[list(dict_states.keys())[0]])) ax.stackplot(time_axis, dict_states.values(), labels=dict_states.keys(), step='post', **kwargs) # Reverse the legend, in the same way as they are stacked handles, lab = ax.get_legend_handles_labels() ax.legend(handles[::-1], lab[::-1])
def add_legend(self, ax: plt.Axes): """ ax: The current axis. Use `not-OAA-derived` for now so the legend is plotted in the bottom right of the whole figure.""" handles, labels = ax.get_legend_handles_labels() logger.debug(labels) by_label = dict(zip(labels, handles)) logger.debug(by_label) plt.legend(by_label.values(), by_label.keys(), loc='lower right', framealpha=1, facecolor=self.legend_facecolor, title=self.label_legend_title)
def _make_distance_over_time_plot_legend( plotter: CustomCILinePlotter, fig: plt.Figure, ax: plt.Axes, hue_col: str, style_col: str, ) -> None: """Add legend to distance over time plot.""" plotter.add_legend_data(ax) handles, labels = ax.get_legend_handles_labels() if hue_col == style_col: # Only one key, so legend can fit into one row. ncol = len(handles) else: # Different keys. Legend needs two rows. # Make number of columns large enough to fit hue and style each in one row. n_hue = len(plotter.lower[hue_col].dtype.categories) n_style = len(plotter.lower[style_col].dtype.categories) ncol = max(n_hue, n_style) # Delete subtitles del handles[0], labels[0] # delete hue subtitle del handles[n_hue], labels[n_hue] # delete style subtitle # Pad the smaller row, if they're different length, so its entries are centered if n_hue > n_style: larger_handles, larger_labels = handles[:n_hue], labels[:n_hue] smaller_handles, smaller_labels = handles[n_hue:], labels[n_hue:] else: larger_handles, larger_labels = handles[n_hue:], labels[n_hue:] smaller_handles, smaller_labels = handles[:n_hue], labels[:n_hue] delta = len(larger_handles) - len(smaller_handles) pad_start = delta // 2 + (delta % 2) pad_end = delta // 2 empty = mpatches.Patch(color="white") smaller_handles = [empty] * pad_start + smaller_handles + [empty ] * pad_end smaller_labels = [""] * pad_start + smaller_labels + [""] * pad_end # Reassemble. Note that matplotlib fills column by column, so we zip to "transpose". handles = list(itertools.chain(*zip(larger_handles, smaller_handles))) labels = list(itertools.chain(*zip(larger_labels, smaller_labels))) outside_legend( handles=handles, labels=labels, ncol=ncol, fig=fig, ax=ax, )
def plot_3B(ax: plt.Axes): data = pool(DataExp2) data.plot_stacked_bar(ax) n = len(ExpConfig.glo_exp2) y_human, y1, y2 = np.zeros(n), np.zeros(n), np.zeros(n) for pid in DataExp2.pids: data = DataExp2(pid) y_human += data.plot_line_human()[0] m1 = data.load_model( models.ChoiceModel4Param, DataExp1(pid).build_model(models.ChoiceModel4Param).fit()) y1 += data.plot_line_model(m1.predict(m1.fit())) y2 += data.plot_line_model( data.cross_validate(models.ChoiceModel4Param)) y_human, y1, y2 = y_human / len(DataExp2.pids), y1 / len( DataExp2.pids), y2 / len(DataExp2.pids) err = [np.sqrt(p * (1 - p) / len(DataExp2.pids) / 20) for p in y_human] ax.errorbar(DataExp2.x, y_human, err, label='Human $\pm$ sem', color=colors['decision_human'], capsize=5, capthick=1, lw=1, ms=3, fmt='o', zorder=3) ax.plot(DataExp2.x, y1, 'o--', label='Transfer model', color=colors['decision_transfer'], lw=1, ms=3, zorder=2) ax.plot(DataExp2.x, y2, 'o-', label='Fitted model', color=colors['decision_model'], lw=1, ms=3, zorder=2) handles, labels = ax.get_legend_handles_labels() ax.legend(handles[::-1], labels[::-1], loc='upper right', handler_map={ErrorbarContainer: HandlerErrorbar(yerr_size=0.35)}) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) plt.tight_layout()
def _legend_with_triplot_fix(ax: plt.Axes, **kwargs): """Add legend for triplot with fix that avoids duplicate labels. Parameters ---------- ax : plt.Axes Matplotlib axes to apply legend to. **kwargs Extra keyword arguments passed to `ax.legend`. """ handles, labels = ax.get_legend_handles_labels() # reverse to avoid blank line color by_label = dict(zip(reversed(labels), reversed(handles))) ax.legend(by_label.values(), by_label.keys(), **kwargs)
def legend_last_to_first(ax: plt.Axes, **kwargs): """Move the last element of the legend to first. Parameters ---------- ax : matplotlib.axes.Axes Matplotlib axes to create a legend on. kwargs : dict Arguments passed to :py:obj:`matplotlib.axes.Axes.legend`. """ if ax.get_legend() is None: ax.legend() handles, labels = ax.get_legend_handles_labels() handles.insert(0, handles.pop()) labels.insert(0, labels.pop()) ax.legend(handles, labels, **kwargs)
def legend( self, gmeq: Eq, axes: Axes, do_legend: bool, do_half: bool, do_ray_slowness: bool = False, ) -> None: """Construct and place a legend.""" if not do_legend: return handles, labels = axes.get_legend_handles_labels() if gmeq.eta_ >= 1: loc_ = ("center right" if do_half else "upper left" if do_ray_slowness else "lower left") else: loc_ = ("upper right" if do_half else "upper left" if do_ray_slowness else "lower left") axes.legend(handles[::-1], labels[::-1], loc=loc_)
def plot_appliance_consumptions(consumptions: np.ndarray, appliances_dict: AppliancesDict, time_axis: np.ndarray = None, differentiative_factor: str = 'type', labels_list: List[Any] = None, ax: plt.Axes = None, **kwargs) -> None: """Plot the consumption of the appliances. Args: consumptions: A ndarray of size = (n_steps, n_appliances) and values are the consumption of each appliance appliances_dict: The dictionary of appliances from simulator time_axis: The datetime axis to plot differentiative_factor: The key in the appliance_dict that should be used to differentiate the appiances ex: ('type', 'name', 'related_activity') labels_list: Optional list of the labels to be plotted Usefull for choosing an order ax: an ax on which to plot the activities """ # Finds all the attributes or uses the ones given attributes_list = (np.unique(appliances_dict[differentiative_factor]) if labels_list is None else labels_list) # Sum up consumption mapping these appliances consumptions_to_plot = [ np.sum( consumptions[:, appliances_dict[differentiative_factor] == attribute], axis=-1) for attribute in attributes_list ] if time_axis is None: # Creates an ax of the length of states time_axis = np.arange(len(consumptions)) ax.stackplot(time_axis, *consumptions_to_plot, labels=attributes_list, step='post') # Reverse the legend, in the same way as they are stacked handles, lab = ax.get_legend_handles_labels() ax.legend(handles[::-1], lab[::-1])
def plot_stack_states(states: States, labels: StateLabels, ax: plt.Axes = None, **kwargs) -> Any: """Plot the stacked states. Args: states: the states to be stacked in the plot labels: the labels of the different states values in the states array ax: An optional ax object on which to plot the labels **kwargs: any arguments passed to 'plt.stackplot' Returns: fig, ax: a matplotlib figure and ax objects """ # stack plot with an hourhly axis if ax is None: fig = plt.figure(figsize=(16, 9)) ax = fig.add_subplot(1, 1, 1) need_return = True else: need_return = False # stack the states for the stack plot stacked_states = np.apply_along_axis(np.bincount, 0, np.array(states, int), minlength=np.max(states) + 1) ax.stackplot( np.arange(0, states.shape[1]), # Create an x axis stacked_states, labels=labels) handles, lab = ax.get_legend_handles_labels() # Reverse the legend, so they follow the states order ax.legend(handles[::-1], lab[::-1], loc='right') if need_return: return fig, ax
def plot(self, ax: plt.Axes): utils.format_axes(ax) with_both_bounds = np.logical_and(np.isfinite(self.sed_lower), np.isfinite(self.sed_upper)) E_mean = np.sqrt(self.E_left * self.E_right) E_err_left = E_mean - self.E_left E_err_right = self.E_right - E_mean # check if the same data has already been plotted and listed on legend label = str(self) _, legend_texts = ax.get_legend_handles_labels() for legend_text in legend_texts: if label == legend_text: return fmt = self.marker ax.errorbar( E_mean[with_both_bounds], self.sed_mean[with_both_bounds], xerr=[E_err_left[with_both_bounds], E_err_right[with_both_bounds]], yerr=( (self.sed_mean - self.sed_lower)[with_both_bounds], (self.sed_upper - self.sed_mean)[with_both_bounds], ), fmt=fmt, color=self.color, label=label, ) with_upper_bound = np.logical_not(with_both_bounds) ax.errorbar( E_mean[with_upper_bound], self.sed_upper[with_upper_bound], xerr=[E_err_left[with_upper_bound], E_err_right[with_upper_bound]], yerr=self.sed_upper[with_upper_bound] / 2, uplims=True, fmt=fmt, color=self.color, )
def _unfolded_fit( eigs: ndarray, unfolded: ndarray, title: str = "Unfolding Fit", mode: PlotMode = "block", outfile: Path = None, fig: Figure = None, axes: Axes = None, ) -> PlotResult: """Plot the unfolding fit against the step function. Parameters ---------- unfolded: ndarray The unfolded eigenvalues to plot. title: string The plot title string mode: "block" (default) | "noblock" | "save" | "return" If "block", call plot.plot() and display plot in a blocking fashion. If "noblock", attempt to generate plot in nonblocking fashion. If "save", save plot to pathlib Path specified in `outfile` argument If "return", return (fig, axes), the matplotlib figure and axes object for modification. outfile: Path If mode="save", save generated plot to Path specified in `outfile` argument. Intermediate directories will be created if needed. kind: "scatter" (default) | "line" Whether to use a scatterplot or line plot. fig: Figure If provided with `axes`, configure plotting with the provided `fig` object instead of creating a new figure. Useful for creating subplots. axes: Axes If provided with `fig`, plot to the provided `axes` object. Useful for creating subplots. Returns ------- (fig, axes): (Figure, Axes) The handles to the matplotlib objects, only if `mode` is "return". """ _configure_sbn_style() # cmap = plt.cm.cividis cmap = plt.cm.gist_heat fig, axes = _setup_plotting(fig, axes) N = len(unfolded) step_vals = np.arange(0, N) df_line = pd.DataFrame({"Step Function": step_vals, "Unfolded λ": unfolded}) # df_scatter = pd.DataFrame({"Step Function": steps, "Outlier": np.abs(unfolded)}) sbn.lineplot(data=df_line, ax=axes) mean = np.mean(np.abs(unfolded)) # 0 is left of cmap, color_max is right of cmap color = (5 * np.abs(unfolded) / mean) ** 2 size = (10 * (np.abs(unfolded) - mean) / mean) ** 2 inlier = np.abs(unfolded - step_vals) < 5 color[inlier] = color.min() min_size = 0.5 size[inlier] = min_size size[size < 1] = min_size axes.scatter( x=step_vals, y=unfolded, s=size, # size of points c=color, # color of points cmap=cmap, # should be color blind safe marker="o", # color="red", edgecolors="white", linewidths=0.1, label="Outlier", alpha=0.4, ) # plt.setp(ax_scatter, label="Outlier") axes.set(title=title, xlabel="Eigenvalue Index", ylabel="Unfolded Value") handles, labels = axes.get_legend_handles_labels() line_handles, line_labels = handles[:-1], labels[:-1] # axes.legend(line_handles, line_labels) cmap_handles = [Rectangle((0, 0), 1, 1)] # seems you only need a handler map for legend element that need a custom handler handler_map = dict(zip(cmap_handles, [HandlerColormap(cmap, num_stripes=16)])) labels = [line_labels[0], line_labels[1], "Inlier / Outlier"] axes.legend( handles=line_handles + cmap_handles, labels=labels, handler_map=handler_map, fontsize=12, ).set_visible(True) return _handle_plot_mode(mode, fig, axes, outfile)
def plot_3C(ax: plt.Axes, bin_edges_human: np.ndarray = np.array( [-1000, -4.5, -3, -1.5, 0, 1.5, 3, 4.5, 1000]), bin_edges_model: np.ndarray = np.array( [-1000, -4.5, -3, -1.5, 0, 1.5, 3, 4.5, 1000])): Δ, p_human, p1, p2 = [], [], [], [] for pid in DataExp2.pids: data = DataExp2(pid) model = data.build_model(models.BayesianIdealObserver) df = model.predict(model.fit()) Δ += list(np.log(df['C']) - np.log(df['H'])) model = data.load_model( models.ChoiceModel4Param, DataExp1(pid).build_model(models.ChoiceModel4Param).fit()) p1 += list(model.predict(model.fit())['C']) p2 += list(data.cross_validate(models.ChoiceModel4Param)['C']) p_human += list((data.df['choice'] == 'C') * 1.0) df = pd.DataFrame({'Δ': Δ, 'p_human': p_human, 'p1': p1, 'p2': p2}) x_human, y_human, yerr_human, x_model, y1, yerr1, y2, yerr2 = [], [], [], [], [], [], [], [] df['bin'] = pd.cut(df['Δ'], bin_edges_human, labels=False) for i in range(len(bin_edges_human) - 1): _df = df[df['bin'] == i] x_human.append(_df['Δ'].mean()) y_human.append(_df['p_human'].mean()) yerr_human.append(_df['p_human'].sem()) df['bin'] = pd.cut(df['Δ'], bin_edges_model, labels=False) for i in range(len(bin_edges_model) - 1): _df = df[df['bin'] == i] x_model.append(_df['Δ'].mean()) y1.append(_df['p1'].mean()) yerr1.append(_df['p1'].sem()) y2.append(_df['p2'].mean()) yerr2.append(_df['p2'].sem()) ax.errorbar(x_human, y_human, yerr_human, label='Human $\pm$ sem', color=colors['decision_human'], fmt='.', capsize=2, ms=2, capthick=0.5, zorder=1) ax.plot(x_model, y1, '--', color=colors['decision_transfer'], label='Transfer model', ms=1, zorder=0) ax.plot(x_model, y2, '-', color=colors['decision_model'], label='Fitted model', ms=1, zorder=0) ax.set_xlabel(r'logit( $P_\mathregular{ideal}(S=C\,|\,\bf{X}$) )') ax.set_ylabel(r'$P$(choice=$C\,|\,\bf{X}$)') ax.set_ylim(0, 1) handles, labels = ax.get_legend_handles_labels() ax.legend(handles[::-1], labels[::-1], loc='lower right', handler_map={ErrorbarContainer: HandlerErrorbar(yerr_size=0.25)}) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) plt.tight_layout()
def decorate_axes( ax: plt.Axes, catalogue: VelociraptorCatalogue, comment: Union[str, None] = None, legend_loc: str = "upper left", redshift_loc: str = "lower right", comment_loc: str = "lower left", ) -> None: """ Decorates the axes with information about the redshift and scale-factor. """ markerfirst = "right" not in legend_loc if len(ax.get_legend_handles_labels()[0]): # Only create the legend if we have handles available to plot, # otherwise we get a warning from matplotlib printed to the # console. legend = ax.legend(loc=legend_loc, markerfirst=markerfirst) fontsize = legend.get_texts()[0].get_fontsize() else: fontsize = None label_switch = { redshift_loc: f"$z={catalogue.z:2.3f}$\n$a={catalogue.a:2.3f}$", comment_loc: comment, } distance_from_edge = 0.025 for loc, label in label_switch.items(): if label is not None: # First need to parse the 'loc' string try: va, ha = loc.split(" ") except ValueError: if loc == "right": ha = "right" va = "center" elif loc == "center": ha = "center" va = "center" if va == "lower": y = distance_from_edge va = "bottom" elif va == "upper": y = 1.0 - distance_from_edge va = "top" elif va == "center": y = 0.5 else: raise AttributeError( f"Unknown location string {loc}. Choose e.g. lower right") if ha == "left": x = distance_from_edge elif ha == "right": x = 1.0 - distance_from_edge elif ha == "center": x = 0.5 ax.text( x, y, label, ha=ha, va=va, transform=ax.transAxes, multialignment=ha, fontsize=fontsize, ) return