Color And Colormap Options#
Visual styling options to adjust colors and colormapping:
Parameters |
Description |
---|---|
bgcolor (str or None, default=None) |
Background color of the data area of the plot |
color (str or list or column name or None, default=None) |
Defines the color(s) to use for the plot. Accepts:
|
c (str or list or column name or None, default=None) |
Alias for |
cmap (str or list or dict or colormap object or None, default=None) |
The colormap to use for continuous or categorical color mapping. Accepts:
If not specified, a default colormap is automatically chosen based on the data type:
You can override these defaults by explicitly setting |
colorbar (bool or None, default=None) |
Enables a colorbar. Enabled by default for these plots: |
colormap (str or list or colormap object or None, default=None) |
Alias for |
color_key (str or list or dict or None, default=None) |
Alias for |
clim (tuple or None, default=None) |
Lower and upper bound of the color scale |
cnorm (str, default=’linear’) |
Color scaling which must be one of |
rescale_discrete_levels (bool or None, default=None) |
If |
robust (bool or None, default=None) |
If this option is True and no |
symmetric (bool or None, default=None) |
Whether the data are symmetric around zero. If left unset, the data
will be checked for symmetry as long as the size is less than
|
check_symmetric_max (int, default=1000000) |
Size above which to stop checking for symmetry by default on the data. |
bgcolor
#
The bgcolor
option sets the background color of the data area of the plot. It accepts any valid CSS color string such as ‘white’, ‘lightgray’, or hex codes like ‘#f0f0f0’. This can be useful to improve contrast or match the theme of a larger dashboard or presentation.
import hvplot.pandas # noqa
df = hvplot.sampledata.earthquakes("pandas")
df.hvplot.scatter(
x='lon', y='lat', c='mag', cmap='inferno_r',
bgcolor='#f5f5f5', # light gray background
title="Earthquake Magnitudes by Location",
)
color
#
The color
option sets the color of the plotted elements. It accepts:
A single color name or hex code (e.g.,
'red'
,'#1f77b4'
) to apply uniformly.A list of colors to cycle through when plotting multiple groups (e.g., with
by='column'
).A column name to map colors based on data values (for categorical or continuous color encoding).
A column containing per-point color values (e.g., hex codes).
If both color
and c
are provided, color
takes precedence. For categorical data, hvPlot automatically uses a discrete colormap unless overridden.
import hvplot.pandas # noqa
df = hvplot.sampledata.penguins("pandas")
df['custom_color'] = df['species'].map({
'Adelie': 'red',
'Gentoo': 'purple',
'Chinstrap': 'grey',
})
plot_opts = dict(x="flipper_length_mm", y="body_mass_g", frame_width=250)
(
df.hvplot.scatter(
color="#d133ff", title="Colored by single hex color", **plot_opts
) +
df.hvplot.scatter(
by="species", color=["blue", "green", "orange"],
title="Groups colored by custom list", **plot_opts
) +
df.hvplot.scatter(
color="species", title="Colored by column name", **plot_opts
) +
df.hvplot.scatter(
color="custom_color", title="Colored by mapped color column", **plot_opts
)
).cols(2)
Note
Coloring Categorical Data: color
vs by
When visualizing categorical data, both the color
and by
keywords can produce color-separated groups, but they behave differently:
color='<categorical column name>'
performs vectorized color mapping within a single plot. Each unique value in the column is mapped to a distinct color, using the defaultglasbey_category10
categorical colormap. This approach is efficient and results in a single-layer plot with all points colored by their category.by='<categorical column name>'
splits the data into multiple groups and creates an overlay of plots, one per unique value in the column. However, it does not honor thecmap
you provide. Instead, it assigns colors by cycling through a color list. If no colors are specified, it defaults to theglasbey_hv
color cycle. You can override this by passing a custom list of colors tocolor=[...]
.
In addition, using color='<categorical column>'
is usually faster than by='<categorical column>'
, especially when working with large datasets or many unique categories. This is because color
performs vectorized color mapping within a single plot, while by
generates an overlay of multiple plots — each of which is rendered and managed separately. See by
keyword
Summary of differences:
Keyword |
Coloring Mechanism |
Supports |
Default Color Scheme |
Faster |
---|---|---|---|---|
|
Vectorized color mapping |
✅ Yes |
|
✅Yes |
|
Splits into overlays by category |
❌ No |
|
❌ No |
Use color
when you want a single plot with color-encoded values.
Use by
when you want separate layers per category — for subplots or layout purposes.
See also
c
#
Alias for color
above.
cmap
#
The cmap
option controls the colormap used when mapping numerical or categorical data values to color. It supports:
Named colormaps from Bokeh, Matplotlib, or Colorcet (e.g.,
'viridis'
,'plasma'
,'coolwarm'
)Lists of color strings or hex codes (for custom sequences)
Dictionaries (for categorical color mappings)
Colormap objects from Matplotlib or HoloViews
hvPlot selects a default colormap based on the data type, but cmap
lets you override this behavior. Only one of cmap
, colormap
, or color_key
should be used at a time.
import hvplot.pandas # noqa
import matplotlib as mpl
df = hvplot.sampledata.earthquakes("pandas")
plot_opts = dict(x="lon", y="lat", color="mag", frame_width=250)
plot1 = df.hvplot.scatter(
cmap="plasma_r", title="Named cmap 'plasma_r'", **plot_opts
)
plot2 = df.hvplot.scatter(
cmap=[
'#f7fbff', '#deebf7', '#c6dbef', '#9ecae1',
'#6baed6', '#4292c6', '#2171b5', '#084594',
], title="Custom list cmap", **plot_opts
)
plot3 = df.hvplot.scatter(
cmap=mpl.colormaps["OrRd"], title="MPL colormap object 'OrRd'", **plot_opts
)
plot4 = df.hvplot.scatter(
cmap={
'Shallow': 'orange',
'Intermediate': '#C70039',
'Deep': '#581845',
}, title="Dict keys categorical cmap", **{**plot_opts, **{"color": "depth_class"}},
)
(plot1 + plot2 + plot3 + plot4).cols(2)
Tip
Prefer Colorcet
for color mapping
While you can use any valid Matplotlib or Bokeh colormap with cmap
, we recommend using the Colorcet
library when possible. It provides a wide selection of perceptually accurate, well-tested colormaps optimized for data visualization.
Colorcet
is also natively integrated with other HoloViz tools like HoloViews and Datashader, which means:
No extra setup is required
Colormaps work consistently across all visualizations
Defaults like
glasbey_category10
andkbc_r
come fromColorcet
This makes it a reliable and visually accessible choice for both scientific and presentation-quality plots.
colormap
#
Alias for cmap
above.
color_key
#
Alias for cmap
above.
clim
#
The clim
option sets the lower and upper bounds of the color scale for continuous color mapping. It accepts a tuple like (min, max)
and is useful when:
You want consistent color scaling across multiple plots.
You want to clip outliers or focus on a specific data range.
This option is most effective when used with gridded plots like image
, heatmap
, or rasterized plots (datashade=True
, rasterize=True
). It works especially well with xarray datasets or plots where a colorbar is enabled.
In standard scatter plots or overlays created from tabular (pandas) data, clim
may have no visible effect unless color mapping and a colorbar are explicitly involved.
If clim
is not specified, the color scale is inferred from the data — or from the 2nd and 98th percentiles when robust=True
.
import hvplot.xarray # noqa
df = hvplot.sampledata.air_temperature("xarray").sel(time="2014-02-25 12:00")
plot1 = df.hvplot.image(clim=(250, 280), title="Colorbar clipped at clim values", frame_width=250)
plot2 = df.hvplot.image(title="Default colorbar scale", frame_width=250)
plot1 + plot2
See also
cnorm
#
The cnorm
option controls how data values are mapped to colors in a colormap. It affects the distribution of colors across the range of values.
Accepted values:
‘linear’ (default): evenly maps values across the colormap.
‘log’: applies logarithmic scaling, useful for data with large dynamic range.
‘eq_hist’: uses histogram equalization to emphasize contrast in sparse or skewed data.
import hvplot.pandas # noqa
df = hvplot.sampledata.earthquakes("pandas")
opts = dict(
x='lon', y='lat', c='depth',
cmap='plasma_r', frame_width=200,
)
plot1 = df.hvplot.scatter(title="Linear (default) color scaling", **opts,)
plot2 = df.hvplot.scatter(cnorm='log', title="Log color scaling", **opts,)
plot3 = df.hvplot.scatter(cnorm='eq_hist', title="Histogram Equalization", **opts,)
(plot1 + plot2 + plot3).cols(2)
rescale_discrete_levels
#
The rescale_discrete_levels
option improves the visual contrast of discrete values when using cnorm='eq_hist'
. By default, it adjusts the lower bound of the colormap so that non-zero values appear higher on the scale–helpful when low counts would otherwise appear faded.
This only has an effect if:
cnorm='eq_hist'
is setThe color values are discrete (e.g., counts or categories).
import hvplot.pandas # noqa
df = hvplot.sampledata.earthquakes("pandas")
# Simulate discrete values by binning magnitude
df['mag_bin'] = df['mag'].round()
opts = dict(
x='lon', y='lat', c='mag_bin', cmap='viridis_r',
cnorm='eq_hist', frame_width=250,
)
plot1 = df.hvplot.scatter(title="Discrete Color Scaling (Default)", **opts)
plot2 = df.hvplot.scatter(rescale_discrete_levels=False, title="Without Discrete Color Scaling", **opts)
plot1 + plot2
robust
#
The robust
option adjusts how the colormap range is computed for image plots. When set to True
and no explicit color limits (clim
) are provided, hvPlot calculates the color limits based on the 2nd and 98th percentiles rather than the extreme minimum and maximum values, reducing the impact of outliers.
In the following example using the air_temperature
dataset, we will add 2 extreme data points to show how the robust
keyword can help with visualizing the data without the outliers distorting the plot.
import hvplot.xarray # noqa
ds = hvplot.sampledata.air_temperature("xarray").sel(time="2014-02-25 12:00")
ds.air[0, 0] = 50
ds.air[-1, -1] = 500
opts = dict(width=350, cmap='viridis',)
plot1 = ds.hvplot.image(title="Plot with outliers", **opts)
plot2 = ds.hvplot.image(robust=True, title="Using robust=True", **opts)
plot1 + plot2
Notice how the colorbar in the second plot is now clipped to within the range of 240 to 290 with the colors in the plot evenly distributed.
symmetric
#
The symmetric
option controls whether the colormap range is centered around zero. If you do not explicitly set symmetric=True
and no color limits are provided via clim
, hvPlot automatically checks your data by computing the 5th and 95th percentiles. If the 5th percentile is below 0 and the 95th percentile is above 0, the option is enabled so that the colormap is balanced about 0.
Note
For lazily loaded or very large xarray datasets, this check is skipped for performance reasons and defaults to False
.
import hvplot.xarray # noqa
ds = hvplot.sampledata.air_temperature("xarray")
# Select a single date and convert to Celsius to get
# both negative and positive values around 0
data = ds.sel(time='2014-02-25 12:00') - 273.15
plot1 = data.hvplot.image(title="Symmetric True by default", width=350)
plot2 = data.hvplot.image(symmetric=False, title="Symmetric=False", width=350)
plot1 + plot2
In this example, the left image uses the symmetric colormap scaling (centered at zero), while the right image shows the default color scaling without enforcing symmetry. Notice that when the temperature values are symmetric around 0, the coolwarm
colormap is used by default.
check_symmetric_max
#
The check_symmetric_max
option sets an upper limit on the number of data elements for which the automatic symmetry check is performed. When the dataset’s size exceeds this threshold, hvPlot skips the symmetry check and defaults to treating the data as non-symmetric. By default this limit is 1,000,000 elements which usually works well for most datasets. However, you can adjust it if you want to force or avoid the symmetric check for smaller or larger datasets.
import hvplot.xarray # noqa
da = hvplot.sampledata.air_temperature("xarray").sel(time="2014-02-25 12:00") - 273.15
plot1 = da.hvplot.image(width=350, title="Default check for symmetry")
plot2 = da.hvplot.image(check_symmetric_max=10, width=350, title="Avoid symmetry check above 10")
plot1 + plot2