def plot_3E(ax: plt.Axes): sns.set_palette(sns.color_palette(['white'])) L = [] for pid in DataExp2.pids: data = DataExp2(pid) m1 = data.load_model( models.ChoiceModel4Param, DataExp1(pid).build_model(models.ChoiceModel4Param).fit()) df = data.cross_validate(models.ChoiceModel4Param) df['choice'] = data.df['choice'] L.append( m1.fit().log_likelihood - np.log(df.apply(lambda row: row[row['choice']], axis=1)).sum()) sns.boxplot(data=L, fliersize=0, ax=ax, linewidth=0.5, width=0.2) sns.scatterplot(x=np.linspace(-0.09, 0.09, 12), y=L, fc='white', ec=sns_edge_color('white'), ax=ax, s=5, linewidth=0.5, zorder=11, clip_on=False, legend=False) # sns.stripplot(data=L, jitter=True, ax=ax, size=2.5, linewidth=0.5, zorder=10, clip_on=False) ax.axhline(0, 0, 1, color='k', zorder=1) ax.set_xlim(-0.2, 0.2) ax.set_ylabel(r'$\mathcal{L}$(transfer) - $\mathcal{L}$(fitted)') ax.set_xticks([]) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) ax.spines['bottom'].set_visible(False) plt.tight_layout()
def draw_LX_bounds(self, ax: plt.Axes, redshifts_on: bool = True): self.hconv = 0.70 / self.h ax.axhspan(self.bins[0][0] * 1e44 * self.hconv**2, self.bins[-1][1] * 1e44 * self.hconv**2, facecolor='lime', linewidth=0, alpha=0.2) ax.axhline(self.bins[0][0] * 1e44 * self.hconv**2, color='lime', linewidth=1, alpha=0.1) for i, (luminosity_min, luminosity_max, redshift_min, redshift_max) in enumerate(self.bins): ax.axhline(luminosity_max * 1e44 * self.hconv**2, color='lime', linewidth=1, alpha=0.1) # Print redshift bounds once every 2 bins to avoid clutter. if i % 2 == 0 and redshifts_on: ax.text(10**ax.get_xlim()[0], 10**(0.5 * np.log10(luminosity_min * luminosity_max)) * 1e44 * self.hconv**2, f"$z$ = {redshift_min:.3f} - {redshift_max:.3f}", horizontalalignment='left', verticalalignment='center', color='k', alpha=0.3)
def ma_plot(filename: str, *, count_col: str = DESEQ2_BASE_MEAN, padj_col: str = DESEQ2_PADJ, lfc_col: str = DESEQ2_LOG2_CHANGE, color_col: str = None, color: str = palette.gray(shade=30), accent_color: str = palette.red(), threshold: float = 0.05, use_threshold: bool = True, ax: plt.Axes = None, decorations: bool = True, **kwargs): data = pd.read_csv(filename) _validate(data, count_col, padj_col, lfc_col) if color_col is None: color_col = f"clr{np.random.randint(10000, 90000)}" data[color_col] = color if use_threshold: data.loc[data[padj_col] < threshold, color_col] = accent_color if ax is None: _, ax = plt.subplots() ax.scatter(data[count_col], data[lfc_col], color=data[color_col], **kwargs) if decorations: ax.axhline(0, ls="--", color=palette.black()) ax.set_ylabel("Log$_2$ Fold Change") ax.set_xlabel("Average Normalized Counts")
def _plot_image(self, axis: plt.Axes=None, title: str=''): plt.ioff() if axis is None: fig, axis = plt.subplots() axis.imshow(self.array, cmap=get_dicom_cmap()) axis.axhline(self.positions['vertical']*self.array.shape[0], color='r') # y axis.axvline(self.positions['horizontal']*self.array.shape[1], color='r') # x _remove_ticklabels(axis) axis.set_title(title)
def _plot_image(self, axis: plt.Axes=None, title: str=''): plt.ioff() if axis is None: fig, axis = plt.subplots() axis.imshow(self.image.array, cmap=get_dicom_cmap()) axis.axhline(self.positions['horizontal']*self.image.array.shape[0], color='r') # y axis.axvline(self.positions['vertical']*self.image.array.shape[1], color='r') # x _remove_ticklabels(axis) axis.set_title(title)
def mirror(spec_top: MsmsSpectrum, spec_bottom: MsmsSpectrum, color_ions: bool = True, annotate_ions: bool = True, ax: plt.Axes = None): """ Mirror plot two MS/MS spectra. Parameters ---------- spec_top : MsmsSpectrum The spectrum to be plotted on the top. spec_bottom : MsmsSpectrum The spectrum to be plotted on the bottom. color_ions : bool, optional Flag indicating whether or not to color annotated fragment ions. The default is True. annotate_ions : bool, optional Flag indicating whether or not to annotate fragment ions. The default is True. ax : plt.Axes, optional Axes instance on which to plot the spectrum. If None the current Axes instance is used. Returns ------- plt.Axes The matplotlib Axes instance on which the spectra are plotted. """ if ax is None: ax = plt.gca() # Top spectrum. spectrum(spec_top, color_ions, annotate_ions, False, ax) # Mirrored bottom spectrum. spectrum(spec_bottom, color_ions, annotate_ions, True, ax) ax.axhline(0, color='#9E9E9E', zorder=10) # Update axes so that both spectra fit. min_mz = max([ 0, math.floor(spec_top.mz[0] / 100 - 1) * 100, math.floor(spec_bottom.mz[0] / 100 - 1) * 100 ]) max_mz = max([ math.ceil(spec_top.mz[-1] / 100 + 1) * 100, math.ceil(spec_bottom.mz[-1] / 100 + 1) * 100 ]) ax.set_xlim(min_mz, max_mz) ax.yaxis.set_major_formatter( mticker.FuncFormatter(lambda x, pos: f'{abs(x):.0%}')) ax.set_ylim(-1.15 if annotate_ions else -1.05, 1.15 if annotate_ions else 1.05) return ax
def entropy_vs_coupling( ax: plt.Axes, int_coupling: Union[list, np.ndarray], int_entropy: Union[list, np.ndarray], int_peaks: Optional[Union[list, np.ndarray]] = None, fit_coupling: Union[list, np.ndarray] = None, fit_entropy: Union[list, np.ndarray] = None, peak_diff_coupling: Optional[Union[list, np.ndarray]] = None, peak_diff: Optional[Union[list, np.ndarray]] = None, ) -> plt.Axes: """ Plots fit and integrated entropy vs coupling gate Args: ax (): int_coupling (): int_entropy (): int_peaks (): Optional pass in to also plot the peak of integrated entropy as dotted line fit_coupling (): fit_entropy (): peak_diff_coupling: x axis for peak - final peak_diff: peak entropy - final entropy Returns: """ ax.set_title('Entropy vs Coupling Gate') ax.set_xlabel('Coupling Gate (mV)') ax.set_ylabel('Entropy (kB)') line = ax.plot(int_coupling, int_entropy, marker='.', label='From integration')[0] if int_peaks is not None: color = line.get_color() ax.plot(int_coupling, int_peaks, marker='.', linestyle='--', color=color, label='Peak from integration') ax.axhline(y=np.log(3), color='black', linestyle=':') if peak_diff_coupling is not None and peak_diff is not None: ax.plot(peak_diff_coupling, peak_diff, marker='x', linestyle=':', color='C3', label='Peak - Final') ax.plot(fit_coupling, fit_entropy, marker='+', label='From dN/dT fit') ax.axhline(y=np.log(2), color='black', linestyle=':') ax.set_ylim(0, 1.5) ax.legend() return ax
def measureTau(abf: pyabf.ABF, sweep: int, epoch: int = 3, percentile: float = .05, ax: plt.Axes = None): abf.setSweep(sweep) # use epoch table to determine puff times puffTimeStart = abf.sweepEpochs.p1s[epoch] / abf.sampleRate puffTimeEnd = abf.sweepEpochs.p2s[epoch] / abf.sampleRate # calculate baseline level baselineStart = puffTimeStart - .1 baselineEnd = puffTimeStart baselineMean = getMean(abf, baselineStart, baselineEnd) # find antipeak antipeakIndex = getAntiPeakIndex(abf.sweepY, abf.sampleRate, puffTimeStart, puffTimeStart + .5) antipeakLevel = abf.sweepY[antipeakIndex] # find portion of curve to fit curveIndex1, curveIndex2 = getCurveIndexes(abf.sweepY, antipeakIndex, baselineMean, percentile) curveYs = -abf.sweepY[curveIndex1:curveIndex2] curveXs = np.arange(len(curveYs)) / abf.sampleRate try: p0 = (500, 15, 0) # start with values near those we expect params, cv = scipy.optimize.curve_fit(monoExp, curveXs, curveYs, p0) except: print(f"FIT FAILED (sweep {sweep})") return None m, t, b = params curveYsIdeal = monoExp(curveXs, m, t, b) tauMS = 1000 / t if (tauMS < 0): return None if ax: yPad = abs(antipeakLevel - baselineMean) * .1 ax.plot(abf.sweepX, abf.sweepY, alpha=.5) ax.grid(alpha=.5, ls='--') ax.axhline(baselineMean, ls='--', color='k') ax.plot(abf.sweepX[curveIndex1:curveIndex2], -curveYsIdeal, color='k') ax.set(title=f"first sweep tau = {tauMS:.02f} ms") ax.axis([ baselineStart - .1, baselineStart + 1, antipeakLevel - yPad, baselineMean + yPad ]) ax.axvspan(puffTimeEnd, puffTimeEnd + .5, color='g', alpha=.1) ax.axvspan(puffTimeEnd + .5, puffTimeEnd + .6, color='m', alpha=.1) return tauMS
def _plot_2d(ax: plt.Axes, xlabel: str, ylabel: str, x: np.ndarray, y: np.ndarray, xlim, ylim, plot_kw): ax.set_xlabel(xlabel) ax.set_ylabel(ylabel) ax.axhline(0, c='k') ax.axvline(0, c='k') if xlim: ax.set_xlim(xlim) if ylim: ax.set_ylim(ylim) ax.plot(x, y, **plot_kw)
def _plot_flatness(self, direction: str, axis: plt.Axes=None): plt.ioff() if axis is None: fig, axis = plt.subplots() data = self.flatness[direction.lower()] axis.set_title(direction.capitalize() + " Flatness") axis.plot(data['profile'].values) _remove_ticklabels(axis) axis.axhline(data['profile max'], color='r') axis.axhline(data['profile min'], color='r') axis.axvline(data['profile left'], color='g', linestyle='-.') axis.axvline(data['profile right'], color='g', linestyle='-.')
def _plot_flatness(self, direction: str, axis: plt.Axes=None): plt.ioff() if axis is None: fig, axis = plt.subplots() data = self.flatness[direction.lower()] axis.set_title(direction.capitalize() + " Flatness") axis.plot(data['profile'].values) _remove_ticklabels(axis) axis.axhline(data['profile max'], color='r') axis.axhline(data['profile min'], color='r') axis.axvline(data['profile left'], color='g', linestyle='-.') axis.axvline(data['profile right'], color='g', linestyle='-.')
def integrated_entropy(ax: plt.Axes, xs: List[np.ndarray], datas: List[np.ndarray], labels: list) -> plt.Axes: """Plots integrated entropy vs gate voltage in real mV""" ax.set_xlabel('Sweep gate (mV)') ax.set_ylabel('Entropy (kB)') for x, data, label in zip(xs, datas, labels): ax.plot(x, data, label=label) for v in np.log(2), np.log(3): ax.axhline(v, linestyle=':', color='black') leg = ax.legend() ax.get_legend().set_title('Gamma/T') return ax
def plot_fits(fits: xr.Dataset, offset: float = 0., ax: plt.Axes = None, **kwargs) -> None: """Plot the fitted curves.""" if ax is None: ax = plt.gca() kwargs.setdefault("xlim", [0, fits["x"][-1].item()]) kwargs.setdefault("marker", "o") kwargs.setdefault("fillstyle", "none") kwargs.setdefault("ls", "none") fits["yobs"].plot.line(ax=ax, **kwargs) ax.plot(fits["x"], fits["ycalc"]) diff = fits["y"] - fits["ycalc"] shift = offset + fits["y"].min() - diff.max() diff += shift ax.axhline(shift, ls='--', alpha=0.5, color="black") ax.plot(fits["x"], diff) return
def _plot_image(self, axis: plt.Axes=None, title: str=''): plt.ioff() if axis is None: fig, axis = plt.subplots() axis.imshow(self.image.array, cmap=get_dicom_cmap()) # show vertical/axial profiles left_profile = self.positions['vertical left'] right_profile = self.positions['vertical right'] axis.axvline(left_profile, color='b') axis.axvline(right_profile, color='b') # show horizontal/transverse profiles bottom_profile = self.positions['horizontal bottom'] top_profile = self.positions['horizontal top'] axis.axhline(bottom_profile, color='b') axis.axhline(top_profile, color='b') _remove_ticklabels(axis) axis.set_title(title)
def scatter_peaks_no_peaks( top_eco: pd.DataFrame, top_naked: pd.DataFrame, non_top_eco: pd.DataFrame, non_top_naked: pd.DataFrame, ax: plt.Axes = None, ): if not ax: _, ax = plt.subplots(figsize=(12, 12)) ax.set_xlabel("Chromatin") ax.set_ylabel("Naked") ax.scatter( non_top_eco, non_top_naked, alpha=0.2, label="All Points", ) ax.scatter(top_eco, top_naked, label="Open ATAC") ax.axvline(non_top_eco.mean(), color="C0") ax.axvline(top_eco.mean(), color="C1") ax.axhline(non_top_naked.mean(), color="C0") ax.axhline(top_naked.mean(), color="C1") ax.legend( loc="upper right", frameon=False, shadow=False, ) # We concatenate the two DFs to a single one so that the dropna() call will # "synced" between the two different rows top = pd.DataFrame({"chrom": top_eco, "naked": top_naked}).dropna(axis=0) all_ = pd.DataFrame({ "chrom": non_top_eco, "naked": non_top_naked }).dropna(axis=0) r_top, _ = scipy.stats.pearsonr(top.loc[:, "chrom"], top.loc[:, "naked"]) r_all, _ = scipy.stats.pearsonr(all_.loc[:, "chrom"], all_.loc[:, "naked"]) ax.text(0.01, 0.8, f"R (top) = {r_top} \nR (rest) = {r_all}", transform=ax.transAxes) return ax
def add_sh_order_lines(ax: plt.Axes, order=None, args_dict=None, x_flag=True, y_flag=True): if args_dict is None: args_dict = {} from src.utils.sphere import sh if order is None: order = sh.i2nm(np.floor(ax.get_xlim()[1]))[0] n = np.arange(order) m = n locs = sh.nm2i(n, m) + 0.5 for loc in locs: if x_flag: ax.axvline(loc, color='red', **args_dict) if y_flag: ax.axhline(loc, color='red', **args_dict)
def add_milestone_lines( ax: plt.Axes, milestones: List[Dict[str, Union[str, int]]], min_value: float, max_value: float, delta: float, ) -> plt.Axes: """Add the lines for the milestones the user reached. :param ax: The axis to draw the milestones into. :param milestones: The milestones to consider. Each must have a threshold and color. :param min_value: The minimum value to determine if a milestone should be included. :param max_value: The maximum value to determine if a milestone should be inlcuded. :param delta: Determines how "far away" milestone lines are still included. """ for milestone in milestones: if max_value + delta >= milestone["threshold"] >= min_value - delta: ax.axhline(y=milestone["threshold"], color=milestone["color"], zorder=-1) return ax
def _plot_image(self, axis: plt.Axes = None, title: str = ''): plt.ioff() if axis is None: fig, axis = plt.subplots() axis.imshow(self.image.array, cmap=get_dicom_cmap()) #show horizontal profiles left_profile = ( self.positions['horizontal'] - self.widths['horizontal'] / 2) * self.image.array.shape[0] right_profile = ( self.positions['horizontal'] + self.widths['horizontal'] / 2) * self.image.array.shape[0] axis.axhline(left_profile, color='r') # X axis.axhline(right_profile, color='r') # X #show vertical profiles bottom_profile = ( self.positions['vertical'] - self.widths['vertical'] / 2) * self.image.array.shape[1] top_profile = (self.positions['vertical'] + self.widths['vertical'] / 2) * self.image.array.shape[1] axis.axvline(bottom_profile, color='r') # Y axis.axvline(top_profile, color='r') # Y _remove_ticklabels(axis) axis.set_title(title)
def add_contacts_to_plot(qc_frame: pd.DataFrame, axis: pyplot.Axes) -> None: if "OWC" in qc_frame: owc = qc_frame["OWC"].values[ 0] # OWC is assumed constant in the dataframe axis.axhline(owc, color="black", linestyle="--", linewidth=1) axis.annotate(f"OWC={owc:g}", (0, owc)) if "GOC" in qc_frame: goc = qc_frame["GOC"].values[0] axis.axhline(goc, color="black", linestyle="--", linewidth=1) axis.annotate(f"GOC={goc:g}", (0, goc)) if "GWC" in qc_frame: gwc = qc_frame["GWC"].values[0] axis.axhline(gwc, color="black", linestyle="--", linewidth=1) axis.annotate(f"GWC={gwc:g}", (0, gwc))
def draw_curve(plot_type: str, ax: plt.Axes, data: pd.DataFrame, x_grid: Union[list, np.ndarray, to.Tensor], x_label: Optional[Union[str, Sequence[str]]] = None, y_label: Optional[str] = None, curve_label: Optional[str] = None, area_label: Optional[str] = None, vline_level: Optional[float] = None, vline_label: str = 'approx. solved', title: Optional[str] = None, show_legend: bool = True, legend_kwargs: dict = None, plot_kwargs: dict = None) -> plt.Figure: """ Create a box or violin plot for a list of data arrays or a pandas DataFrame. The plot is neither shown nor saved. .. note:: If you want to have a tight layout, it is best to pass axes of a figure with `tight_layout=True` or `constrained_layout=True`. If you want to order the 4th element to the 2nd position in terms of colors use .. code-block:: python palette.insert(1, palette.pop(3)) :param plot_type: tye of categorical plot, pass box or violin :param ax: axis of the figure to plot on :param data: pandas DataFrame containing the columns `mean`, `std`, `min`, and `max` depending on the `plot_type` :param x_grid: values to plot the data over, e.g. time :param x_label: labels for the categories on the x-axis, if `data` is not given as a `DataFrame` :param y_label: label for the y-axis, pass `None` to set no label :param curve_label: label of the (1-dim) curve :param area_label: label of the (transparent) area :param vline_level: if not `None` (default) add a vertical line at the given level :param vline_label: label for the vertical line :param show_legend: if `True` the legend is shown, useful when handling multiple subplots :param title: title displayed above the figure, set to None to suppress the title :param legend_kwargs: keyword arguments forwarded to pyplot's `legend()` function, e.g. `loc='best'` :param plot_kwargs: keyword arguments forwarded to seaborn's `boxplot()` or `violinplot()` function :return: handle to the resulting figure """ plot_type = plot_type.lower() if plot_type not in ['mean_std', 'min_mean_max']: raise pyrado.ValueErr(given=plot_type, eq_constraint='mean_std or min_mean_max') if not isinstance(data, pd.DataFrame): raise pyrado.TypeErr(given=data, expected_type=pd.DataFrame) if x_label is not None and not isinstance(x_label, str): raise pyrado.TypeErr(given=x_label, expected_type=str) if y_label is not None and not isinstance(y_label, str): raise pyrado.TypeErr(given=y_label, expected_type=str) # Set defaults which can be overwritten by passing plot_kwargs plot_kwargs = merge_dicts([dict(alpha=0.3), plot_kwargs]) legend_kwargs = dict() if legend_kwargs is None else legend_kwargs # palette = sns.color_palette() if palette is None else palette # Preprocess if isinstance(x_grid, list): x_grid = np.array(x_grid) elif isinstance(x_grid, to.Tensor): x_grid = x_grid.detach().cpu().numpy() # Plot if plot_type == 'mean_std': if not ('mean' in data.columns and 'std' in data.columns): raise pyrado.KeyErr(keys="'mean' and 'std'", container=data) num_stds = 2 if area_label is None: area_label = rf'$\pm {num_stds}$ std' ax.fill_between(x_grid, data['mean'] - num_stds * data['std'], data['mean'] + num_stds * data['std'], label=area_label, **plot_kwargs) elif plot_type == 'min_mean_max': if not ('mean' in data.columns and 'min' in data.columns and 'max' in data.columns): raise pyrado.KeyErr(keys="'mean' and 'min' and 'max'", container=data) if area_label is None: area_label = r'min \& max' ax.fill_between(x_grid, data['min'], data['max'], label=area_label, **plot_kwargs) # plot mean last for proper z-ordering plot_kwargs['alpha'] = 1 ax.plot(x_grid, data['mean'], label=curve_label, **plot_kwargs) # Postprocess if vline_level is not None: # Add dashed line to mark a threshold ax.axhline(vline_level, c='k', ls='--', lw=1., label=vline_label) if x_label is None: ax.get_xaxis().set_ticks([]) if y_label is not None: ax.set_ylabel(y_label) if show_legend: ax.legend(**legend_kwargs) if title is not None: ax.set_title(title) return plt.gcf()
def plot_heatmap( dataset: netCDF4.Dataset, ax: plt.Axes = None, color: str = "charge", residues: list = None, zerobased: bool = False, ): """Plot the states, or the charges as colored blocks Parameters ---------- dataset - netCDF$.Dataset containing Protons information. ax - matplotlib Axes object color - 'charge', 'state', 'taut' , color by charge, by state, or charge and shade by tautomer residues - list, residues to plot zerobased - bool default False - use zero based labeling for states. Returns ------- ax - plt.Axes """ # Convert to array, and make sure types are int if ax is None: ax = plt.gca() if zerobased: label_offset = 0 else: label_offset = 1 if color == "charge": vmin = -2 vmax = 2 center = 0 cmap = sns.diverging_palette( 25, 244, l=60, s=95, sep=80, center="light", as_cmap=True ) ticks = np.arange(vmin, vmax + 1) boundaries = np.arange(vmin - 0.5, vmax + 1.5) cbar_kws = {"ticks": ticks, "boundaries": boundaries, "label": color.title()} elif color == "state": vmin = 0 + label_offset vmax = label_offset + np.amax(dataset["Protons/Titration/state"][:, :]) ticks = np.arange(vmin, vmax + 1) boundaries = np.arange(vmin - 0.5, vmax + 1.5) cbar_kws = {"ticks": ticks, "boundaries": boundaries, "label": color.title()} center = None cmap = "Accent" else: raise ValueError("color argument should be 'charge', or 'state'.") to_plot = None if residues is None: if color == "charge": to_plot = charge_taut_trace(dataset)[0][:, :] elif color == "state": titration_states = dataset["Protons/Titration/state"][:, :] to_plot = titration_states + label_offset else: if isinstance(residues, int): residues = [residues] residues = np.asarray(residues).astype(np.int) if color == "charge": to_plot = charge_taut_trace(dataset)[0][:, residues] elif color == "state": to_plot = dataset["Protons/Titration/state"][:, residues] + label_offset ax = sns.heatmap( to_plot.T, ax=ax, vmin=vmin, vmax=vmax, center=center, xticklabels=int(np.floor(to_plot.shape[0] / 7)) - 1, yticklabels=int(np.floor(to_plot.shape[1] / 4)) - 1, cmap=cmap, cbar_kws=cbar_kws, edgecolor="None", snap=True, ) for residue in range(to_plot.T.shape[1]): ax.axhline(residue, lw=0.4, c="w") ax.set_ylabel("Residue") ax.set_xlabel("Update") return ax
def draw_categorical( plot_type: str, ax: plt.Axes, data: Union[list, np.ndarray, to.Tensor, pd.DataFrame], x_label: Optional[Union[str, Sequence[str]]], y_label: Optional[str], vline_level: float = None, vline_label: str = "approx. solved", palette=None, title: str = None, show_legend: bool = True, legend_kwargs: dict = None, plot_kwargs: dict = None, ) -> plt.Figure: """ Create a box or violin plot for a list of data arrays or a pandas DataFrame. The plot is neither shown nor saved. If you want to order the 4th element to the 2nd position in terms of colors use .. code-block:: python palette.insert(1, palette.pop(3)) .. note:: If you want to have a tight layout, it is best to pass axes of a figure with `tight_layout=True` or `constrained_layout=True`. :param plot_type: tye of categorical plot, pass box or violin :param ax: axis of the figure to plot on :param data: list of data sets to plot as separate boxes :param x_label: labels for the categories on the x-axis, if `data` is not given as a `DataFrame` :param y_label: label for the y-axis, pass `None` to set no label :param vline_level: if not `None` (default) add a vertical line at the given level :param vline_label: label for the vertical line :param palette: seaborn color palette, pass `None` to use the default palette :param show_legend: if `True` the legend is shown, useful when handling multiple subplots :param title: title displayed above the figure, set to None to suppress the title :param legend_kwargs: keyword arguments forwarded to pyplot's `legend()` function, e.g. `loc='best'` :param plot_kwargs: keyword arguments forwarded to seaborn's `boxplot()` or `violinplot()` function :return: handle to the resulting figure """ plot_type = plot_type.lower() if plot_type not in ["box", "violin"]: raise pyrado.ValueErr(given=plot_type, eq_constraint="box or violin") if not isinstance(data, (list, to.Tensor, np.ndarray, pd.DataFrame)): raise pyrado.TypeErr( given=data, expected_type=[list, to.Tensor, np.ndarray, pd.DataFrame]) # Set defaults which can be overwritten plot_kwargs = merge_dicts([dict(alpha=1), plot_kwargs]) # by default no transparency alpha = plot_kwargs.pop( "alpha") # can't pass the to the seaborn plotting functions legend_kwargs = dict() if legend_kwargs is None else legend_kwargs palette = sns.color_palette() if palette is None else palette # Preprocess if isinstance(data, pd.DataFrame): df = data else: if isinstance(data, list): data = np.array(data) elif isinstance(data, to.Tensor): data = data.detach().cpu().numpy() if x_label is not None and not len(x_label) == data.shape[1]: raise pyrado.ShapeErr(given=data, expected_match=x_label) df = pd.DataFrame(data, columns=x_label) if data.shape[0] < data.shape[1]: print_cbt( f"Less data samples {data.shape[0]} then data dimensions {data.shape[1]}", "y", bright=True) # Plot if plot_type == "box": ax = sns.boxplot(data=df, ax=ax, **plot_kwargs) elif plot_type == "violin": plot_kwargs = merge_dicts([ dict(alpha=0.3, scale="count", inner="box", bw=0.3, cut=0), plot_kwargs ]) ax = sns.violinplot(data=df, ax=ax, palette=palette, **plot_kwargs) # Plot larger circles for medians (need to memorize the limits) medians = df.median().to_numpy() left, right = ax.get_xlim() locs = ax.get_xticks() ax.scatter(locs, medians, marker="o", s=30, zorder=3, color="white", edgecolors="black") ax.set_xlim((left, right)) # Postprocess if alpha < 1 and plot_type == "box": for patch in ax.artists: r, g, b, a = patch.get_facecolor() patch.set_facecolor((r, g, b, alpha)) elif alpha < 1 and plot_type == "violin": for violin in ax.collections[::2]: violin.set_alpha(alpha) if vline_level is not None: # Add dashed line to mark a threshold ax.axhline(vline_level, c="k", ls="--", lw=1.0, label=vline_label) if x_label is None: ax.get_xaxis().set_ticks([]) if y_label is not None: ax.set_ylabel(y_label) if show_legend: ax.legend(**legend_kwargs) if title is not None: ax.set_title(title) return plt.gcf()
def plot_tautomer_heatmap( dataset: netCDF4.Dataset, ax: plt.Axes = None, residues: list = None, zerobased: bool = False, ): """Plot the charge of residues on a blue-red (negative-positive) scale, and add different shades for different tautomers. Parameters ---------- dataset - netCDF4 dataset containing protons data ax - matplotlib Axes object residues - list, residues to plot zerobased - bool default False - use zero based labeling for states. Returns ------- plt.Axes """ # Convert to array, and make sure types are int if ax is None: ax = plt.gca() if zerobased: label_offset = 0 else: label_offset = 1 # color charges, and add shade for tautomers vmin = -2 vmax = 2 center = 0 cmap = sns.diverging_palette( 25, 244, l=60, s=95, sep=80, center="light", as_cmap=True ) ticks = np.arange(vmin, vmax + 1) boundaries = np.arange(vmin - 0.5, vmax + 1.5) cbar_kws = {"ticks": ticks, "boundaries": boundaries, "label": "Charge"} taut_vmin = 0 + label_offset taut_vmax = label_offset + np.amax(dataset["Protons/Titration/state"][:, :]) taut_ticks = np.arange(taut_vmin, taut_vmax + 1) taut_boundaries = np.arange(taut_vmin - 0.5, taut_vmax + 1.5) taut_cbar_kws = {"boundaries": taut_boundaries} taut_center = None taut_cmap = "Greys" to_plot = None if residues is None: to_plot, taut_to_plot = charge_taut_trace(dataset) else: if isinstance(residues, int): residues = [residues] residues = np.asarray(residues).astype(np.int) charges, tauts = charge_taut_trace(dataset) to_plot = charges[:, residues] taut_to_plot = tauts[:, residues] mesh = ax.pcolor(to_plot.T, cmap=cmap, vmin=vmin, vmax=vmax, snap=True, alpha=1.0) plt.colorbar(mesh, ax=ax, **cbar_kws) taut_mesh = ax.pcolor( taut_to_plot.T, cmap=taut_cmap, vmin=taut_vmin, vmax=taut_vmax, alpha=0.1, snap=True, ) for residue in range(to_plot.T.shape[0]): ax.axhline(residue, lw=0.4, c="w") ax.set_ylabel("Residue") ax.set_xlabel("Update") return ax
def add_geodesic_grid(ax: plt.Axes, manifold: Stereographic, line_width=0.1): import math # define geodesic grid parameters N_EVALS_PER_GEODESIC = 10000 STYLE = "--" COLOR = "gray" LINE_WIDTH = line_width # get manifold properties K = manifold.k.item() R = manifold.radius.item() # get maximal numerical distance to origin on manifold if K < 0: # create point on R r = torch.tensor((R, 0.0), dtype=manifold.dtype) # project point on R into valid range (epsilon border) r = manifold.projx(r) # determine distance from origin max_dist_0 = manifold.dist0(r).item() else: max_dist_0 = math.pi * R # adjust line interval for spherical geometry circumference = 2 * math.pi * R # determine reasonable number of geodesics # choose the grid interval size always as if we'd be in spherical # geometry, such that the grid interpolates smoothly and evenly # divides the sphere circumference n_geodesics_per_circumference = 4 * 6 # multiple of 4! n_geodesics_per_quadrant = n_geodesics_per_circumference // 2 grid_interval_size = circumference / n_geodesics_per_circumference if K < 0: n_geodesics_per_quadrant = int(max_dist_0 / grid_interval_size) # create time evaluation array for geodesics if K < 0: min_t = -1.2 * max_dist_0 else: min_t = -circumference / 2.0 t = torch.linspace(min_t, -min_t, N_EVALS_PER_GEODESIC)[:, None] # define a function to plot the geodesics def plot_geodesic(gv): ax.plot(*gv.t().numpy(), STYLE, color=COLOR, linewidth=LINE_WIDTH) # define geodesic directions u_x = torch.tensor((0.0, 1.0)) u_y = torch.tensor((1.0, 0.0)) # add origin x/y-crosshair o = torch.tensor((0.0, 0.0)) if K < 0: x_geodesic = manifold.geodesic_unit(t, o, u_x) y_geodesic = manifold.geodesic_unit(t, o, u_y) plot_geodesic(x_geodesic) plot_geodesic(y_geodesic) else: # add the crosshair manually for the sproj of sphere # because the lines tend to get thicker if plotted # as done for K<0 ax.axvline(0, linestyle=STYLE, color=COLOR, linewidth=LINE_WIDTH) ax.axhline(0, linestyle=STYLE, color=COLOR, linewidth=LINE_WIDTH) # add geodesics per quadrant for i in range(1, n_geodesics_per_quadrant): i = torch.as_tensor(float(i)) # determine start of geodesic on x/y-crosshair x = manifold.geodesic_unit(i * grid_interval_size, o, u_y) y = manifold.geodesic_unit(i * grid_interval_size, o, u_x) # compute point on geodesics x_geodesic = manifold.geodesic_unit(t, x, u_x) y_geodesic = manifold.geodesic_unit(t, y, u_y) # plot geodesics plot_geodesic(x_geodesic) plot_geodesic(y_geodesic) if K < 0: plot_geodesic(-x_geodesic) plot_geodesic(-y_geodesic)
def plot_rt(fig: plt.Figure, target_df: pd.DataFrame, ax: plt.Axes, county_name: str) -> None: above = [1, 0, 0] middle = [1, 1, 1] below = [0, 0, 0] cmap = ListedColormap(np.r_[np.linspace(below, middle, 25), np.linspace(middle, above, 25)]) color_mapped = lambda y: np.clip(y, .5, 1.5) - .5 target_df = target_df.loc[( target_df.index.get_level_values('name') == county_name)] start_dt = pd.to_datetime( target_df['Rt'].index.get_level_values('Date').min()) index = pd.to_datetime(target_df['Rt'].index.get_level_values('Date')) values = target_df['Rt'].values # Plot dots and line ax.plot(index, values, c='k', zorder=1, alpha=.25) ax.scatter(index, values, s=40, lw=.5, c=cmap(color_mapped(values)), edgecolors='k', zorder=2) # Aesthetically, extrapolate credible interval by 1 day either side lowfn = interp1d(date2num(index), target_df['90_CrI_LB'].values, bounds_error=False, fill_value='extrapolate') highfn = interp1d(date2num(index), target_df['90_CrI_UB'].values, bounds_error=False, fill_value='extrapolate') extended = pd.date_range(start=start_dt - pd.Timedelta(days=3), end=index[-1] + pd.Timedelta(days=1)) ax.fill_between(extended, lowfn(date2num(extended)), highfn(date2num(extended)), color='k', alpha=.1, lw=0, zorder=3) ax.axhline(1.0, c='k', lw=1, label='$R_t=1.0$', alpha=.25) ax.set_title(f'{county_name}', loc='left', fontsize=20, fontweight=0, color='#375A97') # Formatting ax.xaxis.set_major_locator(mdates.MonthLocator()) ax.xaxis.set_major_formatter(mdates.DateFormatter('%b')) ax.xaxis.set_minor_locator(mdates.DayLocator()) ax.yaxis.set_major_locator(ticker.MultipleLocator(1)) ax.yaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.1f}")) ax.yaxis.tick_right() ax.spines['left'].set_visible(False) ax.spines['bottom'].set_visible(False) ax.spines['right'].set_visible(False) ax.margins(0) ax.grid(which='major', axis='y', c='k', alpha=.1, zorder=-2) ax.margins(0) ax.set_ylim(0.0, 5.0) ax.set_xlim( start_dt - pd.Timedelta(days=3), target_df.index.get_level_values('Date')[-1] + pd.Timedelta(days=1)) ax.xaxis.set_major_locator(mdates.WeekdayLocator()) ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %d')) fig.set_facecolor('w')
def _draw_truth_offdiag(ax: plt.Axes, truth_x, truth_y, **kwargs): ax.axvline(truth_x, **kwargs) ax.axhline(truth_y, **kwargs)
def error_array(ax: plt.Axes, err_array: ListOrArray, x_array: typing.Optional[ListOrArray] = None, statistics: typing.Optional[typing.Dict[str, float]] = None, threshold: float = None, cumulative: bool = False, color: str = 'grey', name: str = "error", title: str = "", xlabel: str = "index", ylabel: typing.Optional[str] = None, subplot_arg: int = 111, linestyle: str = "-", marker: typing.Optional[str] = None): """ high-level function for plotting raw error values of a metric :param fig: matplotlib axes :param err_array: an nx1 array of values :param x_array: an nx1 array of x-axis values :param statistics: optional dictionary of {metrics.StatisticsType.value: value} :param threshold: optional value for horizontal threshold line :param cumulative: set to True for cumulative plot :param name: optional name of the value array :param title: optional plot title :param xlabel: optional x-axis label :param ylabel: optional y-axis label :param subplot_arg: optional matplotlib subplot ID if used as subplot :param linestyle: matplotlib linestyle :param marker: optional matplotlib marker style for points """ if cumulative: if x_array is not None: ax.plot(x_array, np.cumsum(err_array), linestyle=linestyle, marker=marker, color=color, label=name) else: ax.plot(np.cumsum(err_array), linestyle=linestyle, marker=marker, color=color, label=name) else: if x_array is not None: ax.plot(x_array, err_array, linestyle=linestyle, marker=marker, color=color, label=name) else: ax.plot(err_array, linestyle=linestyle, marker=marker, color=color, label=name) if statistics is not None: for stat_name, value in statistics.items(): color = next(ax._get_lines.prop_cycler)['color'] if stat_name == "std" and "mean" in statistics: mean, std = statistics["mean"], statistics["std"] ax.axhspan(mean - std / 2, mean + std / 2, color=color, alpha=0.5, label=stat_name) else: ax.axhline(y=value, color=color, linewidth=2.0, label=stat_name) if threshold is not None: ax.axhline(y=threshold, color='red', linestyle='dashed', linewidth=2.0, label="threshold") plt.ylabel(ylabel if ylabel else name) plt.xlabel(xlabel) plt.title(title) plt.legend(frameon=True)
def plot_heatmap( dataset: netCDF4.Dataset, ax: plt.Axes = None, color: str = "charge", residues: list = None, zerobased: bool = False, ): """Plot the states, or the charges as colored blocks Parameters ---------- dataset - netCDF$.Dataset containing Protons information. ax - matplotlib Axes object color - 'charge', 'state', 'taut' , color by charge, by state, or charge and shade by tautomer residues - list, residues to plot zerobased - bool default False - use zero based labeling for states. Returns ------- ax - plt.Axes """ # Convert to array, and make sure types are int if ax is None: ax = plt.gca() if zerobased: label_offset = 0 else: label_offset = 1 if color == "charge": vmin = -2 vmax = 2 center = 0 cmap = sns.diverging_palette(25, 244, l=60, s=95, sep=80, center="light", as_cmap=True) ticks = np.arange(vmin, vmax + 1) boundaries = np.arange(vmin - 0.5, vmax + 1.5) cbar_kws = { "ticks": ticks, "boundaries": boundaries, "label": color.title() } elif color == "state": vmin = 0 + label_offset vmax = label_offset + np.amax(dataset["Protons/Titration/state"][:, :]) ticks = np.arange(vmin, vmax + 1) boundaries = np.arange(vmin - 0.5, vmax + 1.5) cbar_kws = { "ticks": ticks, "boundaries": boundaries, "label": color.title() } center = None cmap = "Accent" else: raise ValueError("color argument should be 'charge', or 'state'.") to_plot = None if residues is None: if color == "charge": to_plot = charge_taut_trace(dataset)[0][:, :] elif color == "state": titration_states = dataset["Protons/Titration/state"][:, :] to_plot = titration_states + label_offset else: if isinstance(residues, int): residues = [residues] residues = np.asarray(residues).astype(np.int) if color == "charge": to_plot = charge_taut_trace(dataset)[0][:, residues] elif color == "state": to_plot = dataset[ "Protons/Titration/state"][:, residues] + label_offset ax = sns.heatmap( to_plot.T, ax=ax, vmin=vmin, vmax=vmax, center=center, xticklabels=int(np.floor(to_plot.shape[0] / 7)) - 1, yticklabels=int(np.floor(to_plot.shape[1] / 4)) - 1, cmap=cmap, cbar_kws=cbar_kws, edgecolor="None", snap=True, ) for residue in range(to_plot.T.shape[1]): ax.axhline(residue, lw=0.4, c="w") ax.set_ylabel("Residue") ax.set_xlabel("Update") return ax
def plot_tautomer_heatmap( dataset: netCDF4.Dataset, ax: plt.Axes = None, residues: list = None, zerobased: bool = False, ): """Plot the charge of residues on a blue-red (negative-positive) scale, and add different shades for different tautomers. Parameters ---------- dataset - netCDF4 dataset containing protons data ax - matplotlib Axes object residues - list, residues to plot zerobased - bool default False - use zero based labeling for states. Returns ------- plt.Axes """ # Convert to array, and make sure types are int if ax is None: ax = plt.gca() if zerobased: label_offset = 0 else: label_offset = 1 # color charges, and add shade for tautomers vmin = -2 vmax = 2 center = 0 cmap = sns.diverging_palette(25, 244, l=60, s=95, sep=80, center="light", as_cmap=True) ticks = np.arange(vmin, vmax + 1) boundaries = np.arange(vmin - 0.5, vmax + 1.5) cbar_kws = {"ticks": ticks, "boundaries": boundaries, "label": "Charge"} taut_vmin = 0 + label_offset taut_vmax = label_offset + np.amax( dataset["Protons/Titration/state"][:, :]) taut_ticks = np.arange(taut_vmin, taut_vmax + 1) taut_boundaries = np.arange(taut_vmin - 0.5, taut_vmax + 1.5) taut_cbar_kws = {"boundaries": taut_boundaries} taut_center = None taut_cmap = "Greys" to_plot = None if residues is None: to_plot, taut_to_plot = charge_taut_trace(dataset) else: if isinstance(residues, int): residues = [residues] residues = np.asarray(residues).astype(np.int) charges, tauts = charge_taut_trace(dataset) to_plot = charges[:, residues] taut_to_plot = tauts[:, residues] mesh = ax.pcolor(to_plot.T, cmap=cmap, vmin=vmin, vmax=vmax, snap=True, alpha=1.0) plt.colorbar(mesh, ax=ax, **cbar_kws) taut_mesh = ax.pcolor( taut_to_plot.T, cmap=taut_cmap, vmin=taut_vmin, vmax=taut_vmax, alpha=0.1, snap=True, ) for residue in range(to_plot.T.shape[0]): ax.axhline(residue, lw=0.4, c="w") ax.set_ylabel("Residue") ax.set_xlabel("Update") return ax
def render_violinplot( ax: plt.Axes, data: [Sequence[list], Sequence[np.ndarray]], x_labels: Sequence[str], y_label: str, vline_level: float = None, vline_label: str = 'approx. solved', alpha: float = 0.7, show_inner_quartiles: bool = False, show_legend: bool = True, legend_loc: str = 'best', title: str = None, use_seaborn: bool = False, ) -> plt.Figure: """ Create a violin plot for a list of data arrays. Every entry results in one column of the violin plot. The plot is neither shown nor saved. .. note:: If you want to have a tight layout, it is best to pass axes of a figure with `tight_layout=True` or `constrained_layout=True`. :param ax: axis of the figure to plot on :param data: list of data sets to plot as separate violins :param x_labels: labels for the categories on the x-axis :param y_label: label for the y-axis :param vline_level: if not `None` (default) add a vertical line at the given level :param vline_label: label for the vertical line :param alpha: transparency (alpha-value) for violin body (including the border lines) :param show_inner_quartiles: display the 1st and 3rd quartile with a thick line :param show_legend: flag if the legend entry should be printed, set to `True` when using multiple subplots :param legend_loc: location of the legend, ignored if `show_legend = False` :param title: title displayed above the figure, set to None to suppress the title :return: handle to the resulting figure """ if use_seaborn: # Plot the data import seaborn as sns import pandas as pd df = pd.DataFrame(data, x_labels).T ax = sns.violinplot(data=df, scale='count', inner='stick', bw=0.3, cut=0) # cut controls the max7min values medians = np.zeros(len(data)) for i in range(len(data)): medians[i] = np.median(data[i]) x_grid = np.arange(0, len(medians)) ax.scatter(x_grid, medians, marker='o', s=50, zorder=3, color='white', edgecolors='black') else: # Plot the data violin = ax.violinplot(data, showmeans=False, showmedians=False, showextrema=False) # Set custom color scheme for pc in violin['bodies']: pc.set_facecolor('#b11226') pc.set_edgecolor('black') pc.set_alpha(alpha) # Set axis style ax.set_xticks(np.arange(1, len(x_labels) + 1)) ax.set_xticklabels(x_labels) quartiles_up, medians, quartiles_lo = np.zeros(len(data)), np.zeros( len(data)), np.zeros(len(data)) data_mins, data_maxs = np.zeros(len(data)), np.zeros(len(data)) for i in range(len(data)): quartiles_up[i], medians[i], quartiles_lo[i] = np.percentile( data[i], [25, 50, 75]) data_mins[i], data_maxs[i] = min(data[i]), max(data[i]) x_grid = np.arange(1, len(medians) + 1) ax.scatter(x_grid, medians, marker='o', s=50, zorder=3, color='white', edgecolors='black') ax.vlines(x_grid, data_mins, data_maxs, color='k', linestyle='-', lw=1, alpha=alpha) if show_inner_quartiles: ax.vlines(x_grid, quartiles_up, quartiles_lo, color='k', linestyle='-', lw=5) # Add dashed line to mark the approx solved threshold if vline_level is not None: ax.axhline(vline_level, c='k', ls='--', lw=1.0, label=vline_label) ax.set_ylabel(y_label) if show_legend: ax.legend(loc=legend_loc) if title is not None: ax.set_title(title) return plt.gcf()
def render_boxplot( ax: plt.Axes, data: [Sequence[list], Sequence[np.ndarray]], x_labels: Sequence[str], y_label: str, vline_level: float = None, vline_label: str = 'approx. solved', alpha: float = 1., colorize: bool = False, show_fliers: bool = False, show_legend: bool = True, legend_loc: str = 'best', title: str = None, ) -> plt.Figure: """ Create a box plot for a list of data arrays. Every entry results in one column of the box plot. The plot is neither shown nor saved. .. note:: If you want to have a tight layout, it is best to pass axes of a figure with `tight_layout=True` or `constrained_layout=True`. :param ax: axis of the figure to plot on :param data: list of data sets to plot as separate boxes :param x_labels: labels for the categories on the x-axis :param y_label: label for the y-axis :param vline_level: if not `None` (default) add a vertical line at the given level :param vline_label: label for the vertical line :param alpha: transparency (alpha-value) for boxes (including the border lines) :param colorize: colorize the core of the boxes :param show_fliers: show outliers (more the 1.5 of the inter quartial range) as circles :param show_legend: flag if the legend entry should be printed, set to True when using multiple subplots :param legend_loc: location of the legend, ignored if `show_legend = False` :param title: title displayed above the figure, set to None to suppress the title :return: handle to the resulting figure """ medianprops = dict(linewidth=1., color='firebrick') meanprops = dict(marker='D', markeredgecolor='black', markerfacecolor='purple') boxprops = dict(linewidth=1.) whiskerprops = dict(linewidth=1.) capprops = dict(linewidth=1.) # Plot the data box = ax.boxplot( data, boxprops=boxprops, whiskerprops=whiskerprops, capprops=capprops, meanprops=meanprops, meanline=False, showmeans=False, medianprops=medianprops, showfliers=show_fliers, notch=False, patch_artist=colorize, # necessary to colorize the boxes labels=x_labels, widths=0.7) if colorize: for i, patch in enumerate(box['boxes']): patch.set_facecolorf(f'C{i%10}') patch.set_alpha(alpha) # Add dashed line to mark the approx solved threshold if vline_level is not None: ax.axhline(vline_level, c='k', ls='--', lw=1., label=vline_label) ax.set_ylabel(y_label) if show_legend: ax.legend(loc=legend_loc) if title is not None: ax.set_title(title) return plt.gcf()
def overlay_entropy_profiles(self, axes: plt.Axes = None, r_units: str = 'r500', k_units: str = 'K500adi', vkb05_line: bool = True, color: str = 'k', alpha: float = 1., markersize: float = 1, linewidth: float = 0.5) -> None: stand_alone = False if axes is None: stand_alone = True fig, axes = plt.subplots() axes.loglog() axes.set_xlabel(f'$r$ [{r_units}]') axes.set_ylabel(f'$K$ [${k_units}$]') axes.axvline(1, linestyle=':', color=color, alpha=alpha) # Set-up entropy data fields = [ 'K_500', 'K_1000', 'K_1500', 'K_2500', 'K_0p15r500', 'K_30kpc' ] K_stat = dict() if k_units == 'K500adi': K_conv = 1 / getattr(self, 'K_500_adi') axes.axhline(1, linestyle=':', color=color, alpha=alpha) elif k_units == 'keVcm^2': K_conv = np.ones_like(getattr(self, 'K_500_adi')) axes.fill_between(np.array(axes.get_xlim()), y1=np.nanmin(self.K_500_adi), y2=np.nanmax(self.K_500_adi), facecolor='k', alpha=0.3) else: raise ValueError("Conversion unit unknown.") for field in fields: data = np.multiply(getattr(self, field), K_conv) K_stat[field] = (np.nanpercentile(data, 16), np.nanpercentile(data, 50), np.nanpercentile(data, 84)) K_stat[field.replace('K', 'num')] = np.count_nonzero(~np.isnan(data)) # Set-up radial distance data r_stat = dict() if r_units == 'r500': r_conv = 1 / getattr(self, 'r_500') elif r_units == 'r2500': r_conv = 1 / getattr(self, 'r_2500') elif r_units == 'kpc': r_conv = np.ones_like(getattr(self, 'r_2500')) else: raise ValueError("Conversion unit unknown.") for field in ['r_500', 'r_1000', 'r_1500', 'r_2500']: data = np.multiply(getattr(self, field), r_conv) if k_units == 'K500adi': data[np.isnan(self.K_500_adi)] = np.nan r_stat[field] = (np.nanpercentile(data, 16), np.nanpercentile(data, 50), np.nanpercentile(data, 84)) r_stat[field.replace('r', 'num')] = np.count_nonzero(~np.isnan(data)) data = np.multiply(getattr(self, 'r_500') * 0.15, r_conv) if k_units == 'K500adi': data[np.isnan(self.K_500_adi)] = np.nan r_stat['r_0p15r500'] = (np.nanpercentile(data, 16), np.nanpercentile(data, 50), np.nanpercentile(data, 84)) r_stat['num_0p15r500'] = np.count_nonzero(~np.isnan(data)) data = np.multiply( np.ones_like(getattr(self, 'r_2500')) * 30 * unyt.kpc, r_conv) if k_units == 'K500adi': data[np.isnan(self.K_500_adi)] = np.nan r_stat['r_30kpc'] = (np.nanpercentile(data, 16), np.nanpercentile(data, 50), np.nanpercentile(data, 84)) r_stat['num_30kpc'] = np.count_nonzero(~np.isnan(data)) for suffix in [ '_500', '_1000', '_1500', '_2500', '_0p15r500', '_30kpc' ]: x_low, x, x_hi = r_stat['r' + suffix] y_low, y, y_hi = K_stat['K' + suffix] num_objects = f"{r_stat['num' + suffix]}, {K_stat['num' + suffix]}" point_label = f"r{suffix:.<17s} Num(x,y) = {num_objects}" if stand_alone: axes.scatter(x, y, label=point_label, s=markersize) axes.errorbar(x, y, yerr=[[y_hi - y], [y - y_low]], xerr=[[x_hi - x], [x - x_low]], ls='none', ms=markersize, lw=linewidth) else: axes.scatter(x, y, color=color, alpha=alpha, s=markersize) axes.errorbar(x, y, yerr=[[y_hi - y], [y - y_low]], xerr=[[x_hi - x], [x - x_low]], ls='none', ecolor=color, alpha=alpha, ms=markersize, lw=linewidth) if vkb05_line: if r_units == 'r500' and k_units == 'K500adi': r = np.linspace(*axes.get_xlim(), 31) k = 1.40 * r**1.1 / self.hconv axes.plot(r, k, linestyle='--', color=color, alpha=alpha) else: print(( "The VKB05 adiabatic threshold should be plotted only when both " "axes are in scaled units, since the line is calibrated on an NFW " "profile with self-similar halos with an average concentration of " "c_500 ~ 4.2 for the objects in the Sun et al. (2009) sample." )) if k_units == 'K500adi': r_r500, S_S500_50, S_S500_10, S_S500_90 = self.get_shortcut() plt.fill_between(r_r500, S_S500_10, S_S500_90, color='grey', alpha=0.5, linewidth=0) plt.plot(r_r500, S_S500_50, c='k') if stand_alone: plt.legend() plt.show()
def plot_triple_data( ax: plt.Axes, lab_values: ty.List[ty.List[str]], lab_names: ty.Tuple[str, str, str, str], f_mean: np.ndarray, f_min: np.ndarray, f_max: np.ndarray, f_std: np.ndarray, outer_label: str = None, outer_value: str = None, mean_all: float = None, max_all: float = None, min_all: float = None, min_lower_limit: float = 0, hypa_labels: ty.Dict[str, ty.Dict[str, str]] = None, ): """Plot groups of groups of columns f_mean.shape (x, y, z) z groups of (groups of columns) y groups of columns x columns per group example of (3, 4, 2) shape ^ | x x |x xxx xx x |xxx xx xxx xx xx x |xxx xxx x x x xxx xxx xxx x x |xxx xxx xxx xxx xxx xxx xxx xxx |xxx xxx xxx xxx xxx xxx xxx xxx .-----------------------------------> """ logg = logging.getLogger(f"c.{__name__}.plot_triple_data") logg.setLevel("INFO") logg.debug("Start plot_triple_data") logg.debug(f"f_mean.shape: {f_mean.shape}") logg.debug(f"outer_value: {outer_value} outer_label {outer_label}") logg.debug(f"lab_names: {lab_names}") logg.debug(f"lab_values: {lab_values}") logg.debug(f"hypa_labels: {hypa_labels}") if outer_label is not None and outer_value is not None: if hypa_labels is not None: lab_values_disp = [] for il, this_lab_values in enumerate(lab_values): this_lab_name = lab_names[il] new_disp_values = [] for this_lab_value in this_lab_values: # if I know how to translate if this_lab_name in hypa_labels: disp_lab_value = hypa_labels[this_lab_name][ this_lab_value] logg.debug(f"disp_lab_value: {disp_lab_value}") # use the current available label, no translation else: disp_lab_value = this_lab_value new_disp_values.append(disp_lab_value) lab_values_disp.append(new_disp_values) # translate the outer label if you have the translation available if outer_label in hypa_labels: outer_value_disp = hypa_labels[outer_label][outer_value] else: outer_value_disp = outer_value else: lab_values_disp = lab_values outer_value_disp = outer_value logg.debug(f"lab_values_disp: {lab_values_disp}") logg.debug(f"outer_value_disp: {outer_value_disp}") title = "" if outer_label is not None and outer_value is not None: title += f"{outer_label}" # title += f": \\textbf{{{outer_value_disp}}}" title += f": $\\bf{{{outer_value_disp}}}$" title += "\n" title += f"{lab_names[0]}" title += f": {lab_values_disp[0]}" title += "\n" title += f" grouped by {lab_names[1]}" title += f": {lab_values_disp[1]}" title += "\n" title += f" grouped by {lab_names[2]}" title += f": {lab_values_disp[2]}" ax.set_title(title, fontsize=14) lab_fontsize = 14 ax.set_ylabel("F-score (min/mean/max and std-dev)", fontsize=lab_fontsize) ax.set_xlabel(f"{lab_names[1]} ({lab_names[2]})", fontsize=lab_fontsize) f_dim = f_mean.shape # the width of each super group width_outer_group = 0.9 # scale the available space by 0.8 to leave space between subgroups width_inner_group = width_outer_group * 0.8 / f_dim[1] # the columns are side by side width_inner_col = width_inner_group / f_dim[0] # where the z super groups of columns start x_outer_pos = np.arange(f_dim[2]) # where the y sub groups start x_inner_pos = np.arange(f_dim[1]) * width_outer_group / f_dim[1] # where to put the ticks for each subgroup x_inner_ticks = x_inner_pos + (width_inner_group / 2) # all the ticks and the relative labels all_x_ticks = np.array([]) all_xticklabels = [] # if there are too many groups of columns draw less info too_many_groups = f_dim[1] * f_dim[2] > 12 if not too_many_groups: err_capsize = 5 std_capsize = 3 std_capthick = 4 xticklabels_rot = 0 xticklabels_ha = "center" else: err_capsize = 3 std_capsize = 2 std_capthick = 3 xticklabels_rot = 30 xticklabels_ha = "right" # for each super group for iz in range(f_dim[2]): # where this group starts shift_group = x_outer_pos[iz] # where to put the ticks this_ticks = x_inner_ticks + shift_group all_x_ticks = np.hstack((all_x_ticks, this_ticks)) this_labels = [ f"{vy} ({lab_values_disp[2][iz]})" for vy in lab_values_disp[1] ] all_xticklabels.extend(this_labels) # reset the cycler cc = cycler(color=[ "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf", ]) ax.set_prop_cycle(cc) # for each column for ix in range(f_dim[0]): # how much this batch of y columns must be shifted shift_col = width_inner_col * ix x_col = x_inner_pos + shift_col + shift_group # extract the values of the y columns in this batch y_f = f_mean[ix, :, iz] # only put the label for the first z slice the_label = lab_values_disp[0][ix] if iz == 0 else None # plot the bars ax.bar( x=x_col, height=y_f, width=width_inner_col, label=the_label, align="edge", capsize=err_capsize, ) # compute the relative min/max y_min = y_f - f_min[ix, :, iz] y_max = f_max[ix, :, iz] - y_f y_err = np.vstack((y_min, y_max)) ax.errorbar( x=x_col + width_inner_col / 2, y=y_f, yerr=y_err, linestyle="None", capsize=err_capsize, ecolor="k", ) # get the standard deviation y_std = f_std[ix, :, iz] ax.errorbar( x_col + width_inner_col / 2, y_f, yerr=y_std, linestyle="None", capsize=std_capsize, capthick=std_capthick, ecolor="b", ) if min_all is not None and min_all < min_lower_limit: # logg.debug(f"Resettin min_all: {min_all} to min_lower_limit") min_all = min_lower_limit if max_all is not None and min_all is not None: ax.set_ylim(top=max_all * 1.01, bottom=min_all * 0.99) elif max_all is not None: ax.set_ylim(top=max_all * 1.01) elif min_all is not None: ax.set_ylim(bottom=min_all * 0.99) if mean_all is not None: ax.axhline(mean_all) bottom, top = ax.get_ylim() mean_rescaled = ((mean_all - bottom) / (top - bottom)) * 1.01 ax.annotate( text=f"{mean_all:.03f}", xy=(0.005, mean_rescaled), xycoords="axes fraction", fontsize=13, ) ax.set_xticks(all_x_ticks) ax.set_xticklabels( labels=all_xticklabels, rotation=xticklabels_rot, horizontalalignment=xticklabels_ha, ) ax.legend(title=f"{lab_names[0]}", title_fontsize=lab_fontsize)