基于过滤的Altair图表数据计算新序列

1 投票
1 回答
40 浏览
提问于 2025-04-14 16:39

我有一个时间序列的数据框,想用它来制作一个热图(显示最近一次观察中两个相关值之间的差异)和两个折线图——一个显示这些序列本身,另一个显示序列之间的差异。我已经能做出前两个图表,但我不知道怎么实时计算这两个选定序列之间的差距(使用 .selection_point())。下面是一些代码片段。

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

ex_ts = pd.DataFrame(
    np.random.random((10, 5)),
    columns=['a', 'b', 'c', 'd', 'e'],
    index=(
        pd.date_range(
            start=pd.to_datetime('today')-pd.Timedelta(9, unit='D'), 
            end=pd.to_datetime('today')).strftime('%Y-%m-%d')
        )
)

ex_ts_long = ex_ts.stack().reset_index().set_axis(
    ['date', 'category', 'diff'],
    axis=1
).assign(
    x = lambda a: a['category'],
    y = lambda a: a['category']
)

print(ex_ts_long.head())

#          date category      diff  x  y
# 0  2024-03-03        a  0.910670  a  a
# 1  2024-03-03        b  0.608069  b  b
# 2  2024-03-03        c  0.797001  c  c
# 3  2024-03-03        d  0.139386  d  d
# 4  2024-03-03        e  0.147499  e  e

def get_last_diff(i):
    return ex_ts.sub(ex_ts.iloc[:,i], axis=0).iloc[-1,:]

ex_z = pd.concat(
    [get_last_diff(i) for i in np.arange(0, 5)],
    axis=1
).set_axis(
    ex_ts.columns,
    axis=1).stack().reset_index().set_axis(
    ['x', 'y', 'diff'],
    axis=1
).round(2)

print(ex_z.head())

#    x  y  diff
# 0  a  a  0.00
# 1  a  b -0.29
# 2  a  c -0.16
# 3  a  d -0.27
# 4  a  e -0.38

select_x = alt.selection_point(fields=['x'], name='select_x')
select_y = alt.selection_point(fields=['y'], name='select_y')

base = alt.Chart(ex_z).encode(
    x='x',
    y='y',
    color='diff'
).add_params(
    select_x
).add_params(
    select_y
).properties(
    width=500,
    height=500
)

hmap = base.mark_rect()
text = base.mark_text(fontWeight='bold').encode(
    text='diff',
    color=alt.value('red')
)
hmap_chart = (hmap + text)

line_1 = alt.Chart(ex_ts_long).mark_line().encode(
    x='date',
    y='diff',
    color='category'
).transform_filter(select_x | select_y)

tmp = alt.vconcat(hmap_chart, line_1)

上面的代码可以创建一个热图,你可以点击它来过滤下面的图表。不过,问题是我想在第一个折线图中计算这两个序列之间的差异,并把它画出来。

我最有希望的尝试是通过把两个过滤后的图表加在一起,来创建一个新的图表。我在这两个过滤后的图表中进行了聚合,这样我就可以引用新的变量来创建我想要的变量,但这似乎没有成功。下面是更多的示例代码。

rhs_line1 = alt.Chart(df_long).mark_line().transform_filter(
    select_y
).transform_aggregate(
    agg_y = 'sum(spread)',
    groupby=['date']
).encode(x='date:T', y='agg_y:Q')

rhs_line2 = alt.Chart(df_long).mark_line().transform_filter(
    select_x
).transform_aggregate(
    agg_x = 'sum(spread)',
    groupby=['date']
).encode(
    x='date:T',
    y='agg_x:Q'
)

rhs_line =(rhs_line1 + rhs_line1).transform_calculate(
    spread = 'datum.agg_y - datum.agg_x'
).encode(
    x='date:T',
    y='spread:Q'
)

final = alt.vconcat(hmap_chart, alt.hconcat(line_1, rhs_line))

1 个回答

0

当涉及到数据转换时,弄清楚数据发生了什么可能会比较棘手。使用 .transformed_data() 方法查看图表中的数据,或者点击右上角的三个点菜单,选择“在 Vega 编辑器中打开图表”,然后在“数据查看器”标签中查看数据如何随着你的选择而变化,这样会非常有帮助。

通过这样做,我发现你的情况中 spread 实际上是未定义的,因为分层图表的数据没有合并在一起;相反,有两个数据框,一个包含 agg_y,另一个包含 agg_x,所以在计算转换中无法进行 agg_x - agg_y 的操作。我不确定是否有办法将它们合并,但无论如何,由于你的数据结构,我觉得你不应该这样处理。

既然你想要相减两个列,我觉得更简单的方法是先转换数据框,然后根据选择动态识别要相减的列,你可以这样做:

dynamic_title = alt.Title(alt.expr(f'"Difference between " + {select_x.name}.x + " and " + {select_y.name}.y'))
rhs_line = alt.Chart(ex_ts_long, title=dynamic_title).transform_pivot(
    'category', 'diff', groupby=['date']
).transform_calculate(
    spread = f'datum[{select_x.name}.x] - datum[{select_y.name}.y]'
).mark_line(color='grey').encode(
    x='date',
    y=alt.Y('spread:Q').scale(domain=(-1, 1)),
)

alt.vconcat(
    hmap_chart,
    alt.hconcat(line_1, rhs_line).resolve_scale(color='independent')
)

在这里输入图片描述

撰写回答