Много ребер на графике в Python - PullRequest
0 голосов
/ 02 ноября 2018

У меня есть следующий скрипт:

import pandas as pd
from igraph import *

df_p_c = pd.read_csv('data/edges.csv')

...

edges = list_edges
vertices = list(dict_case_to_number.keys())

g = Graph(edges=edges, directed=True)

plot(g, bbox=(6000, 6000))

У меня 2300 ребер с редкой связью. Это мой сюжет: all area И вот несколько его частей:

enter image description here enter image description here

Этот график не читается, поскольку расстояние между краями слишком мало. Как я могу иметь большее расстояние между краями? Только ребра из одной «семьи» имеют небольшое расстояние.

Есть ли другой способ улучшить участки с большим количеством граней? Я ищу какой-либо способ визуализации корреляции родитель-потомок, это может быть другой пакет Python.

Ответы [ 3 ]

0 голосов
/ 05 ноября 2018

Кажется, у вас много маленьких, отсоединенных компонентов. Если вам нужен информативный график, я думаю, вам следует отсортировать и сгруппировать связанные компоненты по размеру. Кроме того, в основе многих алгоритмов компоновки сети лежит то, что существует один гигантский компонент. Следовательно, если вам нужны разумные координаты, вам часто нужно вычислять компоновку для каждого компонента отдельно, а затем располагать компоненты относительно друг друга. Я бы перестроил ваш график следующим образом:

enter image description here

Я написал код для этого графика, используя networkx, так как это мой предпочтительный модуль. Однако было бы очень просто заменить функции networkx функциями igraph. Две функции, которые вам нужно заменить: networkx.connected_component_subgraphs и все, что вы хотите использовать для component_layout_func.

#!/usr/bin/env python

import numpy as np
import matplotlib.pyplot as plt
import networkx


def layout_many_components(graph,
                           component_layout_func=networkx.layout.spring_layout,
                           pad_x=1., pad_y=1.):
    """
    Arguments:
    ----------
    graph: networkx.Graph object
        The graph to plot.

    component_layout_func: function (default networkx.layout.spring_layout)
        Function used to layout individual components.
        You can parameterize the layout function by partially evaluating the
        function first. For example:

        from functools import partial
        my_layout_func = partial(networkx.layout.spring_layout, k=10.)
        pos = layout_many_components(graph, my_layout_func)

    pad_x, pad_y: float
        Padding between subgraphs in the x and y dimension.

    Returns:
    --------
    pos : dict node : (float x, float y)
        The layout of the graph.

    """

    components = _get_components_sorted_by_size(graph)
    component_sizes = [len(component) for component in components]
    bboxes = _get_component_bboxes(component_sizes, pad_x, pad_y)

    pos = dict()
    for component, bbox in zip(components, bboxes):
        component_pos = _layout_component(component, bbox, component_layout_func)
        pos.update(component_pos)

    return pos


def _get_components_sorted_by_size(g):
    subgraphs = list(networkx.connected_component_subgraphs(g))
    return sorted(subgraphs, key=len)


def _get_component_bboxes(component_sizes, pad_x=1., pad_y=1.):
    bboxes = []
    x, y = (0, 0)
    current_n = 1
    for n in component_sizes:
        width, height = _get_bbox_dimensions(n, power=0.8)

        if not n == current_n: # create a "new line"
            x = 0 # reset x
            y += height + pad_y # shift y up
            current_n = n

        bbox = x, y, width, height
        bboxes.append(bbox)
        x += width + pad_x # shift x down the line
    return bboxes


def _get_bbox_dimensions(n, power=0.5):
    # return (np.sqrt(n), np.sqrt(n))
    return (n**power, n**power)


def _layout_component(component, bbox, component_layout_func):
    pos = component_layout_func(component)
    rescaled_pos = _rescale_layout(pos, bbox)
    return rescaled_pos


def _rescale_layout(pos, bbox):

    min_x, min_y = np.min([v for v in pos.values()], axis=0)
    max_x, max_y = np.max([v for v in pos.values()], axis=0)

    if not min_x == max_x:
        delta_x = max_x - min_x
    else: # graph probably only has a single node
        delta_x = 1.

    if not min_y == max_y:
        delta_y = max_y - min_y
    else: # graph probably only has a single node
        delta_y = 1.

    new_min_x, new_min_y, new_delta_x, new_delta_y = bbox

    new_pos = dict()
    for node, (x, y) in pos.items():
        new_x = (x - min_x) / delta_x * new_delta_x + new_min_x
        new_y = (y - min_y) / delta_y * new_delta_y + new_min_y
        new_pos[node] = (new_x, new_y)

    return new_pos


def test():
    from itertools import combinations

    g = networkx.Graph()

    # add 100 unconnected nodes
    g.add_nodes_from(range(100))

    # add 50 2-node components
    g.add_edges_from([(ii, ii+1) for ii in range(100, 200, 2)])

    # add 33 3-node components
    for ii in range(200, 300, 3):
        g.add_edges_from([(ii, ii+1), (ii, ii+2), (ii+1, ii+2)])

    # add a couple of larger components
    n = 300
    for ii in np.random.randint(4, 30, size=10):
        g.add_edges_from(combinations(range(n, n+ii), 2))
        n += ii

    pos = layout_many_components(g, component_layout_func=networkx.layout.circular_layout)

    networkx.draw(g, pos, node_size=100)

    plt.show()


if __name__ == '__main__':

    test()

EDIT

Если вы хотите плотно расположить подграфы, вам необходимо установить пакет прямоугольника (pip install rectangle-packer) и заменить _get_component_bboxes этой версией:

import rpack 

def _get_component_bboxes(component_sizes, pad_x=1., pad_y=1.):
    dimensions = [_get_bbox_dimensions(n, power=0.8) for n in component_sizes]
    # rpack only works on integers; sizes should be in descending order
    dimensions = [(int(width + pad_x), int(height + pad_y)) for (width, height) in dimensions[::-1]]
    origins = rpack.pack(dimensions)
    bboxes = [(x, y, width-pad_x, height-pad_y) for (x,y), (width, height) in zip(origins, dimensions)]
    return bboxes[::-1]

enter image description here

0 голосов
/ 11 ноября 2018

Знаете ли вы, какой смысл вы ищете? Или вы исследуете? Или это конкретный вопрос о проблемах масштабирования?

Пока что вы хорошо поработали, увидев общую структуру. Некоторые идеи, которые вы могли бы рассмотреть, чтобы создать новый словарь с несколькими подпрограммами для его поддержки. Например, если вы сделаете маленький кластер набором точек и ребер, которые вместе, то вы можете построить гистограммы, визуализации кластеров, наложенных друг на друга, сравнить кластеры с длинными узлами и без них и т. Д. один.

0 голосов
/ 05 ноября 2018

Вы можете оформить заказ networkx , который является довольно хорошей библиотекой графов. Networkx имеет прямую поддержку печати для matplotlib .

Поддерживаются различные типы макетов, например пружинный макет, случайный макет и еще несколько

Особенно стоит взглянуть на компоновку пружины , в которой есть несколько интересных параметров для вашего варианта использования:

k (float (по умолчанию = None)) - Оптимальное расстояние между узлами. Если нет расстояние установлено в 1 / sqrt (n), где n - количество узлов. Увеличение это значение для перемещения узлов дальше друг от друга.

Или оба в сочетании с пользовательским макетом:

pos (dict или None необязательно (по умолчанию = None)) - начальные позиции для узлы как словарь с узлом в качестве ключей и значения в качестве координаты список или кортеж Если Нет, используйте случайные начальные позиции.

исправлено (список или None необязательно (по умолчанию = None)) - узлы, которые должны быть зафиксированы на начальном положение.

Вес ребра также может быть тем, что вы можете настроить, чтобы получить результаты, которые вам нравятся:

weight (строка или None необязательно (по умолчанию ='’’’’)) - край атрибут, который содержит числовое значение, используемое для веса ребра. Если Нет, тогда все веса ребер равны 1.

Я бы порекомендовал объединить networkx с bokeh , который представляет собой новую библиотеку для создания графиков, которая создает веб-графики html / js. Он имеет прямую поддержку networkx и обладает некоторыми приятными функциями, такими как простая интеграция инструментов наведения узлов. Если ваш график не слишком большой, производительность довольно хорошая. (Я построил графики с примерно 20000 узлами и несколькими тысячами ребер).

С объединением обеих библиотек все, что вам нужно, это следующий фрагмент кода для простого примера (из документации), который пытается создать оптимизированный макет:

import networkx as nx

from bokeh.io import show, output_file
from bokeh.plotting import figure
from bokeh.models.graphs import from_networkx

G=nx.karate_club_graph()  # Replace with your own graph

plot = figure(title="Networkx Integration Demonstration", x_range=(-1.1,1.1), y_range=(-1.1,1.1),
              tools="", toolbar_location=None)

graph = from_networkx(G, nx.spring_layout, scale=2, center=(0,0))
plot.renderers.append(graph)

output_file("networkx_graph.html")
show(plot)
...