基于过滤的Altair图表数据计算新序列
我有一个时间序列的数据框,想用它来制作一个热图(显示最近一次观察中两个相关值之间的差异)和两个折线图——一个显示这些序列本身,另一个显示序列之间的差异。我已经能做出前两个图表,但我不知道怎么实时计算这两个选定序列之间的差距(使用 .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')
)