Exemplo n.º 1
0
def add_custom_ticks(ax: plt.Axes, tick_interp: np.ndarray):
    # I'm playing some tricks here, and just relabeling the ticks on the x-axis
    xticks = np.array(ax.get_xticks(), dtype=np.int)

    # This catch is necessary because sometimes the last tick is out of bonds for our
    # interpreted temp data
    if xticks[-1] > tick_interp[-1].size:
        xticks = xticks[:-1]
    ax.set_xticklabels(["{:.0f}".format(t) for t in tick_interp[xticks]])
Exemplo n.º 2
0
def update_disc_plot(ax: plt.Axes, d_x, d_g_z, update_interval):
    clear_line(ax, 'dx')
    clear_line(ax, 'dgz')

    x = np.arange(1, len(d_x) + 1)
    ax.plot(x, d_x, color='#308862', label='D(x)', gid='dx')
    ax.plot(x, d_g_z, color='#B23F62', label='D(G(z))', gid='dgz', alpha=0.9)
    ax.legend(loc='upper right', framealpha=0.75, bbox_to_anchor=(1.008, 1.136), ncol=5)
    ax.set_xlim(left=1, right=len(x) + 0.01)
    ax.set_ylim(0, 1.0)
    ax.set_yticks(np.arange(0, 1, 0.1))
    ticks = ax.get_xticks()
    ax.set_xticklabels([f'{t:.0f}' for t in ticks * update_interval])
Exemplo n.º 3
0
def update_loss_plot(ax: plt.Axes, d_loss, d_fake_loss, g_loss, update_interval, separate=False):
    clear_line(ax, 'd_loss')
    clear_line(ax, 'g_loss')

    x = np.arange(1, len(d_loss) + 1)

    if separate:
        ax.plot(x, np.add(d_loss, d_fake_loss), color='dodgerblue', label='D Loss', gid='d_loss')
        clear_line(ax, 'd_real_loss')
        ax.plot(x, d_loss, color='lightseagreen', label='D Loss(Real)', gid='d_real_loss')
        clear_line(ax, 'd_fake_loss')
        ax.plot(x, d_fake_loss, color='mediumpurple', label='D Loss(Fake)', gid='d_fake_loss')
    else:
        ax.plot(x, d_loss, color='dodgerblue', label='D Loss', gid='d_loss')

    ax.plot(x, g_loss, color='coral', label='G Loss', gid='g_loss', alpha=0.9)
    ax.legend(loc='upper right', framealpha=0.75, bbox_to_anchor=(1.008, 1.136), ncol=5)
    ax.set_xlim(left=1, right=len(x) + 0.01)
    ticks = ax.get_xticks()
    ax.set_xticklabels([f'{t:.0f}' for t in ticks * update_interval])
Exemplo n.º 4
0
def add_percent_axis(ax: plt.Axes, data_size, flip_axis: bool = False) -> plt.Axes:
    """
    Adds a twin axis with percentages to a count plot.

    Args:
        ax: Plot axes figure to add percentage axis to
        data_size: Total count to use to normalize percentages
        flip_axis: Whether the countplot had its axes flipped

    Returns:
        Twin axis that percentages were added to
    """
    if flip_axis:
        ax_perc = ax.twiny()
        ax_perc.set_xticks(100 * ax.get_xticks() / data_size)
        ax_perc.set_xlim(
            (
                100.0 * (float(ax.get_xlim()[0]) / data_size),
                100.0 * (float(ax.get_xlim()[1]) / data_size),
            )
        )
        ax_perc.xaxis.set_major_formatter(mtick.PercentFormatter())
        ax_perc.xaxis.set_tick_params(labelsize=10)
    else:
        ax_perc = ax.twinx()
        ax_perc.set_yticks(100 * ax.get_yticks() / data_size)
        ax_perc.set_ylim(
            (
                100.0 * (float(ax.get_ylim()[0]) / data_size),
                100.0 * (float(ax.get_ylim()[1]) / data_size),
            )
        )
        ax_perc.yaxis.set_major_formatter(mtick.PercentFormatter())
        ax_perc.yaxis.set_tick_params(labelsize=10)
    ax_perc.grid(False)
    return ax_perc
Exemplo n.º 5
0
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()
Exemplo n.º 6
0
def break_axis(
    amin,
    amax=None,
    xy='x',
    ax: plt.Axes = None,
    fun_draw: Callable = None,
    margin=0.05,
) -> (plt.Axes, plt.Axes):
    """
    :param amin: data coordinate to start breaking from
    :param amax: data coordinate to end breaking at
    :param xy: 'x' or 'y'
    :param fun_draw: if not None, fun_draw(ax1) and fun_draw(ax2) will
    be run to recreate ax. Use the same function as that was called for
    with ax. Use, e.g., fun_draw=lambda ax: ax.plot(x, y)
    :return: axs: a list of axes created
    """

    if amax is None:
        amax = amin

    if ax is None:
        ax = plt.gca()

    if xy == 'x':
        rect = ax.get_position().bounds
        lim = ax.get_xlim()
        prop_min = (amin - lim[0]) / (lim[1] - lim[0])
        prop_max = (amax - lim[0]) / (lim[1] - lim[0])
        rect1 = np.array([rect[0], rect[1], rect[2] * prop_min, rect[3]])
        rect2 = [
            rect[0] + rect[2] * prop_max, rect[1], rect[2] * (1 - prop_max),
            rect[3]
        ]

        fig = ax.figure  # type: plt.Figure
        ax1 = fig.add_axes(plt.Axes(fig=fig, rect=rect1))
        ax1.update_from(ax)
        if fun_draw is not None:
            fun_draw(ax1)
        ax1.set_xticks(ax.get_xticks())
        ax1.set_xlim(lim[0], amin)
        ax1.spines['right'].set_visible(False)

        ax2 = fig.add_axes(plt.Axes(fig=fig, rect=rect2))
        ax2.update_from(ax)
        if fun_draw is not None:
            fun_draw(ax2)
        ax2.set_xticks(ax.get_xticks())
        ax2.set_xlim(amax, lim[1])
        ax2.spines['left'].set_visible(False)
        ax2.set_yticks([])

        ax.set_visible(False)
        # plt.show()  # CHECKED
        axs = [ax1, ax2]

    elif xy == 'y':
        rect = ax.get_position().bounds
        lim = ax.get_ylim()
        prop_all = ((amin - lim[0]) + (lim[1] - amax)) / (1 - margin)
        prop_min = (amin - lim[0]) / prop_all
        prop_max = (lim[1] - amax) / prop_all
        rect1 = np.array([rect[0], rect[1], rect[2], rect[3] * prop_min])
        rect2 = [
            rect[0], rect[1] + rect[3] * (1 - prop_max), rect[2],
            rect[3] * (1 - prop_max)
        ]

        fig = ax.figure  # type: plt.Figure
        ax1 = fig.add_axes(plt.Axes(fig=fig, rect=rect1))
        ax1.update_from(ax)
        if fun_draw is not None:
            fun_draw(ax1)
        ax1.set_yticks(ax.get_yticks())
        ax1.set_ylim(lim[0], amin)
        ax1.spines['top'].set_visible(False)

        ax2 = fig.add_axes(plt.Axes(fig=fig, rect=rect2))
        ax2.update_from(ax)
        if fun_draw is not None:
            fun_draw(ax2)
        ax2.set_yticks(ax.get_yticks())
        ax2.set_ylim(amax, lim[1])
        ax2.spines['bottom'].set_visible(False)
        ax2.set_xticks([])

        ax.set_visible(False)
        # plt.show()  # CHECKED
        axs = [ax1, ax2]

    else:
        raise ValueError()

    return axs
Exemplo n.º 7
0
def cases_and_deaths(
    data: pd.DataFrame,
    dates: bool = False,
    ax: plt.Axes = None,
    smooth: bool = True,
    cases: str = "cases",
    deaths: str = "deaths",
    tight_layout=False,
    **kwargs,
) -> plt.Axes:
    """
    A simple chart showing observed new cases cases as vertical bars and
    a smoothed out prediction of this curve.

    Args:
        data:
            A dataframe with ["cases", "deaths"] columns.
        dates:
            If True, show dates instead of days in the x-axis.
        ax:
            An explicit matplotlib axes.
        smooth:
            If True, superimpose a plot of a smoothed-out version of the cases
            curve.
        cases:
        deaths:
            Name of the cases/deaths columns in the dataframe.
    """

    if not dates:
        data = data.reset_index(drop=True)

    # Smoothed data
    col_names = {cases: _("Cases"), deaths: _("Deaths")}
    if smooth:
        from pydemic import fitting as fit

        smooth = pd.DataFrame(
            {
                _("{} (smooth)").format(col_names[cases]):
                fit.smoothed_diff(data[cases]),
                _("{} (smooth)").format(col_names[deaths]):
                fit.smoothed_diff(data[deaths]),
            },
            index=data.index,
        )
        ax = smooth.plot(legend=False, lw=2, ax=ax)

    # Prepare cases dataframe and plot it
    kwargs.setdefault("alpha", 0.5)
    new_cases = data.diff().fillna(0)
    new_cases = new_cases.rename(col_names, axis=1)

    if "ylim" not in kwargs:
        deaths = new_cases.iloc[:, 1]
        exp = np.log10(deaths[deaths > 0]).mean()
        exp = min(10, int(exp / 2))
        kwargs["ylim"] = (10**exp, None)
    ax: plt.Axes = new_cases.plot.bar(width=1.0, ax=ax, **kwargs)

    # Fix xticks
    periods = 7 if dates else 10
    xticks = ax.get_xticks()
    labels = ax.get_xticklabels()
    ax.set_xticks(xticks[::periods])
    ax.set_xticklabels(labels[::periods])
    ax.tick_params("x", rotation=0)
    ax.set_ylim(1, None)
    if tight_layout:
        fig = ax.get_figure()
        fig.tight_layout()
    return ax
Exemplo n.º 8
0
    def plot_map(self,
                 im: np.array,
                 title: str = '',
                 cbar_unit: str = None,
                 tag: str = None,
                 meta: dict = None,
                 cmap: str = 'viridis',
                 view_extent: np.array = None,
                 outline: bool = False,
                 points: bool = False,
                 point_color: bool = False,
                 rectangle: bool = False,
                 labels: bool = False,
                 ticks: bool = True,
                 scamap: bool = None,
                 ax: plt.Axes = None,
                 hillshade: bool = False,
                 scale_dict: dict = None,
                 grid: bool = True,
                 alpha: float = 1,
                 showplot: bool = False,
                 sci: bool = False,
                 figsize: tuple = None,
                 ashape: bool = None):

        # Main plotting function. Ensures that all other plots have the same parameters
        """ Inputs:
        im: The 3D np array to be plotted. Can be the DEM, the thickness, the error, etc.
        title: The title of the plot
        cbar_unit: The units of the color bar
        tag: The tag of the plot with which it will be saved
        cmap: The colormap wanted for the plot, defaults to viridis
        view_extent: The extent wanted for the plot. Defaults to None which will set the extent to the whole map.
                     Takes as input a numpy array, like the self.point_extent array
        Outline: Boolean value, defaults to False. Set to true if you want the outline plotted on the map.
        points: Boolean value, defaults to False. Set to true if you want the points plotted on the map.
        point_color: Boolean value, defaults to False. Set to true if you want the points colored by the thickness.
                     Could be changed in the future to a scalar map instead of a boolean.
        rectangle: Boolean value, defaults to False. Set to true if you want a rectangle outlining the points' extent
                   in the map. CURRENTLY DOESN'T WORK PROPERLY
        labels: Boolean value, defaults to False. Set to true if you want the x and y labels plotted.
        ticks: Boolean value, defaults to True. Set to true if you want the x and y ticks plotted.
        scamap: Scalarmap object, defaults to None. Set if you want a specific colormap scale. Useful for subplots.
        ax: matplotlib Ax object, defaults to None. Set if you want to specify on which ax to plot.
            Useful for subplots.
        hillshade: Boolean value, defaults to False. Set for a hillshade effect, especially on DEMs
        scale: Dictionary, defaults to None. Parameters to add a scale to the map
        alpha: Float defaults to 1. Sets the transparency of the main map image
        showplot: Boolean, defaults to False. Set to true if you want to see the figure. Only pops up if
                  no ax object is provided.
        """
        # Gets the two last dimensions of the 3D array. Needed because rasters from rasterio are of (1, m, n) size
        if len(im.shape) == 3:
            im = im[0]

        if meta is None:
            meta = self.meta

        # Manages the extent array. The order needed here is different than given from the shapely format
        b = [0, 2, 1, 3]
        extent = [
            rasterio.transform.array_bounds(*im.shape, meta['transform'])[i]
            for i in b
        ]
        xmin, xmax, ymin, ymax = [self.outline.total_bounds[i] for i in b]

        if view_extent is not None:
            view_extent = [view_extent[i] for i in b]
            xmin, xmax, ymin, ymax = view_extent

        fig = None
        if ax is None:
            fig = plt.figure(figsize=figsize, tight_layout=True)
            ax = plt.gca()

        # Plot the image and add a colorbar
        if scamap is None:
            norm = Normalize(np.nanmin(im), np.nanmax(im))
            scamap = plt.cm.ScalarMappable(cmap=cmap, norm=norm)

        if hillshade:
            hills = im_to_hillshade(self.dem_im[0], 225, 40)
            ax.imshow(hills, extent=[self.extent[i] for i in b], cmap='Greys')

        img = ax.imshow(im, cmap=cmap, extent=extent, alpha=alpha)

        if cbar_unit is not None:
            divider = make_axes_locatable(ax)
            cax = divider.append_axes('right', size=0.2, pad=0.1)
            cbar = plt.colorbar(img, cax=cax, orientation='vertical')
            cbar.ax.get_yaxis().labelpad = 15
            cbar.ax.set_title(f'{cbar_unit}')

        c = 'black'
        e = None
        if point_color:
            c = scamap.to_rgba(self.gpr.iloc[:, 2])
            e = 'black'

        # Plots various map accessories if asked
        if points:
            lw = 0.1
            if len(self.gpr) > 1000:
                lw = 0
            ax.scatter(self.gpr.geometry.x,
                       self.gpr.geometry.y,
                       c=c,
                       cmap=cmap,
                       edgecolors=e,
                       linewidths=lw)

        if outline:
            self.outline.plot(ax=ax, facecolor='None', edgecolor='black')

        if rectangle:
            rec = self.create_rectangle(color='red', lw=1)
            ax.add_patch(rec)
        if ashape:
            x, y = self.ashape.unary_union.exterior.xy
            ax.plot(x, y, color='red', lw=1)

        if scale_dict is not None:
            scale_dict = parse_scale_dict(scale_dict)

            y_offset = (ymax - ymin) * scale_dict['y_offset']
            x_offset = (xmax - xmin) * scale_dict['x_offset']

            length = scale_dict['length']
            label_length = length

            if scale_dict['units'] == 'km':
                length *= 1000

            xs = [xmin + x_offset, xmin + x_offset + length]
            ys = [ymin + y_offset, ymin + y_offset]

            bar = ax.plot(xs, ys, linewidth=5, color=scale_dict['color'])
            txt = ax.text(np.mean(xs),
                          np.mean(ys) + y_offset *
                          (1 + 0.05 / scale_dict['y_offset']),
                          f'{label_length} {scale_dict["units"]}',
                          ha='center',
                          va='center',
                          color=scale_dict['color'])
            # txt.set_path_effects([pe.withStroke(linewidth=5, foreground='w')])

        # Customises the map
        ax.set_title(f'{title}')
        ax.set_xlim(xmin, xmax)
        ax.set_ylim(ymin, ymax)
        ax.set_aspect('equal')

        if labels:
            ax.set_xlabel('Eastings [m]')
            ax.set_ylabel('Northing [m]')
        if ticks:
            if sci:
                ax.ticklabel_format(axis='both', style='sci', scilimits=(0, 0))
            else:
                ax.set_yticklabels(
                    ['{:,.0f}'.format(y) for y in ax.get_yticks().tolist()])
                ax.set_xticklabels(
                    ['{:,.0f}'.format(x) for x in ax.get_xticks().tolist()],
                    rotation=-45)

        else:
            ax.set_yticklabels([])
            ax.set_xticklabels([])
            ax.set_xticks([])
            ax.set_yticks([])

        if grid:
            ax.grid()

        # Defines a custom legend for the outline due to a geopandas bug
        # Currently no legend, need to figure out something for every model and dem source
        """handles, labels = ax.get_legend_handles_labels()
        handles.append(mlines.Line2D([], [], color='black', label=data['outline']))
        handles.append(matplotlib.patches.Patch (color='none', label=crs))
        ax.legend(handles=handles, bbox_to_anchor=(0.5, data['leg_pos']), loc="lower center",
                  bbox_transform=fig.transFigure, ncol=len(handles), frameon=True)"""

        if fig is not None:
            if showplot:
                plt.show()
            if tag is not None:
                fig.savefig(f'{self.img_folder}/{tag}.png',
                            bbox_inches='tight')
            return fig, ax

        else:
            return img