我有以下代码,主要完成我想要它做的事情:制作一个按值着色的散点图,以及一个链接的 DynamicMap,当您点击它时显示与每个点相关联的时间序列
import pandas as pd
import holoviews as hv
import panel as pn
from holoviews import streams
import numpy as np
hv.extension('bokeh')
df = pd.DataFrame(data = {'id':['a', 'b', 'c', 'd'], 'type':['d', 'd', 'h', 'h'], 'value':[1,2,3,4], 'x':range(4), 'y':range(4)})
points = hv.Points(data=df, kdims=['x', 'y'], vdims = ['id', 'type', 'value']).redim.range(value=(0,None))
options = hv.opts.Points(size = 10, color = 'value', tools = ['hover', 'tap'])
df_a = pd.DataFrame(data = {'id':['a']*5, 'hour':range(5), 'value':np.random.random(5)})
df_b = pd.DataFrame(data = {'id':['b']*5, 'hour':range(5), 'value':np.random.random(5)})
df_c = pd.DataFrame(data = {'id':['c']*10, 'hour':range(10), 'value':np.random.random(10)})
df_d = pd.DataFrame(data = {'id':['d']*10, 'hour':range(10), 'value':np.random.random(10)})
df_ts = pd.concat([df_a, df_b, df_c, df_d])
df_ts = df_ts.set_index(['id', 'hour'])
stream = hv.streams.Selection1D(source=points)
empty = hv.Curve(df_ts.loc['a']).opts(visible = False)
def tap_station(index):
if not index:
return empty
id = df.iloc[index[0]]['id']
return hv.Curve(df_ts.loc[id], label = str(id)).redim.range(value=(0,None))
ts_curve = hv.DynamicMap(tap_station, kdims=[], streams=[stream]).opts(framewise=True, show_grid=True)
pn.Row(points.opts(options), ts_curve)
但是,我还想做一件事:让“d”和“h”点具有不同的标记形状。
我能做的一件事是将选项行更改为:
options = hv.opts.Points(size = 10, color = 'value', tools = ['hover', 'tap'],
marker = hv.dim("type").categorize({'d': 'square', 'h':'circle'}))
但是有了这个,全息视图不会显示区分两个标记形状的图例
我能做的另一件事是这样的:
df_d = df[df['type'] == 'd']
df_h = df[df['type'] == 'h']
d_points = hv.Points(data=df_d, kdims=['x', 'y'], vdims = ['id', 'type', 'value'], label = 'd').redim.range(value=(0,None)).opts(marker = 's')
h_points = hv.Points(data=df_h, kdims=['x', 'y'], vdims = ['id', 'type', 'value'], label = 'h').redim.range(value=(0,None)).opts(marker = 'o')
d_stream = hv.streams.Selection1D(source=d_points)
h_stream = hv.streams.Selection1D(source=h_points)
这让我得到了我想要的图例,但是我不确定如何制作一个链接到这两个流的 DynamicMap,并响应对两个标记形状的点击。
再次,最终我想要的是一个具有两个标记形状的点图(基于
type
),由value
着色,通过向右拉起时间序列图来响应点击。
感谢您的帮助!
可以使用
rename()
的Stream
方法更改流名称,然后使用tap_station()
函数的两个参数来接收两个流,这里是完整代码:
import pandas as pd
import holoviews as hv
import panel as pn
from holoviews import streams
import numpy as np
hv.extension('bokeh')
df = pd.DataFrame(data = {'id':['a', 'b', 'c', 'd'], 'type':['d', 'd', 'h', 'h'], 'value':[1,2,3,4], 'x':range(4), 'y':range(4)})
points = hv.Points(data=df, kdims=['x', 'y'], vdims = ['id', 'type', 'value']).redim.range(value=(0,None))
options = hv.opts.Points(size = 10, color = 'value', tools = ['hover', 'tap'],
marker = hv.dim("type").categorize({'d': 'square', 'h':'circle'}), show_legend=True)
df_d = df[df['type'] == 'd']
df_h = df[df['type'] == 'h']
d_points = hv.Points(data=df_d, kdims=['x', 'y'], vdims = ['id', 'type', 'value'], label = 'd').redim.range(value=(0,None)).opts(marker = 's')
h_points = hv.Points(data=df_h, kdims=['x', 'y'], vdims = ['id', 'type', 'value'], label = 'h').redim.range(value=(0,None)).opts(marker = 'o')
points = d_points * h_points
# rename the streams
stream_d = hv.streams.Selection1D(source=d_points).rename(index="index_d")
stream_h = hv.streams.Selection1D(source=h_points).rename(index="index_h")
df_a = pd.DataFrame(data = {'id':['a']*5, 'hour':range(5), 'value':np.random.random(5)})
df_b = pd.DataFrame(data = {'id':['b']*5, 'hour':range(5), 'value':np.random.random(5)})
df_c = pd.DataFrame(data = {'id':['c']*10, 'hour':range(10), 'value':np.random.random(10)})
df_d = pd.DataFrame(data = {'id':['d']*10, 'hour':range(10), 'value':np.random.random(10)})
df_ts = pd.concat([df_a, df_b, df_c, df_d])
df_ts = df_ts.set_index(['id', 'hour'])
empty = hv.Curve(df_ts.loc['a']).opts(visible = False)
# receive the two streams by different argument name
def tap_station(index_d, index_h):
if not index_d and not index_h:
return empty
elif index_d:
id = df_d.iloc[index_d[0]]['id']
elif index_h:
id = df_h.iloc[index_h[0]]['id']
return hv.Curve(df_ts.loc[id], label = str(id)).redim.range(value=(0,None))
# pass the two streams to DynamicMap
ts_curve = hv.DynamicMap(tap_station, kdims=[], streams=[stream_d, stream_h]).opts(framewise=True, show_grid=True)
pn.Row(points.opts(options), ts_curve)