首页 > 解决方案 > Python Dash Plotly : .. 返回了一个不可 JSON 序列化的值 (InvalidCallbackReturnValue)

问题描述

几天前我刚刚发现了 Dash。我通过观看一些 youtube 视频并按照此处的示例代码开始学习: https ://dash.plotly.com/datatable/callbacks 特别是最后一部分:使用图表连接后端分页我试图将其应用于我自己的数据,保持代码框架完全相同,我得到:

dash.exceptions.InvalidCallbackReturnValue:children组件属性的回调graphs返回的值不是 JSON 可序列化的。通常,Dash 属性只能是 dash 组件、字符串、字典、数字、None 或它们的列表。

在用户与数据表交互时更新图形的回调中,该函数遍历数据框的列并为每个列绘制一个图。然而,在我的例子中,只有一列可以迭代,所以输出应该只有一个图(尽管如此我保持代码格式相同,我认为这不重要)。这是我能想到的示例代码和我的代码之间的唯一区别。

我已经研究并盯着它第三天了,我看不出我的错误在哪里。我以前从未制作过网络应用程序,所以也不知道如何调试。看来我输出了一个错误的图表,但是我如何找出输出的样子以了解它为什么是错误的?

我正在绘制的数据框的结构是:

Index |   month |    year  |  SumOrder 
0           1        2019     2033
1           1        2020     1232
2           2        2019      221
3           2        2020      292
......... 

原则上,该图应仅代表 2019 年和 2020 年每个月的 SumOrder 条形图。

我的代码在下面,我添加了一些注释以进行澄清。错误似乎在最后一个(第二个)回调中。我也展示了数据的预处理,以防有错误的来源。

> #data upload and preprocessing 
> data = pd.read_excel('Test.xls')        
> ## make columns for month and year 
> data['Date Order'] = data['Date Order'].apply(dateutil.parser.parse, dayfirst = True)
> data['month'] = data['Date Order'].dt.month.astype('category')
> data['year'] = data['Date Order'].dt.year.astype('category')
> data['date'] = data['Date Order'].dt.date.astype('category')      
> ## group by month and year and find the mean for each month and year 
> groupedMonthYear = data.groupby(['month', 'year'])
> df = groupedMonthYear.agg({'SumOrder': 'mean'}).fillna(0).reset_index()    <---- this is the dataframe used 
>
> app = dash.Dash(__name__)
> 
> page_size = 5
> 
> from datetime import datetime as dt
> 
> app.layout = html.Div(
>     className = 'row', 
>     children = [
>         html.H1('data visualised'),
>         
>         html.Br(),
> 
>         html.Div(       
>             dash_table.DataTable(
>                 id = 'table',
>                 columns = [
>                     {'name': i, 'id': i} for i in sorted(df.columns)],
>                 page_current = 0,
>                 page_size = 20,
>                 page_action = 'custom',
>                 
>                 filter_action = 'custom',
>                 filter_query = '',
>         
>                 sort_action = 'custom',
>                 sort_mode = 'multi',
>                 sort_by = []
>             ),                          
>             style = {'height': 750, 'overflowY': 'scroll'},
>             className = 'three columns'
>         ),    
>         
>         html.Br(),
>         
>         html.Div(          
>             id = 'graphs',
>             className = 'one column'
>         ),
>       ]
>    )
>             
>         
> 
> operators = [['ge ', '>='],
>              ['le ', '<='],
>              ['lt ', '<'],
>              ['gt ', '>'],
>              ['ne ', '!='],
>              ['eq ', '='],
>              ['contains '],
>              ['datestartswith ']]
> 
> 
> def split_filter_part(filter_part):
>     for operator_type in operators:
>         for operator in operator_type:
>             if operator in filter_part:
>                 name_part, value_part = filter_part.split(operator, 1)
>                 name = name_part[name_part.find('{') + 1: name_part.rfind('}')]
> 
>                 value_part = value_part.strip()
>                 v0 = value_part[0]
>                 if (v0 == value_part[-1] and v0 in ("'", '"', '`')):
>                     value = value_part[1: -1].replace('\\' + v0, v0)
>                 else:
>                     try:
>                         value = float(value_part)
>                     except ValueError:
>                         value = value_part
> 
>                 # word operators need spaces after them in the filter string,
>                 # but we don't want these later
>                 return name, operator_type[0].strip(), value
>     return [None]*3   
> 
> 
> 
> @app.callback(       
>      Output(component_id='table', component_property = 'data'),
>      [Input(component_id='table', component_property = 'page_current'),
>      Input(component_id='table', component_property = 'page_size'),
>      Input(component_id='table', component_property = 'sort_by'),
>      Input(component_id='table', component_property = 'filter_query')
>      ])
> def update_table(page_current, page_size, sort_by, filter): 
>     filtering_expressions = filter.split(' && ')
>     dff = df  ## make a new variable with the data so we don't change original data
>     for filter_part in filtering_expressions:     
>         ## extract all we need to know about the filter we just applied
>         col_name, operator, filter_value = split_filter_part(filter_part)        
>         
>         if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):
>             # these operators match pandas series operator method names
>             dff = dff.loc[dff[col_name].str.contains(filter_value)]
>         elif operator == 'contains':
>             dff = dff.loc[dff[col_name].str.contains(filter_value)]
>         elif operator == 'datestartswith':
>             # this is a simplification of the front-end filtering logic,
>             # only works with complete fields in standard format
>             dff = dff.loc[dff[col_name].str.startswith(filter_value)]
>             
>     if len(sort_by):
>         dff = dff.sort_values(
>             [col['column_id'] for col in sort_by],
>             ascending =[
>                 col['direction'] == 'asc'
>                 for col in sort_by
>                 ],
>                 inplace = True
>             )
>     
>     return dff.iloc[
>         page_current*page_size: (page_current + 1)*page_size
>         ].to_dict('records')
>                                                    
>                                                    
> @app.callback(
>     Output(component_id='graphs',component_property = 'children'),
>     [Input(component_id='table',component_property ='data')])
> def update_graph(rows):
>     dff = pd.DataFrame(rows)
>     return html.Div(
>         [
>             dcc.Graph(
>                 id=column,
>                 figure={
>                     'data': [
>                         {
>                             'x': dff['month'],
>                             'y': dff[column] if column in dff else [],
>                             'type': 'bar',
>                             'marker': {'colour','#0074D9'},
>                         }
>                     ],
>                     
>                     'layout': {
>                         'xaxis': {'automargin': True},
>                         'yaxis': {'automargin': True},
>                         'height': 250,
>                         'margin': {'t':10, 'l': 10, 'r': 10},
>                     },
>                 },
>             )
>             for column in ['SumOrder']           ## one graph for each column 
>         ]
>     )
> 
> 
> 
> if __name__ == '__main__': 
>     app.run_server(debug=True)

PS:我现在看到它说对象是一个集合。我不明白它怎么会认为它是一个集合,我什至尝试将图形语法包装在 dict() 周围,它仍然给出相同的结果。

标签: pythonpandasplotly-dash

解决方案


推荐阅读