使用 plotly express 并在一页上有多个图形时,随机出现导入错误

Getting Import Error quite randomly when using plotly express and having multiple graphs on one page

Dash 相对较新,这是一个困扰我几个月的问题。我正在制作一个多页面应用程序,它使用卡片和嵌入在卡体中的图表显示一些基本数据趋势。在 30% 的情况下,该应用程序运行良好,没有任何错误,而在另外 70% 的情况下,它会抛出以下任一情况:

  1. ImportError: 无法从部分初始化的模块 'plotly.validator_cache' 导入名称 'ValidatorCache'(很可能是由于循环导入) 或者
  2. ImportError: 无法从部分初始化的模块 'plotly.graph_objects' 导入名称 'Layout'(很可能是由于循环导入)

这两个都是随机出现的,我通常会刷新应用程序以使它们消失。但显然我做错了什么。我有一组触发图形回调的下拉菜单。我一直在为这事绞尽脑汁。任何 help/leads 将不胜感激。我在错误中看到的唯一模式是它们似乎是在回调中调用 plotly express graphs 时出现的。

我做错了什么?我在网上到处搜索寻求帮助,但还没有。

分享一些相关的代码片段(这可能太长而且很多部分对问题来说并不重要,但可以让您大致了解我一直在努力的方向)

import dash
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from plotly.subplots import make_subplots
import plotly.express as px


card_content1_1 = [
    dbc.CardHeader([dbc.Row([html.H5("SALES VOLUME TREND", className = "font-weight-bold text-success"),
                   dbc.Button(
                       html.I(className="fa fa-window-maximize"),
                       color="success",
                       id="sales_maximize",
                       className="ml-auto",
                       # href="www.cogitaas.com"
                   )
                             ])]),
     dbc.CardBody(
        [dcc.Graph(
    id='sales_graph',
    figure={},
            style={'height':'30vh'}
    # className="mt-5"
            )])]


card_stacked_discount = [
    dbc.CardHeader([dbc.Row([html.H5("VOLUMES UNDER DIFFERENT DISCOUNT LEVELS", className="font-weight-bold text-info text-center"),
                   dbc.Button(
                       html.I(className="fa fa-window-maximize"),
                       color="info",
                       id="discount_maximize",
                       className="ml-auto",
                       # href="www.cogitaas.com"
                   )
                             ])]),
    dbc.CardBody(
        [dcc.Dropdown(
                id = 'stacked_discount_dropdown',
                options =stacked_discount_options,
                value=stacked_discount_options[0].get('value'),
                style={'color':'black'},
                # multi=True
            ),
            dcc.Graph(
    id='stacked_discount_graph',
    figure={},
style={'height':'30vh'}
            )])]
cards = html.Div(
        [
            dbc.Row(
                [
                    dbc.Col(dbc.Card(card_content1_1, color="success", outline=True,
                                     style={'height':'auto'}), width=8),


                ],
                className="mb-4",

            ),

            dbc.Row(
                [
                    dbc.Col(dbc.Card(card_stacked_discount, color="info", outline=True), width=8),
                    dbc.Col(dbc.Card([
                        dbc.Row([
                            dbc.Col(dbc.Card(disc_sub_title, color="info", inverse=True)),
                        ]),
                        html.Br(),
                        dbc.Row([
                            dbc.Col(dbc.Card(disc_sub_card1, color="info", outline=True)),
                                              ]),
                    ]), width=4)
                ],
                className="mb-4",
            ),
        ]
    )

tab1_content = dbc.Card(
    dbc.CardBody(
        [cards,]
    ),
    className="mt-3",
)

tabs = dbc.Tabs(dbc.Tab(tab1_content, label="Data", label_style={'color':'blue'}, tab_style={"margin-left":"auto"}),])

content = html.Div([
        html.Div([tabs]),
        ],id="page-content")

app.layout = html.Div([dcc.Location(id="url"), content])


@app.callback(
    dash.dependencies.Output('sales_graph', 'figure'),
    [dash.dependencies.Input('platform-dropdown', 'value'),
     dash.dependencies.Input('signature-dropdown', 'value'),
     dash.dependencies.Input('franchise-dropdown', 'value'),
     dash.dependencies.Input('sales_maximize', 'n_clicks'),
     dash.dependencies.Input('time-dropdown', 'value'),
     ])
def update_sales_graph(plat, sign, fran, maximize, time_per):
    print(str(time_per)+"Test")
    time_ax=[]
    if isinstance(time_per,str):
        time_ax.append(time_per)
        time_per=time_ax
    if (time_per==None) or ('Full Period' in (time_per)):
        dff = df[(df.Platform==plat) & (df.Signature==sign) & (df.Franchise==fran)]
    elif ('YTD' in time_per):
        dff = df[(df.Platform == plat) & (df.Signature == sign) & (df.Franchise == fran) & (df.year==2020)]
    else:
        dff = df[(df.Platform==plat) & (df.Signature==sign) & (df.Franchise==fran) & (df.Qtr_yr.isin(time_per))]

    fig = px.area(dff, x='Date', y='Qty_Orig', color_discrete_sequence=px.colors.qualitative.Dark2)
    fig.add_trace(go.Scatter(x=dff['Date'], y=dff['Outliers'], mode = 'markers', name='Outliers',
                             line=dict(color='darkblue')))
    fig.add_trace(go.Scatter(x=dff['Date'], y=dff['bestfit'], name='Long Term Trend',
                             line=dict(color='darkblue')))
    fig.update_layout(font_family="Rockwell",
                      title={'text': fran + " Volume Trend",
                             'y': 0.99,
                             # 'x': 0.15,
                             # 'xanchor': 'auto',
                             'yanchor': 'top'
                             },
                      legend=dict(
                          orientation="h",
                          # y=-.15, yanchor="bottom", x=0.5, xanchor="center"
                      ),
                      yaxis_visible=False, yaxis_showticklabels=False,
                      xaxis_title=None,
                    margin=dict(l=0, r=0, t=0, b=0, pad=0),
                      plot_bgcolor='White',
                    paper_bgcolor='White',
                                          )
    fig.update_xaxes(showgrid=False, zeroline=True)
    fig.update_yaxes(showgrid=False, zeroline=True)
    changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
    if 'maximize' in changed_id:
        fig.show()
    return fig


@app.callback(
    dash.dependencies.Output('stacked_discount_graph', 'figure'),
    [dash.dependencies.Input('platform-dropdown', 'value'),
     dash.dependencies.Input('signature-dropdown', 'value'),
     dash.dependencies.Input('franchise-dropdown', 'value'),
     dash.dependencies.Input('discount_maximize', 'n_clicks'),
     dash.dependencies.Input('stacked_discount_dropdown', 'value'),
     dash.dependencies.Input('time-dropdown', 'value'),
     ])
def stacked_discount(plat, sign, fran, maximize, sales_days, time_per):
    time_ax=[]
    if isinstance(time_per,str):
        time_ax.append(time_per)
        time_per=time_ax
    # else:
    #     time_per=list(time_per)

    if (time_per==None) or ('Full Period' in (time_per)):
        df_promo = df_promo_vol[(df_promo_vol.Platform==plat) & (df_promo_vol.Signature==sign) & (df_promo_vol.Franchise==fran)]
    elif ('YTD' in time_per):
        df_promo = df_promo_vol[(df_promo_vol.Platform == plat) & (df_promo_vol.Signature == sign) & (df_promo_vol.Franchise == fran) & (df_promo_vol.Year==2020)]
    else:
        df_promo = df_promo_vol[(df_promo_vol.Platform==plat) & (df_promo_vol.Signature==sign) & (df_promo_vol.Franchise==fran) & (df_promo_vol.Qtr_yr.isin(time_per))]

    color_discrete_map = {
        "0 - 10": "orange",
        "10 - 15": "green",
        "15 - 20": "blue",
        "20 - 25": "goldenrod",
        "25 - 30": "magenta",
        "30 - 35": "red",
        "35 - 40": "aqua",
        "40 - 45": "violet",
        "45 - 50": "brown",
        "50 + ": "black"
    }

    category_orders = {'disc_range': ['0 - 10', '10 - 15', '15 - 20', '20 - 25', '25 - 30', '30 - 35', '35 - 40',
                                      '40 - 45', '45 - 50', '50 + ']}

    if (sales_days == None) or (sales_days == 'sales_act'):
        fig = px.bar(df_promo, x='start', y='units_shipped', color='disc_range',
                     color_discrete_map=color_discrete_map,
                     category_orders=category_orders,
                 )
    else:
        fig = px.bar(df_promo, x='start', y='Date', color="disc_range",
                    color_discrete_map=color_discrete_map,
                     category_orders=category_orders,
                     )

    fig.update_layout(font_family="Rockwell",
                      title={'text': fran + " Sales Decomposition",
                             'y': 0.99,
                             'x': 0.1,
                             # 'xanchor': 'auto',
                             'yanchor': 'top'
                             },
                      legend=dict(
                          orientation="h",
                          # y=-.15, yanchor="bottom", x=0.5, xanchor="center"
                      ),
                      # yaxis_visible=False, yaxis_showticklabels=False,
                      xaxis_title=None,
                      margin=dict(l=0, r=0, t=30, b=30, pad=0),
                      plot_bgcolor='White',
                      paper_bgcolor='White',
                      )
    fig.update_xaxes(showgrid=False, zeroline=True)
    fig.update_yaxes(showgrid=False, zeroline=True)
    changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
    if 'maximize' in changed_id:
        fig.show()
    return fig

好吧,看来我可能偶然发现了答案。我在多个回调中使用了几乎相同的输入,这可能会对输入的顺序造成一些干扰。一旦我将代码集成到一个具有多个输出的回调中,问题似乎就消失了。

在处理同样的问题时,我的应用程序中的所有内容都运行良好,然后我制作了一个完全独立的部分和回调,开始抛出那些循环导入错误。

不愿意将我的(正确地)分离的回调重新安排为一个单独的回调,并且发现您可以通过简单地导入脚本所说的未能获得的内容来解决问题。在我的例子中,plotly 试图导入 ValidatorCache 和 Layout,所以将它们添加到顶部解决了问题,现在我的应用程序按预期工作。希望这对遇到类似问题的人有所帮助。

from plotly.graph_objects import Layout
from plotly.validator_cache import ValidatorCache