Understanding Grouping and Coloring Options#
This guide explains the key differences between hvPlot’s three main approaches to handling categorical data: groupby, by, and color/c. Understanding these differences is important for creating effective visualizations and choosing the right approach for your specific use case. These options expect long data, where the categories are values in a column.
Overview of the Three Approaches#
hvPlot provides three primary ways to handle categorical data in your plots:
groupby: Creates interactive widgets with HoloMap / DynamicMap containersby: Creates multiple plot elements in NdOverlay or NdLayout containerscolor/c: Creates vectorized coloring within a single plot element
Each approach produces different outputs, offers different interaction capabilities, and has different performance characteristics.
import hvplot.pandas #noqa
penguins = hvplot.sampledata.penguins("pandas").dropna()
penguins.head(3)
| species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | year | |
|---|---|---|---|---|---|---|---|---|
| 0 | Adelie | Torgersen | 39.1 | 18.7 | 181.0 | 3750.0 | male | 2007 |
| 1 | Adelie | Torgersen | 39.5 | 17.4 | 186.0 | 3800.0 | female | 2007 |
| 2 | Adelie | Torgersen | 40.3 | 18.0 | 195.0 | 3250.0 | female | 2007 |
groupby: Interactive Widgets#
The groupby parameter creates interactive widgets that allow users to filter and explore different subsets of your data dynamically.
What it creates:#
HoloViews containers: HoloMap or DynamicMap object
Interactive widgets: Automatically generated based on data type
Single view at a time: Only one category visible per interaction
When to use:#
When you want to explore different subsets of data interactively
When you have many categories that would clutter a single plot
When building dashboards or interactive reports
When you need to reduce visual complexity
plot_groupby = penguins.hvplot.scatter(
x='bill_length_mm',
y='bill_depth_mm',
groupby='species',
title="Groupby: Interactive Widget",
width=400,
dynamic=False, # loads all the data on the frontend to enable interactivity on the fly
)
plot_groupby
Indexing and Access#
When you use groupby, hvPlot creates a HoloViews container object (HoloMap or DynamicMap) that acts like a dictionary. This container stores separate plot elements for each category value, which is why you can “index into” specific categories.
Why indexing works: The container maps category values (like 'Adelie', 'Chinstrap', 'Gentoo') to individual plot elements. Each category becomes a “key” that you can use to access its corresponding plot.
What happens when you index: You extract a single plot element from the container, giving you just the data for that specific category without the interactive widget.
Use cases:
Extract specific categories for further analysis
Combine individual categories in custom layouts
Access the underlying plot element for advanced customization
# Access specific category: plot['category_value']
adelie_only = plot_groupby['Adelie'] # Shows only Adelie penguins
print(f"Type of groupby plot: {type(plot_groupby)}")
print(f"Type of specific species: {type(adelie_only)}")
adelie_only
Type of groupby plot: <class 'holoviews.core.spaces.HoloMap'>
Type of specific species: <class 'holoviews.element.chart.Scatter'>
by: Multiple Plot Elements#
The by parameter creates multiple plot elements shown simultaneously, either overlaid or in separate subplots.
What it creates:#
When to use:#
When you want to compare categories side-by-side
When you have a manageable number of categories (typically < 10)
When color differentiation is sufficient for your analysis
When you need all data visible at once
overlay_plot = penguins.hvplot.scatter(
x='bill_length_mm',
y='bill_depth_mm',
by='species',
title="By: Overlaid Elements"
)
overlay_plot
plot_by_subplots = penguins.hvplot.scatter(
x='bill_length_mm',
y='bill_depth_mm',
by='species',
width=300,
subplots=True,
)
plot_by_subplots.cols(2)
Indexing and Access#
Similar to groupby, using by creates a HoloViews container object (NdOverlay or NdLayout) that you can index into. However, the behavior is different because all elements are displayed simultaneously.
Why indexing works: The NdOverlay/NdLayout container stores individual plot elements for each category, just like a dictionary mapping category values to plot elements.
What happens when you index: You extract a single plot element from the overlay/layout. This gives you just that category’s data as a standalone plot element, separate from the others.
Key difference from groupby: While groupby shows one category at a time with widgets, by shows all categories together, but you can still extract individual elements in the same way as groupby objects.
# Access specific category: plot['category_value']
adelie_element = overlay_plot['Adelie'] # Returns just the element for Adelie penguins
print(f"Type of 'by' plot: {type(overlay_plot)}")
print(f"Type of specific species element: {type(adelie_element)}")
adelie_element
Type of 'by' plot: <class 'holoviews.core.overlay.NdOverlay'>
Type of specific species element: <class 'holoviews.element.chart.Scatter'>
color/c: Vectorized Coloring#
The color parameter creates vectorized coloring within a single plot element, where each data point is colored based on the category value.
What it creates:#
When to use:#
When you want the best performance for large datasets
When you need smooth, continuous color mapping
When you don’t need to isolate specific categories
plot_color = penguins.hvplot.scatter(
x='bill_length_mm',
y='bill_depth_mm',
color='species',
title="Color: Vectorized Coloring"
)
plot_color
Indexing and Access#
Unlike groupby and by, using color creates a single plot element rather than a container object. This means indexing by category is not possible.
Why indexing doesn’t work: There’s no container structure - just one plot element where the color information is stored as data within the element itself. The categories exist as color mappings, not as separate, accessible plot elements.
What this means: All your data points are part of one unified plot object. The categorical information is encoded in the visual properties (colors) rather than the data structure.
Implications:
You cannot programmatically separate categories
Better performance since there’s only one plot element to render
All data is always visible
Ideal when you want aesthetic grouping without interactive separation
print(f"Type of 'color' plot: {type(plot_color)}")
try:
adelie_color = plot_color['Adelie'] # This will raise an error.
except Exception:
print("Error: You cannot index a single element by category!")
Type of 'color' plot: <class 'holoviews.element.chart.Scatter'>
Error: You cannot index a single element by category!
Understanding Container Objects vs Single Elements#
The key concept underlying these differences is how HoloViews organizes your data:
Container Objects (indexable)#
groupby→ HoloMap/DynamicMap:{category: plot_element}by→ NdOverlay/NdLayout:{category: plot_element}
These containers act like dictionaries where each category value maps to a separate plot element. This structure enables indexing with plot['category_name'].
Single Elements (not indexable)#
color→ Single plot element (Scatter, Curve, etc.)
Here, all data points belong to one plot element. Categories are encoded as visual properties (colors) rather than separate data structures.
This fundamental difference affects not just indexing, but also performance, interactivity, and how you can manipulate the resulting plots.
See also
Visual Output Comparison#
Let’s compare all three approaches side by side to see the differences:
import panel as pn
width = 300
groupby_plot = penguins.hvplot.scatter(
x='bill_length_mm', y='bill_depth_mm',
groupby='species', title="groupby='species'",
frame_width=width, widget_location='bottom_right',
shared_axes=False,
)
by_plot = penguins.hvplot.scatter(
x='bill_length_mm', y='bill_depth_mm',
by='species', title="by='species'",
frame_width=width,
)
color_plot = penguins.hvplot.scatter(
x='bill_length_mm', y='bill_depth_mm',
color='species', title="color='species'",
frame_width=width,
)
pn.Column(pn.Row(groupby_plot, by_plot), color_plot)
Key Visual Differences:#
groupby='species'shows only one species at a time with a widgetby='species'shows all species overlaid with different colors and a legendcolor='species'looks similar tobybut is a single plot element
When using subplots=True with by, you get separate panels:
penguins.hvplot.scatter(
x='bill_length_mm', y='bill_depth_mm',
by='species', subplots=True,
width=300, height=250,
)
Advanced: Combining Approaches#
You can combine these approaches for more complex visualizations:
penguins.hvplot.scatter(
x='bill_length_mm', y='bill_depth_mm',
groupby='island', color='species',
width=500, height=400, dynamic=False,
title="Groupby island, colored by species"
)
This is useful when you want to explore one dimension (island) while maintaining visual differentiation of another (species).
Summary#
Aspect |
|
|
|
|---|---|---|---|
Holoviews Object |
Single Element |
||
Interactivity |
Widget-based |
Static overlay |
Static single plot |
Indexing |
|
|
Not available |
Faceting the plot elements |
Not available |
|
Not available |
Performance |
Variable |
Medium |
Best |
Visual |
One at a time |
All simultaneous |
All simultaneous |
Use case |
Exploration |
Comparison |
Performance/Aesthetics |
Choose the approach that best matches your needs:#
Use
groupbyfor interactive exploration of many categoriesUse
byfor direct comparison of manageable categoriesUse
colorfor performance with large datasets or aesthetic coloring
Further Reading
See the HoloViews Reference Manual for more information on the various objects created by a hvPlot plot.
See also
Overlay plots and split them into subplots in practice