How to scale node colors in Cytoscape

I am trying to visualize a social network using Cytoscape in Dash. I would like each node to be automatically colored based on the number of followers that node has. The current documentation for Cytoscape shows how to do this with classes for a very limited number of classes. But I would like to find a way to translate how this is done in normal Plotly with a colorscale into my network graph.

For the data, I have my element list with the nodes containing the following format:

[{'data': {'id': 'id1',
           'label': 'label1',
           'name': 'name1',
           'weight': 0.3}
...
{'data': {'id': 'idN',
           'label': 'labelN',
           'name': 'nameN',
           'weight': 0.8}]

I have tried reading in the weight as a scaling in a few different places, but the place that made the most sense was to do it was in the default_stylesheet (showing with background-opacity, but I really would prefer to do this is a colorscale) as

default_stylesheet = [
    {
        'selector': 'node',
        'style': {
            'background-color': '#2722AF',
            'background-opacity': 'data(weight)'
        }
    },
    {
        'selector': 'edge',
        'style': {
            'line-color': '#cfd1d3',
            'opacity': 0.3,
            'width': 1
        }
    }
]

However, when I do this I get Error loading layout in my browser.

Any tips would be greatly appreciated! Thank you!

@cj2001

I while ago I devised a workaround for mapping the node weights to a colormap, in order to use the associated colors in a dash-cytoscape app.
Today I’ve uploaded it to the Plotly cloud: https://plot.ly/~empet/15192.

The basic idea is as follows: cover the range on weights with a few adjacent intervals (bins). The weights that fall in the same bin are mapped to the same color in a sequential colormap.

One of the functions involved in this approach returns for each node its class and the associated hexcolor, retrieved from the given colormap.
These new derived node features are then used to define or update the json files (data and style) associated to the dash app.

1 Like

Thanks for that example! This is a pretty plotly-centric approach and I suppose I am looking for a way to come up with a similar approach for Dash-Cytoscape.

In other words, suppose I have a colorscale in go.Scatter() that looks something like this:

node_trace = go.Scatter(
    x = x_coord,
    y = y_coord,
    text = label,
    mode = 'markers',
    hoverinfo = 'text',
    marker = dict(
        color = color_ls,
        colorscale = 'Portland',
        size = 8,
        colorbar=dict(
            title="Number of Followers"
        )
    )
)

I am looking to recreate this same thing in Dash-Cytoscape by having a continuous, automatically established colorbar rather than having to create my own bins and specifying HTML or RGB colors. Is this possible?

@cj2001

You can map each normalized weight to a colormap:

calling the following function:

def weights_to_colors(weights, cmap):
#weights is the list of node weights
# cmap a mpl colormap
    colors01 = cmap(weights)
    colors01 = np.array([c[:3] for c in colors01])
    colors255 =   (255*colors01+0.5).astype(np.uint8)
    hexcolors = [f'#{c[0]:02x}{c[1]:02x}{c[2]:02x}' for c in colors255]
    return hexcolors

But we can map the weights to a Plotly colorscale without involving matplotlib.

The function defined below maps a non-normalized value to a Plotly colorscale with colors given in rgb format:

[[0.0, 'rgb(103, 0, 31)'],
 [0.1, 'rgb(141, 0, 59)'],
 [0.2, 'rgb(184, 10, 78)'],
 [0.3, 'rgb(215, 26, 105)'],
 [0.4, 'rgb(229, 53, 145)'],
 [0.5, 'rgb(222, 101, 176)'],
 [0.6, 'rgb(205, 138, 194)'],
 [0.7, 'rgb(207, 170, 210)'],
 [0.8, 'rgb(219, 201, 226)'],
 [0.9, 'rgb(234, 229, 241)'],
 [1.0, 'rgb(247, 244, 249)']]
from ast import literal_eval
def color_for_val(val, vmin, vmax, pl_colorscale):
    # val: float to be mapped to the Plotlt colorscale
    #[vmin, vmax]: the range of val values
    # pl_colorscale is a Plotly colorscale with colors in the RGB space with R,G, B in  0-255
    # this function maps the normalized value of val to a color in the colorscale
    if vmin >= vmax:
        raise ValueError('vmin must be less than vmax')

    scale = [item[0] for item in pl_colorscale]
    plotly_colors = [item[1] for item in pl_colorscale]# i.e. list of 'rgb(R, G, B)'

    colors_01 = np.array([literal_eval(c[3:]) for c in plotly_colors])/255  #color codes in [0,1]

    v= (val - vmin) / (vmax - vmin) # val is mapped to v in [0,1]
    #find two consecutive values, left and right, in scale such that   v  lie within  the corresponding interval
    idx = np.digitize(v, scale)
    left = scale[idx-1]
    right = scale[idx]

    vv = (v - left) / (right - left) #normalize v with respect to [left, right]

    #get   [0,1]-valued, 0-255, and hex color code for the  color associated to  val
    vcolor01 = colors_01[idx-1] + vv * (colors_01[idx] - colors_01[idx-1])  #linear interpolation
    vcolor255 = (255*vcolor01+0.5).astype(np.uint8)
    hexcolor = f'#{vcolor255[0]:02x}{vcolor255[1]:02x}{vcolor255[2]:02x}'
    #for dash-cytoscale we need the hex representation:
    return  hexcolor 

To each node, node[k], in the list of nodes one associates the class f’c{k}`, and the color corresponding to its weght.
This is an example of mapping some synthetic weights, to the above given colorscale, just for illustration:

1 Like