Plotly Express缩放时仅显示可见数据图例值的实现方法问询
在Plotly Express中仅显示可见数据对应的图例
可以实现,但Plotly Express默认不支持这个功能,需要通过自定义交互逻辑来动态更新图例显示。以下分两种常用场景给出解决方案:
场景1:Jupyter Notebook环境(使用FigureWidget)
利用Plotly的FigureWidget监听轴范围变化事件,实时过滤可见数据并更新图例:
import plotly.graph_objects as go import plotly.express as px import numpy as np # 模拟你的数据(替换为实际的proj_2D和topic_nums) np.random.seed(42) proj_2D = np.random.randn(500, 2) topic_nums = np.random.randint(0, 10, 500) # 生成初始散点图并转为FigureWidget fig_2d = px.scatter( proj_2D, x=0, y=1, color=topic_nums.astype(str), labels={'color': 'topic'} ) fig_2d.update_traces(marker=dict(size=5)) fig_2d.update_layout(autosize=False, width=1000, height=800) fw = go.FigureWidget(fig_2d) # 定义图例更新逻辑 def update_legend(trace, layout, fig): # 获取当前轴范围 x_min, x_max = layout.xaxis.range y_min, y_max = layout.yaxis.range # 收集可见的topic visible_topics = set() for trace in fig.data: # 检查当前trace是否有数据点在可视范围内 x_in_range = np.logical_and(trace.x >= x_min, trace.x <= x_max) y_in_range = np.logical_and(trace.y >= y_min, trace.y <= y_max) if np.any(np.logical_and(x_in_range, y_in_range)): visible_topics.add(trace.legendgroup) # 更新每个trace的图例可见性 for trace in fig.data: trace.showlegend = (trace.legendgroup in visible_topics) # 绑定轴范围变化事件 fw.layout.on_change(update_legend, 'xaxis.range', 'yaxis.range') # 显示交互式图表 fw
场景2:Web应用(使用Dash框架)
如果需要部署为Web应用,用Dash监听图表的relayoutData事件来实现动态更新:
import dash from dash import dcc, html, Input, Output, State import plotly.express as px import numpy as np import pandas as pd # 模拟数据(替换为实际数据) np.random.seed(42) proj_2D = np.random.randn(500, 2) topic_nums = np.random.randint(0, 10, 500) df = pd.DataFrame(proj_2D, columns=['x', 'y']) df['topic'] = topic_nums.astype(str) app = dash.Dash(__name__) app.layout = html.Div([ dcc.Graph(id='scatter-graph', figure={}), ]) @app.callback( Output('scatter-graph', 'figure'), Input('scatter-graph', 'relayoutData'), State('scatter-graph', 'figure') ) def update_legend(relayout_data, fig): # 初始渲染图表 if not fig: fig = px.scatter(df, x='x', y='y', color='topic', labels={'color': 'topic'}) fig.update_traces(marker=dict(size=5)) fig.update_layout(autosize=False, width=1000, height=800) return fig if not relayout_data: return fig # 获取当前轴范围(优先取relayout事件中的新范围,无则用现有范围) x_range = relayout_data.get('xaxis.range', fig['layout']['xaxis']['range']) y_range = relayout_data.get('yaxis.range', fig['layout']['yaxis']['range']) if not x_range or not y_range: return fig # 收集可见的topic visible_topics = set() for trace in fig['data']: x_arr = np.array(trace['x']) y_arr = np.array(trace['y']) in_range = np.logical_and( np.logical_and(x_arr >= x_range[0], x_arr <= x_range[1]), np.logical_and(y_arr >= y_range[0], y_arr <= y_range[1]) ) if np.any(in_range): visible_topics.add(trace['legendgroup']) # 更新图例可见性 for trace in fig['data']: trace['showlegend'] = (trace['legendgroup'] in visible_topics) return fig if __name__ == '__main__': app.run_server(debug=True)
注意事项
- 数据量较大时,建议提前按
topic分组存储数据,减少每次过滤的计算量,提升响应速度。 - 两种方案都是通过判断每个trace是否有数据点落在当前可视范围内,来控制其图例项的显示/隐藏。
内容的提问来源于stack exchange,提问作者peskysam
相关产品推荐
相关产品推荐

