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

如何用Plotly复刻Seaborn的分面血压折线图(适配Dash)

问题:用Plotly复刻Seaborn分面血压折线图

已通过Seaborn实现按VISIT字段分面(2列布局)的血压折线图,核心需求如下:

  • 筛选ID为30的数据
  • 每个子图同时绘制SBP(红色)和DBP(蓝色)折线
  • 折线线型随MODE字段变化
  • 图例置于图外

尝试用Plotly复刻时,两种方案(合并trace、手动添加子图)均无法生成正确的分面布局,需调整代码实现目标效果。


Seaborn实现代码

# LINE PLOT
r_30 = r[r['ID']==30] #choosing person 30

g = sns.FacetGrid(r_30, col="VISIT", sharex=False, sharey=False,aspect=2,col_wrap=2,legend_out=True) # 2 columns, legend is outside of plot

g.map(sns.lineplot, "DATE_TIME", "SBP",color='red',label='SBP',style=r_30['MODE']) # style/shape of line changes with mode

g.map(sns.lineplot, "DATE_TIME", "DBP",color='blue',label='DBP',style=r_30['MODE'])
g.set_axis_labels("DATE_TIME", "Blood Pressure (mmHg)")

现有方案问题分析

  1. 合并trace方案:直接合并px.scatter生成的trace会丢失分面布局的关联逻辑,导致所有数据混乱叠加,无法对应到各VISIT子图。
  2. 手动添加子图方案:硬编码行列位置添加trace的方式无法动态适配VISIT的数量,且未正确关联MODE线型与血压类型(SBP/DBP)的颜色配置。

解决方案

方法1:Plotly Express 简洁实现(推荐)

利用Plotly Express的分面功能,配合数据长格式转换,快速复刻Seaborn的分面逻辑:

import plotly.express as px
import pandas as pd
from dash import Dash, html, dcc

# 数据预处理:将SBP/DBP转为长格式,统一控制颜色和线型
r_30_melted = r_30.melt(
    id_vars=['ID', 'VISIT', 'DATE_TIME', 'MODE'],
    value_vars=['SBP', 'DBP'],
    var_name='BP_Type',
    value_name='Blood_Pressure'
)

# 绘制分面折线图
fig = px.line(
    r_30_melted,
    x="DATE_TIME",
    y="Blood_Pressure",
    color="BP_Type",  # 区分SBP/DBP颜色
    line_dash="MODE",  # 线型随MODE变化
    facet_col="VISIT",  # 按VISIT分面
    facet_col_wrap=2,  # 2列布局
    color_discrete_map={'SBP': 'red', 'DBP': 'blue'},  # 指定固定颜色
    labels={
        'Blood_Pressure': 'Blood Pressure (mmHg)',
        'DATE_TIME': 'DATE_TIME'
    },
    title='Blood Pressure by Visit and Mode'
)

# 配置子图与图例
fig.update_layout(
    legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
    facet_col_spacing=0.1  # 调整子图间距
)
# 取消坐标轴共享,确保每个子图独立显示刻度
fig.update_xaxes(matches=None, showticklabels=True)
fig.update_yaxes(matches=None, showticklabels=True)

# Dash应用集成
app = Dash(__name__)
app.layout = html.Div([
    html.H1('Blood Pressure Dashboard'),
    dcc.Graph(figure=fig)
])

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

方法2:手动创建子图(自定义需求适配)

通过make_subplots动态生成子图,适合更复杂的自定义布局需求:

import plotly.graph_objects as go
from plotly.subplots import make_subplots
from dash import Dash, html, dcc

# 获取唯一VISIT值并排序
unique_visits = sorted(r_30['VISIT'].unique())
num_visits = len(unique_visits)
rows = (num_visits + 1) // 2  # 计算2列布局所需行数
cols = 2

# 创建子图框架
fig = make_subplots(
    rows=rows,
    cols=cols,
    subplot_titles=[f'Visit {v}' for v in unique_visits],
    shared_xaxes=False,
    shared_yaxes=False,
    vertical_spacing=0.1,
    horizontal_spacing=0.05
)

# 定义颜色与线型映射
color_map = {'SBP': 'red', 'DBP': 'blue'}
dash_map = {mode: dash for mode, dash in zip(
    r_30['MODE'].unique(),
    ['solid', 'dash', 'dot', 'dashdot']  # 可根据MODE数量调整线型
)}

# 遍历每个VISIT添加折线
for idx, visit in enumerate(unique_visits, 1):
    row = (idx - 1) // cols + 1
    col = (idx - 1) % cols + 1
    visit_data = r_30[r_30['VISIT'] == visit]
    
    # 添加SBP折线(按MODE区分线型)
    for mode in visit_data['MODE'].unique():
        mode_data = visit_data[visit_data['MODE'] == mode]
        fig.add_trace(
            go.Scatter(
                x=mode_data['DATE_TIME'],
                y=mode_data['SBP'],
                name=f'SBP - {mode}',
                line=dict(color=color_map['SBP'], dash=dash_map[mode]),
                showlegend=(idx == 1)  # 仅第一个子图显示图例,避免重复
            ),
            row=row,
            col=col
        )
    
    # 添加DBP折线(按MODE区分线型)
    for mode in visit_data['MODE'].unique():
        mode_data = visit_data[visit_data['MODE'] == mode]
        fig.add_trace(
            go.Scatter(
                x=mode_data['DATE_TIME'],
                y=mode_data['DBP'],
                name=f'DBP - {mode}',
                line=dict(color=color_map['DBP'], dash=dash_map[mode]),
                showlegend=(idx == 1)
            ),
            row=row,
            col=col
        )

# 调整布局与坐标轴
fig.update_layout(
    legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
    height=400 * rows,  # 根据行数设置图表高度
    title_text='Blood Pressure by Visit and Mode'
)
fig.update_xaxes(title_text='DATE_TIME')
fig.update_yaxes(title_text='Blood Pressure (mmHg)')

# Dash应用集成
app = Dash(__name__)
app.layout = html.Div([
    html.H1('Blood Pressure Dashboard'),
    dcc.Graph(figure=fig)
])

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

内容的提问来源于stack exchange,提问作者dspractician

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 10:40:58