✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚾️ It's finally Baseball season! Root for the home team... & Register for our Sports Analytics Webinar!

Scatter lines+marker all black

I’m trying to visualize a network over time by combining a slider with Scatter. When I create the coordinates with igraph with the follow code, all the nodes and edges are black:

		x = []
		y = []
		for e in edges:
			x += [layout[e[0]][0], layout[e[1]][0], None]
			y += [layout[e[0]][1], layout[e[1]][1], None]

42%20PM

However, when I manually create the coordinates, the colors are correct and change with the slider as expected, but the edges never change.

  x = []
  y = []
  nodes = graph['nodes']
  for n in nodes:
  	x.append(n['x'])
  	y.append(n['y'])

45%20PM
Why is this happening?

@ch3njus The node color is set in the node trace definition. It doesn’t depend on the library you are using to
define your graph.

trace=dict(type='scatter',
           x=[n['x'] for n in nodes],
           y=[n['y']  for n in nodes],
           mode='markers',
           marker=dict(size=8, color=list of color codes of len equal to the len(nodes)),
             )

In order to point out what is wrong in your approach, please paste here your node trace and slider definition.

Here’s a link to my code.

@ch3njus Your data list contains 11 scatter traces of mode='markers+lines'. It seems that such a trace defines a group of nodes and edges.

For example data[2] is defined as follows:

Scatter({
    'hoverinfo': 'text',
    'line': {'color': 'rgb(210,210,210)', 'width': 2},
    'marker': {'color': [rgb(0,0,0), rgb(0,255,0), rgb(255,0,0)],
               'line': {'color': [rgb(0,0,0), rgb(0,255,0), rgb(255,0,0)], 'width': 8},
               'size': 5,
               'symbol': 'circle'},
    'mode': 'lines+markers',
    'name': 'hdsgd',
    'text': [smpl-0, smpl-2, smpl-1],
    'x': [2.8060800255, 3.44368272156, None, 3.44368272156, 2.8060800255, None,
          1.91853539044, 2.8060800255, None, 2.8060800255, 1.91853539044, None],
    'y': [-0.282090636501, -1.11656787037, None, -1.11656787037, -0.282090636501,
          None, 0.283726548186, -0.282090636501, None, -0.282090636501,
          0.283726548186, None]
})

Copying the x, y coordinates we can notice that each node and edge is is inserted into the list multiple times. Why?

xx=[2.8060800255, 3.44368272156, None, 3.44368272156, 2.8060800255, None,
          1.91853539044, 2.8060800255, None, 2.8060800255, 1.91853539044, None]
yy=[-0.282090636501, -1.11656787037, None, -1.11656787037, -0.282090636501,
          None, 0.283726548186, -0.282090636501, None, -0.282090636501,
          0.283726548186, None]

list(zip(xx,yy))
[(2.8060800255, -0.282090636501), #point-1
 (3.44368272156, -1.11656787037), #point-2
 (None, None),
 (3.44368272156, -1.11656787037), #point-2
 (2.8060800255, -0.282090636501), #point-1
 (None, None),
 (1.91853539044, 0.283726548186), #point-3
 (2.8060800255, -0.282090636501), #point-1
 (None, None),
 (2.8060800255, -0.282090636501), #point-1
 (1.91853539044, 0.283726548186), #point-3
 (None, None)]

Let us plot the plotly figure having its data member consisting in just this trace:

import plotly.graph_objs as go

layout=dict(width=500, height=500, xaxis=dict(visible=False),  yaxis=dict(visible=False), hovermode='closest')
fw=go.FigureWidget(data=[data[2]], layout=layout)
fw

We get this plot:

graph-plot

All three nodes have the same color and perhaps you expected to be colored with black, red, respectively green.

Why are they plotted in this way? Because the list data[2].marker.color,of color codes, hasn’t the same length (12) as the lists of x and y-coordinates.

The association point-->color in data[2]trace was performed as follows:

[(2.8060800255, -0.282090636501),#rgb(0,0,0) i.e. black
 (3.44368272156, -1.11656787037),#rgb(255, 0,0) red
 (None, None), #rgb(0,255, 0) green 
 (3.44368272156, -1.11656787037),#from here all points are colored with the default black  color because 
                                 #you  did not provide sufficient color codes (12) in the list assigned 
                                  #to`data[2].marker.color`.
(2.8060800255, -0.282090636501),
  (None, None),
 (1.91853539044, 0.283726548186), 
 (2.8060800255, -0.282090636501),
 (None, None),
 (2.8060800255, -0.282090636501),
 (1.91853539044, 0.283726548186),
 (None, None)]

That’s why all three points were ploted as black points.

As I also said in the initial comment, the length of color code list should be equal to that of list of x and y- coordinates (counting the None, too). The list text, of strings to be displayed on hover, also should have the same length.

One more tip: the marker.line.color cannot be a list of color codes. See details here https://plot.ly/python/reference/#scatter-marker-line-color.

I’m trying to visualize a hypergraph, but plotly doesn’t seem to support that. Instead, I’m trying to represent a hypergraph as a multigraph where my hyperedges are now sets of parallel edges, which is why I have repeated edges. Is there a better way to do this with plotly or another visualization tool?

@ch3njus An undirected hypergraph, H, may be represented by a bipartite graph, (BG), as follows:
if H=(V, E) (where V is the set of vertices and E is the set of hyperedges), is the hypergraph, then the associated bipartite graph is defined by the partitions V and E, and (v in V, e in E) are connected by an edge in BG iff v is a vertex in e (here e being a hyperedge is a subset in V).
Both python-igraph and networkx provide methods for defining bipartite graphs. You can plot such a BG with Plotly.

L McInnes defined a class Hypergraph that has a method that extracts the BG representation of the hypegraph
https://github.com/lmcinnes/hypergraph/tree/master/hypergraph.

Is there a way to arbitrarily color networks created in plotly? e.g. https://upload.wikimedia.org/wikipedia/commons/thumb/5/57/Hypergraph-wikipedia.svg/262px-Hypergraph-wikipedia.svg.png

@ch3njus You can define plotly shapes https://plot.ly/python/shapes/, as closed svg paths to plot hypergraphs like in the posted link. The shapes are defined in the figure layout. The only drawback is that you should set a list of points on the boundary of each shape (the points should be sufficiently dense to get a desired shape).

If x_pts=[xp[0], xp[1], .... xp[n-1]]
y_pts [yp[0], yp[1], ..., yp[n-1]
are the coordinates of n points on the shape boundary, then the closed path is defined as follows:

path='M  '
for k in range(n):
    path+=f'{xp[k]}, {yp[k]} L  '
path+=f'{xp[0]}, {yp[0]}'

The corresponding shape to be appended to the list of shapes, included in the layout definition is defined as follows:

shapes=[]
shapes.append(dict( type= 'path',
                    path=path,
                    fillcolor= 'rgba(44, 160, 101, 0.85)',
                    line=dict (color='rgb(44, 160, 101)'),# shape boundary
                    layer='below'# or 'above', depending where you want the shape to be placed with respect to the figure traces
            )

Hmmm I see. That seems like a pain. I’d have to figure out how to generate random points that form an arbitrary shape. Thanks.