Какая структура данных графа наиболее эффективна в Python?

Мне нужно иметь возможность манипулировать большим (10 ^ 7 узлов) графом в python. Данные, соответствующие каждому узлу / ребру, минимальны, скажем, небольшое количество строк. Какой способ сделать это наиболее эффективно с точки зрения памяти и скорости?

Dict of dicts более гибкий и простой в реализации, но я интуитивно ожидаю, что список списков будет быстрее. Опция списка также потребовала бы, чтобы я держал данные отдельно от структуры, в то время как dicts допускал бы что-то в этом роде:

graph[I][J]["Property"]="value"

Что ты предлагаешь?


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

Загрузка данных в память не является большой проблемой. Это делается раз и навсегда. Отнимающая много времени часть - это посещение узлов, чтобы я мог извлечь информацию и измерить интересующие меня метрики.

Я не думал о том, чтобы сделать каждый узел классом (свойства одинаковы для всех узлов), но похоже, что это добавит дополнительный уровень накладных расходов? Я надеялся, что у кого-то будет какой-то непосредственный опыт в подобном случае, которым они могли бы поделиться. В конце концов, графы - одна из самых распространенных абстракций в CS.

Ответов (7)

Решение

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

Пример генерации и анализа случайных графов Эрдеша-Реньи


"""
Create an G{n,m} random graph with n nodes and m edges
and report some properties.

This graph is sometimes called the Erd##[m~Qs-Rényi graph
but is different from G{n,p} or binomial_graph which is also
sometimes called the Erd##[m~Qs-Rényi graph.
"""
__author__ = """Aric Hagberg ([email protected])"""
__credits__ = """"""
#    Copyright (C) 2004-2006 by 
#    Aric Hagberg 
#    Dan Schult 
#    Pieter Swart 
#    Distributed under the terms of the GNU Lesser General Public License
#    http://www.gnu.org/copyleft/lesser.html

from networkx import *
import sys

n=10 # 10 nodes
m=20 # 20 edges

G=gnm_random_graph(n,m)

# some properties
print "node degree clustering"
for v in nodes(G):
    print v,degree(G,v),clustering(G,v)

# print the adjacency list to terminal 
write_adjlist(G,sys.stdout)

Визуализации также просты:

введите описание изображения здесь

Дополнительная визуализация: http://jonschull.blogspot.com/2008/08/graph-visualization.html

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

Однако с точки зрения производительности загрузка его в память может не быть проблемой, но если вы используете слишком много, вы в конечном итоге переключитесь на диск, что убьет производительность даже высокоэффективных dicts Python. Постарайтесь максимально снизить использование памяти. Кроме того, оперативная память сейчас очень дешевая; если вы занимаетесь подобными вещами часто, нет причин не иметь как минимум 4 ГБ.

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

Без сомнения, NetworkX до сих пор является лучшей структурой данных для графа. Он поставляется с такими утилитами, как вспомогательные функции, структуры и алгоритмы данных, генераторы случайных последовательностей, декораторы, упорядочение Катхилла-Макки, менеджеры контекста.

NetworkX хорош тем, что отлично подходит для графов, орграфов и мультиграфов. Он может писать граф несколькими способами: список смежности, многострочный список смежности, список границ, GEXF, GML. Он работает с Pickle, GraphML, JSON, SparseGraph6 и т. Д.

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

Словарь также может содержать служебные данные, в зависимости от фактической реализации. Хеш-таблица обычно для начала содержит некоторое простое количество доступных узлов, даже если вы можете использовать только пару узлов.

Судя по вашему примеру «Собственность», не лучше ли вам использовать классовый подход для последнего уровня и реальной собственности? Или названия свойств сильно меняются от узла к узлу?

Я бы сказал, что то, что означает «эффективный», зависит от многих вещей, например:

  • скорость обновления (вставка, обновление, удаление)
  • скорость получения произвольного доступа
  • скорость последовательного поиска
  • используемая память

Я думаю, вы обнаружите, что быстрая структура данных обычно потребляет больше памяти, чем медленная. Это не всегда так, но, похоже, большинство структур данных следуют этому.

Словарь может быть простым в использовании и обеспечивать относительно равномерно быстрый доступ, он, скорее всего, будет использовать больше памяти, чем, как вы предполагаете, списки. Однако списки, как правило, содержат больше накладных расходов, когда вы вставляете в них данные, если только они не выделяют предварительно X-узлы, в которых они снова будут использовать больше памяти.

В общем, я предлагаю просто использовать метод, который кажется вам наиболее естественным, а затем провести «стресс-тест» системы, добавив к ней значительный объем данных и посмотреть, не станет ли это проблемой.

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

Создание структуры на основе классов, вероятно, будет иметь больше накладных расходов, чем структура на основе dict, поскольку в классах python фактически используются dicts при их реализации.

Как уже упоминалось, NetworkX очень хорош, есть еще один вариант - igraph . Оба модуля будут иметь большинство (если не все) инструментов анализа, которые могут вам понадобиться, и обе библиотеки обычно используются в больших сетях.

Несмотря на то, что этот вопрос сейчас довольно старый, я думаю, что стоит упомянуть мой собственный модуль python для манипуляции с графами, называемый графическим инструментом . Это очень эффективно, поскольку структуры данных и алгоритмы реализованы на C++ с метапрограммированием шаблонов с использованием библиотеки Boost Graph Library. Поэтому его производительность (как в использовании памяти, так и во время выполнения) сопоставима с чистой библиотекой C++ и может быть на несколько порядков лучше, чем типичный код Python, без ущерба для простоты использования. Сам постоянно использую для работы с очень большими графиками.