Python绘声绘色地破折号:。。返回了不可JSON序列化的值(InvalidCallbackReturnValue)

Python绘声绘色地破折号:。。返回了不可JSON序列化的值(InvalidCallbackReturnValue),python,pandas,plotly-dash,Python,Pandas,Plotly Dash,我几天前才发现Dash。我通过观看一些youtube视频开始学习,并遵循以下示例代码: 特别是最后一部分:用图形连接后端分页 我试图将其应用于我自己的数据,保持代码框架完全相同,我得到: dash.exceptions.InvalidCallbackReturnValue:componentgraphs的属性子项的回调返回了一个不可JSON序列化的值。通常,破折号属性只能是破折号组件、字符串、字典、数字、无或这些属性的列表 在用户与数据表交互时更新图形的回调中,函数遍历dataframe的列,并

我几天前才发现Dash。我通过观看一些youtube视频开始学习,并遵循以下示例代码: 特别是最后一部分:用图形连接后端分页 我试图将其应用于我自己的数据,保持代码框架完全相同,我得到:

dash.exceptions.InvalidCallbackReturnValue:component
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