如何在Streamlit应用中获取Plotly平行坐标图的选中数据?
如何在Streamlit中实现Plotly平行坐标图选中数据实时展示表格?
核心问题说明
Streamlit原生的st.plotly_chart无法直接捕获Plotly图表的选中事件数据,因其交互模型不支持类似Dash的前端回调,需通过自定义HTML嵌入的方式实现数据交互。
解决方案:自定义HTML嵌入Plotly图表并捕获选中事件
通过streamlit.components.v1.html嵌入Plotly图表,用JavaScript监听图表的select事件,将选中的行索引传回Streamlit,再筛选对应数据展示。
完整代码示例:
import plotly.express as px import streamlit as st from streamlit.components.v1 import html import json # 加载数据 df = px.data.iris() # 创建平行坐标图 fig = px.parallel_coordinates( df, color="species_id", dimensions=['sepal_width', 'sepal_length', 'petal_width', 'petal_length'], color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2 ) # 将图表转为JSON格式 fig_json = fig.to_json() # 自定义HTML模板,包含JS监听选中事件 html_template = f""" <script src="https://cdn.plot.ly/plotly-latest.min.js"></script> <div id="plot"></div> <script> const fig = JSON.parse('{fig_json}'); Plotly.newPlot('plot', fig.data, fig.layout); // 监听选中事件 document.getElementById('plot').on('plotly_selected', function(eventData) {{ if (eventData.points.length > 0) {{ // 获取选中的行索引 const selectedIndices = eventData.points.map(point => point.pointNumber); // 将数据发送回Streamlit window.parent.postMessage({{ type: 'plotly_selection', data: selectedIndices }}, '*'); }} }}); </script> """ # 嵌入图表 html(html_template, height=600) # 接收前端传来的选中数据 selected_indices = st.session_state.get('selected_indices', []) # 展示选中的数据表格 if selected_indices: selected_df = df.iloc[selected_indices] st.subheader("选中的数据") st.dataframe(selected_df) else: st.subheader("未选中任何数据") # 使用JS回调捕获前端消息 st.components.v1.html(""" <script> window.addEventListener('message', function(event) { if (event.data.type === 'plotly_selection') { // 将数据存入Streamlit的session state window.parent.document.querySelector('iframe').contentWindow.postMessage({{ type: 'streamlit:setSessionState', key: 'selected_indices', value: event.data.data }}, '*'); // 触发Streamlit重新运行 window.parent.document.querySelector('.stButton button').click(); } }); </script> """, height=0) # 隐藏的按钮用于触发重新运行(不可见) with st.container(): st.markdown("<style>.stButton {visibility: hidden;}</style>", unsafe_allow_html=True) if st.button('Refresh'): pass
替代方案:使用Altair实现交互
如果不想自定义HTML,Altair是更适配Streamlit的可视化库,原生支持选择交互,无需额外JS代码:
import altair as alt import streamlit as st from vega_datasets import data df = data.iris() # 创建平行坐标图和选择器 brush = alt.selection_interval() chart = alt.Chart(df).transform_window( index='count()' ).transform_fold( ['sepalLength', 'sepalWidth', 'petalLength', 'petalWidth'], as_=['dimension', 'value'] ).mark_line().encode( x='dimension:N', y='value:Q', color='species:N', detail='index:N', opacity=alt.condition(brush, alt.value(1), alt.value(0.1)) ).add_params(brush) # 展示图表 st.altair_chart(chart, use_container_width=True) # 展示选中的数据 selected_df = df.iloc[brush['index']] if brush['index'] else df st.dataframe(selected_df)
内容的提问来源于stack exchange,提问作者Mauro
相关产品推荐
相关产品推荐

