holoviz / hvplot

A high-level plotting API for pandas, dask, xarray, and networkx built on HoloViews
https://hvplot.holoviz.org
BSD 3-Clause "New" or "Revised" License
1.13k stars 108 forks source link

Exception: "None of the available storage backends were able to support the supplied data format." #470

Open johann-petrak opened 4 years ago

johann-petrak commented 4 years ago

I try to use hvplot.networkx from hvplot version 0.5.2 with bokeh to visualize a network graph.

This is code that works: a simple graph is created using networkx, and some attributes are added to the graph nodes, then hvplot is used to show the graph and show hover tooltips from the attributes:

import networkx as nx
import hvplot.networkx as hvnx

nodes = [1,2,3,4,5,6,7,8,9,10]
edges = [(1,2), (1,3), (1,4), (4,5), (4,3), (8,9), (8,7), (7,6)]

nxg1 = nx.Graph()
nxg1.add_nodes_from(nodes)
nxg1.add_edges_from(edges)
pos = nx.spring_layout(nxg1)

# some attributes for the nodes
for node in nxg1.nodes:
    nxg1.nodes[node]["label"] = f"Node{node}"
    nxg1.nodes[node]["desc"] = "some description"

hvnx.draw(nxg1, pos, hover_cols="all")

The following code throws the exception: here the node has an attribute "x" (or "y", but "z" is ok):

import networkx as nx
import hvplot.networkx as hvnx

nodes = [1,2,3,4,5,6,7,8,9,10]
edges = [(1,2), (1,3), (1,4), (4,5), (4,3), (8,9), (8,7), (7,6)]

nxg1 = nx.Graph()
nxg1.add_nodes_from(nodes)
nxg1.add_edges_from(edges)
pos = nx.spring_layout(nxg1)

# some attributes for the nodes
for node in nxg1.nodes:
    nxg1.nodes[node]["label"] = f"Node{node}"
    nxg1.nodes[node]["desc"] = "some description"
    nxg1.nodes[node]["x"] = pos[node][0]
    nxg1.nodes[node]["y"] = pos[node][1]
hvnx.draw(nxg1, pos, hover_cols="all")

Exception:

DataError                                 Traceback (most recent call last)
<ipython-input-7-930eafbe4a1f> in <module>
     16     nxg1.nodes[node]["x"] = pos[node][0]
     17     nxg1.nodes[node]["x"] = pos[node][1]
---> 18 hvnx.draw(nxg1, pos, hover_cols="all")

~/software/anaconda/envs/polads/lib/python3.6/site-packages/hvplot/networkx.py in draw(G, pos, **kwargs)
    223 
    224     # Construct Graph object
--> 225     g = _from_networkx(G, pos, **params)
    226 
    227     if 'nodelist' in kwargs:

~/software/anaconda/envs/polads/lib/python3.6/site-packages/hvplot/networkx.py in _from_networkx(G, positions, nodes, cls, **kwargs)
    107             dim = col
    108         vdims.append(dim)
--> 109     nodes = cls.node_type(node_data, vdims=vdims)
    110 
    111     # Construct graph

~/software/anaconda/envs/polads/lib/python3.6/site-packages/holoviews/core/data/__init__.py in __init__(self, data, kdims, vdims, **kwargs)
    337         validate_vdims = kwargs.pop('_validate_vdims', True)
    338         initialized = Interface.initialize(type(self), data, kdims, vdims,
--> 339                                            datatype=kwargs.get('datatype'))
    340         (data, self.interface, dims, extra_kws) = initialized
    341         super(Dataset, self).__init__(data, **dict(kwargs, **dict(dims, **extra_kws)))

~/software/anaconda/envs/polads/lib/python3.6/site-packages/holoviews/core/data/interface.py in initialize(cls, eltype, data, kdims, vdims, datatype)
    290                 error = ' '.join([error, priority_error])
    291                 raise six.reraise(DataError, DataError(error, intfc), sys.exc_info()[2])
--> 292             raise DataError(error)
    293 
    294         return data, interface, dims, extra_kws

DataError: None of the available storage backends were able to support the supplied data format.

Why is "x" or "y" not usable as an attribute, are there other names that would not work and if there are reserved names, could the error message be more informative?

philippjfr commented 4 years ago

This will require some thought your intuition about the cause here is correct in that 'x' and 'y' are the default dimensions describing the positions of the nodes and are therefore reserved by default. We will have to consider where to add a better error and/or whether to use default dimension names which are not going to clash.

johann-petrak commented 4 years ago

As the example shows, the values of the x and y attributes actually are the positions that would get pased on as pos to the draw function. So in this case, the draw function could just use the x and y attributes and it seems natural to do this, so it would be good to allow something like pos=("x","y") to retrieve the positions directly from the nodes instead of from a separate data structure.

If the draw function needs to use some names for x and y for the internal data structure which should not clash with names derived from the nodes, then some scheme for reserved names would be good, e.g. "x" and "y" or "__name" in general. It could then be documented that attributes starting with a double underscore cannot be used.