FAQ#
Here is a list of questions we have either been asked by users or potential pitfalls we hope to help users avoid:
Q: How should I use HoloViews as a short qualified import?
A: We recommend importing HoloViews using import holoviews as hv
.
Q: How do I specify axis labels?
A: Axes are labeled with the label of the corresponding Dimension,
which for a Pandas dataframe will default to the name of that column.
If you want to define your own specific label to display for a
dimension, you can provide a tuple containing the column name and your
preferred label for it. For instance, if the column is named x_col
,
you can make the label ‘X Label’ using:
curve = hv.Curve(df, ('x_col', 'X Label'), 'y_col')
This is the recommended way to specify labels in a declarative way,
which will persist when applying operations to your data. You can also
change the labels later, even after the object has been defined, by
passing arguments (or an unpacked dictionary) to redim.label
:
curve = hv.Curve(df, 'x_col', 'y_col')
curve = curve.redim.label(x_col='X Label', y_col='Label for Y')
To override a label for plotting it is also possible to use the
xlabel
and ylabel
plot options:
curve = hv.Curve(df, 'x_col', 'y_col')
curve = curve.opts(xlabel='X Label', ylabel='Label for Y')
Q: How do I adjust the x/y/z axis bounds (matplotlib’s xlim, ylim)?
A: Pass an unpacked dictionary containing the kdims/vdims’ names
as keys and a tuple of the bounds as values into redim.range
.
This constrains the bounds of x_col to (0, max(x_col))
.
curve = hv.Curve(df, 'x_col', 'y_col')
curve = curve.redim.range(x_col=(0, None))
As in the discussion of labels above, this approach allows you to declaratively associate ranges
with the dimensions of your data in a way that will persist even if
you apply operations to the object. This same method is applicable to
adjust the limits of the colormapping range (i.e. clim
).
To override the range specifically for plotting it is also possible to
set the xlim
and ylim
plot options:
hv.Curve(df, 'x_col', 'y_col').opts(xlim=(0, None), ylim=(0, 10))
This approach allows you to customize objects easily as a final step, but note that the values won’t be applied to the underlying data, and thus won’t be inherited if this object is subsequently used in an operation or data selection command.
Q: How do I control the auto-ranging/normalization of axis limits across frames in a HoloMap or objects in a Layout?
A: Where feasible, HoloViews defaults to normalizing axis ranges
across all objects that are presented together, so that they can be
compared directly. If you don’t want objects that share a dimension to
be normalized together in your layout, you can change the axiswise
normalization option to True, making each object be normalized
independently, e.g. for a layout of Curve
objects use:
your_layout.opts(opts.Curve(axiswise=True))
Alternatively you may also set shared_axes=False
on the Layout itself:
your_layout.opts(shared_axes=False)
Similarly, if you have a HoloMap composed of multiple frames in an
animation or controlled with widgets, you can make each frame be
normalized independently by changing framewise
to True:
your_holomap.opts(framewise=True)
Q: How do I make only a single axis be shared across a layout?
A: Even when shared_axes=True
, HoloViews will only share axes
that have the same Dimension, so just make sure that axes that you
want to be independent have a different name or label. Here, the x
axis should be shared, but the y should be independent:
xs = range(-10,11) ; ys = [100-x**2 for x in xs]
hv.Curve((xs, ys), 'x', 'y') + hv.Curve((xs, ys), 'x', 'z')
Q: Why doesn’t my DynamicMap respect the ``framewise=False`` option for axis normalization across frames?
A: Unfortunately, HoloViews has no way of knowing the axis ranges
of objects that might be returned by future calls to a DynamicMap’s
callback function, and so there is no way for it to fully implement
framewise=False
normalization (even though such normalization
is the default in HoloViews). Thus, as a special case, a DynamicMap
(whether created specifically or as the return value of various
operations that accept a dynamic=True
argument) will by default
compute its ranges using data from the first frame only. If that is not
the behavior you want, you can either set framewise=True
on it to enable
normalization on every frame independently, or you can manually
determine the appropriate axis range yourself and set that, e.g. with
.redim.range()
as described above.
Q: The default figure size is so tiny! How do I enlarge it?
A: Depending on the selected backend…
# for matplotlib:
hv_obj = hv_obj.opts(fig_size=500)
# for bokeh:
hv_obj = hv_obj.opts(width=1000, height=500)
Q: How do I get a legend on my overlay figure?
A: Legends are generated in two different ways, depending on the
Overlay
type you are using. When using *
to generate a normal Overlay
,
the legends are generated from the labels of the Elements.
Alternatively, you can construct an NdOverlay
, where the key dimensions
and values will become part of the legend. The
Dimensioned Containers user guide
shows an example of an NdOverlay
in action.
Q: How do I export a figure?
A: The easiest way to save a figure is the hv.save
utility,
which allows saving plots in different formats depending on what is
supported by the selected backend:
# Using bokeh
hv.save(obj, 'plot.html', backend='bokeh')
# Using matplotlib
hv.save(obj, 'plot.svg', backend='matplotlib')
Note that the backend is optional and will default to the currently
activated backend (i.e. hv.Store.current_backend
).
Q: Can I export and customize a bokeh or matplotlib figure directly?
A: Sometimes it is useful to customize a plot further using the
underlying plotting API used to render it. The hv.render
method
returns the rendered representation of a holoviews object as bokeh or
matplotlib figure:
# Using bokeh
p = hv.render(obj, backend='bokeh')
# Using matplotlib
fig = hv.render(obj, backend='matplotlib')
Note that the backend is optional and will default to the currently
activated backend (i.e. hv.Store.current_backend
).
If the main reason you want access to the object is to somehow customize it before it is
plotted, instead consider that it is possible to write so called hooks
:
def hook(plot, element):
# The bokeh/matplotlib figure
plot.state
# A dictionary of handles on plot subobjects, e.g. in matplotlib
# artist, axis, legend and in bokeh x_range, y_range, glyph, cds etc.
plot.handles
hv.Curve(df, 'x_col', 'y_col').opts(hooks=[hook])
These hooks can modify the backend specific representation, e.g. the matplotlib figure, before it is displayed, allowing arbitrary customizations to be applied which are not implemented or exposed by HoloViews itself.
Q: What if I need to do more complex customization supported by the backend but not exposed in HoloViews?
A: If you need to, you can easily access the underlying Bokeh or
Matplotlib figure and then use Bokeh or Matplotlib’s API directly on
that object. For instance, if you want to force Bokeh to use a
fixed list of tick labels for a HoloViews object h
, you can
grab the corresponding Bokeh figure b
, edit it to your heart’s
content as a Bokeh figure, and then show it as for any other Bokeh
figure:
import holoviews as hv
hv.extension('bokeh')
h = hv.Curve([1,2,7], 'x_col', 'y_col')
from bokeh.io import show
from bokeh.models.tickers import FixedTicker
b = hv.render(h)
b.axis[0].ticker = FixedTicker(ticks=list(range(0, 10)))
show(b)
Once you debug a modification like this manually as above, you’ll probably want to set it up to apply automatically whenever a Bokeh plot is generated for that HoloViews object:
import holoviews as hv
from bokeh.models.tickers import FixedTicker
hv.extension('bokeh')
def update_axis(plot, element):
b = plot.state
b.axis[0].ticker = FixedTicker(ticks=list(range(0, 10)))
h = hv.Curve([1,2,7], 'x_col', 'y_col')
h = h.opts(hooks=[update_axis])
h
Here, you’ve wrapped your Bokeh-API calls into a function, then
supplied that to HoloViews so that it can be run automatically
whenever object h
is viewed.
Q: Can I avoid generating extremely large HTML files when exporting my notebook?
A: It is very easy to visualize large volumes of data with HoloMaps, and all available display data is embedded in the HTML snapshot when sliders are used so that the result can be viewed without using a Python server process. It is therefore worth being aware of file size when authoring a notebook or web page to be published on the web. Useful tricks to reduce file size of HoloMaps include:
Reducing the figure size.
Selecting fewer frames for display (e.g selecting a smaller number of keys in any displayed
HoloMap
object)Displaying your data in a more highly compressed format such as
webm
,mp4
or animatedgif
, while being aware that those formats may introduce visible artifacts.When using bokeh use lower precision dtypes (e.g. float16 vs. float64)
Replace figures with lots of data with images prerendered by datashade().
It is also possible to generate web pages that do not actually include
all of the data shown, by specifying a DynamicMap
as described
Live Data rather than a HoloMap. The
DynamicMap will request data only as needed, and so requires a Python
server to be running alongside the viewable web page. Such pages are
more difficult to share by email or on web sites, but much more feasible
for large datasets.
Q: I wish to use special characters in my title, but then attribute access becomes confusing.
A: The title format "{label} {group} {dimensions}"
is simply a default
that you can override. If you want to use a lot of special characters
in your titles, you can pick simple group
and label
strings
that let you refer to the object easily in the code, and then you can
set the plot title directly, using the plot option
title="my new title"
.
You can also use 2-tuples when specifying group
and label
where
the first item is the short name used for attribute access and the
second name is the long descriptive name used in the title.
Q: Help! I don’t know how to index into my object!
A: In any Python session, you can look at print(obj)
to see
the structure of obj
. For
an explanation of how this information helps you index into your
object, see our Composing Elements
user guide.
Q: How do I create a Layout or Overlay object from an arbitrary list?
A: You can supply a list of elements
directly to the Layout
and
Overlay
constructors. For instance, you can use
hv.Layout(elements)
or hv.Overlay(elements)
.
Q: How do I provide keyword arguments for items with spaces?
A: If your column names have spaces, you may predefine a dictionary using curly braces and unpack it.
bounds = {'x col': (0, None), 'z col': (None, 10)}
curve = hv.Curve(df, 'x col', ['y col', 'z col'])
curve = curve.redim.range(**bounds)
Q: How do I plot data without storing it first as a pandas/xarray object?
A: HoloViews typically uses pandas and xarray objects in its examples, but it can accept standard Python data structures as well. Whatever data type is used, it needs to be provided to the first argument of the Element as a single object, so if you are using a pair of lists, be sure to pass them as a tuple, not as two separate arguments.
Q: Help! How do I find out the options for customizing the appearance of my object?
A: If you are in the IPython/Jupyter Notebook you can use the cell magic
%%output info=True
at the top of your code cell. This will
present the available style and plotting options for that object.
The same information is also available in any Python session using
hv.help(obj)
. For more information on customizing the display
of an object, see our Customizing Plots
user guide.
Q: Why are my .options(), .relabel(), .redim(), and similar settings not having any effect?
A: By default, HoloViews object methods like .options and
.redim return a copy of your object,
rather than modifying your original object. In HoloViews,
making a copy of the object is cheap, because only the metadata
is copied, not the data, and returning a copy makes it simple
to work with a variety of differently customized versions of any given
object. You can use .opts()
or pass clone=False
to
.options()
if you wish to modify the object in place, or you can
just reassign the new object to the old name (as in e =
e.relabel("New Label")
).
Q: Why isn’t my %%opts cell magic being applied to my HoloViews object?
A: %%opts is convenient because it tab-completes, but it can be confusing
because of the “magic” way that it works. Specifically, if you use it at
the top of a Jupyter notebook cell, the indicated options will be applied
to the return value of that cell, if it’s a HoloViews object. So, if you
want a given object to get customized, you need to make sure it is
returned from the cell, or the options won’t ever be applied, and you
should only access it after it has been returned, or the options won’t
yet have been applied. For instance, if you use renderer.save()
to export an object and only then return that object as the output of
a cell, the exported object won’t have the options applied, because
they don’t get applied until the object is returned
(during IPython’s “display hooks” processing). So to make sure that
options get applied, (a) return the object from a cell, and then (b)
access it (e.g. for exporting) after the object has been returned.
To avoid confusion, you may prefer to use .opts() directly on the
object to ensure that the options have been applied before exporting.
Example code below:
%%opts Curve [width=1000]
# preceding cell
curve = hv.Curve([1, 2, 3])
# next cell
hv.renderer('bokeh').save(curve, 'example_curve')
Q: My output looks different from what is shown on the website
A: HoloViews is organized as data structures that have corresponding plotting code implemented in different plotting-library backends, and each library will have differences in behavior. Moreover, the same library can give different results depending on its own internal options and versions. For instance, Matplotlib supports a variety of internal plotting backends, and these can have inconsistent output. HoloViews will not switch Matplotlib backends for you, but when using Matplotlib we strongly recommend selecting the ‘agg’ backend for consistency:
from matplotlib import pyplot
pyplot.switch_backend('agg')
You can generally set options explicitly to make the output more consistent across HoloViews backends, but in general HoloViews tries to use each backend’s defaults where possible.
Q: Why do my HoloViews and GeoViews objects work fine separately but are mismatched when overlaid?
A: GeoViews works precisely the same as HoloViews, except that
GeoViews is aware of geographic projections. If you take an
hv.Points
object in lon,lat coordinates and overlay it on a
GeoViews map in Web Mercator, the HoloViews object will be in
entirely the wrong coordinate system, with the HoloViews object all
appearing at one tiny spot on the globe. If you declare the same
object as gv.Points
, then GeoViews will (a) assume it is in
lon,lat coordinates (which HoloViews cannot assume, as it knows
nothing of geography), and (b) convert it into the coordinates
needed for display (e.g. Web Mercator). So, just make sure that
anything with geographic coordinates is defined as a GeoViews object,
and make sure to declare the coordinates (crs=...
) if the data is
in anything other than lon,lat.
Q: Where have my custom styles gone after unpickling my object?
A: HoloViews objects are designed to pickle and unpickle your core
data only, if you use Python’s pickle.load
and
pickle.dump
. Because custom options are kept separate from
your data, you need to use the corresponding methods Store.dump
and
Store.load
if you also want to save and restore per-object
customization. You can import Store
from the main namespace with
from holoviews import Store
.
Q: Why are the sizing options so different between the Matplotlib and Bokeh backends?
A: The way plot sizes are computed is handled in radically different ways by these backends, with Matplotlib building plots ‘inside out’ (from plot components with their own sizes) and Bokeh building them ‘outside in’ (fitting plot components into a given overall size). Thus there is not currently any way to specify sizes in a way that is comparable between the two backends.
Q: Why don’t you let me pass matplotlib_option as a style through to matplotlib?
A: We have selected a subset of default allowable style options
that are most commonly useful in order to hide the more arcane
matplotlib options. If you do need such an option to be passed to
the plotting system, you are welcome to declare that this is allowed.
For instance, say you may want the 'filternorm'
option to be passed
to matplotlib’s imshow
command when displaying an Image
element:
import holoviews as hv
from holoviews import Store
hv.extension('matplotlib')
Store.add_style_opts(hv.Image, ['filternorm'], backend='matplotlib')
Now you can freely use 'filternorm'
in .opts()
and in the
%opts
line/cell magic, including tab-completion!
Q: What I want to change is about how HoloViews works, not about the underlying backend. Is that possible?
A: Sure, if you need more customization and configurability than is possible with either HoloViews options or with extra backend-specific code as above, then you can always subclass the plotting class used for a HoloViews element and modify any of its behavior. You can also add your own Element types, which need corresponding plotting classes before they will be viewable in a given backend. The resulting objects will still interact normally with other HoloViews objects (e.g. in Layout or Overlay configurations).