Skip to content

Document the Styling Options #1574

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open

Document the Styling Options #1574

wants to merge 17 commits into from

Conversation

Azaya89
Copy link
Collaborator

@Azaya89 Azaya89 commented May 13, 2025

closes #1573

This PR documents the Styling Options in the new API reference guide.

@Azaya89 Azaya89 requested a review from Copilot May 13, 2025 14:20
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR updates the API reference guide by documenting the new Styling Options, specifically highlighting the fontscale option. Key changes include:

  • Repositioning the fontscale documentation block in hvplot/converter.py.
  • Updating the option arrays to reflect the new organization of fontscale.
  • Adding a link and navigation entry for Styling Options in the documentation page.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
hvplot/converter.py Moved the fontscale documentation from the Size And Layout section and adjusted its placement in option lists.
doc/ref/plotting_options/index.md Added a documentation link for Styling Options and updated navigation to include styling options.
Comments suppressed due to low confidence (2)

hvplot/converter.py:226

  • The fontscale documentation was removed from the Size And Layout Options block and reinserted later. Please confirm that this repositioning clearly conveys its usage and grouping alongside other styling options.
    -    fontscale : number

hvplot/converter.py:520

  • Removing 'fontscale' from this option list and later re-adding it in a different section may lead to confusion. Verify that the ordering of options remains clear for future maintenance.
    -        'fontscale',

" cmap='viridis',\n",
" clim=(4.5, 6.5),\n",
" title=\"Earthquakes with Clipped Magnitude Scale\"\n",
") # It doesn't look like it worked"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The clim keyword used in the example doesn't seem like it changed anything. I'm open to a better example that shows it working.

Copy link
Member

Choose a reason for hiding this comment

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

Can you please open an issue?

Copy link
Member

@maximlt maximlt left a comment

Choose a reason for hiding this comment

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

I'm a bit uncomfortable with the fact that, except fontsize and fontscale, these styling options are effectively color spec/mapping options.

Should we rename it Color Options and move the font options elsewhere? I would also love if we could have a place where we can add more information on colormaps (display the default ones, show a list of colormaps users can pick from, etc.).

"df = hvsampledata.penguins(\"pandas\")\n",
"\n",
"# Color by species column\n",
"df.hvplot.scatter(\n",
Copy link
Member

Choose a reason for hiding this comment

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

Can you please add other examples that show how to set color with the other options it accepts? Single color name, list of colors, array, etc.

"metadata": {},
"source": [
"(option-color)=\n",
"## `color`\n",
Copy link
Member

Choose a reason for hiding this comment

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

We need to explain somewhere the difference between color and by. See also https://discourse.holoviz.org/t/use-color-instead-of-by-when-possible/6784

"(option-cmap)=\n",
"## `cmap`\n",
"\n",
"The `cmap` option controls the colormap used when mapping numerical or categorical data values to color. It supports:\n",
Copy link
Member

Choose a reason for hiding this comment

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

Can you please add examples showing how to use all these options?

"- Named colormaps (e.g., 'viridis', 'plasma', 'coolwarm')\n",
"- Lists of color strings or hex codes (for custom sequences)\n",
"- Dictionaries (for categorical color mappings)\n",
"- Colormap objects from Matplotlib or HoloViews\n",
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to include links.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Links to the color palettes?

Copy link
Member

Choose a reason for hiding this comment

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

Links and/or additional information to guide a user to find and use these colormap objects (imagine a user who has no clue about all this).

"- Dictionaries (for categorical color mappings)\n",
"- Colormap objects from Matplotlib or HoloViews\n",
"\n",
"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."
Copy link
Member

Choose a reason for hiding this comment

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

Should we reference the default colormaps here too or is it enough to have them in the docstring?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i will add it in the note differentiating by and color

"The `fontsize` option sets the font size for different text elements in the plot. It can be:\n",
"- A single value (e.g. 12, '14pt', or '10px') to apply to all text.\n",
"- A dictionary to control individual elements like title, axes labels, or ticks.\n",
"Example: {'title': '15pt', 'xlabel': '12pt', 'ylabel': '12pt', 'ticks': 10}\n",
Copy link
Member

Choose a reason for hiding this comment

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

Hm that looks very Bokeh specific. Does it work too with Matplotlib? I'd suggest adding a Matplotlib example here .

"# Simulate discrete values by binning magnitude\n",
"df['mag_bin'] = df['mag'].round()\n",
"\n",
"df.hvplot.scatter(\n",
Copy link
Member

Choose a reason for hiding this comment

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

Please add a plot with rescale_discrete_levels=False (True is the default) so people can compare.

"plot1 = ds.hvplot.image(width=350)\n",
"plot2 = ds.hvplot.image(robust=True, width=350)\n",
"\n",
"plot1 + plot2"
Copy link
Member

Choose a reason for hiding this comment

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

Let's use this example from xarray, it makes it more obvious robust can help with outliers https://docs.xarray.dev/en/latest/user-guide/plotting.html#robust

"ds = hvsampledata.air_temperature(\"xarray\")\n",
"# Select a single date and convert to Celsius to get\n",
"# both negative and positive values around 0\n",
"data = ds.sel(time='2014-02-25 12:00') - 273\n",
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"data = ds.sel(time='2014-02-25 12:00') - 273\n",
"data = ds.sel(time='2014-02-25 12:00') - 273.15\n",

Comment on lines 502 to 505
"ds = hvsampledata.air_temperature(\"xarray\").sel(time=\"2014-02-25 12:00\")\n",
"\n",
"plot1 = (ds - 273).hvplot.image(width=350, title=\"Default check for symmetry\")\n",
"plot2 = (ds - 273).hvplot.image(check_symmetric_max=10, width=350, title=\"Avoid symmetry check above 10\")\n",
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"ds = hvsampledata.air_temperature(\"xarray\").sel(time=\"2014-02-25 12:00\")\n",
"\n",
"plot1 = (ds - 273).hvplot.image(width=350, title=\"Default check for symmetry\")\n",
"plot2 = (ds - 273).hvplot.image(check_symmetric_max=10, width=350, title=\"Avoid symmetry check above 10\")\n",
"da = hvsampledata.air_temperature(\"xarray\").sel(time=\"2014-02-25 12:00\") - 273.15\n",
"\n",
"plot1 = da.hvplot.image(width=350, title=\"Default check for symmetry\")\n",
"plot2 = da.hvplot.image(check_symmetric_max=10, width=350, title=\"Avoid symmetry check above 10\")\n",

@Azaya89
Copy link
Collaborator Author

Azaya89 commented May 16, 2025

I'm a bit uncomfortable with the fact that, except fontsize and fontscale, these styling options are effectively color spec/mapping options.

Should we rename it Color Options and move the font options elsewhere?

I'd say let's finish with documenting all the options then we can decide holistically how and what to re-arrange.

@Azaya89
Copy link
Collaborator Author

Azaya89 commented May 16, 2025

Thanks for the review @maximlt . This looks much better now.

@Azaya89 Azaya89 added the NF SDG 2025 NumFocus Software Development Grant 2025 label May 16, 2025
@Azaya89 Azaya89 requested a review from maximlt May 19, 2025 11:25
Copy link
Member

@maximlt maximlt left a comment

Choose a reason for hiding this comment

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

I liked a lot many of the changes you made. Left a few comments part of this review.

"\n",
"- **`color='<categorical column name>'`** assigns colors *within a single plot* based on the values in the categorical column using the default `glasbey_category10` colormap. You can customize the colormap using the `cmap` keyword like in the examples above. This maps the values in the categorical column to colors using the specified colormap.\n",
"\n",
"- **`by='<categorical column name>'`** splits the data into multiple groups and creates an [overlay](inv:holoviews#holoviews.core.element.NdOverlay) of plots, one per unique value in the column. However, it does **not** honor the `cmap` you provide. Instead, it uses a default color cycle (`glasbey_hv`) to assign colors per group.\n",
Copy link
Member

Choose a reason for hiding this comment

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

Ugh wait isn't that a bug?

However, it does not honor the cmap you provide. Instead, it uses a default color cycle (glasbey_hv) to assign colors per group.\

Copy link
Collaborator Author

@Azaya89 Azaya89 May 20, 2025

Choose a reason for hiding this comment

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

tbh, I don't know or can't remember if it's a bug or it's by design. This is just the result of me trying out all the different options and combinations and writing out what I see.

Edit: I asked ChatGPT and got a better explanation:
"""
When you use by='<categorical column>', hvPlot groups the data and produces an overlay of plots (NdOverlay or NdLayout). Each group becomes a separate layer, and the plotting system (e.g. HoloViews + Bokeh) assigns a color to each layer via a style cycle — not via colormap mapping like cmap='viridis'.

So what happens when you provide a cmap?
• If cmap is a named colormap (like 'viridis', 'kbc_r', etc.), it is ignored because these colormaps are designed for continuous data, and by= creates discrete overlays.
• If you pass a list of colors, hvPlot automatically wraps it in a Cycle(...), which is used by HoloViews to color the overlaid elements — and that’s why it works. This is expected behavior.

So it’s not a bug — it’s that by supports color cycling, but not colormap mapping.
"""

"(option-color_key)=\n",
"## `color_key`\n",
"\n",
"The `color_key` option defines a categorical color mapping, primarily used with `datashade=True`. It maps distinct categories in your dataset to specific colors.\n",
Copy link
Member

Choose a reason for hiding this comment

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

Can we say color_key should only be used exclusively with datashade=True? Until we figure out why it needs to be used instead of cmap (still not clear to me, maybe the answer is in your colormapping notebook?).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm, I will try to re-write the explanation and examples to make it clearer.

"\n",
"For example:\n",
"- `cmap='viridis'` is great for smooth gradients like 'magnitude' or 'depth'.\n",
"- `color_key={'Shallow': 'blue', 'Deep': 'green'}` is best for labeled classes like 'depth_class'.\n",
Copy link
Member

Choose a reason for hiding this comment

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

So for a datashaded plot we cannot use cmap={'Shallow': 'blue', 'Deep': 'green'}?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It works for categorical columns because internally cmap becomes color_key:

if 'cmap' in self._style_opts and self.datashade:
    ...
    if isinstance(cmap, dict):
        opts['color_key'] = cmap
    else:
        opts['cmap'] = process_cmap(cmap, levels, categorical=categorical)

https://github.com/holoviz/hvplot/blob/main/hvplot/converter.py#L1977-L1983

The difference is when you want to map colors for use in continuous columns. I will update the explanation and examples used.

Copy link
Member

Choose a reason for hiding this comment

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

Ok thanks. I'm trying to understand why color_key is needed, and if not, I'd be really happy to deprecate it (having colormap and cmap is already enough...). I am trying to avoid documenting too much a feature I'd rather hide if it doesn't bring much to the table.

Copy link
Member

Choose a reason for hiding this comment

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

I'm still confused :) Why should one use color_key over cmap since it seems like they produce exactly the same plots when given a list or a dict?

image image

"metadata": {},
"outputs": [],
"source": [
"import hvplot.pandas # noqa\n",
Copy link
Member

Choose a reason for hiding this comment

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

Not sure this cell is required here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Which cell?

Copy link
Member

Choose a reason for hiding this comment

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

    "import hvsampledata\n",
    "\n",
    "df = hvsampledata.penguins(\"pandas\")\n",
    "df.head(3)"

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's the cell where the penguins dataset is imported for the examples. If I remove it there, then I'll have to import it twice (once each for the bokeh and mpl examples).

Copy link
Member

Choose a reason for hiding this comment

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

Ok I understand. What confused me was the .head(3) call, no sure why it's there in the middle of this reference page?

image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK. Since df there serves both plots which are not in the same cell, I wanted to show a snippet of the dataframe before its plotted.

Copy link
Member

Choose a reason for hiding this comment

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

I still think it's strange to call .head(3) in the middle of this page.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK. I can remove that line then.

Copy link
Member

@maximlt maximlt left a comment

Choose a reason for hiding this comment

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

I left a few comments. The PR needs to be updated to resolve a few conflicts with the main branch.

@Azaya89 Azaya89 marked this pull request as draft May 22, 2025 15:26
@Azaya89 Azaya89 marked this pull request as ready for review May 28, 2025 17:45
@Azaya89 Azaya89 requested a review from maximlt May 28, 2025 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NF SDG 2025 NumFocus Software Development Grant 2025
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Document the style options
2 participants