在 Altair 中绘制指示分组堆叠条形图净值的标记

Plot markers indicating the net value for a grouped, stacked bar chart in Altair

我有一个分组堆叠条形图here, Python using the Altair package, with positive and negative values. How can I plot a marker to indicate the Net value for each bar? A plot here使用红线标记,但菱形或点也可以。

df1=pd.DataFrame(10*np.random.rand(4,3),index=["A","B","C","D"],columns=["I","J","K"])
print(df1)
df2=pd.DataFrame(-10*np.random.rand(4,3),index=["A","B","C","D"],columns=["I","J","K"])
df3=pd.DataFrame(10*np.random.rand(4,3),index=["A","B","C","D"],columns=["I","J","K"])

def prep_df(df, name):
    df = df.stack().reset_index()
    df.columns = ['c1', 'c2', 'values']
    df['DF'] = name
    return df

df1 = prep_df(df1, 'DF1')
df2 = prep_df(df2, 'DF2')
df3 = prep_df(df3, 'DF3')

df = pd.concat([df1, df2, df3])

alt.Chart(df).mark_bar().encode(

    # tell Altair which field to group columns on
    x=alt.X('c2:N', title=None),

    # tell Altair which field to use as Y values and how to calculate
    y=alt.Y('sum(values):Q',
        axis=alt.Axis(
            grid=False,
            title=None)),

    # tell Altair which field to use to use as the set of columns to be  represented in each group
    column=alt.Column('c1:N', title=None),

    # tell Altair which field to use for color segmentation
    color=alt.Color('DF:N',
            scale=alt.Scale(
                # make it look pretty with an enjoyable color pallet
                range=['#96ceb4', '#ffcc5c','#ff6f69'],
            ),
        ))\
    .configure_view(
        # remove grid lines around column clusters
        strokeOpacity=0
    )

我试过在单独的 df 中计算“Net”,然后做类似的事情:

tick = alt.Chart(source).mark_tick(
    color='red',
    thickness=2,
    size=40 * 0.9,  # controls width of tick.
).encode(
    x=alt.X('c2:N', title=None),
    y=alt.Y('Net')
)

但错误是:'Net' 未定义

无需预先计算总和; Altair 可以直接做到这一点。这里的技巧是多面图表不能分层,所以你必须改为分面分层图表:

base = alt.Chart(df).encode(
    x=alt.X('c2:N', title=None),
    y=alt.Y('sum(values):Q',
        axis=alt.Axis(
            grid=False,
            title=None)),
)

bars = base.mark_bar().encode(
    color=alt.Color('DF:N',
            scale=alt.Scale(
                range=['#96ceb4', '#ffcc5c','#ff6f69'],
            ),
        )
)

net = base.mark_tick(
    color='red',
    thickness=2,
    size=18,
)

alt.layer(bars, net).facet(
    column=alt.Column('c1:N', title=None)
).configure_view(
    strokeOpacity=0
)