You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Dash实现ID选择及分时段双滑块过滤绘图功能

问题描述

我有一个单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'])

我已经用Dash实现了ID选择功能,以及单个RangeSlider对全时段TIMEINTERVAL的过滤绘图,现有代码如下:

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
# 注意:原代码中pd.read_csv(df)有误,需替换为实际文件路径
df = pd.read_csv("your_data.csv",sep=',',parse_dates=['DATE_TIME'], 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)

现在需要新增两个RangeSlider,分别针对TIME_OF_DAY为**Morning(06:00-17:59)和Night(18:00-05:59)**的数据进行TIMEINTERVAL范围过滤,请问如何实现?

解决方案

要实现分时段的TIMEINTERVAL过滤,需要从布局和回调逻辑两方面修改:

1. 修改Dash布局,新增两个RangeSlider

在原布局中添加针对Morning和Night的RangeSlider,每个滑块对应明确的时段说明,参数和原滑块保持一致:

app.layout = html.Div([
    html.H4('Interactive Scatter Plot'),
    dcc.Graph(id="scatter-plot",style={'width': '130vh', 'height': '80vh'}),
    html.P("选择患者ID:"),
    dcc.Dropdown(df.ID.unique(), id='pandas-dropdown-1'),
    html.P("全时段TIMEINTERVAL过滤:"),
    dcc.RangeSlider(
        id='range-slider-all',
        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("Morning时段(06:00-17:59)TIMEINTERVAL过滤:"),
    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]
    ),
    html.P("Night时段(18:00-05:59)TIMEINTERVAL过滤:"),
    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.Div(id='dd-output-container')
])

2. 修改回调函数,接收新输入并调整过滤逻辑

更新回调的Input参数,对不同时段的记录应用对应的滑块范围:

@app.callback(
    Output("scatter-plot", "figure"),
    Input("pandas-dropdown-1", "value"),
    Input("range-slider-all", "value"),
    Input("range-slider-morning", "value"),
    Input("range-slider-night", "value"),
    prevent_initial_call=True)

def update_lineplot(patient_id, slider_all, slider_morning, slider_night):
    # 先筛选当前患者的数据
    patient_df = df[df['ID'] == patient_id].copy()
    
    # Morning时段过滤
    morning_low, morning_high = slider_morning
    morning_mask = (patient_df['TIME_OF_DAY'] == 'Morning') & \
                   (patient_df['TIMEINTERVAL'] >= morning_low) & \
                   (patient_df['TIMEINTERVAL'] < morning_high)
    
    # Night时段过滤
    night_low, night_high = slider_night
    night_mask = (patient_df['TIME_OF_DAY'] == 'Night') & \
                 (patient_df['TIMEINTERVAL'] >= night_low) & \
                 (patient_df['TIMEINTERVAL'] < night_high)
    
    # 其他时段(Afternoon/Evening)使用全时段滑块
    all_low, all_high = slider_all
    other_mask = (~patient_df['TIME_OF_DAY'].isin(['Morning', 'Night'])) & \
                 (patient_df['TIMEINTERVAL'] >= all_low) & \
                 (patient_df['TIMEINTERVAL'] < all_high)
    
    # 合并所有符合条件的记录
    filtered_df = patient_df[morning_mask | night_mask | other_mask]

    if filtered_df.shape[0] != 0:
        fig = px.line(filtered_df, x="DATE_TIME", y=["SBP", "DBP"],
                         hover_data=['TIMEINTERVAL', 'TIME_OF_DAY'], facet_col='VISIT',
                         facet_col_wrap=2,
                         symbol='MODE',
                         color='TIME_OF_DAY',  # 用颜色区分时段,更直观
                         facet_row_spacing=0.1,
                         facet_col_spacing=0.09)

        fig.update_xaxes(matches=None, showticklabels=True)

        return fig
    else:
        return dash.no_update

3. 完整修正后的代码

from dash import Dash, html, dcc, Input, Output, no_update
import pandas as pd
import numpy as np
import random
import plotly.express as px

# ---------------------- 构造数据集(如果是读取CSV则注释这部分) ----------------------
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'])
# ---------------------- 数据集构造结束 ----------------------

# READING THE DATA(如果用CSV则取消注释)
# df = pd.read_csv("your_data.csv",sep=',',parse_dates=['DATE_TIME'], infer_datetime_format=True)

app = Dash(__name__)

app.layout = html.Div([
    html.H4('Interactive Scatter Plot'),
    dcc.Graph(id="scatter-plot",style={'width': '130vh', 'height': '80vh'}),
    html.P("选择患者ID:"),
    dcc.Dropdown(df.ID.unique(), id='pandas-dropdown-1'),
    html.P("全时段TIMEINTERVAL过滤:"),
    dcc.RangeSlider(
        id='range-slider-all',
        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("Morning时段(06:00-17:59)TIMEINTERVAL过滤:"),
    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]
    ),
    html.P("Night时段(18:00-05:59)TIMEINTERVAL过滤:"),
    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.Div(id='dd-output-container')
])

@app.callback(
    Output("scatter-plot", "figure"),
    Input("pandas-dropdown-1", "value"),
    Input("range-slider-all", "value"),
    Input("range-slider-morning", "value"),
    Input("range-slider-night", "value"),
    prevent_initial_call=True)

def update_lineplot(patient_id, slider_all, slider_morning, slider_night):
    patient_df = df[df['ID'] == patient_id].copy()
    
    # Morning时段过滤
    morning_low, morning_high = slider_morning
    morning_mask = (patient_df['TIME_OF_DAY'] == 'Morning') & \
                   (patient_df['TIMEINTERVAL'] >= morning_low) & \
                   (patient_df['TIMEINTERVAL'] < morning_high)
    
    # Night时段过滤
    night_low, night_high = slider_night
    night_mask = (patient_df['TIME_OF_DAY'] == 'Night') & \
                 (patient_df['TIMEINTERVAL'] >= night_low) & \
                 (patient_df['TIMEINTERVAL'] < night_high)
    
    # 其他时段用全时段滑块
    all_low, all_high = slider_all
    other_mask = (~patient_df['TIME_OF_DAY'].isin(['Morning', 'Night'])) & \
                 (patient_df['TIMEINTERVAL'] >= all_low) & \
                 (patient_df['TIMEINTERVAL'] < all_high)
    
    filtered_df = patient_df[morning_mask | night_mask | other_mask]

    if filtered_df.shape[0] != 0:
        fig = px.line(filtered_df, x="DATE_TIME", y
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 23:50:35