将 DynamicMap 链接到 Holoviews 中的两个流

问题描述 投票:0回答:1

我有以下代码,主要完成我想要它做的事情:制作一个按值着色的散点图,以及一个链接的 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
着色,通过向右拉起时间序列图来响应点击。

感谢您的帮助!

python bokeh holoviews
1个回答
0
投票

可以使用

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)
© www.soinside.com 2019 - 2024. All rights reserved.