Ejemplo n.º 1
0
def post_process_plt(df: pd.DataFrame,
                     ax: plt.axes.Axes,
                     percent: bool = False) -> plt.figure.Figure:
    """Post-process the Matplotlib Axes instance produced by :func:`pre_process_plt`.

    The post-processing invovles further formatting of the legend, the x-axis and the y-axis.

    Parameters
    ----------
    df : :class:`pandas.DataFrame`
        A DataFrame holding the accumulated SBU usage.
        See :func:`pre_process_df` and :func:`.get_agregated_sbu`.

    ax : :class:`matplotlib.Axes<matplotlib.axes.Axes>`
        An Axes instance produced by :func:`pre_process_plt`.

    percent : class`bool`
        If ``True``, apply additional formatting for handling percentages.

    Returns
    -------
    :class:`matplotlib.figure.Figure`:
        A Matplotlib Figure constructed from **ax**.

    """
    df_max = df.max().max()
    decimals = 1 - len(str(int(df.values.max())))
    y_max = round(df_max, decimals) + 10**-decimals

    # Format the y-axis
    ax.yaxis.set_major_formatter(plt.ticker.StrMethodFormatter('{x:,.0f}'))
    ax.set(ylim=(0, y_max))

    # Format the x-axis
    i = len(df.index) // 6 or 1
    ax.set(xticks=df.index[0::i])

    today = date.today().strftime('%d %b %Y')
    if percent:
        ax.set_ylabel('SBUs (System Billing Units)  /  %')
        ax.set_title('Accumulated % SBU usage: {}'.format(today),
                     fontdict={'fontsize': 18})
        ax.legend_.set_title('Project (PI): % SBU')
    else:
        ax.set_ylabel('SBUs (System Billing Units)  /  hours')
        ax.set_title('Accumulated SBU usage: {}'.format(today),
                     fontdict={'fontsize': 18})
        ax.legend_.set_title('Project (PI): SBU')
    return ax.get_figure()
Ejemplo n.º 2
0
def get_textobj_dimensions(axes: _matplotlib.axes.Axes,
                           texts: list) -> _numpy.ndarray:
    """
	get the dimensions (width, height) of a list of text objects
	"""
    if not isinstance(axes, _matplotlib.axes.Axes):
        raise TypeError("bad type of 'axes'")
    # get info from axes
    inv_trans = axes.transData.inverted()
    # text width/height must be calculated after rendering
    renderer = axes.get_figure().canvas.get_renderer()
    whs = _numpy.empty((len(texts), 2), dtype=float)
    for wh, t_obj in zip(whs, texts):
        wh[:] = _render_textobj_wh(t_obj, renderer, inv_trans)
    return whs
Ejemplo n.º 3
0
def send_plot(
    user: User, context, plot: matplotlib.axes.Axes, caption: str = ""
):
    """Send a pandas plot to the specified chat"""
    fig = plot.get_figure()
    fig.tight_layout()
    image = io.BytesIO()
    fig.savefig(image, format="png", dpi=300)
    plt.close(fig)
    image.seek(0)
    context.bot.send_photo(
        chat_id=user.telegram_id,
        photo=image,
        parse_mode="Markdown",
        caption=caption,
    )
    pass
Ejemplo n.º 4
0
def plot_lookahead_final_acceptance_fractions(
        sampler_df: Union[pd.DataFrame, str],
        population_sizes: Union[np.ndarray, History],
        relative: bool = False,
        fill: bool = False,
        alpha: float = None,
        t_min: int = 0,
        title: str = "Composition of final acceptances",
        size: tuple = None,
        ax: mpl.axes.Axes = None):
    """Plot fraction of look-ahead samples in final acceptances,
    over generations.

    Parameters
    ----------
    sampler_df:
        Dataframe or file as generated via
        `RedisEvalParallelSampler(log_file=...)`.
    population_sizes:
        The sizes of the populations of accepted particles. If a History is
        passed, those values are extracted automatically, otherwise should
        be for the same time values as `sampler_df`.
    relative:
        Whether to normalize the total evaluations for each generation to 1.
    fill:
        If True, instead of lines, filled areas are drawn that sum up to the
        totals.
    alpha:
        Alpha value for lines or areas.
    t_min:
        The minimum generation to show. E.g. a value of 1 omits the first
        generation.
    title:
        Plot title.
    size:
        The size of the plot in inches.
    ax:
        The axis object to use.

    Returns
    -------
    ax: Axis of the generated plot.
    """
    # process input
    if isinstance(sampler_df, str):
        sampler_df = pd.read_csv(sampler_df, sep=',')
    if alpha is None:
        alpha = 0.7 if fill else 1.0

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # get numbers of final acceptances
    if isinstance(population_sizes, History):
        pop = population_sizes.get_all_populations()

        population_sizes = np.array(
            [pop.loc[pop.t == t, 'particles'] for t in sampler_df.t],
            dtype=float).flatten()

    # restrict to t >= 0
    population_sizes = population_sizes[sampler_df.t >= t_min]
    sampler_df = sampler_df[sampler_df.t >= t_min]

    # extract variables
    t = sampler_df.t

    n_la_acc = sampler_df.n_lookahead_accepted
    # actual look-ahead acceptances cannot be more than requested
    n_la_acc = np.minimum(n_la_acc, population_sizes)

    # actual acceptances are the remaining ones, as these are always later
    n_act_acc = population_sizes - n_la_acc

    # normalize
    if relative:
        n_la_acc /= population_sizes
        n_act_acc /= population_sizes
        population_sizes /= population_sizes

    # plot
    if fill:
        ax.fill_between(t,
                        n_la_acc,
                        population_sizes,
                        alpha=alpha,
                        label="Actual")
        ax.fill_between(t, 0, n_la_acc, alpha=alpha, label="Look-ahead")
    else:
        ax.plot(t,
                population_sizes,
                linestyle='--',
                marker='o',
                color='black',
                alpha=alpha,
                label="Population size")
        ax.plot(t, n_act_acc, marker='o', alpha=alpha, label="Actual")
        ax.plot(t, n_la_acc, marker='o', alpha=alpha, label="Look-ahead")

    # prettify plot
    ax.legend()
    ax.set_title(title)
    ax.set_xlabel("Population index")
    ax.set_ylabel("Final acceptances")
    ax.set_ylim(bottom=0)
    # enforce integer ticks
    ax.xaxis.set_major_locator(MaxNLocator(integer=True))
    if size is not None:
        fig.set_size_inches(size)

    return ax
Ejemplo n.º 5
0
def plot_lookahead_evaluations(sampler_df: Union[pd.DataFrame, str],
                               relative: bool = False,
                               fill: bool = False,
                               alpha: float = None,
                               t_min: int = 0,
                               title: str = "Total evaluations",
                               size: tuple = None,
                               ax: mpl.axes.Axes = None):
    """Plot total vs look-ahead evaluations over the generations.

    Parameters
    ----------
    sampler_df:
        Dataframe or file as generated via
        `RedisEvalParallelSampler(log_file=...)`.
    relative:
        Whether to normalize the total evaluations for each generation to 1.
    fill:
        If True, instead of lines, filled areas are drawn that sum up to the
        totals.
    alpha:
        Alpha value for lines or areas.
    t_min:
        The minimum generation to show. E.g. a value of 1 omits the first
        generation.
    title:
        Plot title.
    size:
        The size of the plot in inches.
    ax:
        The axis object to use.

    Returns
    -------
    ax: Axis of the generated plot.
    """
    # process input
    if isinstance(sampler_df, str):
        sampler_df = pd.read_csv(sampler_df, sep=',')
    if alpha is None:
        alpha = 0.7 if fill else 1.0

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # restrict to t >= 0
    sampler_df = sampler_df[sampler_df.t >= t_min]

    # extract variables
    t = sampler_df.t
    n_la = sampler_df.n_lookahead
    n_eval = sampler_df.n_evaluated
    n_act = n_eval - n_la

    # normalize
    if relative:
        n_la /= n_eval
        n_act /= n_eval
        n_eval /= n_eval

    # plot
    if fill:
        ax.fill_between(t, n_la, n_eval, alpha=alpha, label="Actual")
        ax.fill_between(t, 0, n_la, alpha=alpha, label="Look-ahead")
    else:
        ax.plot(t,
                n_eval,
                linestyle='--',
                marker='o',
                color='black',
                alpha=alpha,
                label="Total")
        ax.plot(t, n_act, marker='o', alpha=alpha, label="Actual")
        ax.plot(t, n_la, marker='o', alpha=alpha, label="Look-ahead")

    # prettify plot
    ax.legend()
    ax.set_title(title)
    ax.set_xlabel("Population index")
    ax.set_ylabel("Evaluations")
    ax.set_ylim(bottom=0)
    # enforce integer ticks
    ax.xaxis.set_major_locator(MaxNLocator(integer=True))
    if size is not None:
        fig.set_size_inches(size)

    return ax
Ejemplo n.º 6
0
def plot_acceptance_rates_trajectory(histories: Union[List, History],
                                     labels: Union[List, str] = None,
                                     title: str = "Acceptance rates",
                                     yscale: str = 'lin',
                                     size: tuple = None,
                                     ax: mpl.axes.Axes = None,
                                     colors: List[str] = None,
                                     normalize_by_ess: bool = False):
    """
    Plot of acceptance rates over all iterations, i.e. one trajectory
    per history.

    Parameters
    ----------
    histories:
        The histories to plot from. History ids must be set correctly.
    labels:
        Labels corresponding to the histories. If None are provided,
        indices are used as labels.
    title:
        Title for the plot.
    yscale:
        The scale on which to plot the counts. Can be one of 'lin', 'log'
        (basis e) or 'log10'
    size:
        The size of the plot in inches.
    ax:
        The axis object to use.
    normalize_by_ess: bool, optional (default = False)
        Indicator to use effective sample size for the acceptance rate in
        place of the population size.

    Returns
    -------
    ax: Axis of the generated plot.
    """
    # preprocess input
    histories = to_lists(histories)
    labels = get_labels(labels, len(histories))
    if colors is None:
        colors = [None] * len(histories)
    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # extract sample numbers
    times = []
    samples = []
    pop_sizes = []
    for history in histories:
        # note: the first entry of time -1 is trivial and is thus ignored here
        h_info = history.get_all_populations()
        times.append(np.array(h_info['t'])[1:])
        if normalize_by_ess:
            ess = np.zeros(len(h_info['t']) - 1)
            for t in np.array(h_info['t'])[1:]:
                w = history.get_weighted_distances(t=t)['w']
                ess[t - 1] = effective_sample_size(w)
            pop_sizes.append(ess)
        else:
            pop_sizes.append(
                np.array(history.get_nr_particles_per_population().values[1:]))
        samples.append(np.array(h_info['samples'])[1:])

    # compute acceptance rates
    rates = []
    for sample, pop_size in zip(samples, pop_sizes):
        rates.append(pop_size / sample)

    # apply scale
    ylabel = "Acceptance rate"
    if yscale == 'log':
        rates = [np.log(rate) for rate in rates]
        ylabel = "log(" + ylabel + ")"
    elif yscale == 'log10':
        rates = [np.log10(rate) for rate in rates]
        ylabel = "log10(" + ylabel + ")"

    # plot
    for t, rate, label, color in zip(times, rates, labels, colors):
        ax.plot(t, rate, 'x-', label=label, color=color)

    # add labels
    ax.legend()
    ax.set_title(title)
    ax.set_ylabel(ylabel)
    ax.set_xlabel("Population index $t$")
    # set size
    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax
Ejemplo n.º 7
0
def plot_sample_numbers_trajectory(histories: Union[List, History],
                                   labels: Union[List, str] = None,
                                   rotation: int = 0,
                                   title: str = "Required samples",
                                   yscale: str = 'lin',
                                   size: tuple = None,
                                   ax: mpl.axes.Axes = None):
    """
    Plot of required sample number over all iterations, i.e. one trajectory
    per history.

    Parameters
    ----------
    histories: Union[List, History]
        The histories to plot from. History ids must be set correctly.
    labels: Union[List ,str], optional
        Labels corresponding to the histories. If None are provided,
        indices are used as labels.
    rotation: int, optional (default = 0)
        Rotation to apply to the plot's x tick labels. For longer labels,
        a tilting of 45 or even 90 can be preferable.
    title: str, optional (default = "Required samples")
        Title for the plot.
    yscale: str, optional (default = 'lin')
        The scale on which to plot the counts. Can be one of 'lin', 'log'
        (basis e) or 'log10'
    size: tuple of float, optional
        The size of the plot in inches.
    ax: matplotlib.axes.Axes, optional
        The axis object to use.

    Returns
    -------
    ax: Axis of the generated plot.
    """
    # preprocess input
    histories = to_lists(histories)
    labels = get_labels(labels, len(histories))

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # extract sample numbers
    times = []
    samples = []
    for history in histories:
        # note: the first entry corresponds to the calibration and should
        # be included here to be fair against methods not requiring
        # calibration
        h_info = history.get_all_populations()
        times.append(np.array(h_info['t']))
        samples.append(np.array(h_info['samples']))

    # apply scale
    ylabel = "Samples"
    if yscale == 'log':
        samples = [np.log(sample) for sample in samples]
        ylabel = "log(" + ylabel + ")"
    elif yscale == 'log10':
        samples = [np.log10(sample) for sample in samples]
        ylabel = "log10(" + ylabel + ")"

    # plot
    for t, sample, label in zip(times, samples, labels):
        ax.plot(t, sample, 'x-', label=label)

    # add labels
    if any(lab is not None for lab in labels):
        ax.legend()
    ax.set_title(title)
    ax.set_ylabel(ylabel)
    ax.set_xlabel("Population index $t$")
    # set size
    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax
Ejemplo n.º 8
0
def plot_sample_numbers(histories: Union[List, History],
                        labels: Union[List, str] = None,
                        rotation: int = 0,
                        title: str = "Required samples",
                        size: tuple = None,
                        ax: mpl.axes.Axes = None):
    """
    Stacked bar plot of required numbers of samples over all iterations.

    Parameters
    ----------

    histories: Union[List, History]
        The histories to plot from. History ids must be set correctly.
    labels: Union[List ,str], optional
        Labels corresponding to the histories. If None are provided,
        indices are used as labels.
    rotation: int, optional (default = 0)
        Rotation to apply to the plot's x tick labels. For longer labels,
        a tilting of 45 or even 90 can be preferable.
    title: str, optional (default = "Total required samples")
        Title for the plot.
    size: tuple of float, optional
        The size of the plot in inches.
    ax: matplotlib.axes.Axes, optional
        The axis object to use.

    Returns
    -------

    ax: Axis of the generated plot.
    """
    # preprocess input
    histories = to_lists(histories)
    labels = get_labels(labels, len(histories))

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    n_run = len(histories)

    # extract sample numbers
    samples = []
    for history in histories:
        # note: the first entry corresponds to the calibration and should
        # be included here to be fair against methods not requiring
        # calibration
        samples.append(np.array(history.get_all_populations()['samples']))

    # create matrix
    n_pop = max(len(sample) for sample in samples)
    matrix = np.zeros((n_pop, n_run))
    for i_sample, sample in enumerate(samples):
        matrix[:len(sample), i_sample] = sample

    # plot bars
    for i_pop in reversed(range(n_pop)):
        ax.bar(x=np.arange(n_run),
               height=matrix[i_pop, :],
               bottom=np.sum(matrix[:i_pop, :], axis=0),
               label=f"Generation {i_pop-1}")

    # add labels
    ax.set_xticks(np.arange(n_run))
    ax.set_xticklabels(labels, rotation=rotation)
    ax.set_title(title)
    ax.set_ylabel("Samples")
    ax.set_xlabel("Run")
    ax.legend()
    # set size
    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax
Ejemplo n.º 9
0
def plot_distance_weights(
    log_files: Union[List[str], str],
    ts: Union[List[int], List[str], int, str] = "last",
    labels: Union[List[str], str] = None,
    colors: Union[List[Any], Any] = None,
    linestyles: Union[List[str], str] = None,
    keys_as_labels: bool = True,
    keys: List[str] = None,
    xticklabel_rotation: float = 0,
    normalize: bool = True,
    size: Tuple[float, float] = None,
    xlabel: str = "Summary statistic",
    ylabel: str = "Weight",
    title: str = None,
    ax: mpl.axes.Axes = None,
    **kwargs,
) -> mpl.axes.Axes:
    """Plot distance weights, one curve per argument.

    Assumes that the weights to be plotted from each file and timepoint have
    the same keys.

    Parameters
    ----------
    log_files:
        The weight log files, as passed to the distance functions, e.g. as
        "scale_log_file" or "info_log_file".
    ts:
        Time points to plot. Defaults to the last entry in each file.
    labels:
        A label for each log file.
    colors:
        A color for each log file.
    linestyles:
        Linestyles to apply.
    keys_as_labels:
        Whether to use the summary statistic keys as x tick labels.
    keys:
        Data keys to plot.
    xticklabel_rotation:
        Angle by which to rotate x tick labels.
    normalize:
        Whether to normalize the weights to sum 1.
    size:
        Figure size in inches, (width, height).
    xlabel:
        x-axis label.
    ylabel:
        y-axis label.
    title:
        Plot title.
    ax:
        Axis object to use.
    **kwargs:
        Additional keyword arguments are passed on to `plt.plot()`
        when plotting lines.

    Returns
    -------
    The used axis object.
    """
    log_files, ts, colors, linestyles = to_lists(log_files, ts, colors,
                                                 linestyles)
    labels = get_labels(labels, len(log_files))

    # default keyword arguments
    if "marker" not in kwargs:
        kwargs["marker"] = "x"

    n_run = len(log_files)

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # add a line per file
    for log_file, t, label, color, linestyle in zip(log_files, ts, labels,
                                                    colors, linestyles):
        weights = load_dict_from_json(log_file)
        if t == "last":
            t = max(weights.keys())
        weights = weights[t]
        if keys is None:
            keys = list(weights.keys())
        weights = np.array([weights[key] for key in keys])
        if normalize:
            weights /= weights.sum()
        ax.plot(
            weights,
            label=label,
            color=color,
            linestyle=linestyle,
            **kwargs,
        )

    # add labels
    if n_run > 1:
        ax.legend()

    # x axis ticks
    if keys_as_labels:
        ax.set_xticks(np.arange(len(keys)))
        ax.set_xticklabels(keys, rotation=xticklabel_rotation)
    else:
        # enforce integer labels
        ax.xaxis.set_major_locator(MaxNLocator(integer=True))

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_title(title)

    if size is not None:
        fig.set_size_inches(size)

    return ax
Ejemplo n.º 10
0
def plot_eps_walltime_lowlevel(end_times: List,
                               eps: List,
                               labels: Union[List, str] = None,
                               unit: str = 's',
                               xscale: str = 'linear',
                               yscale: str = 'log',
                               title: str = "Epsilon over walltime",
                               size: tuple = None,
                               ax: mpl.axes.Axes = None) -> mpl.axes.Axes:
    """Low-level access to `plot_eps_walltime`.
    Directly define `end_times` and `eps`. Note that both should be arrays of
    the same length and at the beginning include a value for the calibration
    iteration. This is just what `pyabc.History.get_all_populations()` returns.
    The first time is used as the base time differences to which are plotted.
    The first epsilon is ignored.
    """
    # preprocess input
    end_times = to_lists(end_times)
    labels = get_labels(labels, len(end_times))
    n_run = len(end_times)

    # check time unit
    if unit not in TIME_UNITS:
        raise AssertionError(f"`unit` must be in {TIME_UNITS}")

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # extract relative walltimes
    walltimes = []
    for end_ts in end_times:
        # compute differences to base
        diffs = end_ts[1:] - end_ts[0]
        # as seconds
        diffs = [diff.total_seconds() for diff in diffs]
        # append
        walltimes.append(diffs)

    # disregard calibration epsilon (inf)
    eps = [ep[1:] for ep in eps]

    for wt, ep, label in zip(walltimes, eps, labels):
        wt = np.asarray(wt)
        # apply time unit
        if unit == MINUTE:
            wt /= 60
        elif unit == HOUR:
            wt /= (60 * 60)
        elif unit == DAY:
            wt /= (60 * 60 * 24)
        # plot
        ax.plot(wt, ep, label=label, marker='o')

    # prettify plot
    if n_run > 1:
        ax.legend()
    ax.set_title(title)
    ax.set_xlabel(f"Time [{unit}]")
    ax.set_ylabel("Epsilon")
    ax.set_xscale(xscale)
    ax.set_yscale(yscale)
    # enforce integer ticks
    ax.xaxis.set_major_locator(MaxNLocator(integer=True))

    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax
Ejemplo n.º 11
0
def plot_total_walltime(histories: Union[List[History], History],
                        labels: Union[List, str] = None,
                        unit: str = 's',
                        rotation: int = 0,
                        title: str = "Total walltimes",
                        size: tuple = None,
                        ax: mpl.axes.Axes = None) -> mpl.axes.Axes:
    """Plot total walltimes, for each history one single-color bar.

    Parameters
    ----------
    histories:
        The histories to plot from. History ids must be set correctly.
    labels:
        Labels corresponding to the histories. If None are provided,
        indices are used as labels.
    unit:
        Time unit to use ('s', 'm', 'h', 'd' as seconds, minutes, hours, days).
    rotation:
        Rotation to apply to the plot's x tick labels. For longer labels,
        a tilting of 45 or even 90 can be preferable.
    title:
        Title for the plot.
    size: tuple of float, optional
        The size of the plot in inches.
    ax: matplotlib.axes.Axes, optional
        The axis object to use.

    Returns
    -------
    ax: Axis of the generated plot.
    """
    # preprocess input
    histories = to_lists(histories)
    labels = get_labels(labels, len(histories))
    n_run = len(histories)

    # check time unit
    if unit not in TIME_UNITS:
        raise AssertionError(f"`unit` must be in {TIME_UNITS}")

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # extract total walltimes
    walltimes = []
    for h in histories:
        abc = h.get_abc()
        walltimes.append((abc.end_time - abc.start_time).total_seconds())
    walltimes = np.asarray(walltimes)

    # apply time unit
    if unit == MINUTE:
        walltimes /= 60
    elif unit == HOUR:
        walltimes /= (60 * 60)
    elif unit == DAY:
        walltimes /= (60 * 60 * 24)

    # plot bars
    ax.bar(x=np.arange(n_run), height=walltimes, label=labels)

    # prettify plot
    ax.set_xticks(np.arange(n_run))
    ax.set_xticklabels(labels, rotation=rotation)
    ax.set_title(title)
    ax.set_xlabel("Run")
    ax.set_ylabel(f"Time [{unit}]")
    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax
Ejemplo n.º 12
0
def plot_walltime_lowlevel(end_times: List,
                           start_times: Union[List, None] = None,
                           labels: Union[List, str] = None,
                           show_calibration: bool = None,
                           unit: str = 's',
                           rotation: int = 0,
                           title: str = "Walltime by generation",
                           size: tuple = None,
                           ax: mpl.axes.Axes = None) -> mpl.axes.Axes:
    """Low-level access to `plot_walltime`.
    Directly define `end_times` and `start_times`."""
    # preprocess input
    end_times = to_lists(end_times)
    labels = get_labels(labels, len(end_times))
    n_run = len(end_times)

    # check start times
    if start_times is None:
        if show_calibration:
            raise AssertionError(
                "To plot the calibration iteration, start times are needed.")
        # fill in dummy times which will not be used anyhow
        start_times = [datetime.datetime.now() for _ in range(n_run)]

    # check time unit
    if unit not in TIME_UNITS:
        raise AssertionError(f"`unit` must be in {TIME_UNITS}")

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # extract relative walltimes
    walltimes = []
    for start_t, end_ts in zip(start_times, end_times):
        times = [start_t, *end_ts]
        # compute stacked differences
        diffs = [end - start for start, end in zip(times[:-1], times[1:])]
        # as seconds
        diffs = [diff.total_seconds() for diff in diffs]
        # append
        walltimes.append(diffs)
    walltimes = np.asarray(walltimes)

    # create matrix
    n_pop = max(len(wt) for wt in walltimes)
    matrix = np.zeros((n_pop, n_run))
    for i_run, wt in enumerate(walltimes):
        matrix[:len(wt), i_run] = wt

    if not show_calibration:
        matrix = matrix[1:, :]

    # apply time unit
    if unit == MINUTE:
        matrix /= 60
    elif unit == HOUR:
        matrix /= (60 * 60)
    elif unit == DAY:
        matrix /= (60 * 60 * 24)

    # plot bars
    for i_pop in reversed(range(matrix.shape[0])):
        pop_ix = i_pop - 1
        if not show_calibration:
            pop_ix = i_pop
        ax.bar(x=np.arange(n_run),
               height=matrix[i_pop, :],
               bottom=np.sum(matrix[:i_pop, :], axis=0),
               label=f"Generation {pop_ix}")

    # prettify plot
    ax.set_xticks(np.arange(n_run))
    ax.set_xticklabels(labels, rotation=rotation)
    ax.set_title(title)
    ax.set_xlabel("Run")
    ax.set_ylabel(f"Time [{unit}]")
    ax.legend()
    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax
Ejemplo n.º 13
0
def plot_epsilons(histories: Union[List, History],
                  labels: Union[List, str] = None,
                  colors: List = None,
                  scale: str = None,
                  title: str = "Epsilon values",
                  size: tuple = None,
                  ax: mpl.axes.Axes = None):
    """
    Plot epsilon trajectory.

    Parameters
    ----------

    histories: Union[List, History]
        The histories to plot from. History ids must be set correctly.
    labels: Union[List ,str], optional
        Labels corresponding to the histories. If None are provided,
        indices are used as labels.
    colors: List, optional
        Colors to use for the lines. If None, then the matplotlib
        default values are used.
    scale: str, optional (default='lin')
        Scaling to apply to the y axis.
        Must be one of 'lin', 'log', 'log10'.
    title: str, optional (default = "Epsilon values")
        Title for the plot.
    size: tuple of float, optional
        The size of the plot in inches.
    ax: matplotlib.axes.Axes, optional
        The axis object to use. A new one is created if None.

    Returns
    -------

    ax: Axis of the generated plot.
    """
    # preprocess input
    histories, labels = to_lists_or_default(histories, labels)
    if colors is None:
        colors = [None for _ in range(len(histories))]
    if scale is None:
        scale = 'lin'

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # extract epsilons
    eps = []
    for history in histories:
        # note: first entry is from calibration and thus translates to inf,
        # thus must be discarded
        eps.append(np.array(history.get_all_populations()['epsilon'][1:]))

    # scale
    eps = _apply_scale(eps, scale)

    # plot
    for ep, label, color in zip(eps, labels, colors):
        ax.plot(ep, 'x-', label=label, color=color)

    # format
    ax.set_xlabel("Population index")
    ax.set_ylabel(_get_ylabel(scale))
    ax.legend()
    ax.set_title(title)
    # enforce integer ticks
    ax.xaxis.set_major_locator(MaxNLocator(integer=True))
    # set size
    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax
Ejemplo n.º 14
0
def plot_polar_histogram(
    values: np.ndarray,
    r_edges: np.ndarray,
    theta_edges: np.ndarray,
    ax: mpl.axes.Axes = None,  # Not sure how to type this (not optional
    # but hard to find default)
    cmap: Optional[str] = None,
    vmin: Optional[float] = None,
    vmax: Optional[float] = None,
    symmetric_color_limits: bool = False,
    interpolation_factor: float = 5,
    angle_convention: str = "clock",
):
    """Plot color polar plot

    :param values: Values to color-code
    :param r_edges: Edges in radius
    :param theta_edges: Edges in angle
    :param ax: matplotlib.axes.Axes. Needs to be `polar=True`
    :param cmap: matplotlib colormap
    :param vmin: lower limit of colorbar range. If None, from data
    :param vmax: upper limit of colorbar range. If None, from data
    :param symmetric_color_limits: How to obtain lower and upper
    limits of colormap from data. If False, limits are (min, max). If
    True, limits are (-max(abs), max(abs))
    :param interpolation_factor: If None or 1, no interpolation is
    performed. If int > 1, each sector is broken in interpolation_factor
    parts, so they look more circular and less polygonal
    :param angle_convention: If "clock", angles increase clockwise
    and are 0 for positive y axis. If "math", angles increase
    counterclockwise and are 0 for positive x axis
    """

    if interpolation_factor is not None:
        values, theta_edges, r_edges = interpolate_polarmap_angles(
            values, theta_edges, r_edges, factor=interpolation_factor)
    theta, r = np.meshgrid(theta_edges, r_edges)

    # Select color limits:
    if symmetric_color_limits:
        vmax_ = np.nanmax(np.abs(values))
        vmin_ = -vmax_
    else:
        vmax_ = np.nanmax(values)
        vmin_ = np.nanmin(values)

    # Only use them if not specified in input
    if vmin is None:
        vmin = vmin_
    if vmax is None:
        vmax = vmax_

    # Plot histogram/map
    fig = ax.get_figure()
    im = ax.pcolormesh(theta, r, values, cmap=cmap, vmin=vmin, vmax=vmax)
    fig.colorbar(im, ax=ax, cmap=cmap)

    if angle_convention == "clock":
        # Adjusting axis and sense of rotation to make it compatible
        # with [2]: Direction of movement is y axis, angles increase clockwise
        ax.set_theta_zero_location("N")
        ax.set_theta_direction(-1)
Ejemplo n.º 15
0
def plot_acceptance_rates_trajectory(histories: Union[List, History],
                                     labels: Union[List, str] = None,
                                     rotation: int = 0,
                                     title: str = "Acceptance rates",
                                     yscale: str = 'lin',
                                     size: tuple = None,
                                     ax: mpl.axes.Axes = None,
                                     colors: List[str] = None):
    """
    Plot of acceptance rates over all iterations, i.e. one trajectory
    per history.

    Parameters
    ----------

    histories: Union[List, History]
        The histories to plot from. History ids must be set correctly.
    labels: Union[List ,str], optional
        Labels corresponding to the histories. If None are provided,
        indices are used as labels.
    rotation: int, optional (default = 0)
        Rotation to apply to the plot's x tick labels. For longer labels,
        a tilting of 45 or even 90 can be preferable.
    title: str, optional (default = "Acceptance rates")
        Title for the plot.
    yscale: str, optional (default = 'lin')
        The scale on which to plot the counts. Can be one of 'lin', 'log'
        (basis e) or 'log10'
    size: tuple of float, optional
        The size of the plot in inches.
    ax: matplotlib.axes.Axes, optional
        The axis object to use.

    Returns
    -------

    ax: Axis of the generated plot.
    """
    # preprocess input
    histories, labels = to_lists_or_default(histories, labels)
    if colors is None:
        colors = [None] * len(histories)
    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # extract sample numbers
    times = []
    samples = []
    pop_sizes = []
    for history in histories:
        # note: the first entry of time -1 is trivial and is thus ignored here
        h_info = history.get_all_populations()
        times.append(np.array(h_info['t'])[1:])
        samples.append(np.array(h_info['samples'])[1:])
        pop_sizes.append(
            np.array(history.get_nr_particles_per_population().values[1:]))

    # compute acceptance rates
    rates = []
    for sample, pop_size in zip(samples, pop_sizes):
        rates.append(pop_size / sample)

    # apply scale
    ylabel = "Acceptance rate"
    if yscale == 'log':
        rates = [np.log(rate) for rate in rates]
        ylabel = "log(" + ylabel + ")"
    elif yscale == 'log10':
        rates = [np.log10(rate) for rate in rates]
        ylabel = "log10(" + ylabel + ")"

    # plot
    for t, rate, label, color in zip(times, rates, labels, colors):
        ax.plot(t, rate, 'x-', label=label, color=color)

    # add labels
    ax.legend()
    ax.set_title(title)
    ax.set_ylabel(ylabel)
    ax.set_xlabel("Population index $t$")
    # set size
    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax
Ejemplo n.º 16
0
def plot_lookahead_acceptance_rates(sampler_df: Union[pd.DataFrame, str],
                                    t_min: int = 0,
                                    title: str = "Acceptance rates",
                                    size: tuple = None,
                                    ax: mpl.axes.Axes = None):
    """Plot acceptance rates for look-ahead vs ordinary samples.
    The ratios are relative to all accepted particles, including eventually
    discarded ones.

    Parameters
    ----------
    sampler_df:
        Dataframe or file as generated via
        `RedisEvalParallelSampler(log_file=...)`.
    t_min:
        The minimum generation to show. E.g. a value of 1 omits the first
        generation.
    title:
        Plot title.
    size:
        The size of the plot in inches.
    ax:
        The axis object to use.

    Returns
    -------
    ax: Axis of the generated plot.
    """
    # process input
    if isinstance(sampler_df, str):
        sampler_df = pd.read_csv(sampler_df, sep=',')

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # restrict to t >= 0
    sampler_df = sampler_df[sampler_df.t >= t_min]

    # extract variables

    # time
    t = sampler_df.t

    # look-ahead acceptances and samples
    n_la_acc = sampler_df.n_lookahead_accepted
    n_la = sampler_df.n_lookahead

    # total acceptances and samples
    n_all_acc = sampler_df.n_accepted
    n_all = sampler_df.n_evaluated

    # difference (actual proposal)
    n_act_acc = n_all_acc - n_la_acc
    n_act = n_all - n_la

    # plot
    ax.plot(t,
            n_all_acc / n_all,
            linestyle='--',
            marker='o',
            color='black',
            label="Combined")
    ax.plot(t, n_act_acc / n_act, marker='o', label="Actual")
    ax.plot(t, n_la_acc / n_la, marker='o', label="Look-ahead")

    # prettify plot
    ax.legend()
    ax.set_title(title)
    ax.set_xlabel("Population index")
    ax.set_ylabel("Acceptance rate")
    ax.set_ylim(bottom=0)
    # enforce integer ticks
    ax.xaxis.set_major_locator(MaxNLocator(integer=True))
    if size is not None:
        fig.set_size_inches(size)

    return ax
Ejemplo n.º 17
0
def plot_total_sample_numbers(histories: Union[List, History],
                              labels: Union[List, str] = None,
                              rotation: int = 0,
                              title: str = "Total required samples",
                              yscale: str = 'lin',
                              size: tuple = None,
                              ax: mpl.axes.Axes = None):
    """
    Bar plot of total required sample number over all iterations, i.e.
    a single-colored bar per history, in contrast to `plot_sample_numbers`,
    which visually distinguishes iterations.

    Parameters
    ----------
    histories: Union[List, History]
        The histories to plot from. History ids must be set correctly.
    labels: Union[List ,str], optional
        Labels corresponding to the histories. If None are provided,
        indices are used as labels.
    rotation: int, optional (default = 0)
        Rotation to apply to the plot's x tick labels. For longer labels,
        a tilting of 45 or even 90 can be preferable.
    title: str, optional (default = "Total required samples")
        Title for the plot.
    yscale: str, optional (default = 'lin')
        The scale on which to plot the counts. Can be one of 'lin', 'log'
        (basis e) or 'log10'
    size: tuple of float, optional
        The size of the plot in inches.
    ax: matplotlib.axes.Axes, optional
        The axis object to use.

    Returns
    -------
    ax: Axis of the generated plot.
    """
    # preprocess input
    histories = to_lists(histories)
    labels = get_labels(labels, len(histories))

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    n_run = len(histories)

    # extract sample numbers
    samples = []
    for history in histories:
        # note: the first entry corresponds to the calibration and should
        # be included here to be fair against methods not requiring
        # calibration
        samples.append(np.sum(history.get_all_populations()['samples']))
    samples = np.array(samples)

    # apply scale
    ylabel = "Total samples"
    if yscale == 'log':
        samples = np.log(samples)
        ylabel = "log(" + ylabel + ")"
    elif yscale == 'log10':
        samples = np.log10(samples)
        ylabel = "log10(" + ylabel + ")"

    # plot bars
    ax.bar(x=np.arange(n_run), height=samples)

    # add labels
    ax.set_xticks(np.arange(n_run))
    ax.set_xticklabels(labels, rotation=rotation)
    ax.set_title(title)
    ax.set_ylabel(ylabel)
    ax.set_xlabel("Run")
    # set size
    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax
Ejemplo n.º 18
0
def plot_effective_sample_sizes(
    histories: Union[List, History],
    labels: Union[List, str] = None,
    rotation: int = 0,
    title: str = "Effective sample size",
    relative: bool = False,
    colors: List = None,
    size: tuple = None,
    ax: mpl.axes.Axes = None,
):
    """
    Plot effective sample sizes over all iterations.

    Parameters
    ----------

    histories: Union[List, History]
        The histories to plot from. History ids must be set correctly.
    labels: Union[List ,str], optional
        Labels corresponding to the histories. If None are provided,
        indices are used as labels.
    rotation: int, optional (default = 0)
        Rotation to apply to the plot's x tick labels. For longer labels,
        a tilting of 45 or even 90 can be preferable.
    title: str, optional (default = "Total required samples")
        Title for the plot.
    relative: bool, optional (default = False)
        Whether to show relative sizes (to 1) or w.r.t. the real number
        of particles.
    colors: List, optional
        Colors to use for the lines. If None, then the matplotlib
        default values are used.
    size: tuple of float, optional
        The size of the plot in inches.
    ax: matplotlib.axes.Axes, optional
        The axis object to use. A new one is created if None.

    Returns
    -------

    ax: Axis of the generated plot.
    """
    # preprocess input
    histories = to_lists(histories)
    labels = get_labels(labels, len(histories))
    if colors is None:
        colors = [None for _ in range(len(histories))]

    # create figure
    if ax is None:
        fig, ax = plt.subplots()
    else:
        fig = ax.get_figure()

    # extract effective sample sizes
    essss = []  # :)
    for history in histories:
        esss = []
        for t in range(0, history.max_t + 1):
            # we need the weights not normalized to 1 for each model
            w = history.get_weighted_distances(t=t)['w']
            ess = effective_sample_size(w)
            if relative:
                ess /= len(w)
            esss.append(ess)
        essss.append(esss)

    # plot
    for esss, label, color in zip(essss, labels, colors):
        ax.plot(range(0, len(esss)), esss, 'x-', label=label, color=color)

    # format
    ax.set_xlabel("Population index")
    ax.set_ylabel("ESS")
    if any(lab is not None for lab in labels):
        ax.legend()
    ax.set_title(title)
    # enforce integer ticks
    ax.xaxis.set_major_locator(MaxNLocator(integer=True))
    # set size
    if size is not None:
        fig.set_size_inches(size)
    fig.tight_layout()

    return ax