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)
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
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)
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)
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)
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)
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)
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())
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')
Custom colormaps¶
You can make your own custom colormaps by providing a list of hex colors:
img.relabel('Listed colors').opts(cmap=['#0000ff', '#8888ff', '#ffffff', '#ff8888', '#ff0000'], colorbar=True, width=400)