Altair:基于列表成员关系设置轴标签颜色条件
解决调度图冲突设备Y轴标签高亮问题
方法一:给数据新增标记列(推荐)
在DataFrame中添加一列标记设备是否存在冲突,之后直接在Altair的条件判断中引用该列:
import streamlit as st import altair as alt import pandas as pd data = [ {"task": "Task 1", "start": 1, "finish": 10, "equipment": "XXX-101"}, {"task": "Task 2", "start": 9, "finish": 20, "equipment": "XXX-101"}, {"task": "Task 3", "start": 6, "finish": 8, "equipment": "XXX-102"}, {"task": "Task 4", "start": 9, "finish": 12, "equipment": "XXX-102"}, {"task": "Task 5", "start": 13, "finish": 18, "equipment": "XXX-102"}, {"task": "Task 6", "start": 5, "finish": 15, "equipment": "XXX-103"}, {"task": "Task 7", "start": 16, "finish": 18, "equipment": "XXX-103"}, {"task": "Task 8", "start": 6, "finish": 8, "equipment": "XXX-104"}, {"task": "Task 9", "start": 4, "finish": 12, "equipment": "XXX-104"}, {"task": "Task 10", "start": 11, "finish": 16, "equipment": "XXX-104"}, ] dataframe = pd.DataFrame(data) equipment_has_clashes = ["XXX-101", "XXX-104"] # 新增列标记是否有冲突 dataframe['has_clash'] = dataframe['equipment'].isin(equipment_has_clashes) chart = ( alt.Chart(dataframe) .mark_bar(height=20) .encode( x=alt.X("start").title("time"), x2=("finish"), y=alt.Y( "equipment", axis=alt.Axis( labelColor=alt.condition( # 直接引用新增的标记列 'datum.has_clash', if_true=alt.value("red"), if_false=alt.value("black"), ) ), ), tooltip=[alt.Tooltip(t) for t in ["task", "equipment", "start", "finish"]], color=alt.Color("task", type="nominal").scale(scheme="category20"), ) .properties(height=alt.Step(30)) .configure_view(strokeWidth=1) .configure_axis(domain=True, grid=True) .interactive() ) st.altair_chart(chart, use_container_width=True)
方法二:直接在Altair表达式中判断成员关系
如果不想修改原始数据,可以用Vega-Lite的表达式语法,通过indexof函数检查设备是否在冲突列表中(返回值不等于-1则表示存在):
import streamlit as st import altair as alt import pandas as pd data = [ {"task": "Task 1", "start": 1, "finish": 10, "equipment": "XXX-101"}, {"task": "Task 2", "start": 9, "finish": 20, "equipment": "XXX-101"}, {"task": "Task 3", "start": 6, "finish": 8, "equipment": "XXX-102"}, {"task": "Task 4", "start": 9, "finish": 12, "equipment": "XXX-102"}, {"task": "Task 5", "start": 13, "finish": 18, "equipment": "XXX-102"}, {"task": "Task 6", "start": 5, "finish": 15, "equipment": "XXX-103"}, {"task": "Task 7", "start": 16, "finish": 18, "equipment": "XXX-103"}, {"task": "Task 8", "start": 6, "finish": 8, "equipment": "XXX-104"}, {"task": "Task 9", "start": 4, "finish": 12, "equipment": "XXX-104"}, {"task": "Task 10", "start": 11, "finish": 16, "equipment": "XXX-104"}, ] dataframe = pd.DataFrame(data) equipment_has_clashes = ["XXX-101", "XXX-104"] chart = ( alt.Chart(dataframe) .mark_bar(height=20) .encode( x=alt.X("start").title("time"), x2=("finish"), y=alt.Y( "equipment", axis=alt.Axis( labelColor=alt.condition( # 使用Vega-Lite表达式判断成员关系 f"indexof({equipment_has_clashes}, datum.equipment) !== -1", if_true=alt.value("red"), if_false=alt.value("black"), ) ), ), tooltip=[alt.Tooltip(t) for t in ["task", "equipment", "start", "finish"]], color=alt.Color("task", type="nominal").scale(scheme="category20"), ) .properties(height=alt.Step(30)) .configure_view(strokeWidth=1) .configure_axis(domain=True, grid=True) .interactive() ) st.altair_chart(chart, use_container_width=True)
说明
- 方法一逻辑清晰,便于后续扩展(比如基于该列做其他可视化调整),且不需要熟悉Vega-Lite的表达式语法。
- 方法二无需修改原始数据,但要遵循Vega-Lite的表达式规则:
indexof函数第一个参数是数组,第二个是要查找的元素,返回索引位置,不存在则返回-1。
内容的提问来源于stack exchange,提问作者multipitch
相关产品推荐
相关产品推荐

