python - 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() 周围,它仍然给出相同的结果。
解决方案
推荐阅读
- python - 为什么我通过 RPAframework 移动鼠标时出现错误
- ionic-framework - 通过下载它们在 Ionic App 中使用多个证书
- reactjs - 使用 formick 验证模式自动完成材料 UI 字段验证
- xml - 使用 XSLT 读取空 XML 元素的属性
- adaptive-cards - 为什么最新版本的自适应卡不再可用于 SPFX 集成?
- python - 如何处理错误处理代码中的错误?
- r - “runjags”R 包run.jags 函数并行方法。并行方法使用多少个内核?
- graphql - 对 GraphQL 查询进行排序
- javascript - JS 使用 map() 获取键名
- image - Parquet 可以用来存储图片吗?有什么好处吗?