Styling Mapping#

import numpy as np
import holoviews as hv
from holoviews import dim, opts

hv.extension('bokeh')

One of the major benefits of HoloViews is the fact that Elements are simple, declarative wrappers around your data, with clearly defined semantics describing how the dimensions of the data map to the screen. Usually the key dimensions (kdims) and value dimensions map to coordinates of the plot axes and/or the colormapped intensity. However there are a huge number of ways to augment the visual representation of an element by mapping dimensions to visual attributes. In this section we will explore how we can declare such mappings including complex transforms specified by so called dim objects.

To illustrate this point let us create a set of three points with x/y-coordinates and alpha, color, marker and size values and then map each of those value dimensions to a visual attribute by name. Note that by default kdims is x,y. However, in this example we also show that the names of the dimensions can be changed and we use ‘x values’ and ‘y values’ to represent the data series names.

data = {
    'x values': [0, 1, 0.5],
    'y values': [1, 0, 0.5],
    'alpha': [0.5, 1, 0.3],
    'color': ['red', 'blue', 'green'],
    'marker': ['circle', 'triangle', 'diamond'],
    'size': [15, 25, 40]
}

opts.defaults(opts.Points(size=8, line_color='black'))

hv.Points(data, kdims=['x values','y values'] , vdims=['alpha', 'color', 'marker', 'size']).opts(
    alpha='alpha', color='color', marker='marker', size='size')

This is the simplest approach to style mapping, dimensions can be mapped to visual attributes directly by name. However often columns in the data will not directly map to a visual property, e.g. we might want to normalize values before mapping them to the alpha, or apply a scaling factor to some values before mapping them to the point size; this is where dim transforms come in. Below are a few examples of using dim transforms to map a dimension in the data to the visual style in the plot:

points = hv.Points(np.random.rand(400, 4))

bins   = [0, .25, 0.5, .75, 1]
labels = ['circle', 'triangle', 'diamond', 'square']

layout = hv.Layout([
    points.relabel('Alpha' ).opts(alpha =dim('x').norm()),
    points.relabel('Angle' ).opts(angle =dim('x').norm()*360, marker='dash'),
    points.relabel('Color' ).opts(color =dim('x')),
    points.relabel('Marker').opts(marker=dim('x').bin(bins, labels)),
    points.relabel('Size'  ).opts(size  =dim('x')*10)
])

layout.opts(opts.Points(width=250, height=250, xaxis=None, yaxis=None)).cols(5)

What are dim transforms?#

In the above example we saw how to use an dim to define a transform from a dimension in your data to the visual property on screen. A dim therefore is a simple way to declare a deferred transform of your data. In the simplest case an dim simply returns the data for a dimension without transforming it, e.g. to look up the 'alpha' dimension on the points object we can create an dim and use the apply method to evaluate the expression:

from holoviews import dim

ds = hv.Dataset(np.random.rand(10, 4)*10, ['x', 'y'], ['alpha', 'size'])

dim('alpha').apply(ds)
array([9.41934542, 2.20303254, 5.98627838, 9.72096516, 9.15278291,
       5.12775957, 8.04964206, 9.29019986, 4.01946371, 4.03497654])

Mathematical operators#

An dim declaration allow arbitrary mathematical operations to be performed, e.g. let us declare that we want to subtract 5 from the ‘alpha’ dimension and then compute the min:

math_op = (dim('alpha')-5).min()
math_op
(dim('alpha')-5).min()

Printing the repr of the math_op we can see that it builds up an nested expression. To see the transform in action we will once again apply it on the points:

math_op.apply(ds)
np.float64(-2.796967459916809)

dim objects implement most of the NumPy API, supports all standard mathematical operators and also support NumPy ufuncs.

Custom functions#

In addition to standard mathematical operators it is also possible to declare custom functions which can be applied by name. By default HoloViews ships with three commonly useful functions.

norm#

Unity based normalization or features scaling normalizing the values to a range between 0-1 (optionally accepts min/max values as limits, which are usually provided by the plotting system) using the expression:

(values - min) / (max-min)

for example we can rescale the alpha values into a 0-1 range:

dim('alpha').norm().apply(ds)
array([0.95987996, 0.        , 0.50322955, 1.        , 0.92442307,
       0.38903342, 0.77768847, 0.94270163, 0.24161312, 0.24367657])

bin#

Bins values using the supplied bins specified as the edges of each bin:

bin_op = dim('alpha').bin([0, 5, 10])

bin_op.apply(ds)
array([7.5, 2.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 2.5, 2.5])

It is also possible to provide explicit labels for each bin which will replace the bin center value:

dim('alpha').bin([0, 5, 10], ['Bin 1', 'Bin 2']).apply(ds)
array([np.str_('Bin 2'), np.str_('Bin 1'), np.str_('Bin 2'),
       np.str_('Bin 2'), np.str_('Bin 2'), np.str_('Bin 2'),
       np.str_('Bin 2'), np.str_('Bin 2'), np.str_('Bin 1'),
       np.str_('Bin 1')], dtype=object)

categorize#

Maps a number of discrete values onto the supplied list of categories, e.g. having binned the data into 2 discrete bins we can map them to two discrete marker types ‘circle’ and ‘triangle’:

dim(bin_op).categorize({2.5: 'circle', 7.5: 'square'}).apply(ds)
array(['square', 'circle', 'square', 'square', 'square', 'square',
       'square', 'square', 'circle', 'circle'], dtype=object)

This can be very useful to map discrete categories to markers or colors.

Style mapping with dim transforms#

This allows a huge amount of flexibility to express how the data should be mapped to visual style without directly modifying the data. To demonstrate this we will use some of the more complex:

points.opts(
    alpha =(dim('x')+0.2).norm(),
    angle =np.sin(dim('y'))*360,
    color =dim('x')**2,
    marker=dim('y').bin(bins, labels),
    size  =dim('x')**dim('y')*20, width=500, height=500)

Let’s summarize the style transforms we have applied:

  • alpha=(dim('x')+0.2).norm(): The alpha are mapped to the x-values offset by 0.2 and normalized.

  • angle=np.sin(dim('x'))*360: The angle of each marker is the sine of the y-values, multiplied by 360

  • color='x': The points are colormapped by square of their x-values.

  • marker=dim('y').bin(bins, labels): The y-values are binned and each bin is assignd a unique marker.

  • size=dim('x')**dim('y')*20: The size of the points is mapped to the x-values exponentiated with the y-values and scaled by 20

These are simply illustrative examples, transforms can be chained in arbitrarily complex ways to achieve almost any mapping from dimension values to visual style.

Colormapping#

Color cycles and styles are useful for categorical plots and when overlaying multiple subsets, but when we want to map data values to a color it is better to use HoloViews’ facilities for color mapping. Certain image-like types will apply colormapping automatically; e.g. for Image, QuadMesh or HeatMap types the first value dimension is automatically mapped to the color. In other cases the values to colormap can be declared by providing a color style option that specifies which dimension to map into the color value.

Named colormaps#

HoloViews accepts colormaps specified either as an explicit list of hex or HTML colors, as a Matplotlib colormap object, or as the name of a bokeh, matplotlib, and colorcet palettes/colormap (which are available when the respective library is imported). The named colormaps available are listed here (suppressing the _r versions) and illustrated in detail in the separate Colormaps user guide:

def format_list(l):
    print(' '.join(sorted([k for k in l if not k.endswith('_r')])))

format_list(hv.plotting.list_cmaps())
Accent Blues Bokeh BrBG Bright BuGn BuPu BuRd CMRmap Category10 Category20 Category20b Category20c Cividis Colorblind Dark2 GnBu Grays Greens Greys HighContrast Inferno Iridescent Light Magma MediumContrast Muted OrRd Oranges PRGn Paired Pastel1 Pastel2 PiYG Plasma PuBu PuBuGn PuOr PuRd Purples RdBu RdGy RdPu RdYlBu RdYlGn Reds Set1 Set2 Set3 Spectral Sunset TolPRGn TolRainbow TolYlOrBr Turbo Vibrant Viridis Wistia YlGn YlGnBu YlOrBr YlOrRd afmhot autumn bgy bgyw binary bjy bkr bky blues bmw bmy bone brg bwr bwy cet_CET_C1 cet_CET_C10 cet_CET_C10s cet_CET_C11 cet_CET_C11s cet_CET_C1s cet_CET_C2 cet_CET_C2s cet_CET_C3 cet_CET_C3s cet_CET_C4 cet_CET_C4s cet_CET_C5 cet_CET_C5s cet_CET_C6 cet_CET_C6s cet_CET_C7 cet_CET_C7s cet_CET_C8 cet_CET_C8s cet_CET_C9 cet_CET_C9s cet_CET_CBC1 cet_CET_CBC2 cet_CET_CBD1 cet_CET_CBD2 cet_CET_CBL1 cet_CET_CBL2 cet_CET_CBL3 cet_CET_CBL4 cet_CET_CBTC1 cet_CET_CBTC2 cet_CET_CBTD1 cet_CET_CBTL1 cet_CET_CBTL2 cet_CET_CBTL3 cet_CET_CBTL4 cet_CET_D1 cet_CET_D10 cet_CET_D11 cet_CET_D12 cet_CET_D13 cet_CET_D1A cet_CET_D2 cet_CET_D3 cet_CET_D4 cet_CET_D6 cet_CET_D7 cet_CET_D8 cet_CET_D9 cet_CET_I1 cet_CET_I2 cet_CET_I3 cet_CET_L1 cet_CET_L10 cet_CET_L11 cet_CET_L12 cet_CET_L13 cet_CET_L14 cet_CET_L15 cet_CET_L16 cet_CET_L17 cet_CET_L18 cet_CET_L19 cet_CET_L2 cet_CET_L20 cet_CET_L3 cet_CET_L4 cet_CET_L5 cet_CET_L6 cet_CET_L7 cet_CET_L8 cet_CET_L9 cet_CET_R1 cet_CET_R2 cet_CET_R3 cet_CET_R4 cet_bgy cet_bgyw cet_bjy cet_bkr cet_bky cet_blues cet_bmw cet_bmy cet_bwy cet_circle_mgbm_67_c31 cet_circle_mgbm_67_c31_s25 cet_colorwheel cet_coolwarm cet_cwr cet_cyclic_bgrmb_35_70_c75 cet_cyclic_bgrmb_35_70_c75_s25 cet_cyclic_grey_15_85_c0 cet_cyclic_grey_15_85_c0_s25 cet_cyclic_isoluminant cet_cyclic_mrybm_35_75_c68 cet_cyclic_mrybm_35_75_c68_s25 cet_cyclic_mybm_20_100_c48 cet_cyclic_mybm_20_100_c48_s25 cet_cyclic_mygbm_30_95_c78 cet_cyclic_mygbm_30_95_c78_s25 cet_cyclic_mygbm_50_90_c46 cet_cyclic_mygbm_50_90_c46_s25 cet_cyclic_protanopic_deuteranopic_bwyk_16_96_c31 cet_cyclic_protanopic_deuteranopic_wywb_55_96_c33 cet_cyclic_rygcbmr_50_90_c64 cet_cyclic_rygcbmr_50_90_c64_s25 cet_cyclic_tritanopic_cwrk_40_100_c20 cet_cyclic_tritanopic_wrwc_70_100_c20 cet_cyclic_wrkbw_10_90_c43 cet_cyclic_wrkbw_10_90_c43_s25 cet_cyclic_wrwbw_40_90_c42 cet_cyclic_wrwbw_40_90_c42_s25 cet_cyclic_ymcgy_60_90_c67 cet_cyclic_ymcgy_60_90_c67_s25 cet_dimgray cet_diverging_bkr_55_10_c35 cet_diverging_bky_60_10_c30 cet_diverging_bwg_20_95_c41 cet_diverging_bwr_20_95_c54 cet_diverging_bwr_40_95_c42 cet_diverging_bwr_55_98_c37 cet_diverging_cwm_80_100_c22 cet_diverging_gkr_60_10_c40 cet_diverging_gwr_55_95_c38 cet_diverging_gwv_55_95_c39 cet_diverging_isoluminant_cjm_75_c23 cet_diverging_isoluminant_cjm_75_c24 cet_diverging_isoluminant_cjo_70_c25 cet_diverging_linear_bjr_30_55_c53 cet_diverging_linear_bjy_30_90_c45 cet_diverging_linear_protanopic_deuteranopic_bjy_57_89_c34 cet_diverging_protanopic_deuteranopic_bwy_60_95_c32 cet_diverging_rainbow_bgymr_45_85_c67 cet_diverging_tritanopic_cwr_75_98_c20 cet_fire cet_glasbey cet_glasbey_bw cet_glasbey_bw_minc_20 cet_glasbey_bw_minc_20_hue_150_280 cet_glasbey_bw_minc_20_hue_330_100 cet_glasbey_bw_minc_20_maxl_70 cet_glasbey_bw_minc_20_minl_30 cet_glasbey_category10 cet_glasbey_cool cet_glasbey_dark cet_glasbey_hv cet_glasbey_light cet_glasbey_warm cet_gouldian cet_gray cet_gwv cet_isolum cet_isoluminant_cgo_70_c39 cet_isoluminant_cgo_80_c38 cet_isoluminant_cm_70_c39 cet_kb cet_kbc cet_kbgyw cet_kg cet_kgy cet_kr cet_linear_bgy_10_95_c74 cet_linear_bgyw_15_100_c67 cet_linear_bgyw_15_100_c68 cet_linear_bgyw_20_98_c66 cet_linear_blue_5_95_c73 cet_linear_blue_95_50_c20 cet_linear_bmw_5_95_c86 cet_linear_bmw_5_95_c89 cet_linear_bmy_10_95_c71 cet_linear_bmy_10_95_c78 cet_linear_gow_60_85_c27 cet_linear_gow_65_90_c35 cet_linear_green_5_95_c69 cet_linear_grey_0_100_c0 cet_linear_grey_10_95_c0 cet_linear_kbc_5_95_c73 cet_linear_kbgoy_20_95_c57 cet_linear_kbgyw_10_98_c63 cet_linear_kbgyw_5_98_c62 cet_linear_kgy_5_95_c69 cet_linear_kry_0_97_c73 cet_linear_kry_5_95_c72 cet_linear_kry_5_98_c75 cet_linear_kryw_0_100_c71 cet_linear_kryw_5_100_c64 cet_linear_kryw_5_100_c67 cet_linear_protanopic_deuteranopic_kbjyw_5_95_c25 cet_linear_protanopic_deuteranopic_kbw_5_95_c34 cet_linear_protanopic_deuteranopic_kbw_5_98_c40 cet_linear_protanopic_deuteranopic_kyw_5_95_c49 cet_linear_ternary_blue_0_44_c57 cet_linear_ternary_green_0_46_c42 cet_linear_ternary_red_0_50_c52 cet_linear_tritanopic_kcw_5_95_c22 cet_linear_tritanopic_krjcw_5_95_c24 cet_linear_tritanopic_krjcw_5_98_c46 cet_linear_tritanopic_krw_5_95_c46 cet_linear_wcmr_100_45_c42 cet_linear_worb_100_25_c53 cet_linear_wyor_100_45_c55 cet_rainbow cet_rainbow4 cet_rainbow_bgyr_10_90_c83 cet_rainbow_bgyr_35_85_c72 cet_rainbow_bgyr_35_85_c73 cet_rainbow_bgyrm_35_85_c69 cet_rainbow_bgyrm_35_85_c71 cividis colorwheel cool coolwarm copper cubehelix cwr cyclic_isoluminant dimgray fire flag gist_earth gist_gray gist_grey gist_heat gist_ncar gist_rainbow gist_stern gist_yarg gist_yerg glasbey glasbey_cool glasbey_dark glasbey_hv glasbey_light glasbey_warm gnuplot gnuplot2 gouldian gray grey gwv hot hsv inferno isolum jet kb kbc kbgyw kg kgy kr linear_blue_5_95_c73 linear_green_5_95_c69 magma nipy_spectral ocean pink plasma prism rainbow rainbow4 seismic spring summer tab10 tab20 tab20b tab20c terrain turbo twilight twilight_shifted viridis winter

To use one of these colormaps simply refer to it by name with the cmap style option:

ls = np.linspace(0, 10, 400)
xx, yy = np.meshgrid(ls, ls)
bounds=(-1,-1,1,1)   # Coordinate system: (left, bottom, right, top)
img = hv.Image(np.sin(xx)*np.cos(yy), bounds=bounds).opts(colorbar=True, width=400)

img.relabel('PiYG').opts(cmap='PiYG') + img.relabel('PiYG_r').opts(cmap='PiYG_r')