Dash Choosing an ID and then Make Plot with Multiple Sliders
Question:
I have a dataset which is similar to below one. Please note that there are multiple values for a single ID.
import pandas as pd
import numpy as np
import random
df = pd.DataFrame({'DATE_TIME':pd.date_range('2022-11-01', '2022-11-05 23:00:00',freq='20min'),
'SBP':[random.uniform(110, 160) for n in range(358)],
'DBP':[random.uniform(60, 100) for n in range(358)],
'ID':[random.randrange(1, 3) for n in range(358)],
'TIMEINTERVAL':[random.randrange(1, 200) for n in range(358)]})
df['VISIT'] = df['DATE_TIME'].dt.day
df['MODE'] = np.select([df['VISIT']==1, df['VISIT'].isin([2,3])], ['CKD', 'Dialysis'], 'Late TPL')
df['TIME'] = df['DATE_TIME'].dt.time
df['TIME'] = df['TIME'].astype('str')
def to_day_period(s):
bins = ['0', '06:00:00', '13:00:00', '18:00:00', '23:00:00', '24:00:00']
labels = ['Night', 'Morning', 'Afternoon', 'Evening', 'Night']
return pd.cut(
pd.to_timedelta(s),
bins=list(map(pd.Timedelta, bins)),
labels=labels, right=False, ordered=False
)
df['TIME_OF_DAY'] = to_day_period(df['TIME'])
I would like to use Dash so that I can firstly choose the ID, and then make a plot of that chosen ID. Besides, I made a slider to choose the time interval between measurements in terms of minutes. This slider should work for Morning and Night values separately. So, I have already implemented a slider which works for all day and night times. I would like to have two sliders, one for Morning values from 06:00 until 17:59, and other one called Night from 18 until 05:59.
from dash import Dash, html, dcc, Input, Output
import pandas as pd
import os
import plotly.express as px
# FUNCTION TO CHOOSE A SINGLE PATIENT
def choose_patient(dataframe_name, id_number):
return dataframe_name[dataframe_name['ID']==id_number]
# FUNCTION TO CHOOSE A SINGLE PATIENT WITH A SINGLE VISIT
def choose_patient_visit(dataframe_name, id_number,visit_number):
return dataframe_name[(dataframe_name['ID']==id_number) & (dataframe_name['VISIT']==visit_number)]
# READING THE DATA
df = pd.read_csv(df,sep=',',parse_dates=['DATE_TIME','DATE'], infer_datetime_format=True)
# ---------------------------------------------------- dash example ----------------------------------------------------
app = Dash(__name__)
app.layout = html.Div([
html.H4('Interactive Scatter Plot'),
dcc.Graph(id="scatter-plot",style={'width': '130vh', 'height': '80vh'}),
html.P("Filter by time interval:"),
dcc.Dropdown(df.ID.unique(), id='pandas-dropdown-1'), # for choosing ID,
dcc.RangeSlider(
id='range-slider',
min=0, max=600, step=10,
marks={0: '0', 50: '50', 100: '100', 150: '150', 200: '200', 250: '250', 300: '300', 350: '350', 400: '400',
450: '450', 500: '500', 550: '550', 600: '600'},
value=[0, 600]
),
html.Div(id='dd-output-container')
])
@app.callback(
Output("scatter-plot", "figure"),
Input("pandas-dropdown-1", "value"),
Input("range-slider", "value"),
prevent_initial_call=True)
def update_lineplot(value, slider_range):
low, high = slider_range
df1 = df.query("ID == @value & TIMEINTERVAL >= @low & TIMEINTERVAL < @high").copy()
if df1.shape[0] != 0:
fig = px.line(df1, x="DATE_TIME", y=["SBP", "DBP"],
hover_data=['TIMEINTERVAL'], facet_col='VISIT',
facet_col_wrap=2,
symbol='MODE',
facet_row_spacing=0.1,
facet_col_spacing=0.09)
fig.update_xaxes(matches=None, showticklabels=True)
return fig
else:
return dash.no_update
app.run_server(debug=True, use_reloader=False)
How can I implement such two sliders? One slider should work for Night values of TIME_OF_DAY column and another one for Morning values of TIME_OF_DAY column. I look at Dash website, but there is no such tool available.
Answers:
The solution below only requires vey minor modifications to your example code. It essentially filters the original dataframe twice (once for TIME_OF_DAY=’Night’, and once for TIME_OF_DAY=’Morning’) and concatenates them before plotting.
I’ve also modified the bins in the to_day_period
function to only produce two labels that match the request in the text ("Morning values from 06:00 until 17:59, and […] Night from 18 until 05:59."), but the dash code can similarily be used for more categories.
bins = ['0', '06:00:00', '18:00:00', '24:00:00']
labels = ['Night', 'Morning', 'Night']
Dash code
app = Dash(__name__)
app.layout = html.Div([
html.H4('Interactive Scatter Plot with ABPM dataset'),
html.P("Select ID:"),
dcc.Dropdown(df.ID.unique(), id='pandas-dropdown-1'), # for choosing ID,
html.P("Filter by time interval during nighttime (18:00-6:00):"),
dcc.RangeSlider(
id='range-slider-night',
min=0, max=600, step=10,
marks={0: '0', 50: '50', 100: '100', 150: '150', 200: '200', 250: '250', 300: '300', 350: '350', 400: '400',
450: '450', 500: '500', 550: '550', 600: '600'},
value=[0, 600]
),
html.P("Filter by time interval during daytime (6:00-18:00):"),
dcc.RangeSlider(
id='range-slider-morning',
min=0, max=600, step=10,
marks={0: '0', 50: '50', 100: '100', 150: '150', 200: '200', 250: '250', 300: '300', 350: '350', 400: '400',
450: '450', 500: '500', 550: '550', 600: '600'},
value=[0, 600]
),
dcc.Graph(id="scatter-plot", style={'width': '130vh', 'height': '80vh'}),
html.Div(id='dd-output-container')
])
@app.callback(
Output("scatter-plot", "figure"),
Input("pandas-dropdown-1", "value"),
Input("range-slider-night", "value"),
Input("range-slider-morning", "value"),
prevent_initial_call=True)
def update_lineplot(value, slider_range_night, slider_range_morning):
low_night, high_night = slider_range_night
low_morning, high_morning = slider_range_morning
df_night = df.query("ID == @value & TIME_OF_DAY == 'Night' & TIMEINTERVAL >= @low_night & TIMEINTERVAL < @high_night").copy()
df_morning = df.query("ID == @value & TIME_OF_DAY == 'Morning' & TIMEINTERVAL >= @low_morning & TIMEINTERVAL < @high_morning").copy()
df1 = pd.concat([df_night, df_morning], axis=0).sort_values(['TIME'])
if df1.shape[0] != 0:
fig = px.line(df1, x="DATE_TIME", y=["SBP", "DBP"],
hover_data=['TIMEINTERVAL'], facet_col='VISIT',
facet_col_wrap=2,
symbol='MODE',
facet_row_spacing=0.1,
facet_col_spacing=0.09)
fig.update_xaxes(matches=None, showticklabels=True)
return fig
else:
return no_update
app.run_server(debug=True, use_reloader=False)
Application
In the screenshot below, all the "Morning"/"Daytime" observations have been filtered for TIMEINTERVAL, while the "Night" observations remain unaffected:
I have a dataset which is similar to below one. Please note that there are multiple values for a single ID.
import pandas as pd
import numpy as np
import random
df = pd.DataFrame({'DATE_TIME':pd.date_range('2022-11-01', '2022-11-05 23:00:00',freq='20min'),
'SBP':[random.uniform(110, 160) for n in range(358)],
'DBP':[random.uniform(60, 100) for n in range(358)],
'ID':[random.randrange(1, 3) for n in range(358)],
'TIMEINTERVAL':[random.randrange(1, 200) for n in range(358)]})
df['VISIT'] = df['DATE_TIME'].dt.day
df['MODE'] = np.select([df['VISIT']==1, df['VISIT'].isin([2,3])], ['CKD', 'Dialysis'], 'Late TPL')
df['TIME'] = df['DATE_TIME'].dt.time
df['TIME'] = df['TIME'].astype('str')
def to_day_period(s):
bins = ['0', '06:00:00', '13:00:00', '18:00:00', '23:00:00', '24:00:00']
labels = ['Night', 'Morning', 'Afternoon', 'Evening', 'Night']
return pd.cut(
pd.to_timedelta(s),
bins=list(map(pd.Timedelta, bins)),
labels=labels, right=False, ordered=False
)
df['TIME_OF_DAY'] = to_day_period(df['TIME'])
I would like to use Dash so that I can firstly choose the ID, and then make a plot of that chosen ID. Besides, I made a slider to choose the time interval between measurements in terms of minutes. This slider should work for Morning and Night values separately. So, I have already implemented a slider which works for all day and night times. I would like to have two sliders, one for Morning values from 06:00 until 17:59, and other one called Night from 18 until 05:59.
from dash import Dash, html, dcc, Input, Output
import pandas as pd
import os
import plotly.express as px
# FUNCTION TO CHOOSE A SINGLE PATIENT
def choose_patient(dataframe_name, id_number):
return dataframe_name[dataframe_name['ID']==id_number]
# FUNCTION TO CHOOSE A SINGLE PATIENT WITH A SINGLE VISIT
def choose_patient_visit(dataframe_name, id_number,visit_number):
return dataframe_name[(dataframe_name['ID']==id_number) & (dataframe_name['VISIT']==visit_number)]
# READING THE DATA
df = pd.read_csv(df,sep=',',parse_dates=['DATE_TIME','DATE'], infer_datetime_format=True)
# ---------------------------------------------------- dash example ----------------------------------------------------
app = Dash(__name__)
app.layout = html.Div([
html.H4('Interactive Scatter Plot'),
dcc.Graph(id="scatter-plot",style={'width': '130vh', 'height': '80vh'}),
html.P("Filter by time interval:"),
dcc.Dropdown(df.ID.unique(), id='pandas-dropdown-1'), # for choosing ID,
dcc.RangeSlider(
id='range-slider',
min=0, max=600, step=10,
marks={0: '0', 50: '50', 100: '100', 150: '150', 200: '200', 250: '250', 300: '300', 350: '350', 400: '400',
450: '450', 500: '500', 550: '550', 600: '600'},
value=[0, 600]
),
html.Div(id='dd-output-container')
])
@app.callback(
Output("scatter-plot", "figure"),
Input("pandas-dropdown-1", "value"),
Input("range-slider", "value"),
prevent_initial_call=True)
def update_lineplot(value, slider_range):
low, high = slider_range
df1 = df.query("ID == @value & TIMEINTERVAL >= @low & TIMEINTERVAL < @high").copy()
if df1.shape[0] != 0:
fig = px.line(df1, x="DATE_TIME", y=["SBP", "DBP"],
hover_data=['TIMEINTERVAL'], facet_col='VISIT',
facet_col_wrap=2,
symbol='MODE',
facet_row_spacing=0.1,
facet_col_spacing=0.09)
fig.update_xaxes(matches=None, showticklabels=True)
return fig
else:
return dash.no_update
app.run_server(debug=True, use_reloader=False)
How can I implement such two sliders? One slider should work for Night values of TIME_OF_DAY column and another one for Morning values of TIME_OF_DAY column. I look at Dash website, but there is no such tool available.
The solution below only requires vey minor modifications to your example code. It essentially filters the original dataframe twice (once for TIME_OF_DAY=’Night’, and once for TIME_OF_DAY=’Morning’) and concatenates them before plotting.
I’ve also modified the bins in the to_day_period
function to only produce two labels that match the request in the text ("Morning values from 06:00 until 17:59, and […] Night from 18 until 05:59."), but the dash code can similarily be used for more categories.
bins = ['0', '06:00:00', '18:00:00', '24:00:00']
labels = ['Night', 'Morning', 'Night']
Dash code
app = Dash(__name__)
app.layout = html.Div([
html.H4('Interactive Scatter Plot with ABPM dataset'),
html.P("Select ID:"),
dcc.Dropdown(df.ID.unique(), id='pandas-dropdown-1'), # for choosing ID,
html.P("Filter by time interval during nighttime (18:00-6:00):"),
dcc.RangeSlider(
id='range-slider-night',
min=0, max=600, step=10,
marks={0: '0', 50: '50', 100: '100', 150: '150', 200: '200', 250: '250', 300: '300', 350: '350', 400: '400',
450: '450', 500: '500', 550: '550', 600: '600'},
value=[0, 600]
),
html.P("Filter by time interval during daytime (6:00-18:00):"),
dcc.RangeSlider(
id='range-slider-morning',
min=0, max=600, step=10,
marks={0: '0', 50: '50', 100: '100', 150: '150', 200: '200', 250: '250', 300: '300', 350: '350', 400: '400',
450: '450', 500: '500', 550: '550', 600: '600'},
value=[0, 600]
),
dcc.Graph(id="scatter-plot", style={'width': '130vh', 'height': '80vh'}),
html.Div(id='dd-output-container')
])
@app.callback(
Output("scatter-plot", "figure"),
Input("pandas-dropdown-1", "value"),
Input("range-slider-night", "value"),
Input("range-slider-morning", "value"),
prevent_initial_call=True)
def update_lineplot(value, slider_range_night, slider_range_morning):
low_night, high_night = slider_range_night
low_morning, high_morning = slider_range_morning
df_night = df.query("ID == @value & TIME_OF_DAY == 'Night' & TIMEINTERVAL >= @low_night & TIMEINTERVAL < @high_night").copy()
df_morning = df.query("ID == @value & TIME_OF_DAY == 'Morning' & TIMEINTERVAL >= @low_morning & TIMEINTERVAL < @high_morning").copy()
df1 = pd.concat([df_night, df_morning], axis=0).sort_values(['TIME'])
if df1.shape[0] != 0:
fig = px.line(df1, x="DATE_TIME", y=["SBP", "DBP"],
hover_data=['TIMEINTERVAL'], facet_col='VISIT',
facet_col_wrap=2,
symbol='MODE',
facet_row_spacing=0.1,
facet_col_spacing=0.09)
fig.update_xaxes(matches=None, showticklabels=True)
return fig
else:
return no_update
app.run_server(debug=True, use_reloader=False)
Application
In the screenshot below, all the "Morning"/"Daytime" observations have been filtered for TIMEINTERVAL, while the "Night" observations remain unaffected: