paulbrodersen / netgraph

Publication-quality network visualisations in python
GNU General Public License v3.0
660 stars 39 forks source link

Multipartite/Shell layouts not working if the number of nodes per layer/shell are not equal #65

Closed OtisVab closed 1 year ago

OtisVab commented 1 year ago

Multipartite/Shell layouts do not work if there are a varying number of nodes per layer/shell due to a np.ones_like call on the layers list in the get_multipartite_layout() function.

Getting the following error code:

  File "/.venv/lib/site-packages/netgraph/_node_layout.py", line 1473, in get_shell_layout
    multipartite_positions = get_multipartite_layout(
  File "/.venv/lib/site-packages/netgraph/_node_layout.py", line 1367, in get_multipartite_layout
    node_spacings = np.ones_like(layers, dtype=float)
  File "<__array_function__ internals>", line 200, in ones_like
  File "/.venv/lib/site-packages/numpy/core/numeric.py", line 281, in ones_like
    res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape)
  File "<__array_function__ internals>", line 200, in empty_like
ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (3,) + inhomogeneous part.

Code to reproduce error (taken from this repo's tests):

def multipartite_graph():
    partitions = [
        list(range(3)),
        list(range(3, 9)),
        list(range(9, 21))
    ]

    edges = list(zip(np.repeat(partitions[0], 2), partitions[1])) \
        + list(zip(np.repeat(partitions[0], 2), partitions[1][1:])) \
        + list(zip(np.repeat(partitions[1], 2), partitions[2])) \
        + list(zip(np.repeat(partitions[1], 2), partitions[2][1:]))

    return partitions, edges

def test_shell_layout():
    shells, edges = multipartite_graph()
    fig, ax = plt.subplots()
    Graph(edges, node_layout='shell', node_layout_kwargs=dict(shells=shells, reduce_edge_crossings=False), node_labels=True, ax=ax)
    return fig

test_shell_layout()

Making the number of nodes on each layer works fine:

def multipartite_graph():
    partitions = [
        list(range(3)),
        list(range(6, 9)),
        list(range(18, 21))
    ]

    edges = list(zip(np.repeat(partitions[0], 2), partitions[1])) \
          + list(zip(np.repeat(partitions[0], 2), partitions[1][1:])) \
          + list(zip(np.repeat(partitions[1], 2), partitions[2])) \
          + list(zip(np.repeat(partitions[1], 2), partitions[2][1:]))

    return partitions, edges

def test_shell_layout():
    shells, edges = multipartite_graph()
    fig, ax = plt.subplots()
    Graph(edges, node_layout='shell', node_layout_kwargs=dict(shells=shells, reduce_edge_crossings=False), node_labels=True, ax=ax)
    return fig

test_shell_layout()

Can probably be fixed by changing the occurences np.ones_like(layers, dtype=float) to np.ones(shape=len(layers), dtype=float) in the get_multipartitide_layout() function

paulbrodersen commented 1 year ago

Thanks for the bug report! Should be fixed in the latest version (4.12.8) on github and PyPI; on conda-forge, the update should be available tomorrow.