Python绘声绘色地破折号:。。返回了不可JSON序列化的值(InvalidCallbackReturnValue)
我几天前才发现Dash。我通过观看一些youtube视频开始学习,并遵循以下示例代码: 特别是最后一部分:用图形连接后端分页 我试图将其应用于我自己的数据,保持代码框架完全相同,我得到: dash.exceptions.InvalidCallbackReturnValue:componentPython绘声绘色地破折号:。。返回了不可JSON序列化的值(InvalidCallbackReturnValue),python,pandas,plotly-dash,Python,Pandas,Plotly Dash,我几天前才发现Dash。我通过观看一些youtube视频开始学习,并遵循以下示例代码: 特别是最后一部分:用图形连接后端分页 我试图将其应用于我自己的数据,保持代码框架完全相同,我得到: dash.exceptions.InvalidCallbackReturnValue:componentgraphs的属性子项的回调返回了一个不可JSON序列化的值。通常,破折号属性只能是破折号组件、字符串、字典、数字、无或这些属性的列表 在用户与数据表交互时更新图形的回调中,函数遍历dataframe的列,并
graphs
的属性子项的回调返回了一个不可JSON序列化的值。通常,破折号属性只能是破折号组件、字符串、字典、数字、无或这些属性的列表
在用户与数据表交互时更新图形的回调中,函数遍历dataframe的列,并为每个列绘制一个图。然而,在我的例子中,只有一列需要迭代,因此输出应该只有一个图形(尽管我保持了代码格式不变,但我认为这并不重要)。这是我能想到的示例代码和我的代码之间的唯一区别
我已经研究了三天了,我不知道我的错误在哪里。我以前从未制作过web应用程序,所以也不知道如何调试。看起来我输出了一个错误的图形,但是我如何找出输出的样子,以了解为什么它是错误的
我正在绘制的数据帧的结构是:
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)
#数据上传和预处理
>data=pd.read\u excel('Test.xls')
>##为月份和年份制作列
>数据['Date Order']=data['Date Order'].apply(dateutil.parser.parse,dayfirst=True)
>数据['month']=数据['Date Order'].dt.month.aType('category'))
>数据['year']=数据['Date Order'].dt.year.aType('category'))
>数据['date']=数据['date Order'].dt.date.aType('category'))
>##按月份和年份分组,找出每个月份和年份的平均值
>groupedMonthYear=data.groupby(['month','year'])
>df=groupedMonthYear.agg({'SumOrder':'mean'}).fillna(0).reset_index()
>app=dash.dash(_名称__)
>
>页面大小=5
>
>从日期时间导入日期时间作为dt
>
>app.layout=html.Div(
>className='行',
>儿童=[
>html.H1(“数据可视化”),
>
>html.Br(),
>
>html.Div(
>dash_table.DataTable(
>id='表',
>列=[
>{'name':i,'id':i}表示已排序(df.columns)中的i,
>页面_当前=0,
>页面大小=20,
>page_action='自定义',
>
>筛选器_操作='自定义',
>过滤器\查询=“”,
>
>排序操作='自定义',
>排序模式='multi',
>排序依据=[]
> ),
>样式={'height':750,'overflowY':'scroll'},
>className=‘三列’
> ),
>
>html.Br(),
>
>html.Div(
>id='图形',
>className='一列'
> ),
> ]
> )
>
>
>
>运算符=[['ge','>='],
>[乐],'',
>['ne','!='],
>['eq','='],
>['contains'],
>['datestartswith']]
>
>
>def分离过滤器部件(过滤器部件):
>对于运算符\输入运算符:
>对于运算符类型中的运算符:
>如果筛选器_部分中的运算符:
>name\u part,value\u part=filter\u part.split(运算符,1)
>name=name_part[name_part.find('{')+1:name_part.rfind('}')]
>
>value\u part=value\u part.strip()
>v0=值\u部分[0]
>如果(v0==值_部分[-1]和v0 in(“'”,“'”,“'”):
>值=值\u零件[1:-1]。替换('\\'+v0,v0)
>其他:
>尝试:
>值=浮动(值\u部分)
>除值错误外:
>值=零件的值
>
>#单词运算符在筛选器字符串中的后面需要空格,
>#但我们以后不要这些
>返回名称,运算符类型[0]。strip(),值
>返回[无]*3
>
>
>
>@app.callback(
>输出(组件_id='table',组件_属性='data'),
>[输入(组件id='table',组件属性='当前页面'),
>输入(component_id='table',component_属性='page_size'),
>输入(component_id='table',component_属性='sort_by'),
>输入(组件\u id='table',组件\u属性='filter\u query')
> ])
>def update_表格(当前页面、页面大小、排序依据、过滤器):
>filtering_expressions=filter.split(“&&”)
>dff=df##用数据创建一个新变量,这样我们就不会更改原始数据
>对于筛选\表达式中的筛选\部分:
>##提取我们需要了解的有关刚刚应用的过滤器的所有信息
>列名称、运算符、筛选器值=拆分筛选器部分(筛选器部分)
>
>如果运算符位于('eq'、'ne'、'lt'、'le'、'gt'、'ge'):
>#这些运算符与系列运算符方法名称匹配
>dff=dff.loc[dff[col\u name].str.contains(filter\u value)]
>elif运算符=='contains':
>dff=dff.loc[dff[col\u name].str.contains(filter\u value)]
>elif运算符=='datestartswith':
>#这是前端过滤逻辑的简化,
>#仅适用于wi