def scatter_base(
    Y,
    colors='blue',
    sort_order=True,
    alpha=None,
    highlights=[],
    right_margin=None,
    left_margin=None,
    projection='2d',
    title=None,
    component_name='DC',
    component_indexnames=[1, 2, 3],
    axis_labels=None,
    colorbars=[False],
    sizes=[1],
    color_map='viridis',
    show_ticks=True,
    ax=None,
) -> Union[Axes, List[Axes]]:
    """Plot scatter plot of data.

    Parameters
    ----------
    Y : np.ndarray
        Data array.
    projection : {'2d', '3d'}

    Returns
    -------
    Depending on whether supplying a single array or a list of arrays,
    return a single axis or a list of axes.
    """
    if isinstance(highlights, dict):
        highlights_indices = sorted(highlights)
        highlights_labels = [highlights[i] for i in highlights_indices]
    else:
        highlights_indices = highlights
        highlights_labels = []
    # if we have a single array, transform it into a list with a single array
    if type(colors) == str: colors = [colors]
    if len(sizes) != len(colors) and len(sizes) == 1:
        sizes = [sizes[0] for i in range(len(colors))]
    axs, panel_pos, draw_region_width, figure_width = setup_axes(
        ax=ax, panels=colors, colorbars=colorbars, projection=projection,
        right_margin=right_margin, left_margin=left_margin,
        show_ticks=show_ticks)
    for icolor, color in enumerate(colors):
        ax = axs[icolor]
        left = panel_pos[2][2*icolor]
        bottom = panel_pos[0][0]
        width = draw_region_width / figure_width
        height = panel_pos[1][0] - bottom
        Y_sort = Y
        if not is_color_like(color) and sort_order:
            sort = np.argsort(color)
            color = color[sort]
            Y_sort = Y[sort]
        if projection == '2d': data = Y_sort[:, 0], Y_sort[:, 1]
        elif projection == '3d': data = Y_sort[:, 0], Y_sort[:, 1], Y_sort[:, 2]
        if not isinstance(color, str) or color != 'white':
            sct = ax.scatter(*data,
                             marker='.',
                             c=color,
                             alpha=alpha,
                             edgecolors='none',  # 'face',
                             s=sizes[icolor],
                             cmap=color_map,
                             rasterized=settings._vector_friendly)
        if colorbars[icolor]:
            width = 0.006 * draw_region_width / len(colors)
            left = panel_pos[2][2*icolor+1] + (1.2 if projection == '3d' else 0.2) * width
            rectangle = [left, bottom, width, height]
            fig = pl.gcf()
            ax_cb = fig.add_axes(rectangle)
            cb = pl.colorbar(sct, format=ticker.FuncFormatter(ticks_formatter),
                             cax=ax_cb)
        # set the title
        if title is not None: ax.set_title(title[icolor])
        # output highlighted data points
        for iihighlight, ihighlight in enumerate(highlights_indices):
            ihighlight = ihighlight if isinstance(ihighlight, int) else int(ihighlight)
            data = [Y[ihighlight, 0]], [Y[ihighlight, 1]]
            if '3d' in projection:
                data = [Y[ihighlight, 0]], [Y[ihighlight, 1]], [Y[ihighlight, 2]]
            ax.scatter(*data, c='black',
                       facecolors='black', edgecolors='black',
                       marker='x', s=10, zorder=20)
            highlight_text = (highlights_labels[iihighlight] if
                              len(highlights_labels) > 0
                              else str(ihighlight))
            # the following is a Python 2 compatibility hack
            ax.text(*([d[0] for d in data] + [highlight_text]),
                    zorder=20,
                    fontsize=10,
                    color='black')
        if not show_ticks:
            ax.set_xticks([])
            ax.set_yticks([])
            if '3d' in projection: ax.set_zticks([])
    # set default axis_labels
    if axis_labels is None:
        axis_labels = [[component_name + str(i) for i in idcs]
                       for idcs in
                       [component_indexnames for iax in range(len(axs))]]
    else:
        axis_labels = [[axis_labels[0], axis_labels[1]] for i in range(len(axs))]
    for iax, ax in enumerate(axs):
        ax.set_xlabel(axis_labels[iax][0])
        ax.set_ylabel(axis_labels[iax][1])
        if '3d' in projection:
            # shift the label closer to the axis
            ax.set_zlabel(axis_labels[iax][2], labelpad=-7)
    for ax in axs:
        # scale limits to match data
        ax.autoscale_view()
    return axs
Plot scatter plot of data.

    Parameters
    ----------
    Y : np.ndarray
        Data array.
    projection : {'2d', '3d'}

    Returns
    -------
    Depending on whether supplying a single array or a list of arrays,
    return a single axis or a list of axes.