Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

dynamically changing slider range issue #432

Open
tcados opened this issue Jan 7, 2019 · 3 comments
Open

dynamically changing slider range issue #432

tcados opened this issue Jan 7, 2019 · 3 comments
Labels

Comments

@tcados
Copy link

tcados commented Jan 7, 2019

I have been trying to make a slider with a dynamic range that is modified via a date picker dropdown in conjunction with intermediate filtered data. There is a very strange glitch that happens when I do it.

I use a date picker dropdown to select the date range, which then filters through a dataset to make a filtered intermediate data set which is stored in an html div. The updated div is then used as an input to a callback to modify the max range of the slider.

When the page initiates, I can move the slider along the full range (0-98)
When I change the upper end of the date, the slider then has a lower max value.
However, when I change the lower end of the date, the section of the slider up till the new start date becomes inactive (whereas the starting point should still be 0.

Please see this video as an example:

I show how it can go to through the full range. I show how when the upper end of the date is changed, it works as expected, and then I show how when the bottom range of the date is changed, it bugs out.

ezgif-5-9388e33d75b9

I made a very simplified example code, and also hosted the page to show the issue:

http://sliderbug.us-west-2.elasticbeanstalk.com

and here is the code:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import pandas as pd
from datetime import datetime

app = dash.Dash()
application = app.server

# list of 100 consecutive dates by day
idx_range = [i for i in range(100)]
full_range_dates = pd.DataFrame(pd.date_range(datetime(2017, 5, 1), periods=100, freq='D'), index=idx_range, columns=['date'])

data = {'idx':idx_range, 'date':full_range_dates}

app.layout = html.Div([

    # Div holder for filtered data
    html.Div(id='intermediate-data', style={'display': 'none'}),

    # dateRange picker
    html.Div([
        html.H3('Date Range:', style={'display': 'inline-block', 'marginRight': '10px'}),

        dcc.DatePickerRange(
            id='date-picker',
            min_date_allowed=datetime(2017, 5, 1),
            max_date_allowed=datetime(2017, 8, 10),
            start_date=datetime(2017, 5, 1),
            end_date=datetime(2017, 8, 10)
        )
    ], style={'display': 'inline-block'}),

    # slider
    dcc.Slider(
        id='slider',
        min=0,
        max=100,
        step=1,
        updatemode='drag',
        value=50,
    ),

    # Chosen slider value
    html.H4(id='output-slider-value', style={'fontSize': 24, 'display': 'inline-block', 'margin-top': '25px'})

],style={'width': '80%', 'margin-left': 'auto',
              'margin-right': 'auto','textAlign': 'center'})


# functions

@app.callback(Output('intermediate-data', 'children'),
              [Input('date-picker', 'start_date'),
               Input('date-picker', 'end_date')])
def filter_data(startdate, enddate):
    filtered_dates = full_range_dates[(full_range_dates['date'] > startdate) & (full_range_dates['date'] < enddate)]
    return filtered_dates.to_json(orient='split')


# Show Value selected by slider
@app.callback(Output('output-slider-value', 'children'),
              [Input('intermediate-data', 'children'),
               Input('slider', 'value')])
def SliderText(df, idx):
    filtered_dates = pd.read_json(df, orient='split')
    date = (filtered_dates['date'].loc[idx+1]).strftime('%Y-%m-%d %H:%M')
    return '{} which is idx {}'.format(date, idx)


# change slider range based on size of filtered data array

@app.callback(Output('slider', 'max'),
              [Input('intermediate-data', 'children')])
def update_slider(df):
    filtered_dates = pd.read_json(df, orient='split')  # , date_format='iso'
    maxval = filtered_dates.shape[0] - 1
    return maxval


if __name__ == '__main__':
    application.run(debug=True, port=8080)

Is there a better way to go about this? The ability to use a dynamic range slider is a very useful tool for users filtering through long datasets.

By the way, I also experienced this issue when passing back the range slider through a callback into a placeholder div and setting app.config.supress_callback_exceptions = True

Thanks in advance,
Troy

@Marc-Andre-Rivet
Copy link
Contributor

@tcados I think the problem comes from only the slider's max value being evaluated. If the same thing is done for the min value (because of the full_range_dates mapping, the min value is not always 0 in this implementation but rather varies with the chosen start date, and then making sure the slider value fits within these bounds), the behavior seems ok.

@app.callback(Output('slider', 'max'),
              [Input('intermediate-data', 'children')])
def update_slider(df):
    filtered_dates = pd.read_json(df, orient='split')  # , date_format='iso'
    maxval = filtered_dates.index[len(filtered_dates.index)-1] -1
    return maxval


@app.callback(Output('slider', 'min'),
              [Input('intermediate-data', 'children')])
def update_slider(df):
    filtered_dates = pd.read_json(df, orient='split')  # , date_format='iso'
    minval = filtered_dates.index[0] -1
    return minval
@app.callback(Output('output-slider-value', 'children'),
              [Input('intermediate-data', 'children'),
               Input('slider', 'value')])
def SliderText(df, idx):
    filtered_dates = pd.read_json(df, orient='split')
    minval = filtered_dates.index[0] -1
    maxval = filtered_dates.index[len(filtered_dates.index)-1] -1

    if(idx < minval):
        idx = minval
    if(idx > maxval):
        idx = maxval

    date = (filtered_dates['date'].loc[idx+1]).strftime('%Y-%m-%d %H:%M')
    return '{} which is idx {}'.format(date, idx)

Let me know if this helps.

@tcados
Copy link
Author

tcados commented Jan 15, 2019

Hi Marc,

Thanks for the response. I suppose this can be gone around by evaluating the minimum value as a different date. But if you always want to keep the beginning of the dataset as 0, as I do with my filtered datasets, and change the slider only based on the length of array, this issue still persists. Even if I make another callback which reevaluates the min value to 0, this issue persists.

In the end I decided to omit this feature from my dashboard, however I think it would have been nice had it worked as expected. I still think this is a bug.

Best,
Troy

@Marc-Andre-Rivet
Copy link
Contributor

@tcados I don't think the implementation provided above can't have that behaviour. Keeping the dataset's first item as 0 requires the idx_range and full_range_dates to be updated appropriately. The point was that the original example did not update both the min and max values of the slider, only the max value.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants