Black Lives Matter. Please consider donating to Black Girls Code today.

[Solved] Has Anyone Made a Date-Range-Slider?

Hi Everyone,

So I know there is a Date-Picker component, I know there is a Range-Slider as well, but I was wondering if someone had figured out how to make a “Date-Range-Slider” which could be used to filter a dataset between two datetime objects.

If anyone has something on this please let me know - thank you!

1 Like
from dateutil.relativedelta import relativedelta

# Usual DASH stuff


        dcc.RangeSlider(
            id = 'datetime_RangeSlider',
            updatemode = 'mouseup', #don't let it update till mouse released
            min = unix_time_millis(d.datetime.min()),
            max = unix_time_millis(d.datetime.max()),
            value = [unix_time_millis(d.datetime.min()),
                         unix_time_millis(d.datetime.max())],
            #TODO add markers for key dates
            marks=get_marks_from_start_end(d.datetime.min(),
                                           d.datetime.max()),
        ),

def get_marks_from_start_end(start, end):
    ''' Returns dict with one item per month
    {1440080188.1900003: '2015-08',
    '''
    result = []
    current = start
    while current <= end:
        result.append(current)
        current += relativedelta(months=1)
    return {unix_time_millis(m):(str(m.strftime('%Y-%m'))) for m in result}

worked for me.

4 Likes

Thanks for the reply. It looks like you call a function “unix_time_millis” which I assume you defined yourself, could you show that function please?

Appreciate the help!

You might want to use a pandas time series here.

Why do you think a pandas time series would be superior to the regular datetime objects? Thanks

epoch = datetime.datetime.utcfromtimestamp(0)

def unix_time_millis(dt):
return (dt - epoch).total_seconds() #* 1000.0

1 Like

Thanks a lot this has worked perfectly!

Here’s an approach I used to create a date RangeSlider. Just sharing as an alternate approach…

   html.Div([
    dcc.RangeSlider(
        id='month-slider',
        updatemode='mouseup',
        count=1,
        min=1,
        max=maxmarks,
        step=1,
        value=[maxmarks-5,maxmarks],
        marks=tags,
        pushable=1
    )
    ],

The following code runs before the RangeSlider is called…

maxmarks=13
tday=pd.Timestamp.today() #gets timestamp of today
m1date=tday+DateOffset(months=-maxmarks+1) #first month on slider
datelist=pd.date_range(m1date, periods=maxmarks, freq='M') # list of months as dates
dlist=pd.DatetimeIndex(datelist).normalize()
tags={} #dictionary relating marks on slider to tags. tags are shown as "Apr', "May', etc
datevalues={} #dictionary relating mark to date value
x=1
for i in dlist:
    tags[x]=(i+DateOffset(months=1)).strftime('%b') #gets the string representation of next month ex:'Apr'
    datevalues[x]=i
    x=x+1

include these libraries…

import pandas as pd
from pandas.tseries.offsets import *

This is what it looks like…
image

2 Likes

This is how the range Slider looks in my case.

The code I used:
dcc.RangeSlider(
id=‘my-range-slider’,
updatemode = ‘mouseup’,
min = unix_time_millis(downloads()[‘AUDITTIME’].min()),
max = unix_time_millis(downloads()[‘AUDITTIME’].max()),
step=None,
value = [unix_time_millis(downloads()[‘AUDITTIME’].min()),
unix_time_millis(downloads()[‘AUDITTIME’].max())],
marks=get_marks_from_start_end(downloads()[‘AUDITTIME’].min(),downloads()[‘AUDITTIME’].max())
)

and it doesnt not seperate the months exactly from 1-30/31 days.

Thanks for sharing the code. It works, except from that the label marks are not shown. So the dots are , but not the date itself. No errors shown and get_marks_from_start_end seems to have the expected output. Any ideas? thanks

I did something like this (with a df that has datetimeindex):

Function

def get__marks(f):

dates = {}
for z in f.index:
    dates[f.index.get_loc(z)] = {}
    dates[f.index.get_loc(z)] = str(z.month) + "-" + str(z.day)

return j

App Layout

dcc.RangeSlider(
    id='range-slider',
    updatemode='mouseup',
    min=0,
    max=len(df.index) - 1,
    count=1,
    step=1,
    value=[0, len(df.index) - 1],
    marks=get_marks(df),
)

How about something like

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

1 Like