Настроить цвет краев в зависимости от выбора узла. Диаграмма Holoviews / Bokeh Chord.

Я создал аккордовую диаграмму, где каждый узел связан с определенной группой (сообществом). У меня есть требование, чтобы всякий раз, когда я нажимаю на конкретный узел, все ребра, связанные с ним, должны быть того же цвета, что и цвет узла.

Например, на этой диаграмме хорды пример хорды. Если я выберу синий узел, цвет края должен быть таким же, как цвет узла (т.е. синий). Но здесь, когда я нажимаю на синий узел, я получаю несколько краев синего и несколько краев оранжевого цвета (в основном край принимает цвет исходного узла). Есть ли способ настроить цвет краев в зависимости от выбора узла? Я вижу несколько вариантов, доступных для edge_selection_line_color поля like, attr, value и т. Д., Но я не мог понять, как использовать это для настройки цвета края, как мое требование. Я уверен, что это довольно простая задача, но я не мог понять, как ее достичь, и застрял в ней. Также дайте мне знать, можно ли использовать боке CustomJS.

Прикрепляем исходный код для справки:

edge_data = [{"from": "Cronje", "to": "Lord Kitchener", "value": 1}, {"from": "Cronje", "to": "Lord Selborne", "value": 1}, {"from": "Cronje", "to": "George Godfrey", "value": 1}, {"from": "Cronje", "to": "Lord Milner", "value": 1}, {"from": "Cronje", "to": "Booker T. Washington", "value": 1}, {"from": "Cronje", "to": "Dada Abdulla", "value": 1}, {"from": "Cronje", "to": "Lord Lansdowne", "value": 1}, {"from": "Cronje", "to": "Escombe", "value": 1}, {"from": "Cronje", "to": "Messrs", "value": 1}, {"from": "Cronje", "to": "Lawley", "value": 1}, {"from": "Cronje", "to": "Sheth Haji Adam", "value": 1}, {"from": "Cronje", "to": "Lord Ripon", "value": 1}, {"from": "Cronje", "to": "Lord Elgin", "value": 1}, {"from": "Sheth Haji Adam", "to": "Lord Kitchener", "value": 1}, {"from": "Sheth Haji Adam", "to": "Lord Selborne", "value": 1}, {"from": "Sheth Haji Adam", "to": "George Godfrey", "value": 1}, {"from": "Sheth Haji Adam", "to": "Dada Abdulla", "value": 1}, {"from": "Sheth Haji Adam", "to": "Booker T. Washington", "value": 1}, {"from": "Sheth Haji Adam", "to": "Lord Milner", "value": 1}, {"from": "Sheth Haji Adam", "to": "Lord Lansdowne", "value": 1}, {"from": "Sheth Haji Adam", "to": "Escombe", "value": 1}, {"from": "Sheth Haji Adam", "to": "Messrs", "value": 1}, {"from": "Sheth Haji Adam", "to": "Lawley", "value": 1}, {"from": "Sheth Haji Adam", "to": "Lord Elgin", "value": 1}, {"from": "Sheth Haji Adam", "to": "Lord Ripon", "value": 1}, {"from": "Lord Selborne", "to": "Lord Elgin", "value": 1}, {"from": "Lord Selborne", "to": "George Godfrey", "value": 1}, {"from": "Lord Selborne", "to": "Merriman", "value": 1}, {"from": "Lord Selborne", "to": "Olive Schreiner", "value": 1}, {"from": "Lord Selborne", "to": "Dada Abdulla", "value": 1}, {"from": "Lord Selborne", "to": "Booker T. Washington", "value": 1}, {"from": "Lord Selborne", "to": "Lord Milner", "value": 1}, {"from": "Lord Selborne", "to": "Lord Kitchener", "value": 1}, {"from": "Lord Selborne", "to": "Lord Lansdowne", "value": 1}, {"from": "Lord Selborne", "to": "Escombe", "value": 1}, {"from": "Lord Selborne", "to": "Messrs", "value": 1}, {"from": "Lord Selborne", "to": "Lawley", "value": 1}, {"from": "Lord Selborne", "to": "John Molteno", "value": 1}, {"from": "Lord Selborne", "to": "Lord Ripon", "value": 1}, {"from": "George Godfrey", "to": "Dada Abdulla", "value": 1}, {"from": "George Godfrey", "to": "Booker T. Washington", "value": 1}, {"from": "George Godfrey", "to": "Lord Milner", "value": 1}, {"from": "George Godfrey", "to": "Lord Kitchener", "value": 1}, {"from": "George Godfrey", "to": "Lord Lansdowne", "value": 1}, {"from": "George Godfrey", "to": "Escombe", "value": 1}, {"from": "George Godfrey", "to": "Messrs", "value": 1}, {"from": "George Godfrey", "to": "Lawley", "value": 1}, {"from": "George Godfrey", "to": "Lord Elgin", "value": 1}, {"from": "George Godfrey", "to": "Lord Ripon", "value": 1}, {"from": "Merriman", "to": "Lord Lansdowne", "value": 1}, {"from": "Merriman", "to": "John Molteno", "value": 1}, {"from": "Merriman", "to": "Olive Schreiner", "value": 1}, {"from": "Olive Schreiner", "to": "Lord Lansdowne", "value": 1}, {"from": "Olive Schreiner", "to": "John Molteno", "value": 1}, {"from": "Dada Abdulla", "to": "Lord Kitchener", "value": 1}, {"from": "Dada Abdulla", "to": "Booker T. Washington", "value": 1}, {"from": "Dada Abdulla", "to": "Lord Milner", "value": 1}, {"from": "Dada Abdulla", "to": "Lord Lansdowne", "value": 1}, {"from": "Dada Abdulla", "to": "Escombe", "value": 1}, {"from": "Dada Abdulla", "to": "Messrs", "value": 1}, {"from": "Dada Abdulla", "to": "Lawley", "value": 1}, {"from": "Dada Abdulla", "to": "Lord Elgin", "value": 1}, {"from": "Dada Abdulla", "to": "Lord Ripon", "value": 1}, {"from": "Booker T. Washington", "to": "Lord Milner", "value": 1}, {"from": "Booker T. Washington", "to": "Lord Kitchener", "value": 1}, {"from": "Booker T. Washington", "to": "Lord Lansdowne", "value": 1}, {"from": "Booker T. Washington", "to": "Escombe", "value": 1}, {"from": "Booker T. Washington", "to": "Messrs", "value": 1}, {"from": "Booker T. Washington", "to": "Lawley", "value": 1}, {"from": "Booker T. Washington", "to": "Lord Elgin", "value": 1}, {"from": "Booker T. Washington", "to": "Lord Ripon", "value": 1}, {"from": "Lord Milner", "to": "Lord Kitchener", "value": 1}, {"from": "Lord Milner", "to": "Lord Lansdowne", "value": 1}, {"from": "Lord Milner", "to": "Escombe", "value": 1}, {"from": "Lord Milner", "to": "Messrs", "value": 1}, {"from": "Lord Milner", "to": "Lawley", "value": 1}, {"from": "Lord Milner", "to": "Lord Elgin", "value": 1}, {"from": "Lord Milner", "to": "Lord Ripon", "value": 1}, {"from": "Lord Kitchener", "to": "Lord Lansdowne", "value": 1}, {"from": "Lord Kitchener", "to": "Escombe", "value": 1}, {"from": "Lord Kitchener", "to": "Messrs", "value": 1}, {"from": "Lord Kitchener", "to": "Lawley", "value": 1}, {"from": "Lord Kitchener", "to": "Lord Elgin", "value": 1}, {"from": "Lord Kitchener", "to": "Lord Ripon", "value": 1}, {"from": "Lord Lansdowne", "to": "Escombe", "value": 1}, {"from": "Lord Lansdowne", "to": "Messrs", "value": 1}, {"from": "Lord Lansdowne", "to": "Lawley", "value": 1}, {"from": "Lord Lansdowne", "to": "John Molteno", "value": 1}, {"from": "Lord Lansdowne", "to": "Lord Elgin", "value": 1}, {"from": "Lord Lansdowne", "to": "Lord Ripon", "value": 1}, {"from": "Escombe", "to": "Messrs", "value": 1}, {"from": "Escombe", "to": "Lawley", "value": 1}, {"from": "Escombe", "to": "Lord Elgin", "value": 1}, {"from": "Escombe", "to": "Lord Ripon", "value": 1}, {"from": "Messrs", "to": "Lawley", "value": 1}, {"from": "Messrs", "to": "Lord Elgin", "value": 1}, {"from": "Messrs", "to": "Lord Ripon", "value": 1}, {"from": "Lawley", "to": "Lord Elgin", "value": 1}, {"from": "Lawley", "to": "Lord Ripon", "value": 1}, {"from": "Lord Elgin", "to": "Lord Ripon", "value": 1}]

community_data = [{"person": "Cronje", "community": "0"}, {"person": "Sheth Haji Adam", "community": "0"}, {"person": "Lord Selborne", "community": "1"}, {"person": "George Godfrey", "community": "0"}, {"person": "Merriman", "community": "1"}, {"person": "Olive Schreiner", "community": "1"}, {"person": "Dada Abdulla", "community": "0"}, {"person": "Booker T. Washington", "community": "0"}, {"person": "Lord Milner", "community": "0"}, {"person": "Lord Kitchener", "community": "0"}, {"person": "Lord Lansdowne", "community": "1"}, {"person": "Escombe", "community": "0"}, {"person": "Messrs", "community": "0"}, {"person": "Lawley", "community": "0"}, {"person": "John Molteno", "community": "1"}, {"person": "Lord Elgin", "community": "0"}, {"person": "Lord Ripon", "community": "0"}]

import pandas as pd
import holoviews as hv
from holoviews import opts, dim
import numpy as np
hv.extension('bokeh')
hv.output(size=300)

##Read the edge data and community data for the chordial
df_json = pd.DataFrame(edge_data)
com_json = pd.DataFrame(community_data)

##Creating pandas dataframe from edge-data with columns source, target and value
df_links = pd.DataFrame(columns = ['source', 'target', 'value'])
df_links['source'] = df_json['from'].values.tolist()
df_links['target'] = df_json['to'].values.tolist()
df_links['value'] = df_json['value'].values.tolist()

##Creating pandas dataframe from community data to associate each node with a particular community
df_nodes = pd.DataFrame(columns=['index', 'source', 'community'])
df_nodes['source'] =  com_json['person'].values.tolist()
df_nodes['community'] = com_json['community'].values.tolist()
df_nodes = df_nodes.sort_values('community')
df_nodes['index'] = [x for x in range(0, len(df_nodes.values))]

##Creating a mapper dictionary to assign each node to a numerical unique value
mapper = {}
df_dict = df_nodes.to_dict()
for items, values in df_dict['index'].items():
    mapper[df_dict['source'][items]] = values
    
##Joining edge-data-frame with community data-frame to assign edge source with the source community    
df_edge_with_community = pd.merge(df_links, df_nodes, on='source')
del df_edge_with_community['index']
df_edge_with_community.rename(columns={'community': 'edge_community'}, inplace=True)

##Assigning each node to numerical value
df_edge_with_community['source'] = df_edge_with_community['source'].map(mapper)
df_edge_with_community['target'] = df_edge_with_community['target'].map(mapper)

##Creating hv dataset from df_nodes
nodes = hv.Dataset(df_nodes, 'index')

##Creating Chordials
chord = hv.Chord((df_edge_with_community, nodes)).select(value=(1, None))
chord.opts(
    opts.Chord(cmap='Category10', edge_cmap='Category10', edge_color=dim('edge_community').str(),
               labels='source',  label_text_font_size='10px',label_text_font_style='bold',  node_color=dim('community').str(),
               edge_alpha= 0.8, node_selection_fill_color=None, edge_nonselection_line_alpha=0, edge_line_width=5,
              edge_hover_line_color=None )) 

person Sayantan Adak    schedule 18.07.2020    source источник