Example #1
0
    def prep_palette(self, pname, binverse=False):
        """
        Prepares a palette based on a name
        :param pname:
        :return:
        """
        res = palettes.grey(256)

        if pname == 'Greys256':
            res = palettes.grey(256)
        elif pname == 'Inferno256':
            res = palettes.inferno(256)
        elif pname == 'Magma256':
            res = palettes.magma(256)
        elif pname == 'Plasma256':
            res = palettes.plasma(256)
        elif pname == 'Viridis256':
            res = palettes.viridis(256)
        elif pname == 'Cividis256':
            res = palettes.cividis(256)
        elif pname == 'Turbo256':
            res = palettes.turbo(256)
        elif pname == 'Bokeh8':
            res = palettes.small_palettes['Bokeh'][8]
        elif pname == 'Spectral11':
            res = palettes.small_palettes['Spectral'][11]
        elif pname == 'RdGy11':
            res = palettes.small_palettes['RdGy'][11]
        elif pname == 'PiYG11':
            res = palettes.small_palettes['PiYG'][11]

        if binverse:
            res = res[::-1]
        return res
def test_cmap_generator_function():
    assert pal.viridis(256) == pal.Viridis256
    assert pal.magma(256) == pal.Magma256
    assert pal.plasma(256) == pal.Plasma256
    assert pal.inferno(256) == pal.Inferno256
    assert pal.gray(256) == pal.Greys256
    assert pal.grey(256) == pal.Greys256
Example #3
0
    def create_interact_ui(doc):

        x, y, z = echelle(freq, power, dnu, fmin, fmax)
        source = ColumnDataSource(data={'image': [np.sqrt(z)]})

        plot = figure(x_range=(x.min(), x.max()), y_range=(y.min(), y.max()))

        cmap = grey(256)[::-1]
        full_plot = plot.image(image='image',
                               x=x.min(),
                               y=y.min(),
                               dw=x.max(),
                               dh=y.max(),
                               source=source,
                               palette=cmap)

        plot.xaxis.axis_label = r'Frequency mod $\Delta\nu$'
        plot.yaxis.axis_label = 'Frequency'

        slider = Slider(start=dnu - 2.,
                        end=dnu + 2.,
                        value=dnu,
                        step=.001,
                        title=r'$\Delta\nu$')

        def update_upon_cadence_change(attr, old, new):
            _, _, z = echelle(freq, power, new, 15, 80)
            full_plot.data_source.data['image'] = [np.sqrt(z)]

        slider.on_change('value', update_upon_cadence_change)

        # Layout all of the plots
        widgets_and_figures = column(slider, plot)
        doc.add_root(widgets_and_figures)
Example #4
0
def test_cmap_generator_function():
    assert pal.viridis(256) == pal.Viridis256
    assert pal.magma(256) == pal.Magma256
    assert pal.plasma(256) == pal.Plasma256
    assert pal.inferno(256) == pal.Inferno256
    assert pal.gray(256) == pal.Greys256
    assert pal.grey(256) == pal.Greys256
    assert pal.turbo(256) == pal.Turbo256
    assert pal.diverging_palette(pal.Reds9, pal.Greys9, n=18, midpoint=0.5) == pal.Reds9 + pal.Greys9[::-1]
Example #5
0
def state_count_tab():

    plotter, StHimark, gsource = displayMap()
    url = join(basename(split(dirname(__file__))[0]), 'data',
               'sensor_count.csv')
    state_count = pd.read_csv(url)
    palette = grey(19)
    palette = palette[::-1]
    color_mapper = LinearColorMapper(palette=palette,
                                     low=state_count['count'].min(),
                                     high=state_count['count'].max())
    grey_merged = StHimark.merge(state_count,
                                 left_on='Nbrhood',
                                 right_on='Neighbourhood')
    merged_json = json.loads(grey_merged.to_json())
    json_data = json.dumps(merged_json)

    geosource = GeoJSONDataSource(geojson=json_data)
    plotter.patches('xs',
                    'ys',
                    source=geosource,
                    fill_color={
                        'field': 'count',
                        'transform': color_mapper
                    },
                    line_color="black",
                    line_width=0.05,
                    fill_alpha=1.0)
    tick_labels = {
        '0': '0',
        '10': '10',
        '20': '20',
        '30': '30',
        '40': '40',
        '50': '>50'
    }
    color_bar = ColorBar(color_mapper=color_mapper,
                         label_standoff=5,
                         width=500,
                         height=20,
                         border_line_color=None,
                         location=(0, 0),
                         orientation='horizontal',
                         major_label_overrides=tick_labels)
    plotter.add_layout(color_bar, 'below')
    layout = column(plotter)
    tab = Panel(child=layout, title='State Uncertainity')
    return tab
def plot_grids(values, grids, title=None, f_w=1000, f_h=1000, silent=False):
    """ plot the grids on map tile background.

    Args:
        values (array): either the input data or output data as a 1-d array
        grids (list): the bounds of each grid

    """
    values = values
    cds = ColumnDataSource(data=grids_data_source(values, grids))
    cm = LinearColorMapper(palette=palettes.grey(10))
    center = (-8231000.0 - 3000, 4977500.0 - 2000)
    view_w = 7000
    view_h = 5000
    p = figure(title=title,
               plot_width=f_w,
               plot_height=f_h,
               x_range=(center[0] - view_w, center[0] + view_w),
               y_range=(center[1] - view_h, center[1] + view_h),
               tools="pan,wheel_zoom,box_zoom,reset,save")
    p.add_tile(CARTODBPOSITRON_RETINA)
    p.axis.visible = False

    r = p.quad(left='left',
               right='right',
               top='top',
               bottom='bottom',
               source=cds,
               line_alpha=0,
               alpha='alpha',
               color={
                   'field': 'color',
                   'transform': cm
               })

    if silent: return p, r
    else: show(p)
Example #7
0
def plot_all(fig, df, start_year, end_year):

    lines = list()  # empty list to hold each plotted line

    source = ColumnDataSource(df)

    offset = 5
    # generate palette the size of dataset but offset each end
    palette = palettes.grey(end_year - start_year + 1 + offset * 2)

    unselected_kwargs = dict(line_width=1.5, line_alpha=0.5)

    # plot all years
    for i, yr in enumerate(range(start_year, end_year + 1)):

        lines.append(
            fig.line(x='day',
                     y=str(yr),
                     source=source,
                     color=palette[i + offset],
                     name=str(yr),
                     **unselected_kwargs))

    return fig, lines
Example #8
0
    "#FDE095", "#FDE299", "#FDE39E", "#FDE5A2", "#FDE6A6", "#FDE8AA",
    "#FEEAAF", "#FEEBB3", "#FEEDB7", "#FEEEBC", "#FEF0C0", "#FEF1C4",
    "#FEF3C9", "#FEF4CD", "#FEF6D1", "#FFF8D6"
]


def totimestamp(dt, epoch=datetime(1970, 1, 1)):
    td = dt - epoch
    # return td.total_seconds()
    return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6


dir_path = os.path.dirname(os.path.realpath(__file__))
plotWidth = 1000
plotHeight = 600
colors = grey(100)
colors = gradient
mapper = LinearColorMapper(palette=colors)
period = timedelta(hours=1)
TOOLS = "hover,save,reset"

df = pd.read_csv(dir_path + '/Tables/LIGHT_LEVELS.csv',
                 index_col=0,
                 parse_dates=True,
                 dayfirst=True)
cn = pd.read_csv(dir_path + '/Tables/channel_names.csv', index_col=0)
an = pd.read_csv(dir_path + '/Tables/area_names.csv', index_col=0)
columnNames = []
for x, y in zip(cn['Area'], cn['Name']):
    columnNames.append(an.loc[x, 'Name'] + " - " + y)
df.columns = columnNames
def create_plot(df, title, power_unit, ylimit=None):
    """

    :param df:
    :param title: string, plot title
    :param power_unit: string, the unit of power used in the database/model
    :param ylimit: float/int, upper limit of y-axis; optional
    :return:
    """

    # Set up data source
    source = ColumnDataSource(data=df)

    # Determine column types for plotting, legend and colors
    x_col = "hour_on_period"
    power_col = "Power"
    commitment_col = "Committed Capacity"
    stable_level_col = "Minimum Output"
    all_reserves = [
        "bottom_reserves",
        "Regulation Down",
        "Load Following Down",
        "Load Following Up",
        "Regulation Up",
        "Frequency Response",
        "Spinning Reserves",
    ]
    active_reserves = list(
        df[all_reserves].columns[df[all_reserves].notna().all()
                                 & df[all_reserves].mean() > 0])

    # Setup the reserve colors
    colors = grey(len(active_reserves) +
                  2)[1:-1]  # skip the white/black colors
    alphas = [0] + [1] * (len(active_reserves) - 1) if active_reserves else []

    # Set up the figure
    plot = figure(
        plot_width=800,
        plot_height=500,
        tools=["pan", "reset", "zoom_in", "zoom_out", "save", "help"],
        title=title,
    )

    # Add reserve area renderers
    area_renderers = plot.vbar_stack(
        stackers=active_reserves,
        x=x_col,
        source=source,
        fill_color=colors,
        fill_alpha=alphas,
        line_color=colors,
        line_alpha=alphas,
        width=1,
    )

    # Add operations to plot
    power_renderer = plot.step(
        name="Power",
        source=source,
        x=x_col,
        y=power_col,
        color="black",
        mode="center",
    )
    commitment_renderer = plot.step(
        name="Committed Capacity",
        source=source,
        x=x_col,
        y=commitment_col,
        color="black",
        line_dash="dashed",
        mode="center",
    )
    stable_level_renderer = plot.step(
        name="Minimum Output",
        source=source,
        x=x_col,
        y=stable_level_col,
        color="black",
        line_dash="dotted",
        mode="center",
    )

    # Add legend items
    legend_items = [
        (commitment_renderer.name, [commitment_renderer]),
        (power_renderer.name, [power_renderer]),
        (stable_level_renderer.name, [stable_level_renderer]),
    ] + list(reversed([(r.name, [r]) for r in area_renderers[1:]]))

    # Add Legend
    legend = Legend(items=legend_items)
    plot.add_layout(legend, "right")
    plot.legend.click_policy = "hide"  # Add interactivity to the legend
    # Note: Doesn't rescale the graph down, simply hides the area
    # Note2: There's currently no way to auto-size legend based on graph size(?)
    # except for maybe changing font size automatically?
    show_hide_legend(plot=plot)  # Hide legend on double click

    # Format Axes (labels, number formatting, range, etc.)
    plot.xaxis.axis_label = "Hour"
    plot.yaxis.axis_label = power_unit
    plot.yaxis.formatter = NumeralTickFormatter(format="0,0")
    plot.y_range.end = ylimit  # will be ignored if ylimit is None

    # Add HoverTools
    # Note: stepped lines or varea charts not yet supported (lines/bars OK)
    # Note: skip bottom renderer for vbars/areas since it's just a helper
    hover_renderers = area_renderers[1:] + [
        commitment_renderer,
        power_renderer,
        stable_level_renderer,
    ]
    for r in hover_renderers:
        tooltips = [("Hour", "@%s" % x_col),
                    (r.name, "@$name{0,0} %s" % power_unit)]
        if r.name == "Power":
            tooltips.append(("% of Committed", "@power_pct_of_committed{0%}"))
        elif r.name == "Minimum Output":
            tooltips.append(
                ("% of Committed", "@min_stable_level_pct_of_committed{0%}"))
        hover = HoverTool(tooltips=tooltips, renderers=[r], toggleable=False)
        plot.add_tools(hover)

    return plot
Example #10
0
from bokeh.plotting import figure
from bokeh.transform import factor_cmap, dodge
from bokeh import palettes
from bokeh import __version__ as bokeh_version
import pandas as pd
import numpy as np

from solarforecastarbiter import datamodel
from solarforecastarbiter.plotting.utils import line_or_step

logger = logging.getLogger(__name__)
PALETTE = (palettes.d3['Category20'][20][::2] +
           palettes.d3['Category20'][20][1::2])
_num_obs_colors = 3
# drop white
OBS_PALETTE = list(palettes.grey(_num_obs_colors + 1)[0:_num_obs_colors])
OBS_PALETTE.reverse()
OBS_PALETTE_TD_RANGE = pd.timedelta_range(freq='10min',
                                          end='60min',
                                          periods=_num_obs_colors)


def construct_timeseries_cds(report):
    """Construct two standardized Bokeh CDS for the timeseries and scatter
    plot functions. One with timeseries data for all observations,
    aggregates, and forecasts in the report, and the other with
    associated metadata sharing a common `pair_index` key.

    Parameters
    ----------
    report: :py:class:`solarforecastarbiter.datamodel.Report`
Example #11
0
def plot_2d(data, summary_axis, time_axis):
    '''
    Plot variance analysis along 2D

    Inputs
        data         [array]  Voxelwise data
                              Can be scalar (vw_variance) or binary (regressor)
        summary_axis [tuple]  One or more axes to summarise variance
        time_axis    [scalar] Axis along which time is encoded

    Outputs
        handle       [object] Figure handle
    '''

    # Safety check
    if data.ndim - 2 != len(summary_axis):
        raise TypeError('Error: incorrect number of summary axes specified.')

    # Mean variance across summary axes
    y = np.mean(data, axis=summary_axis, keepdims=True)

    # Make time the 0th axis, squeeze and transpose
    # y = (slice, time)
    y = np.moveaxis(y, time_axis, 0)
    y = y.squeeze()
    y = y.T

    # Force data into float type
    y = y.astype(float)

    # Create figure
    handle = plotting.figure(plot_width=800,
                             plot_height=500,
                             title='',
                             x_axis_label='Time (TR)',
                             y_axis_label='Slice',
                             tools='pan,box_zoom,reset',
                             tooltips=[('x', '$x'), ('y', '$y'),
                                       ('value', '@image')])

    # If the array is binary, set colour mapper to b/w
    # If it's not binary, use a continuous palette
    if np.array_equal(y, y.astype(bool)):

        # Grayscale colormap
        color_mapper = models.LinearColorMapper(
            palette=palettes.grey(2)[::-1],
            low=0,
            high=1,
        )
    else:
        # Continuous colormap
        color_mapper = models.LinearColorMapper(
            palette=palettes.Viridis256,
            low=0,
            high=np.max(y),
        )

    # Add image
    handle.image(image=[y],
                 x=0,
                 y=0,
                 dh=y.shape[0],
                 dw=y.shape[1],
                 color_mapper=color_mapper,
                 level='image')

    # Create colorbar
    colorbar = models.ColorBar(
        color_mapper=color_mapper,
        label_standoff=12,
        border_line_color=None,
        location=(0, 0),
        ticker=models.AdaptiveTicker(),
    )

    # Add colorbar
    handle.add_layout(colorbar, 'right')

    # Tidy
    handle.x_range.range_padding = 0
    handle.y_range.range_padding = 0
    handle.grid.grid_line_width = 0.5
    handle.title.align = 'center'
    handle.xaxis.axis_label_text_align = 'center'
    handle.yaxis.axis_label_text_align = 'center'
    handle.xaxis.axis_label_text_font_size = '10pt'
    handle.yaxis.axis_label_text_font_size = '10pt'
    handle.xaxis.axis_label_text_font_style = 'normal'
    handle.yaxis.axis_label_text_font_style = 'normal'

    # Return
    return handle
Example #12
0
def plot_density(
    data,
    *,
    year,
    group,
    kernel_function,
    cell_size,
    crs=None,
    bandwidth,
    show_title,
):
    if crs is None:
        crs = {'init': 'epsg:3067'}

    pop = get_xy(data)
    pad = bandwidth * 2
    minx, miny, maxx, maxy = pop['geometry'].total_bounds
    minx -= pad
    miny -= pad
    maxx += pad
    maxy += pad
    w, h = maxx - minx, maxy - miny

    fig = figure(
        title=f"Density of {group.capitalize()} population in Vyborg in {year}",
        x_range=(minx, maxx),
        y_range=(miny, maxy),
    )
    fig.title.visible = show_title
    fig.xaxis.major_tick_line_color = None
    fig.xaxis.minor_tick_line_color = None
    fig.yaxis.major_tick_line_color = None
    fig.yaxis.minor_tick_line_color = None

    fig.xaxis.major_label_text_font_size = '0pt'
    fig.yaxis.major_label_text_font_size = '0pt'

    fig.xgrid.visible = False
    fig.ygrid.visible = False

    water = gpd.read_file('water_clip.shp')
    water.crs = {'init': 'epsg:4326'}
    water.geometry = water.geometry.to_crs(crs)
    water = get_xy(water)
    water_src = GeoJSONDataSource(geojson=water.to_json())

    density = kernel_density_surface(
        data,
        group=group,
        bandwidth=bandwidth,
        cell_size=cell_size,
        kernel_function=kernel_function,
    )

    fig.image(
        [density],
        minx,
        miny,
        w,
        h,
        palette=grey(10)[::-1],
    )
    fig.patches(
        xs='x',
        ys='y',
        source=water_src,
        fill_color='#59d0ff',
        fill_alpha=0.8,
        line_color=None,
        line_width=0,
    )

    return fig
Example #13
0
class ColorController(object):
    """Controls the color of the elements based on the selected method
       and the active palette. When coloring by classification only one
       type of palette will be available. The rest of palettes are
       available when an axis is selected
    """
    LOGGER = logging.getLogger(__name__)

    POINT_SELECTED_COLOR = 'red'
    POINT_UNSELECTED_COLOR = '#d3d3d3'

    INFERNO_PALETTE_ID = 'inferno'
    GREY_PALETTE_ID = 'grey'
    VIRIDIS_PALETTE_ID = 'viridis'
    CLUSTER_CATEGORY_PALETTE_ID = 'cluster'
    DEFAULT_PALETTE_ID = INFERNO_PALETTE_ID
    GREY_PALETTE = list(reversed(grey(256)))
    INFERNO_PALETTE = inferno(256)
    VIRIDIS_PALETTE = viridis(256)
    # 23 RGB colors from Red to Pink for Clustering categorization
    # Keep this list as reference for testing purposes
    # CATEGORY_PALETTE = ['#ff0000', '#ff4000', '#ff8000', '#ffbf00',
    #                    '#ffff00', '#bfff00', '#80ff00', '#40ff00',
    #                    '#00ff00', '#00ff40', '#00ff80', '#00ffbf',
    #                    '#00ffff', '#00bfff', '#0080ff', '#0040ff',
    #                    '#0000ff', '#4000ff', '#8000ff', '#bf00ff',
    #                    '#ff00ff', '#ff00bf', '#ff0080']
    CATEGORY_PALETTE = [
        "red", "black", "orange", "green", "grey", "yellow", "navy"
    ]
    NONE_PALETTE = ["navy"]
    NONE_PALETTE_ID = 'None'
    CATEGORY_METHOD_ID = 'Category'
    AXIS_METHOD_ID = 'Axis'
    NONE_METHOD_ID = 'None'
    NONE_AXIS_ID = 'None'
    DEFAULT_METHOD_ID = NONE_METHOD_ID

    @staticmethod
    def _get_axis_palette_dict():
        """Returns a dictionary of palettes {palette_id, palette}"""
        return dict({
            ColorController.INFERNO_PALETTE_ID:
            ColorController.INFERNO_PALETTE,
            ColorController.GREY_PALETTE_ID:
            ColorController.GREY_PALETTE,
            ColorController.VIRIDIS_PALETTE_ID:
            ColorController.VIRIDIS_PALETTE,
            ColorController.NONE_PALETTE_ID:
            ColorController.NONE_PALETTE
        })

    def __init__(self,
                 input_data_controller,
                 normalization_controller,
                 classification_controller,
                 point_controller,
                 palette_id=NONE_PALETTE_ID):
        self._palette_dict = ColorController._get_axis_palette_dict()
        self._input_data_controller = input_data_controller
        self._normalization_controller = normalization_controller
        self._classification_controller = classification_controller
        self._point_controller = point_controller
        self._active_palette_id = ColorController.NONE_PALETTE_ID
        self._selected_axis_id = ColorController.NONE_AXIS_ID
        self.update_palette(palette_id)
        self._active_method = ColorController.DEFAULT_METHOD_ID
        # Prevents any coloring when a user has selected a valid point
        # Note that the name must be unique
        self._selected_point_name = None

    def update_palette(self, palette_id):
        if palette_id not in self._palette_dict:
            ColorController.LOGGER.warn("Palette id '%s' is not known",
                                        palette_id)
        else:
            self._active_palette_id = palette_id

    def update_colors(self):
        """Update points' color accoring to selected method and axis or categories"""
        if self._selected_point_name:
            ColorController.LOGGER.warn(
                "Cannot update colors while a point is selected")
            return
        if self.in_category_mode():
            self._color_points_by_categories()
        elif self.in_axis_mode():
            self._color_points_by_axis()
        else:
            self._color_points_by_initial_settings()

    def update_method(self, method_id):
        self._active_method = method_id
        self.update_legend()

    def update_selected_axis(self, axis_id):
        ColorController.LOGGER.debug("Updating axis ID to '%s'", axis_id)
        if axis_id not in self.get_available_axis_ids():
            raise ValueError("'{}' is not a valid axis".format(axis_id))
        self._selected_axis_id = axis_id

    def update_legend(self):
        ColorController.LOGGER.debug("Updating categories")
        categories = []
        if self.in_category_mode():
            categories = self._classification_controller.get_categories()

        expected_no_categories = self._point_controller.get_number_of_points()
        if categories is None \
                or len(categories) == 0 \
                or len(categories) != expected_no_categories:
            categories = ['N/A' for i in range(0, expected_no_categories)]

        self._point_controller.update_categories(categories)

    def get_available_axis_ids(self):
        return [ColorController.NONE_AXIS_ID]\
                + self._input_data_controller.get_dimensional_labels()

    def get_selected_axis_id(self):
        return self._selected_axis_id

    def get_active_palette(self):
        return self._active_palette_id

    def get_active_color_method(self):
        return self._active_method

    def get_available_palettes(self):
        return self._palette_dict.keys()

    def get_available_color_methods(self):
        return [
            ColorController.AXIS_METHOD_ID, ColorController.CATEGORY_METHOD_ID,
            ColorController.NONE_METHOD_ID
        ]

    def select_point(self, point_name):
        self._selected_point_name = point_name
        self._update_colors_by_point(point_name)

    def unselect_point(self):
        self._selected_point_name = None
        self.update_colors()

    def get_selected_point(self):
        return self._selected_point_name

    def _update_colors_by_point(self, point_name):
        new_colors = [ColorController.POINT_UNSELECTED_COLOR\
                      if name != point_name\
                      else ColorController.POINT_SELECTED_COLOR\
                      for name in self._point_controller.get_point_names()]

        self._point_controller.update_colors(new_colors)

    def in_axis_mode(self):
        return self._active_method == ColorController.AXIS_METHOD_ID

    def in_category_mode(self):
        return self._active_method == ColorController.CATEGORY_METHOD_ID

    def _color_points_by_initial_settings(self):
        """Will set the color of all points based on the initial palette"""
        ColorController.LOGGER.debug("Coloring by initial settings")
        palette_index_list = np.zeros(
            self._input_data_controller.get_number_of_values())
        self._map_source_points_color(palette_index_list,
                                      ColorController.NONE_PALETTE)

    def _color_points_by_axis(self):
        # get index of colors, we normalize by Feature Scaling to get all values between 0 and 1
        # and then multiply by the length of the palette
        ColorController.LOGGER.debug("Coloring by axis '%s'",
                                     self._selected_axis_id)
        if self._selected_axis_id == ColorController.NONE_AXIS_ID:
            ColorController.LOGGER.warn(
                "No coloring was made since selected axis was None")
            return
        active_palette = self._get_palette()
        values_df = self._input_data_controller.get_dimensional_values(
            filtered=False)
        column_norm = self._normalization_controller\
                      .normalize_feature_scaling(values_df)[self._selected_axis_id]
        palette_index_list = (column_norm * len(active_palette) -
                              1).astype(int)
        # map index with color in the palette and assign to points
        self._map_source_points_color(palette_index_list, active_palette)

    def _color_points_by_categories(self):
        ColorController.LOGGER.debug("Coloring by category")
        categories = self._classification_controller.get_categories()
        if categories is None or len(categories) == 0:
            ColorController.LOGGER.warn(
                "No coloring was made since no categories were found")
            return
        palette = ColorController.CATEGORY_PALETTE
        # Map each category into a specific index
        palette_index_list = self._get_palette_index_list(categories, palette)
        self._map_source_points_color(palette_index_list,
                                      ColorController.CATEGORY_PALETTE)

    def _map_source_points_color(self, palette_index_list, palette):
        max_index_list = max(palette_index_list)
        max_index_palette = len(palette) - 1
        if max_index_list > max_index_palette:
            ColorController.LOGGER.warn(
                ("Max index (%s) in palette index list is"
                 "higher than the palette one (%s), "
                 "higher indexes will be assigned the "
                 "highest palette value"), max_index_list, max_index_palette)
        new_colors = [
            palette[min(max_index_palette, index)]
            for index in palette_index_list
        ]
        self._point_controller.update_colors(new_colors)

    def _get_palette(self):
        return self._palette_dict[self._active_palette_id]

    def _get_palette_index_list(self, categories, palette):
        """Receives a list of category elements, of any type, and returns
           a list of numeric values which are the indexes of the palette
        """
        palette_index_list = []
        category_dict = dict()
        shift = 0
        for category in categories:
            if not category in category_dict:
                category_dict[category] = shift
                shift += 1
            palette_index_list.append(category_dict[category])
        # Once we have the categories indexed, we normalize and multiply
        # by the length of the palette in order to distribute the values
        # return palette_index_list

        # _ is what you use when you run out of ideas for names
        _ = self._normalization_controller\
                .normalize_feature_scaling(DataFrame(palette_index_list))

        return (_ * (len(palette) - 1)).astype(int)[0]