In Depth 3 - Drawing DiHypergraphs

Here we show the fuctionalities and parameters of xgi.draw_bipartite(). It is similar to the networkx corresponding function (+ some bonus) and heavily relies on matplotlib’s Collection plotting.

[1]:
import matplotlib.pyplot as plt

import xgi

Les us first create a small toy hypergraph containing edges of different sizes.

[2]:
diedges = [({0, 1}, {2}), ({1}, {4}), ({2, 3}, {4, 5})]
DH = xgi.DiHypergraph(diedges)
[3]:
xgi.draw_bipartite(DH)
[3]:
(<AxesSubplot: >,
 (<matplotlib.collections.PathCollection at 0x2a0b7da80>,
  <matplotlib.collections.PathCollection at 0x2a0b7dc00>))
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_5_1.png

Basics

Notice that this function returns a tuple (ax, collections) where collections is a tuple (node_collection, edge_node_collection). The collections can be used to plot colorbars as we will see later.

The color, linewidth, transparancy, and style of the hyperedges can all be customised, for example with single values:

[4]:
xgi.draw_bipartite(
    DH,
    node_shape="h",
    node_fc="grey",
    node_ec="r",
    node_size=20,
    edge_marker_size=20,
    edge_marker_shape="p",
    dyad_lw=2,
    arrowsize=10,
    edge_marker_fc="b",
    dyad_color="b",
)
[4]:
(<AxesSubplot: >,
 (<matplotlib.collections.PathCollection at 0x2a0cf9b40>,
  <matplotlib.collections.PathCollection at 0x2a0b4faf0>))
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_9_1.png

Or with multiple values:

[5]:
xgi.draw_bipartite(
    DH,
    node_fc=["g", "grey", "purple", "coral", "r", "b"],
    arrowsize=20,
    edge_marker_fc=["violet", "lightgreen", "skyblue"],
    dyad_color="k",
)
[5]:
(<AxesSubplot: >,
 (<matplotlib.collections.PathCollection at 0x2a0d69480>,
  <matplotlib.collections.PathCollection at 0x109d574f0>))
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_11_1.png

Adding node and edge labels:

[6]:
xgi.draw_bipartite(DH)
[6]:
(<AxesSubplot: >,
 (<matplotlib.collections.PathCollection at 0x2a0dd0f70>,
  <matplotlib.collections.PathCollection at 0x2a0cd47c0>))
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_13_1.png

Arrays of floats and colormaps

In XGI, you can easily color hyperedges according to an EdgeStat, or just an array or a dict with float values:

[7]:
ax, collections = xgi.draw_bipartite(
    DH,
    node_fc=DH.nodes.degree,
    edge_marker_fc=DH.edges.size,
    node_size=DH.nodes.degree,
)

node_coll, edge_marker_coll = collections

plt.colorbar(node_coll, label="nodes")
plt.colorbar(edge_marker_coll, label="edges")
[7]:
<matplotlib.colorbar.Colorbar at 0x2a0ea4160>
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_16_1.png

By default, the colormaps used are “crest_r” and “Reds”. These can be changed:

[8]:
ax, collections = xgi.draw_bipartite(
    DH,
    node_fc=DH.nodes.degree,
    edge_marker_fc=DH.edges.size,
    node_fc_cmap="viridis",
    edge_marker_fc_cmap="tab10",
)

node_coll, edge_marker_coll = collections

plt.colorbar(node_coll, label="nodes")
plt.colorbar(edge_marker_coll, label="edges")
[8]:
<matplotlib.colorbar.Colorbar at 0x2a0fba560>
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_18_1.png

Styling of arrows

By default, the arrowstyle used is "-|>":

[9]:
xgi.draw_bipartite(DH, arrowstyle="-|>")
[9]:
(<AxesSubplot: >,
 (<matplotlib.collections.PathCollection at 0x2a1045450>,
  <matplotlib.collections.PathCollection at 0x2a0cd40d0>))
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_21_1.png

Other styles can be used, see the full list from Matplotlib

[10]:
styles = ["simple", "fancy", "]->"]

fig, axs = plt.subplots(1, len(styles), figsize=(10, 3))

for i, style in enumerate(styles):
    ax = axs[i]
    xgi.draw_bipartite(DH, arrowstyle=style, ax=ax)
    ax.set_title(f"{style}")
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_23_0.png

There is a second argument to style arrows: connectionstyle. The default value is "arc3", but other values can be used

[11]:
styles = ["angle3", "bar", "angle"]

fig, axs = plt.subplots(1, len(styles), figsize=(12, 4))

for i, style in enumerate(styles):
    ax = axs[i]
    xgi.draw_bipartite(DH, connectionstyle=style, ax=ax)
    ax.set_title(f"{style}")
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_25_0.png

Layout

The initial layout is computed based on the bipartite network representation of the hypergraph:

[12]:
pos = xgi.bipartite_spring_layout(DH)
xgi.draw_bipartite(DH, pos=pos)
[12]:
(<AxesSubplot: >,
 (<matplotlib.collections.PathCollection at 0x2a1088430>,
  <matplotlib.collections.PathCollection at 0x2a12e8af0>))
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_28_1.png

A larger example

[13]:
edges = [
    [[8], [0]],
    [[1, 2], [0]],
    [[0, 3], [1]],
    [[1, 3], [2]],
    [[1, 5], [3]],
    [[2, 5], [4]],
    [[3, 4], [5, 6]],
    [[6, 7], [5]],
    [[5, 8], [6]],
    [[6, 8], [7]],
    [[6, 0], [8]],
    [[7, 0], [9]],
]

DH = xgi.DiHypergraph(edges)
[14]:
pos = xgi.bipartite_spring_layout(DH)
xgi.draw_bipartite(DH, pos=pos)
[14]:
(<AxesSubplot: >,
 (<matplotlib.collections.PathCollection at 0x2a134c970>,
  <matplotlib.collections.PathCollection at 0x2a134cdc0>))
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_31_1.png

But, we can also place the edge markers at the barycenters of the node positions using edge_positions_from_barycenters:

[15]:
node_pos = xgi.circular_layout(DH)
edge_pos = xgi.edge_positions_from_barycenters(DH, node_pos)
[16]:
xgi.draw_bipartite(DH, pos=(node_pos, edge_pos))
[16]:
(<AxesSubplot: >,
 (<matplotlib.collections.PathCollection at 0x2a13dcd90>,
  <matplotlib.collections.PathCollection at 0x2a13dd1e0>))
../../_images/api_tutorials_In_Depth_3_-_Drawing_DiHypergraphs_34_1.png