假设我有3个具有不同列名的数据集 . 我想创建一个简洁的破折号应用程序,根据用户选择的数据集,它应该在文本区域中显示列名称(水平条带,类似按钮,但不可点击) . 到目前为止,我能够做到这一点,这里是代码:

import pandas as pd
import numpy as np
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go

data1 = {'Col1':['A', 'B', 'C', 'D', 'E'],
        'Col2':list(np.random.randn(5))}
data2 = {'Col1':['F', 'G', 'H', 'I', 'J'],
        'Col2':list(np.random.randn(5))}
data3 = {'Col1':['K', 'L', 'M', 'N', 'O'],
        'Col2':list(np.random.randn(5))}

df1 = pd.DataFrame(data1, columns=['Col1', 'Col2'])
df2 = pd.DataFrame(data2, columns=['Col1', 'Col2'])
df3 = pd.DataFrame(data3, columns=['Col1', 'Col2'])


app = dash.Dash()

app.layout = html.Div([
        html.Div([dcc.Dropdown(
                 id='dropdown',
                 options=[{'label': 'Option 1', 'value': 1},
                          {'label': 'Option 2', 'value': 2},
                          {'label': 'Option 3', 'value': 3}],
                 placeholder='Select the dataset...',
                 )]),

        html.Div(id='output')
    ])


@app.callback(
    dash.dependencies.Output('output', 'children'),
    [dash.dependencies.Input('dropdown', 'value')])
def update_output(selected):
    if selected==1:
        df = df1
    elif selected==2:
        df = df2
    elif selected==3:
        df = df3

    columns = df.Col1.values

    divs=[]
    for i in range(len(columns)):
        divs.append(
            dcc.Graph(
                figure=go.Figure(
                    data=[go.Bar(
                                x=[10],
                                y=[columns[i]],
                                orientation = 'h',
                                text=['Column number {}: {}'.format(i+1, columns[i])],
                                textposition = 'inside',
                                marker = dict(color = 'rgb(158,202,225)'),
                            )
                        ],
                    layout=go.Layout(
                        xaxis=dict(showticklabels=False, fixedrange=True),
                        yaxis=dict(showticklabels=False, fixedrange=True),
                    )
                ),
                hoverData='',
                style={'width': 400, 'height':250, 'padding': 1},
                id='dynamic-text-area-'+format(i)
            ),
        )

        divs.append(html.Div(style={'height': '1'}))

    return divs

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

enter image description here

现在,我想添加另一个功能,每当用户将鼠标悬停在任何这些列名称上时,会弹出一个饼图,显示一些信息(我将在下面的代码中使用一些简单的虚拟信息进行演示) . 所以我在应用程序布局中添加了一个名为 pie-chart-div 的div,并在循环内部使用动态创建的文本区域图形ID作为输入来输出饼图,如下所示:

for i in range(len(columns)):
    '''
    ###
    The rest of the stuff written above
    ###
    '''
    @app.callback(dash.dependencies.Output('pie-chart-div', 'children'),
              [dash.dependencies.Input('dropdown', 'value'),
               dash.dependencies.Input('dynamic-text-area-{}'.format(i), 'hoverData')])
        def update_graph(df, hoverData):
            values = [df[df.Col1==hoverData].Col2.iloc[0], sum(df.Col2)]
            labels = ['Selected column','All other columns']
            trace = go.Pie(values=values, labels = labels)
            fig = dcc.Graph(
                id='graph',
                figure={
                    'data': [trace],
                    'layout': {
                        'height': 400,
                        'width': 400,
                        'showlegend': False
                    }
                }
            )
            return fig

问题是,这给出了一个错误,说它不能在应用程序布局中显示图形ID . 那是因为破折号无法处理动态数字创建,如here所述 . 这是完整的错误:

File "C:\Users\h473\Desktop\example_app.py", line 82, in update_output
    dash.dependencies.Input('dynamic-text-area-{}'.format(i), 'hoverData')])
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\dash\dash.py", line 827, in callback
    self._validate_callback(output, inputs, state, events)
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\dash\dash.py", line 631, in _validate_callback
    ).replace('    ', ''))
dash.exceptions.NonExistantIdException:
Attempting to assign a callback to the
component with the id "dynamic-text-area-0" but no
components with id "dynamic-text-area-0" exist in the
app's layout.


Here is a list of IDs in layout:
['dropdown', 'output', 'pie-chart-div']


If you are assigning callbacks to components
that are generated by other callbacks
(and therefore not in the initial layout), then
you can suppress this exception by setting
`app.config['suppress_callback_exceptions']=True`.

所以,我试着加入这条线

app.config['suppress_callback_exceptions']=True

但这会给出另一个错误,即 pie-chart-div 已被使用且无法重复使用:

File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\_compat.py", line 35, in reraise
    raise value
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\_compat.py", line 35, in reraise
    raise value
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\flask\app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\dash\dash.py", line 911, in dispatch
    return self.callback_map[target_id]['callback'](*args)
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\dash\dash.py", line 851, in add_context
    output_value = func(*args, **kwargs)
  File "C:\Users\h473\Desktop\NPS SHAP Dashboard\ex.py", line 902, in update_output
    dash.dependencies.Input('dynamic-text-area-{}'.format(i), 'hoverData')])
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\dash\dash.py", line 827, in callback
    self._validate_callback(output, inputs, state, events)
  File "C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\dash\dash.py", line 708, in _validate_callback
    output.component_property).replace('    ', ''))
dash.exceptions.CantHaveMultipleOutputs:
You have already assigned a callback to the output
with ID "pie-chart-div" and property "children". An output can only have
a single callback function. Try combining your inputs and
callback functions together into one function.

那么,我该如何解决这个问题并让它发挥作用呢?


这是我的完整代码:

import pandas as pd
import numpy as np
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go

data1 = {'Col1':['A', 'B', 'C', 'D', 'E'],
        'Col2':list(np.random.randn(5))}
data2 = {'Col1':['F', 'G', 'H', 'I', 'J'],
        'Col2':list(np.random.randn(5))}
data3 = {'Col1':['K', 'L', 'M', 'N', 'O'],
        'Col2':list(np.random.randn(5))}

df1 = pd.DataFrame(data1, columns=['Col1', 'Col2'])
df2 = pd.DataFrame(data2, columns=['Col1', 'Col2'])
df3 = pd.DataFrame(data3, columns=['Col1', 'Col2'])


app = dash.Dash()

app.layout = html.Div([
        html.Div([dcc.Dropdown(
                 id='dropdown',
                 options=[{'label': 'Dataset 1', 'value': 1},
                          {'label': 'Dataset 2', 'value': 2},
                          {'label': 'Dataset 3', 'value': 3}],
                 placeholder='Select the dataset...',
                 )]),

        html.Div(id='output'),

        html.Div(id='pie-chart-div')
    ])

@app.callback(
    dash.dependencies.Output('output', 'children'),
    [dash.dependencies.Input('dropdown', 'value')])
def update_output(selected):
    if selected==1:
        df = df1
    elif selected==2:
        df = df2
    elif selected==3:
        df = df3

    columns = df.Col1.values

    divs=[]
    for i in range(len(columns)):
        divs.append(
            dcc.Graph(
                figure=go.Figure(
                    data=[go.Bar(
                                x=[10],
                                y=[columns[i]],
                                orientation = 'h',
                                text=['Column number {}: {}'.format(i+1, columns[i])],
                                textposition = 'inside',
                                marker = dict(color = 'rgb(158,202,225)'),
                            )
                        ],
                    layout=go.Layout(
                        xaxis=dict(showticklabels=False, fixedrange=True),
                        yaxis=dict(showticklabels=False, fixedrange=True),
                    )
                ),
                hoverData='',
                style={'width': 400, 'height':250, 'padding': 1},
                id='dynamic-text-area-{}'.format(i)
            ),
        )

        divs.append(html.Div(style={'height': '1'}))




        @app.callback(dash.dependencies.Output('pie-chart-div', 'children'),
              [dash.dependencies.Input('dropdown', 'value'),
               dash.dependencies.Input('dynamic-text-area-{}'.format(i), 'hoverData')])
        def update_graph(df, hoverData):
            values = [df[df.Col1==hoverData].Col2.iloc[0], sum(df.Col2)]
            labels = ['Selected column','All other columns']
            trace = go.Pie(values=values, labels = labels)
            fig = dcc.Graph(
                id='graph',
                figure={
                    'data': [trace],
                    'layout': {
                        'height': 400,
                        'width': 400,
                        'showlegend': False
                    }
                }
            )
            return fig



    return divs



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