Giving the colorbar in scatter plot a RangeSlider

Hi,
Hope you are well.
I would like to have a slider that corresponds to the scatter plot colour bar on my graph. This is to remove outliers of data in the colo bar that can result in an uneven distribution of data. I have populated the slider effectively but have difficulty in getting the colour bar to interact with the slider to determine the range. Any help would be hugely appreciated.
I get the error: ‘Lengths must match to compare’ but unsure of how to resolve it
My code is below:

import base64
import io
from flask import Flask
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import plotly_express as px
import pandas as pd


external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
server = Flask(__name__)
app = dash.Dash(external_stylesheets=external_stylesheets, server=server)

app.layout = html.Div([dcc.Upload(
    id='data-table-upload',
    children=html.Div([html.Button('Upload File')],
                      style={'width': '49%', 'height': "60px", 'borderWidth': '1px',
                             'borderRadius': '5px',
                             'textAlign': 'center',

                             }),
    multiple=False
),
    html.Div([html.Div([html.Div([dcc.Graph(id='my-3D-graph', animate=False)], style={'display': 'inline-block',
                                                                                      'width': '74%',
                                                                                      }),
                        html.Div([
                            html.Div([html.Label(
                                ["Select X variable:",
                                 dcc.Dropdown(
                                     id='xaxis-anim-3D',
                                     multi=False,
                                     placeholder="Select an "
                                                 "option "
                                                 "for X")],
                            )],
                                style={
                                    'padding': 10}),
                            html.Div([html.Label(
                                ["Select Y variable:",
                                 dcc.Dropdown(
                                     id='yaxis-anim-3D',
                                     multi=False,
                                     placeholder='Select an option '
                                                 'for Y')],
                            ), ],
                                style={
                                    'padding': 10}),
                            html.Div([html.Label(
                                ["Select color variable:",
                                 dcc.Dropdown(
                                     id='caxis-anim-3D',
                                     multi=False,
                                     placeholder='Select an option for color')],
                            )], style={
                                'padding': 10}),
                            html.Div([
                                html.Label(["Select color bar "
                                            "range:",
                                            dcc.Slider(
                                                id='colorbar-slider',
                                            ), ])
                            ], style={'fontSize': 14,
                                      'font-family': 'Arial',
                                      'height': '20%',
                                      'padding': 15,
                                      'width': '90%'})
                        ],
                            style={
                                'display': 'inline-block',
                                'width': '25%',
                                'float': 'right',
                                'fontSize': 14,
                                'font-family': 'Arial',
                                'backgroundColor': '#ffffff'})
                        ], className='container',
                       style={'padding': 40,
                              'backgroundColor': '#ffffff'})])
])


def parse_contents(contents, filename):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))
        elif 'txt' or 'tsv' in filename:
            df = pd.read_csv(io.StringIO(decoded.decode('utf-8')), delimiter=r'\s+'
                             )
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])
    return df



# POPULATE X AXIS DROPDOWN
@app.callback(Output('xaxis-anim-3D', 'options'),
              [Input('data-table-upload', 'contents')],
              [State('data-table-upload', 'filename')])
def populate_xaxis_dropdown_anim(contents, filename):
    df = parse_contents(contents, filename)
    return [{'label': i, 'value': i} for i in df.columns]


# POPULATE Y AXIS DROPDOWN
@app.callback(Output('yaxis-anim-3D', 'options'),
              [Input('data-table-upload', 'contents')],
              [State('data-table-upload', 'filename')])
def populate_yaxis_dropdown_anim(contents, filename):
    df = parse_contents(contents, filename)
    return [{'label': i, 'value': i} for i in df.columns]


# POPULATE C AXIS DROPDOWN
@app.callback(Output('caxis-anim-3D', 'options'),
              [Input('data-table-upload', 'contents')],
              [State('data-table-upload', 'filename')])
def populate_saxis_dropdown_anim(contents, filename):
    df = parse_contents(contents, filename)
    return [{'label': i, 'value': i} for i in df.columns]


# POPULATE COLORBAR SLIDER SCATTER
@app.callback([Output('colorbar-slider', 'min'),
               Output('colorbar-slider', 'max'),
               Output('colorbar-slider', 'value'),
               # Output('colorbar-slider', 'marks')
               ],
              [Input('data-table-upload', 'contents'),
               Input('caxis-anim-3D', 'value')],
              [State('data-table-upload', 'filename')])
def populate_pressure_slider(contents, caxis, filename):
    df = parse_contents(contents, filename)
    min = df[caxis].min(),
    max = df[caxis].max(),
    value = df[caxis].max(),
    # marks = {str(pressure): str(pressure) for pressure in df[caxis].unique()}
    return min, max, value
        # , marks


# @app.callback(Output('caxis-anim-3D','value'),
#               [Input('colorbar-slider','value')])
# def range_slider_cb(pressure_value):
#     dff = df[df['Pressure'] == pressure_value]


@app.callback(Output('my-3D-graph', 'figure'),
              [Input('data-table-upload', 'contents'),
               Input('xaxis-anim-3D', 'value'),
               Input('yaxis-anim-3D', 'value'),
               Input('caxis-anim-3D', 'value'),
               Input('colorbar-slider', 'value')],
              [State('data-table-upload', 'filename')]
              )
def update_figure(contents, x, y, caxis, color_value, filename):
    df = parse_contents(contents, filename)
    dff = df[df[caxis] == color_value]
    return px.scatter(dff, x=dff[x], y=dff[y], title="", animation_frame="Pressure",
                      animation_group=dff.columns[0],
                      hover_name=dff.columns[0],
                      hover_data={}, template="none", color=dff[caxis],
                      color_continuous_scale='Viridis',
                      ).update_xaxes(showgrid=False, title=x, autorange=True, ticks='outside',
                                     showline=True, showspikes=True, spikethickness=1, spikedash='solid',
                                     mirror=True, tickformat=".1f").update_yaxes(spikedash='solid',
                                                                                 showgrid=False,
                                                                                 title=dict(text=y,
                                                                                            standoff=5),
                                                                                 autorange=True, ticks='outside',
                                                                                 showspikes=True, spikethickness=1,
                                                                                 showline=True, mirror=True,
                                                                                 tickformat=".1f").update_layout(
        clickmode='event+select', hovermode='closest', margin={'l': 80}, autosize=True, font=dict(family='Helvetica',
                                                                                                  ),
        coloraxis_colorbar=dict(title=dict(text=caxis, side='right'), ypad=0),
    ).update_traces(marker=dict(size=10,
                                opacity=0.7,
                                showscale=False,
                                line=dict(width=0.7, color='DarkSlateGrey'),

                                colorscale="Viridis"))


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

My datasheet can be accessed via this link.

Were you able to figure this out yourself msuths1?

I’m looking to do something similar.

Hi @robbohn14
Yes I did.
Here is a reproducible example, hope it helps:

import base64
import io
from flask import Flask
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import plotly_express as px
import pandas as pd


external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
server = Flask(__name__)
app = dash.Dash(external_stylesheets=external_stylesheets, server=server)

app.layout = html.Div([dcc.Upload(
    id='data-table-upload',
    children=html.Div([html.Button('Upload File')],
                      style={'width': '49%', 'height': "60px", 'borderWidth': '1px',
                             'borderRadius': '5px',
                             'textAlign': 'center',

                             }),
    multiple=False
),
    html.Div([html.Div([html.Div([dcc.Graph(id='my-3D-graph', animate=False)], style={'display': 'inline-block',
                                                                                      'width': '74%',
                                                                                      }),
                        html.Div([
                            html.Div([html.Label(
                                ["Select X variable:",
                                 dcc.Dropdown(
                                     id='xaxis-anim-3D',
                                     multi=False,
                                     placeholder="Select an "
                                                 "option "
                                                 "for X")],
                            )],
                                style={
                                    'padding': 10}),
                            html.Div([html.Label(
                                ["Select Y variable:",
                                 dcc.Dropdown(
                                     id='yaxis-anim-3D',
                                     multi=False,
                                     placeholder='Select an option '
                                                 'for Y')],
                            ), ],
                                style={
                                    'padding': 10}),
                            html.Div([html.Label(
                                ["Select color variable:",
                                 dcc.Dropdown(
                                     id='caxis-anim-3D',
                                     multi=False,
                                     placeholder='Select an option for color')],
                            )], style={
                                'padding': 10}),
                            html.Div([
                                html.Label(["Select color bar "
                                            "range:",
                                            dcc.RangeSlider(
                                                id='colorbar-slider',
                                            ), ])
                            ], style={'fontSize': 14,
                                      'font-family': 'Arial',
                                      'height': '20%',
                                      'padding': 15,
                                      'width': '90%'})
                        ],
                            style={
                                'display': 'inline-block',
                                'width': '25%',
                                'float': 'right',
                                'fontSize': 14,
                                'font-family': 'Arial',
                                'backgroundColor': '#ffffff'})
                        ], className='container',
                       style={'padding': 40,
                              'backgroundColor': '#ffffff'})])
])


def parse_contents(contents, filename):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))
        elif 'txt' or 'tsv' in filename:
            df = pd.read_csv(io.StringIO(decoded.decode('utf-8')), delimiter=r'\s+'
                             )
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])
    return df



# POPULATE X AXIS DROPDOWN
@app.callback(Output('xaxis-anim-3D', 'options'),
              [Input('data-table-upload', 'contents')],
              [State('data-table-upload', 'filename')])
def populate_xaxis_dropdown_anim(contents, filename):
    df = parse_contents(contents, filename)
    return [{'label': i, 'value': i} for i in df.columns]


# POPULATE Y AXIS DROPDOWN
@app.callback(Output('yaxis-anim-3D', 'options'),
              [Input('data-table-upload', 'contents')],
              [State('data-table-upload', 'filename')])
def populate_yaxis_dropdown_anim(contents, filename):
    df = parse_contents(contents, filename)
    return [{'label': i, 'value': i} for i in df.columns]


# POPULATE C AXIS DROPDOWN
@app.callback(Output('caxis-anim-3D', 'options'),
              [Input('data-table-upload', 'contents')],
              [State('data-table-upload', 'filename')])
def populate_saxis_dropdown_anim(contents, filename):
    df = parse_contents(contents, filename)
    return [{'label': i, 'value': i} for i in df.columns]


# POPULATE COLORBAR SLIDER SCATTER
@app.callback([Output('colorbar-slider', 'min'),
               Output('colorbar-slider', 'max'),
               Output('colorbar-slider', 'step'),
               Output('colorbar-slider', 'value')
               ],
              [Input('data-table-upload', 'contents'),
               Input('caxis-anim-3D', 'value')],
              [State('data-table-upload', 'filename')])
def populate_pressure_slider(contents, caxis, filename):
    df = parse_contents(contents, filename)
    min_v = round(float(df[caxis].min()), 1)
    max_v = round(float(df[caxis].max()), 1)
    step = 0.1
    value = [round(float(df[caxis].min()), 1), round(float(df[caxis].max()), 1)]
    return min_v, max_v, step, value
# @app.callback(Output('caxis-anim-3D','value'),
#               [Input('colorbar-slider','value')])
# def range_slider_cb(pressure_value):
#     dff = df[df['Pressure'] == pressure_value]


@app.callback(Output('my-3D-graph', 'figure'),
              [Input('data-table-upload', 'contents'),
               Input('xaxis-anim-3D', 'value'),
               Input('yaxis-anim-3D', 'value'),
               Input('caxis-anim-3D', 'value'),
               Input('colorbar-slider', 'value')],
              [State('data-table-upload', 'filename')]
              )
def update_figure(contents, x, y, caxis, color_value, filename):
    df = parse_contents(contents, filename)
    color_val_float = []
    for i in range(0, len(color_value), 1):
        color_val_float.append(float(color_value[i]))
    color_val = color_val_float
    return px.scatter(df, x=df[x], y=df[y], title="", animation_frame="Pressure",
                      animation_group=df.columns[0],
                      hover_name=df.columns[0],
                      hover_data={}, template="none", color=df[caxis],
                      color_continuous_scale='Viridis',
range_color=color_val
                      ).update_xaxes(showgrid=False, title=x, autorange=True, ticks='outside',
                                     showline=True, showspikes=True, spikethickness=1, spikedash='solid',
                                     mirror=True, tickformat=".1f").update_yaxes(spikedash='solid',
                                                                                 showgrid=False,
                                                                                 title=dict(text=y,
                                                                                            standoff=5),
                                                                                 autorange=True, ticks='outside',
                                                                                 showspikes=True, spikethickness=1,
                                                                                 showline=True, mirror=True,
                                                                                 tickformat=".1f").update_layout(
        clickmode='event+select', hovermode='closest', margin={'l': 80}, autosize=True, font=dict(family='Helvetica',
                                                                                                  ),
        coloraxis_colorbar=dict(title=dict(text=caxis, side='right'), ypad=0),
    ).update_traces(marker=dict(size=10,
                                opacity=0.7,
                                showscale=False,
                                line=dict(width=0.7, color='DarkSlateGrey'),

                                colorscale="Viridis"))


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

1 Like

Thank you so much! Yes, this does help and I’ll be sure to do the same thing for others.

I’m trying to adapt this to a pair of traces plotted in a mapbox and not having much luck. Each trace has its own colorbar. I don’t quite see how to get the hooks of the range slider into the right elements of my mapbox figure. My goal is to use the rangeslider to select the values I display (or not) in the respective traces.