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

Using dcc.Radioitems to filter data

Hi there,
I am trying to use radioitems to filter the top 1%, 5% and 10% of my data where the user can select which filter to apply and it will then produce a violin pot with the respective data. Could anyone point me in the right direction to solve this issue. I get the error: ‘The callback for <Output violin-plot.figure>
returned a value having type DataFrame
which is not JSON serializable.’ Thanks!

import dash
import dash_core_components as dcc
import dash_html_components as html
import json
from textwrap import dedent as d
from dash.dependencies import Input, Output, State
import plotly.graph_objs as go
import pandas as pd
import flask

df = pd.read_csv('/Users/mythilisutharson/documents/cam_work/mof_explorer_flourish/MOF_trans_data3.csv')

server = flask.Flask(__name__)
app = dash.Dash(__name__, server=server)
features = df.columns

top_10_percentile = df[df.Deliverable_Capacity_Vol > df.Deliverable_Capacity_Vol.quantile(0.9)]
top_5_percentile = df[df.Deliverable_Capacity_Vol > df.Deliverable_Capacity_Vol.quantile(0.95)]
top_1_percentile = df[df.Deliverable_Capacity_Vol > df.Deliverable_Capacity_Vol.quantile(0.99)]

app.layout = html.Div([
    html.Div([dcc.Graph(id='violin-plot')
              ], style={'width': '65%', 'display': 'inline-block', })
    , html.Div([
        dcc.Markdown(d("""
               **Click Data**

               Click on points in the graph.
           """)),
        html.Pre(id='click-data-stat'),
        html.Label(["Select Variable (Geometrical Property):",
                    dcc.Dropdown(id='yaxis-stat',
                                 placeholder="Select an option for Y",
                                 multi=False,
                                 options=[{'label': i, 'value': i} for i in features
                                          ]
                                 )])

    ], style={'fontSize': 14, 'font-family': 'Arial', 'width': '30%',
              'display': 'inline-block',
              'float': 'right'})
    ,
    html.Div([html.Label(["Select % of structures to analyse:"
                             , dcc.RadioItems(
            id='percentile-type',
            options=[{'label': 'None', 'value': 'None'},
                     {'label': 'Top 1% of structures', 'value': 'Top 1% of structures'}, ],
            value='None',
            labelStyle={'display': 'inline-block'}
        )]),
              ], style={'display': 'inline-block', 'width': '49%'}),

], className='container')


@app.callback(Output('violin-plot', 'figure'),
              [
                  Input('yaxis-stat', 'value'),
                  Input('percentile-type', 'value')
              ],
              )
def update_graph_stat(yaxis_name, percentile_type):
    traces = []
    pressure_set = set(df['Pressure (bar)'])
    pressure_list = sorted(list(pressure_set))
    if percentile_type is 'None':
        data = df
    else:
        data = top_1_percentile
        return data
    if yaxis_name is None:
        return dash.no_update
    for pressure in pressure_list:
        traces.append(go.Violin(y=data[data['Pressure (bar)'] == pressure][yaxis_name], name=pressure,
                                marker={'size': 4}, box_visible=True, opacity=0.6, meanline_visible=True,
                                points='all', text=data[df.columns[0]],
                                hovertemplate=
                                "<b>%{text}</b><br><br>" +
                                "Variable: %{y:.0f}<br>"
                                "Pressure: %{x:. bar}<br>"
                                ))
    return {'data': traces

        , 'layout': go.Layout(
            title=f"<b> Pressure (bar) against {''.join(str(i) for i in yaxis_name)} "
            ,
            xaxis=dict(rangeslider=dict(visible=True), title='Pressure (bar)'),
            yaxis={'title': yaxis_name},

            margin={'l': 50, 'b': 0, 't': 50, 'r': 50},
            hovermode='closest',
        ),
            }


@app.callback(
    Output('click-data-stat', 'children'),
    [Input('violin-plot', 'clickData'),
     ])
def display_click_data_stat(clickData):
    return json.dumps(clickData, indent=2)


app.run_server(debug=True)

Your error is caused by the return statement in your else clause in the update_graph_stat definition. For a figure component, the return data is incorrect - this return statement should mimick the format of the return statement at the bottom of this function.

Also, if this callback is triggered by yaxis-stat, I do not believe percentile_type will be defined. I suggest adding both of these as States so their current values are passed to the callback regardless of how the callback was triggered.

Hi @flyingcujo,
I solved the issue changing percentile_type is None to percentile_type == None fixed it.
Thank you for your response though!

@app.callback(Output('violin-plot', 'figure'),
              [
                  Input('yaxis-stat', 'value'),
                  Input('percentile-type', 'value')
              ],
              )
def update_graph_stat(yaxis_name, percentile_type):
    traces = []
    pressure_set = set(df['Pressure (bar)'])
    pressure_list = sorted(list(pressure_set))
    if percentile_type == 'None':
        data = df
    elif percentile_type == 'Top 1% of structures':
        data = top_1_percentile
    if yaxis_name is None:
        return dash.no_update
    for pressure in pressure_list:
        traces.append(go.Violin(y=data[data['Pressure (bar)'] == pressure][yaxis_name], name=pressure,
                                marker={'size': 4}, box_visible=True, opacity=0.6, meanline_visible=True,
                                points='all', text=data[df.columns[0]],
                                hovertemplate=
                                "<b>%{text}</b><br><br>" +
                                "Variable: %{y:.0f}<br>"
                                "Pressure: %{x:. bar}<br>"
                                ))
    return {'data': traces

        , 'layout': go.Layout(
            title=f"<b> Pressure (bar) against {''.join(str(i) for i in yaxis_name)} "
            ,
            xaxis=dict(rangeslider=dict(visible=True), title='Pressure (bar)'),
            yaxis={'title': yaxis_name},

            margin={'l': 50, 'b': 0, 't': 50, 'r': 50},
            hovermode='closest',
        ),
            }

Excellent!!..I still think you will have the issue I pointed out in my last paragraph though…