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

能否将Dash应用中首个图表的缩放范围作为第二个图表的输入?

需求可实现方案

完全可以实现将第一个图表的缩放日期范围传递给第二个图表的需求。核心思路是利用Dash图表组件的relayoutData属性获取用户缩放后的X轴范围,再将该范围作为输入参数传入第二个图表的回调函数中,对数据进行日期过滤后重新计算占比。

修改后的完整核心代码:

from re import template

import pandas as pd
import plotly.express as px
from dash import Dash, Input, Output, dcc, html
from flask import globals


def init_dashboard(server):
    evicted_df = pd.read_csv("app/data/evicted_jobs_node.csv", sep="\t")
    all_df = pd.read_csv("app/data/all_jobs_node.csv", sep="\t")
    all_df["datetime"] = pd.to_datetime(all_df["datetime"])
    all_df = all_df.set_index(["datetime"])
    all_df["evicted"] = all_df["id_job"].isin(evicted_df["id_job"])

    app = Dash(__name__, server=server, routes_pathname_prefix="/dash/")

    app.layout = html.Div(
        [
            html.Div(
                className="row",
                children=[
                    html.Div(
                        className="six columns",
                        children=[dcc.Graph(id="graph-with-dropdown")],
                        style=dict(width="75%"),
                    ),
                    html.Div(
                        className="six columns",
                        children=[dcc.Graph(id="graph-with-dropdown2")],
                        style=dict(width="25%"),
                    ),
                ],
                style=dict(display="flex"),
            ),
            html.Div(
                className="row",
                children=[
                    html.Div(
                        className="six columns",
                        children=[
                            dcc.Dropdown(
                                id="partition-dropdown",
                                options=[
                                    "Partition (default is all)",
                                    *all_df["partition"].unique(),
                                ],
                                value="Partition (default is all)",
                                clearable=False,
                                searchable=False,
                            )
                        ],
                        style={
                            "width": "50%",
                            "justify-content": "center",
                        },
                    ),
                    html.Div(
                        className="six columns",
                        children=[
                            dcc.Dropdown(
                                id="node-dropdown",
                                options=[
                                    "Number of Nodes (default is all)",
                                    *sorted(
                                        [
                                            int(nodes)
                                            for nodes in all_df["nodes_alloc"].unique()
                                        ]
                                    ),
                                ],
                                value="Number of Nodes (default is all)", 
                                clearable=False,
                                searchable=False,
                            )
                        ],
                        style=dict(width="50%"),
                    ),
                ],
                style=dict(display="flex"),
            ),
        ]
    )
    init_callbacks(app, all_df)
    return app.server


def init_callbacks(app, all_df):
    @app.callback(
        Output("graph-with-dropdown2", "figure"),
        [Input("node-dropdown", "value"), Input("partition-dropdown", "value"), Input("graph-with-dropdown", "relayoutData")],
    )
    def update_evicted_ratio_fig(selected_nodes, selected_partition, relayout_data):
        # 基础过滤:节点和分区
        if selected_nodes != "Number of Nodes (default is all)":
            filtered_df = all_df[all_df["nodes_alloc"] == selected_nodes]
        else:
            filtered_df = all_df

        if selected_partition != "Partition (default is all)":
            filtered_df = filtered_df[filtered_df["partition"] == selected_partition]

        # 日期范围过滤:来自第一个图表的缩放操作
        if relayout_data is not None and 'xaxis.range[0]' in relayout_data and 'xaxis.range[1]' in relayout_data:
            start_date = pd.to_datetime(relayout_data['xaxis.range[0]'])
            end_date = pd.to_datetime(relayout_data['xaxis.range[1]'])
            filtered_df = filtered_df[(filtered_df.index >= start_date) & (filtered_df.index <= end_date)]

        # 计算占比
        total_jobs = len(filtered_df)
        if total_jobs == 0:
            not_evicted_pct = 0
            evicted_pct = 0
        else:
            not_evicted_pct = 100 * len(filtered_df[filtered_df["evicted"] == False]) / total_jobs
            evicted_pct = 100 * len(filtered_df[filtered_df["evicted"] == True]) / total_jobs

        fig = px.bar(
            x=["Not Evicted", "Evicted"],
            y=[not_evicted_pct, evicted_pct],
            color=[False, True],
            color_discrete_map={True: "red", False: "green"},
            labels={"x": "Job Status", "y": "% of Jobs"},
        )
        fig.update_layout(transition_duration=500)

        return fig

    @app.callback(
        Output("graph-with-dropdown", "figure"),
        [Input("node-dropdown", "value"), Input("partition-dropdown", "value")],
    )
    def update_time_series_fig(selected_nodes, selected_partition):
        if selected_nodes != "Number of Nodes (default is all)":
            filtered_df = all_df[all_df["nodes_alloc"] == selected_nodes]
        else:
            filtered_df = all_df

        if selected_partition != "Partition (default is all)":
            filtered_df = filtered_df[filtered_df["partition"] == selected_partition]

        # 构建堆叠柱状图数据
        non_evicted_data = filtered_df[filtered_df["evicted"] == False].groupby([pd.Grouper(freq="6H")]).sum(numeric_only=True)["node_hours"]
        evicted_data = filtered_df[filtered_df["evicted"] == True].groupby([pd.Grouper(freq="6H")]).sum(numeric_only=True)["node_hours"]

        fig = px.bar(
            x=non_evicted_data.index,
            y=non_evicted_data.values,
            labels={"x": "Date", "y": "Node hours"},
            title="Job Status",
            barmode="stack",
            name="Not Evicted"
        )

        fig.add_bar(
            name="Evicted",
            x=evicted_data.index,
            y=evicted_data.values,
        )

        fig.update_layout(transition_duration=500)

        return fig

    return app.server

关键修改说明:

  • 新增graph-with-dropdown的relayoutData作为第二个图表回调的输入,捕获用户缩放操作后的日期范围
  • 在第二个图表的回调函数中添加日期范围过滤逻辑,仅保留选中时间区间内的数据
  • 修正原代码中函数名重复的问题,避免命名冲突
  • 简化占比计算逻辑,移除未使用的冗余变量
  • 优化时间序列图表的数据构建方式,提升代码可读性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 23:15:35