You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取Streamlit中Vega-Altair图表的选中数据点?

在Streamlit中获取Vega-Altair框选数据的解决方案

问题描述

我在Streamlit里用Vega-Altair绘图,已经通过box_selection = alt.selection_interval(name="interval")实现了框选数据点的功能,但没法获取选中的数据做后续处理。在Jupyter Notebook里用alt.JupyterChart能拿到选中数据,但在Streamlit里这个方法没法保留图表状态,试过用st.session_state存图表状态,但图表更新时session_state不会动态同步,需要合适的桥接方案。

附上完整图表生成代码:

import altair as alt
import streamlit as st

def generate_chart(df,xcol,ycol,lbl='off',color='basic',title='', tooltip=['DataPoint','Ccontent']):
  
  legendSelection = alt.selection_point(fields=['Data'], bind='legend')
  zoompan = alt.selection_interval(bind='scales',
                                          # on="[mousedown[event.altKey], mouseup] > mousemove",
                                          translate="[mousedown[event.altKey], mouseup] > mousemove!",
                                          zoom="wheel![event.altKey]"
                                          )
  # Box selection for highlighting or filtering data points
  box_selection = alt.selection_interval(name="interval")
  
  chart = alt.Chart(df).mark_circle(size=150, stroke='#666', strokeWidth=0.8, opacity=1).encode(
    x=
    alt.X(xcol,
        scale=alt.Scale(zero=False),
        axis=alt.Axis(labels=False, ticks=False, domain=False)
    ),

    y=
    alt.Y(ycol,
        scale=alt.Scale(zero=False),
        axis=alt.Axis(labels=False, ticks=False, domain=False)
    ),

    color=alt.condition(box_selection, 'Data:N', alt.value('lightgray'),  # Update color based on selection
                          legend=alt.Legend(columns=1, symbolLimit=0, labelFontSize=14)),        
    tooltip=tooltip,
    opacity=alt.condition(legendSelection, alt.value(1), alt.value(0.2)),  # Use selection to control opacity
    ).add_params(
    box_selection, zoompan, legendSelection
    )
  
  if lbl == 'on':
    text = chart.mark_text(align='left', baseline='middle',dx=15, size=13,color='black').encode(text=tooltip[0], color= alt.value('black'))
  else:
    text = chart.mark_text(align='left', baseline='middle',dx=10).encode()

  result = (chart + text).configure(background="#FFFFFF"
        ).properties(
        # width=800,
        height=700,
        title=title
       ).configure_legend(
          orient='bottom',  
          labelLimit=0,
          titleFontSize=14,
          labelFontSize=14,
          symbolStrokeWidth=0.8
         )
  jchart = alt.JupyterChart(result)
  st.session_state["jchart"] = jchart # For getting selection of points, but does not look like this will work as the object will not get dynamically updated in session state.
  return result #.interactive()

可行解决思路

1. 使用Streamlit的st.altair_chart的on_select参数(Streamlit 1.28+)

Streamlit 1.28版本后新增了对Altair图表选择事件的原生支持,直接通过on_select回调获取选中数据:

  • 替换原有的st.altair_chart(generate_chart(...))调用,添加on_select参数绑定回调函数
  • 在回调中解析选中的区间范围,直接筛选原始DataFrame得到选中数据

示例代码:

def handle_selection(selected_data):
    if selected_data and 'interval' in selected_data:
        # 解析框选的x、y轴范围
        x_range = selected_data['interval']['x']
        y_range = selected_data['interval']['y']
        # 从原始数据中筛选符合范围的记录
        selected_df = df[(df[xcol] >= x_range[0]) & (df[xcol] <= x_range[1]) & 
                        (df[ycol] >= y_range[0]) & (df[ycol] <= y_range[1])]
        st.session_state['selected_data'] = selected_df

# 生成图表后绑定回调渲染
chart = generate_chart(df, xcol='your_x_column', ycol='your_y_column')
st.altair_chart(chart, on_select=handle_selection)

# 展示选中的数据
if 'selected_data' in st.session_state:
    st.subheader('选中的数据')
    st.dataframe(st.session_state['selected_data'])

2. 利用Altair选择状态结合按钮触发筛选

如果需要手动确认选择结果,可以保留Altair的box_selection,添加按钮触发数据筛选:

  • 在图表渲染后,通过按钮点击事件,解析Altair图表的选择参数
  • 用选择参数过滤原始DataFrame,结果存入st.session_state供后续使用

3. 替换为Streamlit原生交互图表

如果对图表样式自定义要求不高,可以改用st.scatter_chart结合st.data_editor的交互能力,原生支持数据点选择与后续处理,但会损失Altair的部分样式定制功能。


内容的提问来源于stack exchange,提问作者Amit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 20:07:50