Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions doc/ref/plotting_options/styling.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,30 @@
"plot2 = df.hvplot(group_label=\"Company\", grid=True, width=400, title=\"grid=True\")\n",
"(plot1 + plot2).cols(1)"
]
},
{
"cell_type": "markdown",
"id": "56cdf3e2",
"metadata": {},
"source": [
"To show grid only on the x-axis, use ``grid='x'``. To show grid only on the y-axis, use ``grid='y'``. To change the grid line style, suffix with ``'dashed'``, ``'dotted'``, ``'dotdash'``, or ``'dashdot'``, e.g. ``grid='x-dashed'``."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7e38dee4",
"metadata": {},
"outputs": [],
"source": [
"import hvplot.pandas # noqa\n",
"\n",
"df = hvplot.sampledata.stocks(\"pandas\", engine_kwargs={\"index_col\" : \"date\"})\n",
"\n",
"plot1 = df.hvplot(group_label=\"Company\", grid='x', width=400, title=\"grid='x'\")\n",
"plot2 = df.hvplot(group_label=\"Company\", grid='y-dashed', width=400, title=\"grid='y-dashed'\")\n",
"(plot1 + plot2).cols(1)"
]
}
],
"metadata": {
Expand Down
18 changes: 15 additions & 3 deletions hvplot/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,8 +475,11 @@ class HoloViewsConverter:
fontsize : number or dict or None, default=None
Set title, label and legend text to the same fontsize. Finer control
by using a dict: ``{'title': '15pt', 'ylabel': '5px', 'ticks': 20}``.
grid : bool or None, default=None
Whether to show a grid.
grid : bool, str, or None, default=None
Whether to show a grid. If True, shows grid on both axes.
If ``'x'`` or ``'y'``, shows grid only on the specified axis.
Suffix with ``'dashed'``, ``'dotted'``, ``'dotdash'``, or ``'dashdot'``
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all these options supported by Matplotlib too? I recently looked into what line_dash (bokeh) / linestyle (matplotlib) supported and documented it there https://hvplot.holoviz.org/en/docs/latest/ref/api/manual/hvplot.hvPlot.line.html#line-dash.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all these options supported by Matplotlib too? I recently looked into what line_dash (bokeh) / linestyle (matplotlib) supported and documented it there

Thanks, I totally neglected matplotlib (and plotly); I'll see if there's a way to convert it; if not just the dash or dotted should be sufficient.

Does it allow to style both the x and y axes at the same time? If not, how about using another approach (e.g. dict from axis to style, list of tuples)?

I personally think at that point, people should use plot.opts(gridstyle={...}), else typing the {"line_dash": "dashed"} would be tedious. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I totally neglected matplotlib (and plotly); I'll see if there's a way to convert it; if not just the dash or dotted should be sufficient.

You can ignore Plotly.

Copy link
Collaborator Author

@ahuang11 ahuang11 Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, holoviews mpl does not support gridstyle so I guess I need to implement it in hv first...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh :/ Had a discussion recently on Discord where I said HoloViews isn't a plotting library. That's exactly what I meant :D

I just realized something. When it comes to plot styling options, hvPlot usually acts as a pass-through. One advantage of that is that the keys and their allowed values are common between hvPlot and HoloViews (so it's easier for users to go from one to the other). One disadvantage is that the keys and their values are backend-dependent. As a result, we haven't attempted to build a "unified styling API" in hvPlot, that would work for all the supported backends.

It looks like this is where we are heading in this PR with e.g. grid="x-dashed". Can I have your thoughts on that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, holoviews mpl does not support gridstyle
holoviz/holoviews#6700

It looks like this is where we are heading in this PR with e.g. grid="x-dashed". Can I have your thoughts on that?.

I actually just added a clause for dict support, so all of grid='x', 'grid='x-dashed', grid={'grid_line_color': 'red'} works.

hvPlot usually acts as a pass-through

To me, hvplot is not only a pass-through--(me being a PM for a second) it's a stress-free, direct plotting experience (at least for me), without having to deal with complex dict structures. Every time I have to use legend_opts or gridstyle, I have to pause and remember the valid key value pairs, which is why I like having a string like x-dashed or x.dashed or xdashed.

Copy link
Collaborator Author

@ahuang11 ahuang11 Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a result, we haven't attempted to build a "unified styling API" in hvPlot, that would work for all the supported backends.

Oh and I also don't fully understand the backend_compat, so I dumped directly in the function, but I imagine there could be a better place if you can help me understand the flow of converter.py, maybe in _process_style?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'm not sure where to do that to be honest. I think you're pretty much introducing a new pattern with a unified plot API for multiple backends. Probably not _process_style as gridstyle is a plot option in HoloViews. But having that in the __init__.py doesn't feel right either?

to change the grid line style, e.g. ``'x-dashed'``.

Resampling Options
------------------
Expand Down Expand Up @@ -1059,7 +1062,16 @@ def __init__(
plot_opts['logy'] = logy

if grid is not None:
plot_opts['show_grid'] = grid
if isinstance(grid, str):
gridstyle = {}
axis = grid[0]
other_axis = 'x' if axis == 'y' else 'y'
if len(grid) > 0:
line_dash = grid[1:].lstrip('-').lstrip('.').lstrip('_')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why lstrip('.').lstrip('_')?

gridstyle[f'{axis}grid_line_dash'] = line_dash
gridstyle[f'{other_axis}grid_line_alpha'] = 0
plot_opts['gridstyle'] = gridstyle
plot_opts['show_grid'] = bool(grid)

if legend is not None:
plot_opts['show_legend'] = bool(legend)
Expand Down
25 changes: 25 additions & 0 deletions hvplot/tests/testoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,31 @@ def test_legend_opts(self, df, backend):
opts = Store.lookup_options(backend, plot, 'plot')
assert opts.kwargs['legend_opts'] == lo

@pytest.mark.parametrize('grid_bool', [True, False])
def test_grid_boolean(self, df, backend, grid_bool):
plot = df.hvplot('x', 'y', grid=grid_bool)
opts = Store.lookup_options(backend, plot, 'plot')
assert opts.kwargs['grid'] is grid_bool

def test_grid_x(self, df, backend):
plot = df.hvplot('x', 'y', grid='x')
opts = Store.lookup_options(backend, plot, 'plot')
assert opts.kwargs['grid'] is True
assert opts.kwargs['gridstyle'] == {'ygrid_line_alpha': 0}

def test_grid_y(self, df, backend):
plot = df.hvplot('x', 'y', grid='y')
opts = Store.lookup_options(backend, plot, 'plot')
assert opts.kwargs['grid'] is True
assert opts.kwargs['gridstyle'] == {'xgrid_line_alpha': 0}

@pytest.mark.parametrize('grid_str', ['x-dashed', 'xdashed', 'x.dashed', 'x_dashed'])
def test_grid_line_dash(self, df, backend, grid_str):
plot = df.hvplot('x', 'y', grid=grid_str)
opts = Store.lookup_options(backend, plot, 'plot')
assert opts.kwargs['grid'] is True
assert opts.kwargs['gridstyle'] == {'ygrid_line_alpha': 0, 'xgrid_line_dash': 'dashed'}


@pytest.fixture(scope='module')
def da():
Expand Down
Loading