Как я могу скопировать граф в Python?

Пусть G - граф. Итак, G - это набор узлов и множество ссылок. Мне нужно найти быстрый способ разбить график. График, который я сейчас работаю, имеет только 120 * 160 узлов, но я мог бы вскоре работать над эквивалентной проблемой, в другом контексте (не в медицине, а в разработке сайтов), с миллионами узлов.

Итак, я сделал, чтобы сохранить все ссылки в матрице графа:

M=numpy.mat(numpy.zeros((len(data.keys()),len(data.keys()))))

Теперь M удерживает 1 в позиции s, t, если node s связано с node t. Я убеждаюсь, что M симметрично M [s, t] = M [t, s], и каждый node связывается с собой M [s, s] = 1.

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

Таким образом, я продолжаю мультиплексирование M с собой, пока число нулей в матрице не уменьшится больше. Теперь у меня есть список подключенных компонентов. И теперь мне нужно сгруппировать эту матрицу.

До сих пор я доволен алгоритмом. Я думаю, что это легко, элегантно и достаточно быстро. У меня возникают проблемы с этой частью.

По существу мне нужно разбить этот граф на его подключенные компоненты.

Я могу пройти через все узлы и посмотреть, с чем они связаны.

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

Далее следует код:

def findzeros(M):
    nZeros=0
    for t in M.flat:
        if not t:
            nZeros+=1
    return nZeros

M=numpy.mat(numpy.zeros((len(data.keys()),len(data.keys()))))    
for s in data.keys():
    MatrixCells[s,s]=1
    for t in data.keys():
        if t<s:
            if (scipy.corrcoef(data[t],data[s])[0,1])>threashold:
                M[s,t]=1
                M[t,s]=1

nZeros=findzeros(M)
M2=M*M
nZeros2=findzeros(M2)

while (nZeros-nZeros2):
    nZeros=nZeros2
    M=M2
    M2=M*M
    nZeros2=findzeros(M2)

Edit:

Было высказано предположение, что я использую разложение SVD. Вот простой пример проблемы на графе 5x5. Мы будем использовать это, поскольку с квадратной матрицей 19200x19200 не так легко увидеть кластеры.

import numpy
import scipy

M=numpy.mat(numpy.zeros((5,5)))

M[1,3]=1
M[3,1]=1
M[1,1]=1
M[2,2]=1
M[3,3]=1
M[4,4]=1
M[0,0]=1

print M

u,s,vh = numpy.linalg.linalg.svd(M)
print u
print s
print vh

По существу, здесь 4 кластера: (0), (1,3), (2), (4) Но я до сих пор не вижу, как svn может помочь в этом контексте.

Ответы

Ответ 1

Почему бы не использовать настоящую библиотеку графа, например Python-Graph? Он имеет функцию для определения подключенных компонентов (хотя пример не указан). Я бы предположил, что выделенная библиотека будет быстрее, чем любой готовый графический код.

EDIT: NetworkX кажется, что это лучший выбор, чем python-graph; его документация (здесь для функции подключенных компонентов), безусловно, есть.

Ответ 2

В SciPy вы можете использовать разреженные матрицы. Также обратите внимание, что существуют более эффективные способы умножения матрицы. Во всяком случае, то, что вы пытаетесь сделать, может быть выполнено путем разложения SVD.

Введение с полезными ссылками.

Ответ 3

Там также graph_tool и networkit, которые имеют эффективные подпрограммы для подключенных компонентов, и оба эффективно сохраняют сеть. Если вы собираетесь работать с миллионами узлов, networkx, скорее всего, не будет достаточным (это чистый python afaik). Оба этих инструмента написаны на С++, поэтому могут обрабатывать анализ больших графиков с разумным временем выполнения.

Как указывает Фил, ваш метод будет иметь ужасно длительные времена вычислений для больших графиков (мы говорим дни, недели, месяцы...), и ваше представление для графика из миллиона узлов потребует чего-то вроде миллиона гигабайт памяти!

Ответ 4

Вот некоторая наивная реализация, которая находит связанные компоненты, используя глубину первого поиска, я написал некоторое время назад. Хотя он очень прост, он хорошо масштабируется до десяти тысяч вершин и краев...


import sys
from operator import gt, lt

class Graph(object):
    def __init__(self):
        self.nodes = set()
        self.edges = {}
        self.cluster_lookup = {}
        self.no_link = {}

    def add_edge(self, n1, n2, w):
        self.nodes.add(n1)
        self.nodes.add(n2)
        self.edges.setdefault(n1, {}).update({n2: w})
        self.edges.setdefault(n2, {}).update({n1: w})

    def connected_components(self, threshold=0.9, op=lt):
        nodes = set(self.nodes)
        components, visited = [], set()
        while len(nodes) > 0:
            connected, visited = self.dfs(nodes.pop(), visited, threshold, op)
            connected = set(connected)
            for node in connected:
                if node in nodes:
                    nodes.remove(node)

            subgraph = Graph()
            subgraph.nodes = connected
            subgraph.no_link = self.no_link
            for s in subgraph.nodes:
                for k, v in self.edges.get(s, {}).iteritems():
                    if k in subgraph.nodes:
                        subgraph.edges.setdefault(s, {}).update({k: v})
                if s in self.cluster_lookup:
                    subgraph.cluster_lookup[s] = self.cluster_lookup[s]

            components.append(subgraph)
        return components

    def dfs(self, v, visited, threshold, op=lt, first=None):
        aux = [v]
        visited.add(v)
        if first is None:
            first = v
        for i in (n for n, w in self.edges.get(v, {}).iteritems()
                  if op(w, threshold) and n not in visited):
            x, y = self.dfs(i, visited, threshold, op, first)
            aux.extend(x)
            visited = visited.union(y)
        return aux, visited

def main(args):
    graph = Graph()
    # first component
    graph.add_edge(0, 1, 1.0)
    graph.add_edge(1, 2, 1.0)
    graph.add_edge(2, 0, 1.0)

    # second component
    graph.add_edge(3, 4, 1.0)
    graph.add_edge(4, 5, 1.0)
    graph.add_edge(5, 3, 1.0)

    first, second = graph.connected_components(op=gt)
    print first.nodes
    print second.nodes

if __name__ == '__main__':
    main(sys.argv)

Ответ 5

Как указывали другие, нет необходимости изобретать велосипед. Многие мысли были приведены в оптимальные методы кластеризации. Здесь - одна известная программа кластеризации.

Ответ 6

Поиск оптимального разбиения графа является NP-трудной задачей, поэтому, независимо от алгоритма, это будет приближение или эвристика. Неудивительно, что различные алгоритмы кластеризации дают (дико) разные результаты.

Выполнение Python алгоритма модульности Ньюмена: модульность

Также: MCL, MCODE, CFinder, NeMo, clusterONE

Ответ 7

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

Многократное выполнение M '= MM не будет эффективным для больших порядков M. Полное умножение матрицы для матриц порядка N будет стоить N умножений и N-1 добавок на элемент, из которых N 2 то есть операции O (N 3). Если вы масштабируете это значение до "миллионов узлов", это будет операция O (10 18) для умножения матрицы-матрицы, из которых вы хотите сделать несколько.

Короче говоря, вы не хотите этого делать. Предложение SVD от Vartec было бы единственным подходящим выбором. Ваш лучший вариант - просто использовать PyMetis, а не пытаться изобрести графовое разбиение.

Ответ 8

Алгоритм SVD здесь не применим, но в противном случае Phil H является правильным.