How to pad matplotlib text objects radially with respect to a centroid? (Python)

Viewed 83

I'm trying to extend my networkx labels outside of the node radius with respect to the centroid I have calculated. For example, if we drew a circle around cluster {34, 5, 33} we would essentially draw a line between each of the nodes and the centroid, then along that vector add radial_pad to it so it's a little bit wider. The reason I'm doing this is because I'm adding piecharts in my actual nodes and I cannot for the life of me figure out how to get the zorder to work to show the label (I've set the zorder to high numbers, calculated the max zorder, added the text objects manually, etc). The next best thing is to move the node label outside of the node.

The geometry is going over my head a bit at the moment converting to and from polar coordinates and offsetting the centroid instead of it being at the origin.

import matplotliob.pyplot as plt
import pandas as pd
import networkx as nx
import numpy as np 

edge_data = [(9, 24, {'weight': 262.65595290640806}), (34, 5, {'weight': 143.01364809653174}), (34, 33, {'weight': 51.394542826823496}), (7, 14, {'weight': 389.22142801036125}), (0, 25, {'weight': 486.6995764468493}), (0, 30, {'weight': 233.1311348728954}), (0, 13, {'weight': 730.2831849050106}), (25, 30, {'weight': 87.14652833884263}), (25, 13, {'weight': 195.41994950707715}), (13, 30, {'weight': 131.22242872081216}), (33, 5, {'weight': 113.96755067449318}), (21, 35, {'weight': 100.30461510188701}), (21, 12, {'weight': 185.51533865658521}), (12, 35, {'weight': 106.65748950363549})]
graph = nx.Graph(edge_data)
pos = nx.nx_agraph.graphviz_layout(graph,prog="neato")

radial_pad = 1
with plt.style.context("seaborn-white"):
    fig, ax = plt.subplots(figsize=(5,5))
    nx.draw_networkx_nodes(graph, pos, ax=ax, node_size=500, node_color="white", edgecolors="black")
    nx.draw_networkx_edges(graph, pos, ax=ax)
    nx.draw_networkx_labels(graph, pos, ax=ax)
    for community in nx.connected_components(graph):
        centroid = np.stack(pd.Series(pos)[list(community)].map(list)).mean(axis=0)
        ax.scatter(*centroid, c="black")
        for node in community:
            x, y = pos[node]
            # How do I pad them with respect to the centroid? 

enter image description here

Here's an extremely crude example of what I'm trying to do:

enter image description here

  • Red is where I want to move the label
  • Blue is the radius between the current node label and the centroid
  • Green is the radius_pad value that we are adding to increase the radius for the end position of the label

Note, I'm not trying to add the colors but I thought it would just help visually.

1 Answers

First compute the vector from the centroid to the node:

delta = pos[node] - centroid

Then convert to the unit vector:

unit_vector = delta / np.linalg.norm(delta)

Finally, compute an offset in the direction of the unit vector, save the result, and draw the labels at the new positions:

label_pos = dict()
label_pos[node] = pos[node] + 0.05 * unit_vector
...
nx.draw_networkx_labels(graph, label_pos, ax=ax)
Related