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

如何在Altair中记录用户点击的Tooltip对应ID至日志或DataFrame?

提取点击国家ID并记录的解决方案

一、基于现有Altair+Streamlit的实现方案

要获取点击国家的ID并记录,需调整Altair选择器配置,并通过Streamlit回调函数捕获选择事件,具体步骤如下:

  1. 修改Altair选择器,绑定ID字段
    明确让selection_multi基于id字段选择,确保选中值为国家ID:
click = alt.selection_multi(fields=['id'], empty="none")
  1. 添加点击事件回调函数
    定义回调函数捕获选中的ID,映射为国家名称后保存到日志文件或DataFrame:
def handle_click(selected):
    if not selected or 'id' not in selected:
        return
    
    # 获取选中的国家ID列表
    selected_ids = selected['id']
    # 匹配对应的国家名称
    selected_countries = country_names[country_names['id'].isin(selected_ids)]
    
    # 初始化会话状态存储点击记录
    if 'click_log_df' not in st.session_state:
        st.session_state.click_log_df = pd.DataFrame(columns=['timestamp', 'country_id', 'country_name'])
    
    # 生成新记录
    new_records = pd.DataFrame({
        'timestamp': [pd.Timestamp.now()] * len(selected_ids),
        'country_id': selected_ids,
        'country_name': selected_countries['name'].tolist()
    })
    
    # 更新记录DataFrame
    st.session_state.click_log_df = pd.concat([st.session_state.click_log_df, new_records], ignore_index=True)
    
    # 写入日志文件(可选)
    with open('country_clicks.log', 'a', encoding='utf-8') as f:
        for _, row in new_records.iterrows():
            f.write(f"{row['timestamp']} | ID:{row['country_id']} | 国家:{row['country_name']}\n")
  1. 绑定回调到Altair图表
    调用st.altair_chart时传入on_select参数:
st.altair_chart(
    background.interactive(),
    use_container_width=False,
    on_select=handle_click
)
  1. 完整修改后的代码
import pandas as pd
import altair as alt
import streamlit as st
from vega_datasets import data


@st.cache
def get_iso_names(url: str) -> pd.DataFrame:
    return pd.read_csv(url)


# Data generators for the background
sphere = alt.sphere()
graticule = alt.graticule()

# Source of land data
source = alt.topo_feature(data.world_110m.url, "countries")
iso_name_url = "https://raw.githubusercontent.com/stefangabos/world_countries/master/data/countries/en/world.csv"
country_names = get_iso_names(iso_name_url)

# 修改选择器,绑定id字段
click = alt.selection_multi(fields=['id'], empty="none")

# 点击事件回调函数
def handle_click(selected):
    if not selected or 'id' not in selected:
        return
    
    selected_ids = selected['id']
    selected_countries = country_names[country_names['id'].isin(selected_ids)]
    
    if 'click_log_df' not in st.session_state:
        st.session_state.click_log_df = pd.DataFrame(columns=['timestamp', 'country_id', 'country_name'])
    
    new_records = pd.DataFrame({
        'timestamp': [pd.Timestamp.now()] * len(selected_ids),
        'country_id': selected_ids,
        'country_name': selected_countries['name'].tolist()
    })
    
    st.session_state.click_log_df = pd.concat([st.session_state.click_log_df, new_records], ignore_index=True)
    
    # 写入日志文件
    with open('country_clicks.log', 'a', encoding='utf-8') as f:
        for _, row in new_records.iterrows():
            f.write(f"{row['timestamp']} | ID:{row['country_id']} | 国家:{row['country_name']}\n")

# Layering and configuring the components
background = (
    alt.layer(
        alt.Chart(sphere).mark_geoshape(fill="lightblue"),
        alt.Chart(graticule).mark_geoshape(stroke="white", strokeWidth=0.5),
        alt.Chart(source)
        .mark_geoshape(
            stroke="black",
            strokeWidth=0.15,
        )
        .encode(
            tooltip=[
                alt.Tooltip("name:N", title="Country"),
                # 可选:在tooltip显示ID方便调试
                # alt.Tooltip("id:Q", title="Country ID"),
            ],
            color=alt.condition(click, alt.value("firebrick"), alt.value("white")),
        )
        .transform_lookup(
            lookup="id",
            from_=alt.LookupData(country_names, "id", ["name"]),
        ),
    )
    .add_selection(click)
    .project("naturalEarth1")
    .properties(width=1200, height=800)
)


st.altair_chart(
    background.interactive(),
    use_container_width=False,
    on_select=handle_click
)

# 可选:在页面显示已记录的点击
if 'click_log_df' in st.session_state:
    st.subheader("已点击国家记录")
    st.dataframe(st.session_state.click_log_df.drop_duplicates(subset=['country_id']))

二、Plotly+Streamlit替代方案

如果觉得Altair的交互捕获不够直观,Plotly与Streamlit的交互绑定更直接,代码示例如下:

import pandas as pd
import plotly.express as px
import streamlit as st
from vega_datasets import data

@st.cache
def get_iso_names(url: str) -> pd.DataFrame:
    return pd.read_csv(url)

# 加载国家数据
iso_name_url = "https://raw.githubusercontent.com/stefangabos/world_countries/master/data/countries/en/world.csv"
country_names = get_iso_names(iso_name_url)
world_topo = data.world_110m.url

# 绘制地图
fig = px.choropleth_mapbox(
    country_names,
    geojson=world_topo,
    locations='id',
    featureidkey="properties.id",
    color='name',
    mapbox_style="carto-positron",
    zoom=1,
    center={"lat": 0, "lon": 0},
    hover_data=['id', 'name'],
    color_discrete_sequence=['white'],
    opacity=0.8
)

fig.update_traces(marker_line_width=0.15, marker_line_color='black')
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0}, width=1200, height=800)

# 点击事件处理函数
def handle_map_click(trace, points, state):
    if not points.point_inds:
        return
    
    # 获取点击的国家数据
    selected_row = country_names.iloc[points.point_inds[0]]
    selected_id = selected_row['id']
    selected_name = selected_row['name']
    
    # 初始化记录存储
    if 'click_log_df' not in st.session_state:
        st.session_state.click_log_df = pd.DataFrame(columns=['timestamp', 'country_id', 'country_name'])
    
    # 添加新记录
    new_record = pd.DataFrame({
        'timestamp': [pd.Timestamp.now()],
        'country_id': [selected_id],
        'country_name': [selected_name]
    })
    st.session_state.click_log_df = pd.concat([st.session_state.click_log_df, new_record], ignore_index=True)
    
    # 写入日志
    with open('country_clicks.log', 'a', encoding='utf-8') as f:
        f.write(f"{pd.Timestamp.now()} | ID:{selected_id} | 国家:{selected_name}\n")

# 绑定点击事件
fig.data[0].on_click(handle_map_click)

# 渲染图表
st.plotly_chart(fig, use_container_width=False)

# 显示记录
if 'click_log_df' in st.session_state:
    st.subheader("已点击国家记录")
    st.dataframe(st.session_state.click_log_df.drop_duplicates(subset=['country_id']))

内容的提问来源于stack exchange,提问作者a-doering

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 10:22:01