Python'da grafikleri (veri yapısı) gösterme


105

Nasıl bir özenle temsil edebilir grafiği içinde Python ? (Sıfırdan başlayarak, yani kitaplık yok!)
Hangi veri yapısı (örneğin, diktler / tuples / dikt (tuples)) hızlı olacak ama aynı zamanda bellek açısından da verimli olacak? Üzerinde
çeşitli grafik işlemleri yapılabilmelidir .

Belirtildiği gibi, çeşitli grafik gösterimleri yardımcı olabilir. Bunları Python'da uygulamak nasıl olur?

Kütüphanelere gelince, bu sorunun oldukça iyi cevapları var.



1
Bir Grafik uygulamak için, yaygın uygulamaları ve bunların hem bellek hem de hızdaki etkinliklerini listeleyen Wikipedia makalesine bakın: en.wikipedia.org/wiki/…
Kassym Dorsel

GitHub.com/thePastor/pangaia'yı deneyebilirsiniz. Standart kütüphanenin varsayılan diktesini kullanmak için biraz yeniden yazılması gerekiyor (bu, kod yazıldığında dışarıda değildi). Diğer uygulamalardan daha zarif hale getirmek için yinelemeli bir veri yapısı kullanır.
theDoctor

1
İçin yönlendirilmiş grafikler, bu Python.org gelen kompozisyon bir anlaşılacağı dictait lists. Temelde bir şey gibi {<parent>: [<child>, ...], ...}.
djvg

Sözlüğü, düğümler olarak anahtarlar ve her anahtar için bitişik düğümlerin bir listesi olarak değerler içeren bitişiklik listesi olarak kullanarak uygulayabilirsiniz.
Shahrukh khan

Yanıtlar:


140

Bu biraz eski bir soru olsa da, bununla karşılaşan herkes için pratik bir cevap vereceğimi düşündüm.

Diyelim ki bağlantılarınız için giriş verilerinizi şöyle bir tuple listesi olarak aldınız:

[('A', 'B'), ('B', 'C'), ('B', 'D'), ('C', 'D'), ('E', 'F'), ('F', 'C')]

Python'daki grafikler için en kullanışlı ve verimli bulduğum veri yapısı, bir dizi settir . Bu, Graphsınıfımızın altında yatan yapı olacaktır . Ayrıca, bu bağlantıların yay (yönlendirilmiş, tek yönlü bağlanmış) veya kenarlar (yönsüz, her iki yolu da birbirine bağlama) olup olmadığını bilmelisiniz. Yönteme bir directedparametre ekleyerek bunu halledeceğiz Graph.__init__. Ayrıca başka yararlı yöntemler de ekleyeceğiz.

import pprint
from collections import defaultdict


class Graph(object):
    """ Graph data structure, undirected by default. """

    def __init__(self, connections, directed=False):
        self._graph = defaultdict(set)
        self._directed = directed
        self.add_connections(connections)

    def add_connections(self, connections):
        """ Add connections (list of tuple pairs) to graph """

        for node1, node2 in connections:
            self.add(node1, node2)

    def add(self, node1, node2):
        """ Add connection between node1 and node2 """

        self._graph[node1].add(node2)
        if not self._directed:
            self._graph[node2].add(node1)

    def remove(self, node):
        """ Remove all references to node """

        for n, cxns in self._graph.items():  # python3: items(); python2: iteritems()
            try:
                cxns.remove(node)
            except KeyError:
                pass
        try:
            del self._graph[node]
        except KeyError:
            pass

    def is_connected(self, node1, node2):
        """ Is node1 directly connected to node2 """

        return node1 in self._graph and node2 in self._graph[node1]

    def find_path(self, node1, node2, path=[]):
        """ Find any path between node1 and node2 (may not be shortest) """

        path = path + [node1]
        if node1 == node2:
            return path
        if node1 not in self._graph:
            return None
        for node in self._graph[node1]:
            if node not in path:
                new_path = self.find_path(node, node2, path)
                if new_path:
                    return new_path
        return None

    def __str__(self):
        return '{}({})'.format(self.__class__.__name__, dict(self._graph))

Bunu a find_shortest_pathve diğer yöntemler oluşturmak için bir "okuyucu alıştırması" olarak bırakacağım .

Yine de bunu eylemde görelim ...

>>> connections = [('A', 'B'), ('B', 'C'), ('B', 'D'),
                   ('C', 'D'), ('E', 'F'), ('F', 'C')]
>>> g = Graph(connections, directed=True)
>>> pretty_print = pprint.PrettyPrinter()
>>> pretty_print.pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'C'},
 'C': {'D'},
 'E': {'F'},
 'F': {'C'}}

>>> g = Graph(connections)  # undirected
>>> pretty_print = pprint.PrettyPrinter()
>>> pretty_print.pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'A', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'B'},
 'E': {'F'},
 'F': {'E', 'C'}}

>>> g.add('E', 'D')
>>> pretty_print.pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'A', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'}}

>>> g.remove('A')
>>> pretty_print.pprint(g._graph)
{'B': {'D', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'}}

>>> g.add('G', 'B')
>>> pretty_print.pprint(g._graph)
{'B': {'D', 'G', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'},
 'G': {'B'}}

>>> g.find_path('G', 'E')
['G', 'B', 'D', 'C', 'F', 'E']

6
Bu soru çok eski olmasına rağmen, bence bu tam da o zamanlar beklediğim cevap türü. Örnek, gerçekten basit bir şekilde aynı zamanda uygulamanın nasıl yürütüleceğini açıklamaya yardımcı oluyor. Farklı açık kaynak kitaplıklarından uygulamalar bulunabilir, ancak açıklama eşit olmaz. Teşekkürler!
shad0w_wa1k3r

2
kenarlara ağırlık eklemek için ne tür bir değişiklik gerekli?
pshirishreddy

3
@pshirishreddy İlginç soru! Bunu düşünmemiştim, ama içgüdülerim heapqlib'i kümeler yerine tuple listelerini yığınlamak için kullanmak olurdu . Örneğin, grafik aşağıdaki gibi yığınlardan oluşan bir diktedir: _graph = {'A': heapify([(0.3, 'D'), (0.5, 'B'), (0.75, 'A'), (0.9, 'C')])}(not: aslında heapifybu şekilde kullanmazsınız , kitaplık için yardımı okuyun), ardından heapqağırlıklı kenarları eklemek ve elde etmek için işlevleri kullanabilirsiniz .
mVChr

@mVChr, bu bir logzaman erişimi anlamına gelir . Ancak hem nodeID'yi hem de ağırlığı eşlemek için kullandığınız sözlüğü nasıl genişletebilirsiniz?
orezvani

Güzel ! İşlev özyinelemeli olarak çağrılır.Bu, düğümleri genişletmeye devam ettiği için bir DFS gibi görünüyor. En kısa yol için, yolların uzunluğunu karşılaştırabilir ve sonunda yalnızca en kısa olanı döndürebiliriz.
Jwalant Bhatt

36

NetworkX harika bir Python grafik kitaplığıdır. İhtiyaç duyduğunuz ve zaten yapmadığı bir şeyi bulmakta zorlanacaksınız.

Ve açık kaynak kodludur, böylece algoritmalarını nasıl uyguladıklarını görebilirsiniz. Ayrıca ek algoritmalar da ekleyebilirsiniz.

https://github.com/networkx/networkx/tree/master/networkx/algorithms


7
Bu nedenle NetworkX harika bir kaynaktır. Açık kaynak olduğundan, algoritmalarını nasıl uyguladıklarını görebilirsiniz. Ayrıca ek algoritmalar da ekleyebilirsiniz.
jterrace

2
İçin yaklaşık 2000 satırlık kod graph.py --> class Graph. Ve tek görmek istediğim nasıl kullandıkları __iter__.
T.Woody

8

İlk olarak, matris temsillerine karşı klasik liste seçimi amaca bağlıdır (gösterimle ne yapmak istediğinize). İyi bilinen problemler ve algoritmalar seçimle ilgilidir. Soyut temsil türünün seçimi, nasıl uygulanması gerektiğini belirler.

İkincisi, soru köşelerin ve kenarların sadece varoluş açısından mı ifade edileceğidir, yoksa bazı ekstra bilgiler mi taşırlar.

Python yerleşik veri türleri bakış açısından, başka bir yerde bulunan herhangi bir değer, hedef nesneye (gizli) bir başvuru olarak ifade edilir. Bir değişkense (yani adlandırılmış referans), o zaman ad ve referans her zaman (dahili) bir sözlükte saklanır. İsimlere ihtiyacınız yoksa, referans kendi kapsayıcınızda saklanabilir - burada muhtemelen Python listesi her zaman liste için özet olarak kullanılacaktır .

Python listesi dinamik bir referans dizisi olarak uygulanır, Python tuple sabit içeriğe sahip statik referans dizisi olarak uygulanır (referansların değeri değiştirilemez). Bu nedenle kolayca indekslenebilirler. Bu şekilde liste, matrislerin uygulanması için de kullanılabilir.

Matrisleri temsil etmenin başka bir yolu, standart modül tarafından uygulanan dizilerdir array- depolanan tür, homojen değer açısından daha kısıtlıdır. Öğeler, değeri doğrudan saklar. (Liste, bunun yerine değer nesnelerine yapılan başvuruları depolar). Bu şekilde, daha fazla bellek verimlidir ve ayrıca değere erişim daha hızlıdır.

Bazen, daha kısıtlı temsiller gibi yararlı bulabilirsiniz bytearray.


7

İki mükemmel grafik kütüphaneleri vardır NetworkX ve iGRAPH . Her iki kitaplık kaynak kodunu GitHub'da bulabilirsiniz. Fonksiyonların nasıl yazıldığını her zaman görebilirsiniz. Ama ben NetworkX'i tercih ediyorum çünkü anlaşılması kolay.
İşlevleri nasıl yaptıklarını öğrenmek için kodlarına bakın. Birden fazla fikir alacaksınız ve ardından veri yapılarını kullanarak nasıl bir grafik yapmak istediğinizi seçebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.