I'm working on a plot.ly wrapper for my networkx plots adapted from https://plot.ly/python/network-graphs/. I can't figure out how to change the width for each connection based on the weights. The weights are in the attr_dict
as weight
. I tried setting go.Line
objects but it wasn't working :(. Any suggestions? (and links to tutorials if possible :) ). Attaching an example of the network structure from a plot I made in matplotlib.
How can I set individual line widths for each connection in plotly?
import requests from ast import literal_eval import plotly.offline as py from plotly import graph_objs as go py.init_notebook_mode(connected=True) # Import Data pos = literal_eval(requests.get("https://pastebin.com/raw/P5gv0FXw").text) df_plot = pd.DataFrame(pos).T df_plot.columns = list("xy") edgelist = literal_eval(requests.get("https://pastebin.com/raw/2a8ErW7t").text) _fig_kws={"figsize":(10,10)} # Plotting Function def plot_networkx_plotly(df_plot, pos, edgelist, _fig_kws): # Nodes node_trace = go.Scattergl( x=df_plot["x"], y=df_plot["y"], mode="markers", ) # Edges edge_trace = go.Scattergl( x=[], y=[], line=[], mode="lines" ) for node_A, node_B, attr_dict in edgelist: xA, yA = pos[node_A] xB, yB = pos[node_B] edge_trace["x"] += [xA, xB, None] edge_trace["y"] += [yA, yB, None] edge_trace["lines"].append(go.Line(width=attr_dict["weight"],color='#888')) # Data data = [node_trace, edge_trace] layout = { "width":_fig_kws["figsize"][0]*100, "height":_fig_kws["figsize"][1]*100, } fig = dict(data=data, layout=layout) py.iplot(fig) return fig plot_networkx_plotly(df_plot, pos, edgelist, _fig_kws) # --------------------------------------------------------------------------- # PlotlyDictValueError Traceback (most recent call last) # <ipython-input-72-4a5d0e26a71d> in <module>() # 46 py.iplot(fig) # 47 return fig # ---> 48 plot_networkx_plotly(df_plot, pos, edgelist, _fig_kws) # <ipython-input-72-4a5d0e26a71d> in plot_networkx_plotly(df_plot, pos, edgelist, _fig_kws) # 25 y=[], # 26 line=[], # ---> 27 mode="lines" # 28 ) # 29 # ~/anaconda/lib/python3.6/site-packages/plotly/graph_objs/graph_objs.py in __init__(self, *args, **kwargs) # 375 d = {key: val for key, val in dict(*args, **kwargs).items()} # 376 for key, val in d.items(): # --> 377 self.__setitem__(key, val, _raise=_raise) # 378 # 379 def __dir__(self): # ~/anaconda/lib/python3.6/site-packages/plotly/graph_objs/graph_objs.py in __setitem__(self, key, value, _raise) # 430 # 431 if self._get_attribute_role(key) == 'object': # --> 432 value = self._value_to_graph_object(key, value, _raise=_raise) # 433 if not isinstance(value, (PlotlyDict, PlotlyList)): # 434 return # ~/anaconda/lib/python3.6/site-packages/plotly/graph_objs/graph_objs.py in _value_to_graph_object(self, key, value, _raise) # 535 if _raise: # 536 path = self._get_path() + (key, ) # --> 537 raise exceptions.PlotlyDictValueError(self, path) # 538 else: # 539 return # PlotlyDictValueError: 'line' has invalid value inside 'scattergl' # Path To Error: ['line'] # Current path: [] # Current parent object_names: [] # With the current parents, 'line' can be used as follows: # Under ('figure', 'data', 'scattergl'): # role: object
Update with Ian Kent's Answer:
I don't think the code below can change the weights for all of the lines. I tried making all of the widths 0.1
with the weights
list and got the following plot:
but then when I did width=0.1
it worked for all of the lines:
1 Answers
Answers 1
I think the issue is in the following line of your code:
edge_trace["lines"].append(go.Line(width=attr_dict["weight"],color='#888'))
Try it with "line" instead of "lines". This is a bit of a confusing aspect of the Plotly API, but in scatter plots, the mode is plural and the argument name to change attributes of the trace is singular. So,
trace = go.Scatter(mode = 'markers', marker = dict(...)) trace = go.Scatter(mode = 'lines', line = dict(...))
Edit: Okay so there turned out to be more issues than just the "lines" now that I've sat down with it:
You have the line
argument as a list of dict-like objects, whereas plotly expects it to be a single dict-like. Building a list of weights then adding all the weights to the line
attribute at once seems to work:
edge_trace = go.Scattergl( x=[], y=[], mode="lines" ) weights = [] for node_A, node_B, attr_dict in edgelist: xA, yA = pos[node_A] xB, yB = pos[node_B] edge_trace["x"] += [xA, xB, None] edge_trace["y"] += [yA, yB, None] weights.append(attr_dict["weight"]) edge_trace['line'] = dict(width=weights,color='#888')
Also, you are plotting the lines in front of the nodes and thus obstructing them. You should change
data = [node_trace, edge_trace]
to
data = [edge_trace, node_trace]
to avoid this.
0 comments:
Post a Comment