Python Plotly交互式图表:将下拉菜单替换为可搜索输入框
问题描述
我想将现有Plotly交互式图表的下拉按钮替换为输入框,实现输入项目名称前缀即可搜索并选择对应选项的功能。目前我已实现支持单选单个项目或全选所有项目的下拉菜单,但希望用户可以通过输入项目名称关键词,点击选择对应项目即可展示对应的图表。我刚接触Plotly,非常感谢大家提供的任何建议。
当前实现的效果图如下:
我的代码如下:
def interactive_multi_plot(actual, forecast_1, forecast_2, title, addAll = True): fig = go.Figure() for column in forecast_1.columns.to_list(): fig.add_trace( go.Scatter( x = forecast_1.index, y = forecast_1[column], name = "Forecast_SI" ) ) button_all = dict(label = 'All', method = 'update', args = [{'visible': forecast_1.columns.isin(forecast_1.columns), 'title': 'All', 'showlegend':True}]) for column in forecast_2.columns.to_list(): fig.add_trace( go.Scatter( x = forecast_2.index, y = forecast_2[column], name = "Forecast_LSTM" ) ) button_all = dict(label = 'All', method = 'update', args = [{'visible': forecast_2.columns.isin(forecast_2.columns), 'title': 'All', 'showlegend':True}]) for column in actual.columns.to_list(): fig.add_trace( go.Scatter( x = actual.index, y = actual[column], name = "True values" ) ) button_all = dict(label = 'All', method = 'update', args = [{'visible': actual.columns.isin(actual.columns), 'title': 'All', 'showlegend':True}]) fig.layout.plot_bgcolor = '#010028' fig.layout.paper_bgcolor = '#010028' def create_layout_button(column): return dict(label = column, method = 'update', args = [{'visible': actual.columns.isin([column]), 'title': column, 'showlegend': True}]) fig.update_layout( updatemenus=[go.layout.Updatemenu( active = 0, buttons = ([button_all] * addAll) + list(actual.columns.map(lambda column: create_layout_button(column))) ) ] ) # Update remaining layout properties fig.update_layout( title_text=title, height=800, font = dict(color='#fff', size=12) ) fig.show()
运行后得到的错误提示如下:
解决方案
原生Plotly的内置下拉菜单不支持搜索功能,要实现输入关键词搜索选择项目的效果,有两种可行方案:
方案1:使用Dash框架实现(推荐,可完美满足搜索需求)
Dash是Plotly官方推出的交互式Web应用框架,自带支持搜索的下拉选择组件,无需复杂开发即可实现需求:
- 直接使用
dcc.Dropdown组件,默认开启搜索匹配功能,输入关键词会自动过滤匹配的项目选项 - 通过组件回调绑定选中值,动态更新图表展示内容即可
核心示例代码:
import dash from dash import dcc, html, Input, Output import plotly.graph_objects as go # 初始化应用 app = dash.Dash(__name__) project_list = actual.columns.tolist() # 页面布局 app.layout = html.Div([ dcc.Dropdown( id='project-selector', options=[{'label': '所有项目', 'value': 'all'}] + [{'label': p, 'value': p} for p in project_list], value='all', placeholder='输入项目名称关键词搜索', searchable=True ), dcc.Graph(id='forecast-graph') ]) # 回调更新图表 @app.callback( Output('forecast-graph', 'figure'), Input('project-selector', 'value') ) def update_graph(selected_project): fig = go.Figure() if selected_project == 'all': # 渲染所有项目的3类数据 for col in actual.columns: fig.add_trace(go.Scatter(x=actual.index, y=actual[col], name='真实值')) fig.add_trace(go.Scatter(x=forecast_1.index, y=forecast_1[col], name='Forecast_SI')) fig.add_trace(go.Scatter(x=forecast_2.index, y=forecast_2[col], name='Forecast_LSTM')) fig.update_layout(title='所有项目预测对比') else: # 仅渲染选中项目的3类数据 fig.add_trace(go.Scatter(x=actual.index, y=actual[selected_project], name='真实值')) fig.add_trace(go.Scatter(x=forecast_1.index, y=forecast_1[selected_project], name='Forecast_SI')) fig.add_trace(go.Scatter(x=forecast_2.index, y=forecast_2[selected_project], name='Forecast_LSTM')) fig.update_layout(title=f'{selected_project} 预测对比') # 复用原有样式配置 fig.update_layout( plot_bgcolor='#010028', paper_bgcolor='#010028', font = dict(color='#fff', size=12), height=800 ) return fig if __name__ == '__main__': app.run_server(debug=True)
方案2:修复现有代码报错(仅保留下拉选择功能,不支持搜索)
现有代码的报错是因为button_all在循环中被多次覆盖,生成的visible数组长度和实际trace数量不匹配导致,每个项目对应3条trace(真实值、SI预测、LSTM预测),需要调整visible数组的生成逻辑:
def interactive_multi_plot(actual, forecast_1, forecast_2, title, addAll = True): fig = go.Figure() total_cols = len(actual.columns) # 先按项目维度添加trace,保证每个项目的3条trace连续 for idx, column in enumerate(actual.columns): fig.add_trace( go.Scatter(x=actual.index, y=actual[column], name="True values") ) fig.add_trace( go.Scatter(x=forecast_1.index, y=forecast_1[column], name="Forecast_SI") ) fig.add_trace( go.Scatter(x=forecast_2.index, y=forecast_2[column], name="Forecast_LSTM") ) # 构造全选按钮 button_all = dict(label = 'All', method = 'update', args = [{'visible': [True]*3*total_cols, 'title': 'All', 'showlegend':True}]) # 构造单个项目按钮 def create_layout_button(column, col_index): visible = [False] * 3 * total_cols visible[col_index*3] = True visible[col_index*3 + 1] = True visible[col_index*3 + 2] = True return dict(label = column, method = 'update', args = [{'visible': visible, 'title': column, 'showlegend': True}]) # 更新布局 fig.update_layout( updatemenus=[go.layout.Updatemenu( active = 0, buttons = ([button_all] * addAll) + [create_layout_button(col, i) for i, col in enumerate(actual.columns)] ) ], plot_bgcolor = '#010028', paper_bgcolor = '#010028', title_text=title, height=800, font = dict(color='#fff', size=12) ) fig.show()
内容的提问来源于stack exchange,提问作者Starry Night
相关产品推荐
相关产品推荐

