pyvis icon indicating copy to clipboard operation
pyvis copied to clipboard

How does one use str ids when calling add_nodes()?

Open jwhendy opened this issue 2 years ago • 1 comments

I'm brand new to this library, so apologies if this is something silly I'm missing.

The docs suggest that nodes can have int or str ids:

n_id (str or int) – The id of the node. The id is mandatory for nodes and they have to be unique. This should obviously be set per node, not globally.

The documentation for add_nodes() doesn't say this explicitly, but my assumption was that globally one could use int or str for node ids. I get different behavior between the two functions:

from pyvis.network import Network

net1 = Network(notebook=True)
net2 = Network(notebook=True)

nodes = ['01', '02']
for n in nodes:
    net1.add_node(n, label=n)
print(net1.nodes)

net2.add_nodes(nodes, label=nodes)
print(net2.nodes)

Result (note discrepancy between id values):

[{'color': '#97c2fc', 'id': '01', 'label': '01', 'shape': 'dot'}, {'color': '#97c2fc', 'id': '02', 'label': '02', 'shape': 'dot'}]
[{'color': '#97c2fc', 'id': 1, 'label': 1, 'shape': 'dot'}, {'color': '#97c2fc', 'id': 2, 'label': 2, 'shape': 'dot'}]

Maybe add_nodes() doesn't say in the docs, but was designed only to take int ids. But this becomes a problem with add_edges(), as it is preserving str ids.

net2.add_edges([('01', '02')])

AssertionError                            Traceback (most recent call last)
Cell In[167], line 1
----> 1 net2.add_edges([('01', '02')])
[...]
AssertionError: non existent node '01'

Could someone let me know if I'm misunderstanding the usage, or could this be a bug where the single and bulk calls need to be aligned in behavior?

jwhendy avatar May 07 '23 16:05 jwhendy

Took a look and found what I think this issue is here.

        for node in nodes:
            # check if node is `number-like`
            try:
                node = int(node)
                self.add_node(node, **nd[node])
            except:
                # or node could be string
                assert isinstance(node, str)
                self.add_node(node, **nd[node])

So if one has str ids that could be int convertible (as my test case / real world example can), it's not possible to preserve the str type when calling bulk add.

add_edges doesn't do this, directly (and blindly) passing the args to add_edge.

        for edge in edges:
            # if incoming tuple contains a weight
            if len(edge) == 3:
                self.add_edge(edge[0], edge[1], width=edge[2])
            else:
                self.add_edge(edge[0], edge[1])

In my view, have add_nodes just check for a valid type, but do not alter it. So something like:

   for node in nodes:
        if isinstance(node, int) or isinstance(node, str):
            self.add_node(node, **nd[node])
        else:
            # error handling for improper id type

Also, I like how the args are parsed for nodes; it appears one can't pass keywords to add_edges() and thus add_edge() is far more flexible... thoughts on allowing add_edges() to take any of the args add_edge() will accpet?

jwhendy avatar May 08 '23 16:05 jwhendy