holoviz / hvplot

A high-level plotting API for pandas, dask, xarray, and networkx built on HoloViews
https://hvplot.holoviz.org
BSD 3-Clause "New" or "Revised" License
1.13k stars 108 forks source link

Document (x/y)formatter and the printf-style #1215

Open MarcSkovMadsen opened 10 months ago

MarcSkovMadsen commented 10 months ago

I've been working on a chat interface for hvPlot. Its already available at Panel-Chat-Examples.

According to https://hvplot.holoviz.org/user_guide/Customization.html the xformatter argument should support a str argument.

image

But when I try, the plot does not show and I see errors raised in the browser console.

import pandas as pd
import hvplot.pandas
import bokeh.sampledata

bokeh.sampledata.download()
from bokeh.sampledata.stocks import MSFT

data = pd.DataFrame(MSFT)[60:120]
data["date"] = pd.to_datetime(data["date"])
data = data.sort_values(by="date", ascending=False).head(100)
plot = data.hvplot(
    x='date',
    y='close',
    kind='line',
    xformatter='%d-%Y',
    xlabel='Date',
    ylabel='Closing Price',
    title='Closing Price Over Time',
    responsive=True,
)
import panel as pn
pn.panel(plot).servable()

image

If I don't use the xformatter, then the plot shows.

bokeh==3.3.1 holoviews==1.18.1 hvplot==0.9.0 panel==1.3.4
hoxbro commented 10 months ago

This is because the xformatter string is not a printf formatted string but a datetime one, this is also what is mentioned in the docstring.

I can be recreated in HoloViews with hv.Curve(data, "date", "close").opts(xformatter="%d-%Y") . The wrap_formatter seems to be the function to change if you want to add more logic around datetime formatted.

maximlt commented 10 months ago

Doing a little more digging on this one. The printf style refers to a family of functions in C to format strings, in Python this is now used to refer to the old string format as in 'pi %5.3f.' % math.pi. When you pass a string to xformatter in hvPlot/HoloViews, it's assumed in this format and a PrintfTickFormatter will be created, the implementation doesn't validate that the format matches on the Python side, it uses the sprintf.js library to format and something is going to fail while rendering (that should be better handle on BokehJS' side?).

What you tried to use is Python's strftime format, that is not included in the printf-style format, but is in .format() and f-string:

image

What I'd like to know is what you were trying to achieve by setting xformatter, as I'd assume that when you zoom in you'd still want the x-ticks to scale to higher resolution values (months->days->hours...).

The DatetimeTickFormatter is the default formatter used for a datetime axis. You can pass a new instance if you wish to customize it. You can for instance update how ticks are formatted at the days level with DatetimeTickFormatter(days="%Y/%m/%d"):

image

Or you can set a context which is a relatively new feature, quite unknown but very useful! For instance with DatetimeTickFormatter(context=RELATIVE_DATETIME_CONTEXT()):

image

I think the only reasonable thing we can do is improve the docs, pointing to the right places and adding some examples.

MarcSkovMadsen commented 10 months ago

Thx. What i am trying to achieve is to enable the ChatBot to set the xformatter also for dates.

It is told that the xformatter is a string and supports printf style formatting. Then it suggests the formatting that fails.

It sounds to me like printf style is obsolete and something modern that a Human or an AI would know how to use should be supported instead?

hoxbro commented 10 months ago

We still use printf formatting for numbers even with the new Python format: f"{1:0.2f}".

What is happening in Python uses the __format__, to determine how it should format the object. E.g. for datetime it will use strftime.