4

I'm creating a plotly python chart for a web app. Right now I am using the 'rangeselector' option to display different views to go back 7 days, 14 days, etc. However - what I actually want is a way to show the data for "this week", "last week", "the week before last week", etc. For the range selector, it always seems to start backward from today. Is there a way to do this in plotly? If not, is there a different way to go about this (i.e., a different charting library, some javascript)?

I'm new to web development, so thank you for the help.

rangeselector=dict(
        buttons=list([
            dict(count=7,
                    label="7d",
                    step="day",
                    stepmode="backward"),
            dict(count=14,
                    label="14d",
                    step="day",
                    stepmode="backward"),
            dict(count=1,
                    label="1m",
                    step="month",
                    stepmode="backward"),
            dict(count=6,
                    label="6m",
                    step="month",
                    stepmode="backward"),
            dict(count=1,
                    label="YTD",
                    step="year",
                    stepmode="todate"),
            dict(step="all")
        ])
    ),

2 Answers2

1

One workaround would be to add buttons that modify the xaxis range, which will also shift the rangeslider accordingly. We can do this by iterating backwards in 7 day increments from the end date to the start date with as many buttons as you like.

In the below example, we create buttons labeled ["All", "This Week", "Last Week", "The Week before Last Week"], and use some sample timeseries data. We need an "All" button to go back to the starting state of the figure.

import plotly.graph_objects as go
import pandas as pd

# Load data
df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv")
df.columns = [col.replace("AAPL.", "") for col in df.columns]
df['Date'] = pd.to_datetime(df['Date'])
df['Timedelta'] = (df['Date'].max() - df['Date']).dt.days

# Create figure
fig = go.Figure()

fig.add_trace(
    go.Scatter(x=list(df.Date), y=list(df.High)))

# Set title
fig.update_layout(
    title_text="Time series with range slider and selectors"
)

## ensure that the button date range includes the last data point
buttons = [
    dict(
        label="All",
        method="relayout",
        args=[
            {
                "xaxis.range": [df['Date'][0], df['Date'].iat[-1]]
            }
        ],
    )
]
button_labels = ["This Week", "Last Week", "The Week before Last Week"]
recent_date = df['Date'].iat[-1]
for i, label in enumerate(button_labels):
    button_end_date = recent_date
    button_start_date = recent_date - pd.Timedelta("7D")
    if button_start_date > df['Date'][0]:
        buttons += [
                dict(
                    label=label,
                    method="relayout",
                    args=[
                        {
                            "xaxis.range": [button_start_date, button_end_date]
                        }
                    ],
                )
        ]
        recent_date = recent_date - pd.Timedelta("7D")

# Add range slider
fig.update_layout(
    xaxis=dict(
        rangeselector=dict(
        ),
        rangeslider=dict(
            visible=True
        ),
        type="date"
    ),
    updatemenus=[
        dict(
            type="buttons",
            direction="right",
            x=0.7,
            y=1.2,
            showactive=True,
            buttons=buttons
        )
    ]
)

fig.show()

enter image description here

Derek O
  • 16,770
  • 4
  • 24
  • 43
  • Thanks - is there a solution that would allow one to go back indefinitely? i.e. I just want to be able to click an arrow and continue to see previous weeks (not just 'this week', 'last week', 'the week before last week') – user2154589 Sep 13 '22 at 03:08
  • such a solution would require you to keep track of the current state of the data. would you consider a `plotly-dash` solution? – Derek O Sep 13 '22 at 03:24
0

If you are willing to consider a plotly-dash based solution, you can create two buttons that modify the date range of the x-axis every time they are clicked. For example:

import plotly.graph_objects as go
from dash import Dash, html, Input, Output, dcc, ctx

import pandas as pd

app = Dash(__name__)

# Load data
df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv")
df.columns = [col.replace("AAPL.", "") for col in df.columns]
df['Date'] = pd.to_datetime(df['Date'])
df['Timedelta'] = (df['Date'].max() - df['Date']).dt.days

# Create figure
fig = go.Figure()

fig.add_trace(
    go.Scatter(x=list(df.Date), y=list(df.High)))

date_range = [df['Date'].min(), df['Date'].max()]

# Set title and range slider
fig.update_layout(
    title_text="Time series with range slider and selectors",
    xaxis=dict(
        rangeselector=dict(
        ),
        rangeslider=dict(
            visible=True
        ),
        type="date"
    )
)

app.layout = html.Div([
    html.Button('Last Week', id='last-week', n_clicks=0),
    html.Button('Next Week', id='next-week', n_clicks=0),
    dcc.Graph(figure=fig, id="time-series-range-slider")
])
@app.callback(
    Output('time-series-range-slider', 'figure'),
    Input('last-week', 'n_clicks'),
    Input('next-week', 'n_clicks'),
    prevent_initial_call=True
)
def update_figure(last_week, next_week, date_range=date_range):
    print(f"current date range: {date_range}")
    if "last-week" == ctx.triggered_id:
        date_range[0] -= pd.Timedelta("7D")
        date_range[1] -= pd.Timedelta("7D")
    if "next-week" == ctx.triggered_id:
        date_range[0] += pd.Timedelta("7D")
        date_range[1] += pd.Timedelta("7D")
    
    print(f"new date range: {date_range}")
    fig.update_layout(
        xaxis_range=date_range
    )
    return fig

if __name__ == '__main__':
    app.run_server(debug=True)

enter image description here

Derek O
  • 16,770
  • 4
  • 24
  • 43