简体   繁体   English

Altair mark_text position 基于条件

[英]Altair mark_text position based on condition

在此处输入图像描述

I have this chart with mark_text to denote the value of each bar.我有这个带有mark_text的图表来表示每个条的值。 There is a slider to go back/forth in time.有一个 slider 到 go 及时来回。 I would like to change the positioning of the text (the numbers in the bars) depending on the Y value: if it is larger than a certain value, it will go just below the top of the bar ( baseline='line-top ), otherwise just above ( baseline='line-bottom' ).我想根据Y值更改文本的位置(条中的数字):如果它大于某个值,它将 go 就在条的顶部下方( baseline='line-top ) ,否则就在上面( baseline='line-bottom' )。 This way, as I slide through time the text will appear below/above the top of the bar depending on Y .这样,当我滑过时间时,文本将根据Y出现在栏顶部的下方/上方。

I think I have to use some condition or expression, but I cannot figure out how.我想我必须使用一些条件或表达式,但我不知道如何。

import numpy as np
import pandas as pd
import altair as alt

np.random.seed(0)

################################################################################

model_keys = ['M1', 'M2']
scene_keys = ['S1', 'S2']
layer_keys = ['L1', 'L2']
time_keys = [1, 2, 3]

ys = []
models = []
dataset = []
layers = []
scenes = []
times = []

for sc in scene_keys:
    for m in model_keys:
        for l in layer_keys:
            for s in range(10):
                y = np.random.rand(10) / 10
                if m == 'M1':
                    y *= 10
                if l == 'L1':
                    y *= 5
                for t in time_keys:
                    y += 1

                    ys += list(y)
                    scenes += [sc] * len(y)
                    models += [m] * len(y)
                    layers += [l] * len(y)
                    times += [t] * len(y)


# ------------------------------------------------------------------------------

df = pd.DataFrame({'Y': ys,
                   'Model': models,
                   'Layer': layers,
                   'Scenes': scenes,
                   'Time': times})

bars = alt.Chart(df, width=100, height=90).mark_bar().encode(
    x=alt.X('Scenes:N',
        title=None,
        axis=alt.Axis(
            grid=False,
            title=None,
            labels=False,
        ),
    ),
    y=alt.Y('Y:Q',
        aggregate='mean',
        axis=alt.Axis(
            grid=True,
            title='Y',
            titleFontWeight='normal',
        ),
    ),
)

text = alt.Chart(df).mark_text(align='center',
    baseline='line-top',
    color='black',
    dy=5,
    fontSize=13,
).encode(
    x=alt.X('Scenes:N'),
    y=alt.Y('mean(Y):Q'),
    text=alt.Text('mean(Y):Q',
        format='.1f'
    ),
)

bars = bars + text

bars = bars.facet(
    row=alt.Row('Model:N',
        title=None,
    ),
    column=alt.Column('Layer:N',
        title=None,
    ),
    spacing={"row": 10, "column": 10},
)

slider = alt.binding_range(
    min=1,
    max=3,
    step=1,
    name='Time',
)
selector = alt.selection_single(
    name='Selector',
    fields=['Time'],
    bind=slider,
    init={'Time': 3}, 
)
bars = bars.add_selection(
    selector
).transform_filter(
    selector
)

bars.save('test.html')

Unfortunately, you can only use conditions with encodings, not (yet) with mark properties, so there is no way to dynamically change the text baseline.不幸的是,您只能将条件与编码一起使用,而不是(还)与标记属性一起使用,因此无法动态更改文本基线。 However, you could add a second text mark that is above the bars and then set the opacity encoding of both text marks to depend on the value of y, so that it is either the text above or below the bar that is visible.但是,您可以在条形上方添加第二个文本标记,然后将两个文本标记的不透明度编码设置为取决于 y 的值,以便条形上方或下方的文本可见。

I believe you need to do the calculation of the average y value in a separate transform, so that you can access the column name in the conditional expression.我相信您需要在单独的转换中计算平均 y 值,以便您可以访问条件表达式中的列名。 I also believe that the charts need to build on each other to filter correctly when the Time value changes, but I am not 100% on either of these two points.我还认为,当时间值发生变化时,图表需要相互构建才能正确过滤,但我对这两点中的任何一点都不是 100%。

Taking the above into consideration, something like this would work:考虑到上述情况,这样的事情会起作用:

bars = alt.Chart(df, width=100, height=90).mark_bar().transform_aggregate(
    y_mean = 'mean(Y)',
    groupby=['Scenes']
).encode(
    x=alt.X(
        'Scenes:N',
        title=None,
        axis=alt.Axis(
            grid=False,
            title=None,
            labels=False,
        ),
    ),
    y=alt.Y(
        'y_mean:Q',
        axis=alt.Axis(
            grid=True,
            title='Y',
            titleFontWeight='normal',
        ),
    ),
)

text = bars.mark_text(
    align='center',
    baseline='line-top',
    color='black',
    dy=5,
    fontSize=13,
).encode(
    text=alt.Text( 'y_mean:Q', format='.1f'),
    opacity=alt.condition( 'datum.y_mean < 3', alt.value(1), alt.value(0))
)

text2 = text.mark_text(
    align='center',
    baseline='line-bottom',
    fontSize=13,
).encode(
    opacity=alt.condition( 'datum.y_mean >= 3', alt.value(1), alt.value(0))
)

bars = bars + text + text2

bars = bars.facet(
    row=alt.Row('Model:N', title=None),
    column=alt.Column('Layer:N', title=None),
    spacing={"row": 10, "column": 10},
)

slider = alt.binding_range(
    min=1,
    max=3,
    step=1,
    name='Time',
)
selector = alt.selection_single(
    name='Selector',
    fields=['Time'],
    bind=slider,
    init={'Time': 2}, 
)
bars = bars.add_selection(
    selector
).transform_filter(
    selector
)
bars

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM