在 altair 中更改折线图的重叠顺序

Changing overlap order of a line chart in altair

我在 Altair 中生成折线图。我想控制哪些行在行堆栈的“顶部”。在我这里的示例中,我希望红线位于顶部(最新日期),然后下降到黄色(最旧日期)位于底部。

我试图用 alt.Colorsort 参数来控制这一点,但无论 sort='ascending'sort='descending' 行重叠的顺序都不会改变。

我该如何控制它?希望我可以在不对源数据框本身进行排序的情况下做到这一点。

data = [{'review_date': dt.date(year=2022, month=2, day=24),  'a':19, 'b':17, 'c':12, 'd':8},
{'review_date': dt.date(year=2022, month=2, day=23),  'a':20, 'b':16, 'c':14, 'd':8},
{'review_date': dt.date(year=2022, month=2, day=22),  'a':22, 'b':16, 'c':14, 'd':10},
{'review_date': dt.date(year=2022, month=2, day=21),  'a':14, 'b':13, 'c':12, 'd':5},]

df = pd.DataFrame(data).melt(id_vars=['review_date'], value_name='price', var_name='contract')
df.review_date = pd.to_datetime(df.review_date)

domain = df.review_date.unique()
range_ = ['red', 'blue', 'gray', 'yellow'] 

alt.Chart(df, title='foo').mark_line().encode(
x=alt.X('contract:N'),
y=alt.Y('price:Q',scale=alt.Scale(zero=False)),
color=alt.Color('review_date:O', sort="ascending", scale=alt.Scale(domain=domain, range=range_)   )
).interactive()

默认情况下,图形标记按照它们在数据框中出现的顺序绘制(如您所述),这意味着数据框中最后的元素将最后绘制并最终位于图表的顶部(称为最高“层”或最高“z-order”):

import pandas as pd
import altair as alt


df = pd.DataFrame({
    'a': [1, 2, 1, 2],
    'b': [1.1, 2.1, 1.0, 2.2],
    'c': ['point1', 'point1', 'point2', 'point2']
})

alt.Chart(df).mark_circle(size=1000).encode(
    x='a',
    y='b',
    color='c'
)

当您设置颜色编码的 sort 参数时,您并没有更改点的 z-order,您只是更改了为它们分配颜色的顺序。在下图中,“point2”仍在顶部,但现在是蓝色而不是橙色:

alt.Chart(df).mark_circle(size=1000).encode(
    x='a',
    y='b',
    color=alt.Color('c', sort='descending')
)

如果我们想要更改 z-ordering 以便“point1”位于顶部,我们必须使用 order 编码来指定它:

alt.Chart(df).mark_circle(size=1000).encode(
    x='a',
    y='b',
    color='c',
    order=alt.Order('c', sort='descending')
)

但是,read in the Vega-Lite documentation order 编码对堆叠标记和路径标记(包括线标记)有特殊的行为,它控制点在一条线上的连接顺序而不是他们的 z-ordering/layering.

因此,我认为实现所需行为的唯一方法是对该列进行排序。您可以在图表构建期间执行此操作:

alt.Chart(df).mark_line(size=10).encode(
    x='a',
    y='b',
    color='c'
)

alt.Chart(df.sort_values('c', ascending=False)).mark_line(size=10).encode(
    x='a',
    y='b',
    color='c'
)