lukas
lukas

Reputation: 141

How to keep only time on plotly graph axis

I'm trying to create a graph with plotly and cufflinks that has dates on the xaxis, some amounts on the primary y, and time on the secondary y.

The data looks like this:

        automatic   manual  time
2019-02-25  206.0   1206.0  2019-02-26 16:58:09
2019-02-26  225.0   136.0   2019-02-27 08:33:49
2019-02-27  213.0   554.0   2019-02-28 07:25:19
2019-02-28  244.0   103.0   2019-03-01 07:32:37
2019-03-01  102.0   119.0   2019-03-04 12:06:37

The setup for the figure is as follows:

fig = go.Figure(**cf.tools.merge_figures([
    df.figure(columns=["automatic", "manual"], kind="bar", barmode="stack"),
    df.figure(columns=["time"])
])).set_axis(["time"], side="right")

The only way I've gotten the graph I want is by setting the date part of each date in the df.time column to the same arbitrary date, like so:

df.loc[:, "time"] = df.time.apply(lambda d: d.replace(year=1967, month=1, day=1))

However, this way I get the wrong hover text and that arbitrary date is displayed at the bottom of the secondary y.

I've tried to set the range on yaxis2 manually like so:

sotimes = [d for d in df.time.tolist() if not pd.isnull(d)]
fig["layout"].update({"yaxis2": {"range": [f"{min(sotimes):%H:%M:%S}", f"{max(sotimes):%H:%M:%S}"]}})

Strangely, this results in yaxis2 being a date range as well.

I've also tried to convert df.time to time only, either as a string or as datetime.time like so:

df.loc[:, "time"] = df.time.apply(
    lambda d: d.time() if not pd.isnull(d) else pd.NaT)
df.loc[:, "time"] = df.time.apply(
    lambda d: f"{d:%H:%M:%S}" if not pd.isnull(d) else "")

Both of these result in yaxis2 not being ordered at all, the times are displayed in the order in which they appear in df.time.


EDIT 1 - add complete code

import cufflinks as cf
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import pandas as pd


# plotly stuff
cf.go_offline()

dct = {
    "date": ["2019-02-25", "2019-02-26", "2019-02-27", "2019-02-28", "2019-03-01"],
    "auto": [206, 225, 213, 244, 102],
    "manual": [1206, 136, 554, 103, 119],
    'time': [pd.Timestamp(2019, 2, 26, 16, 58, 9), pd.Timestamp(2019, 2, 27, 8, 33, 49),
             pd.Timestamp(2019, 2, 28, 7, 25, 19), pd.Timestamp(2019, 3, 1, 7, 32, 37),
             pd.Timestamp(2019, 3, 4, 12, 6, 37)]
}
df = pd.DataFrame(dct).set_index("date")
df.loc[:, "time"] = df.time.apply(lambda d: d.replace(year=1967, month=1, day=1))

fig = go.Figure(**cf.tools.merge_figures([
    df.figure(columns=["auto", "manual"], kind="bar", barmode="stack"),
    df.figure(columns=["time"])
])).set_axis(["time"], side="right")
# sotimes = [d for d in df.time.tolist() if not pd.isnull(d)]
# fig["layout"].update({"yaxis2": {"range": [f"{min(sotimes):%H:%M:%S}", f"{max(sotimes):%H:%M:%S}"]}})
plot(fig)

Upvotes: 0

Views: 1212

Answers (1)

Vivian
Vivian

Reputation: 11

This isn't an answer to the whole question, but creates a working graph the way you would want it to. Using the approach of setting every datetime instance to the same date, you could adapt the tick labels using:

    fig["layout"].update({"yaxis2": {"tickformat": "%H:%M"}})

Upvotes: 1

Related Questions