Como lidar com rótulos e arestas desconhecidos no gráfico Python

Oct 10 2020

Eu tenho duas matrizes, a e b. Eu gostaria de desenhar o networkxgráfico que agrupa os valores próximos uns dos outros e rotulá-los de acordo. Alguma idéia de como fazer isso?

Respostas

1 mathfux Oct 11 2020 at 01:45

Encontrando pares próximos

Seu algoritmo encontra o ponto mais próximo de bcada ponto de, amas você precisa identificar uma lista deles dentro de algum limite de distância (que pode estar vazio na maioria dos casos). Isso pode ser alcançado com a ajuda de scipy.spatial.KDTree:

import numpy as np
from scipy.spatial import KDTree
from itertools import chain
def nearby_pts(a, b, distance):
    # indices of close points of `b` for each point of `a`
    a_data, b_data = np.expand_dims(a, axis=1), np.expand_dims(b, axis=1)
    idx = KDTree(b_data).query_ball_point(a_data, r=distance)
    return idx

Então você pode encontrar arestas que unem pares de índices de pontos próximos de aa b. Isso não pode ser vetorizado totalmente, mas fiz o melhor que pude:

def close_pairs(a, b, distance):
    pts = nearby_pts(a, b, distance).tolist()
    pts_flatten = list(chain(*pts))
    idx = np.repeat(np.arange(len(pts)), [len(n) for n in pts])
    return np.c_[idx, pts_flatten]

Resultado:

>>> close_pairs(a, b, distance=150)
[[0, 12], [1, 11], [2, 13], [3, 7], [5, 10], [5, 15], [6, 8], [7, 1], [8, 2], [9, 3], [9, 14], [10, 0], [11, 6], [12, 4], [13, 5], [13, 15], [14, 3], [15, 10]]

Traçando um gráfico

Agora você está pronto para criar um gráfico a partir das arestas encontradas, mas primeiro você precisa renomear uma segunda seção de nós ( b) para não ser duplicada com a aseção. Então, você pode apenas adicionar len(a)índices de nós de be é isso:

import igraph as ig

pairs_relabel = close_pairs(a, b, distance=150) + [0, len(a)]
g = ig.Graph(n = len(a) + len(b))
g.add_edges(pairs_relabel)

pal = ig.drawing.colors.ClusterColoringPalette(2) #number of colors used is 2
color = pal.get_many([0]*len(a)+[1]*len(b)) #tags of colors
labels = np.r_[a.astype(int), b.astype(int)] #labels are integral values of nodes

ig.plot(g, bbox=(500, 300), vertex_size=24, 
        vertex_color = color, vertex_label_size=9, vertex_label = labels)