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

Dash Plotly回调更新图表失败求助(IBM数据科学课程作业)

汽车销售统计Dash仪表盘回调错误修复

一、原始代码的核心问题与修复

原始代码出现Callback error updating output-container.children的原因如下:

  1. 下拉菜单初始值不匹配
    dropdown-statistics的初始值设为'Select Statistics',但选项列表中无该值,导致回调触发时selected_statistics为无效值,直接进入else分支返回None,无图表渲染。
    修复:将初始值改为选项中的有效值,比如'Recession Period Statistics'。

  2. Plotly图表列名引用错误
    所有图表中使用的y='Sales Volume'是自定义标题,而非数据列名,实际数据列名为Automobile_Sales,直接引用会导致找不到列的错误。
    修复:将y='Sales Volume'改为y='Automobile_Sales',同时通过labels参数设置显示的轴标题。

  3. Recession模式下冗余且错误的代码
    Recession模式不需要input_year,但代码中错误添加了yearly_data = data[data['Year'] == input_year],当未选择年份时input_year为字符串'Select Year',与数值类型的Year列比较会触发类型错误。
    修复:删除该行冗余代码。

  4. Yearly模式下图表逻辑错误
    Y_chart3的标题是“按车型平均销量”,但代码中仅按Year分组,未按Vehicle_Type分组,导致图表不符合预期。
    修复:修改分组逻辑为yearly_data.groupby('Vehicle_Type')['Automobile_Sales'].mean().reset_index()。

二、调整布局后的核心问题与修复

调整布局后出现更新chart1-4.figure的回调错误,原因是:
回调的输出是四个dcc.Graph的figure属性,需要返回Plotly figure对象,但代码中返回的是包含dcc.Graph的html.Div结构,类型完全不匹配。
修复:回调函数直接返回四个Plotly figure实例,而非组件结构。

修复后的完整代码

import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import pandas as pd
import plotly.express as px

# 加载数据
data = pd.read_csv('https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBMDeveloperSkillsNetwork-DV0101EN-SkillsNetwork/Data%20Files/historical_automobile_sales.csv')

# 初始化Dash应用
app = dash.Dash(__name__)
app.title = "Automobile Sales Statistics Dashboard"

# 下拉选项与年份列表
dropdown_options = [
    {'label': 'Yearly Statistics Report', 'value': 'Yearly Statistics'},
    {'label': 'Recession Period Statistics', 'value': 'Recession Period Statistics'}
]
year_list = [i for i in range(1980, 2024, 1)]

# 应用布局
app.layout = html.Div(children=[
    html.H1("Automobile Sales Statistics Dashboard", 
            style={'textAlign': 'center', 'color': '#503D36', 'font-size': 24}),
    
    html.Div([
        html.Label("Select Statistics:"),
        dcc.Dropdown(
            id='dropdown-statistics',
            options=dropdown_options,
            value='Recession Period Statistics',  # 设置有效初始值
            placeholder='Select a report type',
            style={'width': '80%', 'padding': 3, 'font-size': 20}
        )
    ]),
    
    html.Div([
        dcc.Dropdown(
            id='select-year',
            options=[{'label': i, 'value': i} for i in year_list],
            value=1980,  # 设置有效初始年份
            style={'width': '80%', 'padding': 3, 'font-size': 20},
            disabled=True  # 默认禁用
        )
    ]),
    
    # 调整后的布局:固定四个图表容器
    html.Div(style={'display': 'flex', 'justify-content': 'center', 'gap': '20px', 'margin': '20px 0'},
             children=[
                 html.Div(dcc.Graph(id='chart1'), style={'width': '45%'}),
                 html.Div(dcc.Graph(id='chart2'), style={'width': '45%'})
             ]),
    html.Div(style={'display': 'flex', 'justify-content': 'center', 'gap': '20px', 'margin': '20px 0'},
             children=[
                 html.Div(dcc.Graph(id='chart3'), style={'width': '45%'}),
                 html.Div(dcc.Graph(id='chart4'), style={'width': '45%'})
             ])
])

# 回调:控制年份下拉菜单的禁用状态
@app.callback(
    Output('select-year', 'disabled'),
    Input('dropdown-statistics', 'value')
)
def toggle_year_dropdown(selected_stat):
    return selected_stat != 'Yearly Statistics'

# 回调:更新四个图表的figure
@app.callback(
    [Output('chart1', 'figure'),
     Output('chart2', 'figure'),
     Output('chart3', 'figure'),
     Output('chart4', 'figure')],
    [Input('dropdown-statistics', 'value'),
     Input('select-year', 'value')]
)
def update_charts(selected_stat, input_year):
    if selected_stat == 'Recession Period Statistics':
        recession_data = data[data['Recession'] == 1]
        
        # 图表1:衰退期年度平均销量趋势
        yearly_rec = recession_data.groupby('Year')['Automobile_Sales'].mean().reset_index()
        fig1 = px.line(
            yearly_rec,
            x='Year',
            y='Automobile_Sales',
            title="衰退期平均汽车销量波动",
            labels={'Automobile_Sales': '销量'}
        )
        
        # 图表2:各车型平均销量
        average_sales = recession_data.groupby('Vehicle_Type')['Automobile_Sales'].mean().reset_index()
        fig2 = px.bar(
            average_sales,
            x='Vehicle_Type',
            y='Automobile_Sales',
            title="各车型平均销量",
            labels={'Automobile_Sales': '平均销量', 'Vehicle_Type': '车型'}
        )
        
        # 图表3:衰退期各车型广告支出占比
        exp_rec = recession_data.groupby('Vehicle_Type')['Advertising_Expenditure'].sum().reset_index()
        fig3 = px.pie(
            exp_rec,
            values='Advertising_Expenditure',
            names='Vehicle_Type',
            title="衰退期各车型广告支出占比",
            labels={'Advertising_Expenditure': '广告总支出'}
        )
        
        # 图表4:失业率对各车型销量的影响
        un_sales = recession_data.groupby(['Vehicle_Type', 'unemployment_rate'])['Automobile_Sales'].mean().reset_index()
        fig4 = px.bar(
            un_sales,
            x='unemployment_rate',
            y='Automobile_Sales',
            color='Vehicle_Type',
            title="失业率对各车型销量的影响",
            labels={'unemployment_rate': '失业率', 'Automobile_Sales': '平均销量', 'Vehicle_Type': '车型'}
        )
        
        return [fig1, fig2, fig3, fig4]
    
    elif selected_stat == 'Yearly Statistics':
        yearly_data = data[data['Year'] == input_year]
        
        # 图表1:年度总销量趋势(全周期)
        yas = data.groupby('Year')['Automobile_Sales'].sum().reset_index()
        fig1 = px.line(
            yas,
            x='Year',
            y='Automobile_Sales',
            title="年度汽车总销量趋势",
            labels={'Automobile_Sales': '总销量'}
        )
        
        # 图表2:月度总销量(当年)
        mas = yearly_data.groupby('Month')['Automobile_Sales'].sum().reset_index()
        fig2 = px.line(
            mas,
            x='Month',
            y='Automobile_Sales',
            title=f"{input_year}年月度汽车总销量",
            labels={'Automobile_Sales': '总销量', 'Month': '月份'}
        )
        
        # 图表3:当年各车型平均销量
        avr_vdata = yearly_data.groupby('Vehicle_Type')['Automobile_Sales'].mean().reset_index()
        fig3 = px.bar(
            avr_vdata,
            x='Vehicle_Type',
            y='Automobile_Sales',
            title=f"{input_year}年各车型平均销量",
            labels={'Automobile_Sales': '平均销量', 'Vehicle_Type': '车型'}
        )
        
        # 图表4:当年各车型广告支出占比
        exp_data = yearly_data.groupby('Vehicle_Type')['Advertising_Expenditure'].sum().reset_index()
        fig4 = px.pie(
            exp_data,
            values='Advertising_Expenditure',
            names='Vehicle_Type',
            title=f"{input_year}年各车型广告支出占比",
            labels={'Advertising_Expenditure': '广告总支出'}
        )
        
        return [fig1, fig2, fig3, fig4]
    
    # 默认返回空图表
    empty_fig = px.scatter(title="请选择报表类型")
    return [empty_fig, empty_fig, empty_fig, empty_fig]

# 运行应用
if __name__ == '__main__':
    app.run_server(debug=True)

关键修复说明

  • 移除了重复的应用初始化代码,统一设置应用标题
  • 下拉菜单初始值改为有效选项,避免无效触发
  • 修正了所有图表的列名引用错误,通过labels参数设置友好的显示标题
  • 调整回调逻辑,确保返回值与输出属性类型匹配(直接返回Plotly figure对象)
  • 优化了布局的样式,添加间隙和宽度设置,提升可视化效果

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:25:53