9

I am trying to build a dash app with a datetime slider, but the following code does not work - the url displays "Error loading layout". Am I doing something stupid, or are the any tricks needed to build a slider on datetime objects? Reproducible code follows:

import dash
import dash_core_components as dcc
import dash_html_components as html
import datetime

base = datetime.datetime.today()
date_list = [base - datetime.timedelta(days=x) for x in range(0, 10)]

app = dash.Dash()

app.layout = html.Div([
    html.Label('Slider'),
    dcc.RangeSlider(min=min(date_list), 
                    max=max(date_list), 
                    value=min(date_list))
    ])

if __name__ == '__main__':
    app.run_server()
famargar
  • 3,258
  • 6
  • 28
  • 44

5 Answers5

15

Plotly-Dash does currently not support datetime objects.

A solution for you problem is to described in my answer to an other question Plotly datetime


One possible solution is to use two different arguments at the initialization:

  • value
  • marks

Example by using pandas for timestamp generation:

Needed imports:

import pandas as pd
import time

Some needed functions:

daterange = pd.date_range(start='2017',end='2018',freq='W')

def unixTimeMillis(dt):
    ''' Convert datetime to unix timestamp '''
    return int(time.mktime(dt.timetuple()))

def unixToDatetime(unix):
    ''' Convert unix timestamp to datetime. '''
    return pd.to_datetime(unix,unit='s')

def getMarks(start, end, Nth=100):
    ''' Returns the marks for labeling. 
        Every Nth value will be used.
    '''

    result = {}
    for i, date in enumerate(daterange):
        if(i%Nth == 1):
            # Append value to dict
            result[unixTimeMillis(date)] = str(date.strftime('%Y-%m-%d'))

    return result

For the RangeSlider object you can initialize it like this:

dcc.RangeSlider(
                id='year_slider',
                min = unixTimeMillis(daterange.min()),
                max = unixTimeMillis(daterange.max()),
                value = [unixTimeMillis(daterange.min()),
                         unixTimeMillis(daterange.max())],
                marks=getMarks(daterange.min(),
                            daterange.max())
            )
Flavia Giammarino
  • 7,987
  • 11
  • 30
  • 40
Nils
  • 2,665
  • 15
  • 30
  • 1
    Strangely, the keys must be cast as integers as well, not floats. Nice work. https://github.com/plotly/dash-core-components/issues/159 – Sean McCarthy Mar 01 '20 at 21:10
2

This should definitely work and is much easier than any other solution

marks = {int(i):str(j) for i,j in zip(range(len(df.years)), df[“years”])}

And then you can mimic the same in the functional callbacks to update the values by dictionary keys.

scleronomic
  • 4,392
  • 1
  • 13
  • 43
Manikandan
  • 19
  • 2
0

Looking at the documentation, why not try just the regular slider dcc.Slider. You can check the example at the bottom of the documentation: https://dash.plot.ly/getting-started#dash-app-layout.

EDIT: Added code example from the documentation.

You can add this as an element to your list, hmtl.Div.
html.Label('Slider'),
    dcc.Slider(
        min=0,
        max=9,
        marks={i: 'Label {}'.format(i) if i == 1 else str(i) for i in range(1, 6)},
        value=5,
    ),
Mert Karakas
  • 178
  • 1
  • 4
  • 22
0

What are you index units? Pandas' datetime or simply numbers?

I tried doing a little bit of debugging:

app.layout = html.Div([dcc.RangeSlider(id='my-slider',
                                       min=1,
                                       max=5,
                                       step=1,
                                       # value=2,
                                       ),
                       html.H1(id='product')])

It turns out that if I set the value (so uncomment value=2), it breaks. It would be good if you could try without the value argument.

EDIT: The value argument needs to be a list (the range...). Setting value=[2,3]works. This might solve your problem.

Dummy101
  • 1
  • 2
  • I edited the number to provide reproducible datetimes. Having a list of values does not make a difference. It seems like the issue is related to having datetimes instead of float/int (?) – famargar Jun 27 '18 at 13:51
  • Yes, I think the issue comes from the fact that you are using datetimes rather than int/floats. See https://community.plot.ly/t/solved-has-anyone-made-a-date-range-slider/6531/8 – Dummy101 Jun 27 '18 at 14:17
  • 1
    I am surprised that they don't support that yet though, because they do support it for the graphs' rangesliders – Dummy101 Jun 27 '18 at 14:18
  • 1
    Indeed. The plotly forum clearly isn't as good as StackOverflow - that example is hard to replicate. I will try though. – famargar Jun 27 '18 at 15:25
0

Just add-on.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from datetime import datetime, timedelta, datetime, date

dates = ['03-02-2021', '03-03-2021', '03-04-2021', '03-05-2021', '03-06-2021', '03-07-2021']
mytotaldates = {i:datetime.strptime(x, "%m-%d-%Y").date() for i,x in enumerate(dates)}


a = (list(mytotaldates.keys()))

app = dash.Dash()

app.layout = html.Div(children=[   
    dcc.Slider(
        id='Dateslider',
        min=a[0],
        max=a[-1],
        marks=mytotaldates,
        value=a[-1],
    ),
    html.Div(id='OutputContainer')
])


@app.callback(
    Output('OutputContainer', 'children'),
    [Input('Dateslider', 'value')])
def rangerselection(val):
    val = mytotaldates[val]
    return f'Selected Date: {val}'


if __name__ == '__main__':
    app.run_server(debug=False, use_reloader=True)
Prakash
  • 152
  • 1
  • 10