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

Plotly Dash报figure.data参数非法传入错误排查

问题描述
  • 基于Plotly Dash搭建交互式数据看板,预期支持xy、xyy、xyyy等结构的数据集上传,自动生成对应折线图
  • 上传数据后触发固定报错:Invalid argument figure.data passed into Graph
  • 相关参考材料:
原始问题代码
import base64
import datetime
import io
import plotly.graph_objs as go
import cufflinks as cf

import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table

import pandas as pd

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server

colors = {
    "graphBackground": "#F5F5F5",
    "background": "#ffffff",
    "text": "#000000"
}

app.layout = html.Div([
    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        multiple=True
    ),
    dcc.Graph(id='Mygraph'),
    html.Div(id='output-data-upload')
])

def parse_data(contents, filename):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            df = pd.read_excel(io.BytesIO(decoded))
        elif 'txt' or 'tsv' in filename:
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')), delimiter = r'\s+')
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])

    return df


@app.callback(Output('Mygraph', 'figure'),
            [
                Input('upload-data', 'contents'),
                Input('upload-data', 'filename')
            ])
def update_graph(contents, filename):
    fig = {
        'layout': go.Layout(
            plot_bgcolor=colors["graphBackground"],
            paper_bgcolor=colors["graphBackground"])
    }

    if contents:
        contents = contents[0]
        filename = filename[0]
        df = parse_data(contents, filename)
        df = df.set_index(df.columns[0])
        fig['data'] = df.iplot(asFigure=True, kind='scatter', mode='lines+markers', size=1)
        return fig

@app.callback(Output('output-data-upload', 'children'),
            [
                Input('upload-data', 'contents'),
                Input('upload-data', 'filename')
            ])
def update_table(contents, filename):
    table = html.Div()

    if contents:
        contents = contents[0]
        filename = filename[0]
        df = parse_data(contents, filename)

        table = html.Div([
            html.H5(filename),
            dash_table.DataTable(
                data=df.to_dict('rows'),
                columns=[{'name': i, 'id': i} for i in df.columns]
            ),
            html.Hr(),
            html.Div('Raw Content'),
            html.Pre(contents[0:200] + '...', style={
                'whiteSpace': 'pre-wrap',
                'wordBreak': 'break-all'
            })
        ])

    return table

if __name__ == '__main__':
    app.run_server(debug=True)
问题根因

代码存在4个核心问题,直接触发参数非法报错:

  1. Figure对象赋值错误:cufflinks的iplot(asFigure=True)方法返回的是完整的Plotly Figure对象,本身已经包含data(轨迹列表)和layout(布局配置)两个顶级属性。原代码把整个Figure对象直接赋值给fig['data']字段,相当于给要求传入轨迹列表的data参数塞了个完整图结构,直接触发参数类型校验失败。
  2. 文件解析逻辑bug:elif 'txt' or 'tsv' in filename是Python常见逻辑写法错误,非空字符串'txt'的布尔值恒为True,该分支会被所有文件触发,导致csv、excel文件也会按空白符分隔的txt规则解析,极易出现数据解析异常。
  3. 回调返回值缺失:update_graph回调在没有上传文件的初始加载状态下,没有显式返回默认的fig对象,会返回None,也会触发组件参数报错。
  4. 未初始化cufflinks离线模式,部分环境下iplot生成的图结构会缺少必要配置。
修复方案

按以下步骤修改代码即可解决问题:

  1. 导入cufflinks后添加离线模式初始化配置cf.go_offline()
  2. 修正文件解析分支的判断逻辑,把txt/tsv的判断改为elif 'txt' in filename or 'tsv' in filename,解析异常时返回空DataFrame而非html组件,避免后续图形渲染逻辑拿到非表格对象报错
  3. 调整图形生成逻辑:直接使用cufflinks生成的完整Figure对象,再更新自定义的背景色布局配置,不要把整个Figure赋值给data字段
  4. 补全回调的默认返回值,确保无上传内容时也返回合法的空图结构

修复后的核心代码片段:

# 导入cufflinks后添加离线初始化
cf.go_offline()

# 修正parse_data逻辑
def parse_data(contents, filename):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            df = pd.read_excel(io.BytesIO(decoded))
        elif 'txt' in filename or 'tsv' in filename:
            df = pd.read_csv(io.StringIO(decoded.decode('utf-8')), delimiter=r'\s+')
    except Exception as e:
        print(e)
        return pd.DataFrame()
    return df

# 修正图形回调逻辑
@app.callback(Output('Mygraph', 'figure'),
            [Input('upload-data', 'contents'),Input('upload-data', 'filename')])
def update_graph(contents, filename):
    # 初始化默认空图
    fig = go.Figure()
    fig.update_layout(
        plot_bgcolor=colors["graphBackground"],
        paper_bgcolor=colors["graphBackground"]
    )
    if contents:
        contents = contents[0]
        filename = filename[0]
        df = parse_data(contents, filename)
        if not df.empty:
            df = df.set_index(df.columns[0])
            # 生成完整图对象
            fig = df.iplot(asFigure=True, kind='scatter', mode='lines+markers', size=1)
            # 合并自定义背景配置
            fig.update_layout(
                plot_bgcolor=colors["graphBackground"],
                paper_bgcolor=colors["graphBackground"]
            )
    # 所有分支均返回合法fig对象
    return fig

修复后即可正常上传xy、xyy、xyyy结构的表格文件,自动生成多系列折线图,不会再出现figure.data参数非法的报错。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:54:27