Перейти к содержимому

Как создать ориентированный граф питон

  • автор:

Как на Python написать код для построения графа по матрице смежности чтобы только односторонние стрелки отображались?

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

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

Отслеживать

задан 2 июн 2022 в 14:59

1 2 2 бронзовых знака

Что-что нужно? В вершине 4 только одна входящая дуга, и стрелка в неё только одна.

3 июн 2022 в 3:33

Значит отредактируйте матрицу смежности, выберите какие связи/стрелки оставить.

14 ноя 2022 в 17:29

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Параметр «arrows» изменить на False

 nx.draw(G, with_labels=True, node_size=300, arrows=False) 

Отслеживать

ответ дан 14 ноя 2022 в 17:25

1 1 1 бронзовый знак

добро пожаловать на Stack Overflow на русском! пожалуйста, постарайтесь оставлять чуть более развёрнутые ответы. дополнить ответ можно, нажав править

Отображение графа на Python с networkx

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

Можно сделать несколько наблюдений:

  • Английский (en) — основной язык, на который переводятся все остальные языки; в то же время многие англоязычные материалы переводятся на другие языки.
  • Китайский (zh) переводится на японский (ja), но не наоборот.
  • И китайский, и японский материалы переведены на английский, и наоборот.

Я же расскажу о том, как для отображения графов использовать пакет networkx.

Установка networkx

Чтобы установить этот пакет, используйте команду pip:

!pip install networkx

Терминология

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

  • Узел — фундаментальный элемент графа, общеизвестный под названием вершина.
  • Ребро — соединение узлов графа.
  • Неориентированный граф не имеет направления между узлами, то есть не имеет стрелок, а его ребра двунаправлены.

Создание графа

Давайте шаг за шагом создадим граф.

Во-первых, создадим объект класса networkx.classes.graph.Graph:

import networkx as nx G = nx.Graph() print(G) # Graph with 0 nodes and 0 edges

Класс nx.Craph() создает неориентированный граф. Если захочется создать ориентированный, используйте класс nx.DiGraph(directed=True), который возвращает объект networkx.classes.digraph.DiGraph.

В этой статье поговорим об ориентированных графах.

Добавим узлы

Фрагмент кода ниже добавляет три узла без ребер:

G.add_node("Singapore") G.add_node("San Francisco") G.add_node("Tokyo") print(G) # Graph with 3 nodes and 0 edges

Помимо функции add_node() для добавления индивидуальных узлов, чтобы добавить множество узлов, можно воспользоваться функцией add_nodes_from():

G.add_nodes_from(["Riga", "Copenhagen"]) print(G) # Graph with 5 nodes and 0 edges

Сейчас у графа 5 узлов.

Добавим ребра

Теперь, когда узлы определены, определим ребра, чтобы соединить их:

G.add_edge("Singapore","San Francisco") G.add_edge("San Francisco","Tokyo") G.add_edges_from( [ ("Riga","Copenhagen"), ("Copenhagen","Singapore"), ("Singapore","Tokyo"), ("Riga","San Francisco"), ("San Francisco","Singapore"), ] ) print(G) # Graph with 5 nodes and 6 edges

Как и узлы, ребра можно добавлять по одному, при помощи add_edge(), или группами — при помощи add_edges_from() со списком кортежей, представляющих каждый узел.

Рисуем граф

Я покажу основы отображения сетевых графов при помощи пакета networkx. Начнем:

nx.draw(G)

Вы увидите что-то такое:

Запомните, что граф будет другим при каждом вызове draw():

Вот другое изображение того же графа:

Отображение меток

Само собой, граф без меток не очень полезен, если вообще полезен, поэтому давайте отрисуем метки:

nx.draw(G, with_labels = True)

Функция draw() с параметром with_labels — эквивалент функций в списке ниже:

  • nx.draw_networkx_nodes() — рисует все узлы графа;
  • nx.draw_networkx_labels() — рисует метки на каждом узле;
  • nx.draw_networkx_edges() — рисует ребра, соединяющие узлы.

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

И теперь мы видим метку каждого узла:

Применение макетов

Помните, что функция draw() каждый раз использует разные макеты? Так вот, для графа можно указать конкретный макет:

pos = nx.circular_layout(G) nx.draw(G, pos, with_labels = True)

Узлы упорядочены так, что по ним можно очертить круг:

Кроме того, график с круговой компоновкой можно нарисовать с помощью nx.draw_circular(), а не nx.draw():

nx.draw_circular(G, with_labels = True)

Можно попробовать другие макеты:

  • nx.draw_kamada_kawai(G, with_labels = True);
  • nx.draw_planar(G, with_labels = True);
  • nx.draw_random(G, with_labels = True);
  • nx.draw_spectral(G, with_labels = True);
  • nx.draw_spring(G, with_labels = True);
  • nx.draw_shell(G, with_labels = True);

Разметка ребер

Ребра можно отметить при помощи nx.draw_networkx_edge_labels(). Фрагмент кода ниже размечает два ребра трех узлов:

pos = nx.circular_layout(G) nx.draw(G, pos, with_labels = True) nx.draw_networkx_edge_labels( G, pos, edge_labels=< ("Singapore","Tokyo"): '2 flights daily', ("San Francisco","Singapore"): '5 flights daily', >, font_color='red' )

Ориентированный граф

Иногда полезно построить ориентированный граф. В нашем примере ребра могут представлять рейсы между двумя городами. Ориентированный граф позволяет увидеть, какие рейсы идут из одного города в другой. Следующий фрагмент кода показывает наш пример в виде ориентированного графа:

import networkx as nx #---directed graph--- G = nx.DiGraph(directed=True) # add nodes G.add_node("Singapore") G.add_node("San Francisco") G.add_node("Tokyo") G.add_nodes_from(["Riga", "Copenhagen"]) # add edges G.add_edge("Singapore","San Francisco") G.add_edge("San Francisco","Tokyo") G.add_edges_from( [ ("Riga","Copenhagen"), ("Copenhagen","Singapore"), ("Singapore","Tokyo"), ("Riga","San Francisco"), ("San Francisco","Singapore"), ] ) # set layout pos = nx.circular_layout(G) # draw graph nx.draw(G, pos, with_labels = True) # draw edge labels nx.draw_networkx_edge_labels( G, pos, edge_labels=< ("Singapore","Tokyo"): '2 flights daily', ("San Francisco","Singapore"): '5 flights daily', >, font_color='red' )

Теперь вы видите, что есть рейсы из Сингапура в Сан-Франциско и наоборот; с другой стороны, есть рейсы из Риги в Сан-Франциско, но не наоборот:

Настройка узлов

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

options = < 'node_color': 'yellow', # color of node 'node_size': 3500, # size of node 'width': 1, # line width of edges 'arrowstyle': '-|>', # array style for directed graph 'arrowsize': 18, # size of arrow 'edge_color':'blue', # edge color > nx.draw(G, pos, with_labels = True, arrows=True, **options)

Сейчас узлы желтые и они больше, а ребра синие:

Очерчивание узлов

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

pos = nx.circular_layout(G) options = < 'node_color': 'yellow', 'node_size': 8500, 'width': 1, 'arrowstyle': '-|>', 'arrowsize': 18, > nx.draw(G, pos, with_labels = True, arrows=True, **options) ax = plt.gca() ax.collections[0].set_edgecolor("#000000")

Теперь каждый узел обведен черным:

Если не установить размер рисунка, граф будет выглядеть так:

Раскрашивание узлов

Чтобы раскрасить каждый узел разными цветами, можно определить цветовую палитру, такую как в bokeh, и установить значение ключу словаря node_color, затем передав его в draw():

from networkx import * import matplotlib.pyplot as plt from bokeh.palettes import Spectral plt.figure(figsize=(8, 8)) pos = nx.circular_layout(G) options = < 'node_color': Spectral[5], # first 5 colors from the Spectral palette 'node_size': 8500, 'width': 1, 'arrowstyle': '-|>', 'arrowsize': 18, > nx.draw(G, pos=pos, with_labels = True, arrows=True, **options) ax = plt.gca() ax.collections[0].set_edgecolor("#000000")

И теперь узлы графа раскрашены разными цветами:

Если захочется указать свой цвет, установите его вручную:

options = < 'node_color': ['yellow','magenta','lightblue','lightgreen','pink'], 'node_size': 8500, 'width': 1, 'arrowstyle': '-|>', 'arrowsize': 18, >

Вот и все на сегодня. А на наших курсах — полезная теория и много практики:

  • Профессия «Белый хакер» (13 месяцев)
  • Профессия Fullstack-разработчик на Python (16 месяцев)

Краткий каталог курсов

Data Science и Machine Learning

  • Профессия Data Scientist
  • Профессия Data Analyst
  • Курс «Математика для Data Science»
  • Курс «Математика и Machine Learning для Data Science»
  • Курс по Data Engineering
  • Курс «Machine Learning и Deep Learning»
  • Курс по Machine Learning

Python, веб-разработка

  • Профессия Fullstack-разработчик на Python
  • Курс «Python для веб-разработки»
  • Профессия Frontend-разработчик
  • Профессия Веб-разработчик

Мобильная разработка

  • Профессия iOS-разработчик
  • Профессия Android-разработчик

Java и C#

  • Профессия Java-разработчик
  • Профессия QA-инженер на JAVA
  • Профессия C#-разработчик
  • Профессия Разработчик игр на Unity

От основ — в глубину

  • Курс «Алгоритмы и структуры данных»
  • Профессия C++ разработчик
  • Профессия «Белый хакер»

А также

Как создать ориентированный граф питон

graph

Граф задаётся множеством вершин V и множеством рёбер E, соединяющих пары вершин. (Английская терминология: вершина называется vertex или node, а ребро — edge.)

В примере с картой V = , а рёбра из E соединяют страны, граничащие друг с другом (в частности, E содержит рёбра , и ). Отношение «быть соседом» симметрично, так что ребро из x в y возникает одновременно с ребром из y в x. Такие рёбра называются неориентированными (undirected), а соответствующий граф — неориентированным графом (undirected graph).

Бывают и несимметричные отношения, и их изображают ориентированными рёбрами (directed edges). В ориентированном графе наличие ребра из x в y не гарантирует наличие ребра из y в x.

Если явно не указано обратное, то рассматривают только графы без петель (loop) и без кратных рёбер (multiple edge). Такие графы называются простыми (simple).

Самые простые графы — это деревья. Во многих задачах, связанных с графами, используется обходы графа, в результате которых в графе выделяются деревья, состоящие из некоторых вершин графа и некоторых его рёбер. У этих деревьев отмечена вершина, которая называется его корнем (root). Дерево с отмеченной вершиной — корнем — называется корневым деревом (rooted tree).

В корневых деревьях чётко различаются путь к корню и путь от корня. Сосед вершины на пути к корню называется её родителем (parent). Соседи вершины на пути от корня называются её детьми или сыновьими вершинами (child, children). Все вершины на пути из вершины к корню, кроме исходной, называются её предками (ancestor). Все вершины на пути из корня называются потомками (descendant). Вершины, у которых общий родитель, называются братьями (sibling). И наконец вершины, у которых нет потомков, называются листьями (leaf, leaves, terminal vertex).

Представление графа

Рассмотрим граф c $n = |V|$ вершинами $v_1,\ldots‌,v_n$. Его матрицей смежности (adjacency matrix) называется $(n\times n)$-матрица a, в которой $$ a_ = \begin 1,&\text\\ 0 &\text \end $$ Матрица смежности неориентированного графа, таким образом, симметрична ($a_ = a_$). В таком представлении мы можем за время O(1) проверить, соединены ли данные вершины ребром (посмотрев на один элемент массива). В то же время хранение матрицы требует памяти O(n 2 ), что во многих случаях неэкономно. Альтернативное представление — список смежности (adjacency list). Требуемая при этом память пропорциональна размеру графа (сумме числа вершин и числа рёбер). Элементами списка смежности являются списки, по одному для каждой вершины графа. Для вершины u в таком списке хранятся вершины, в которые ведут рёбра из u, то есть вершины v, для которых $(u, v) \in E$. Для ориентированного графа каждое ребро входит только в один из этих списков (для начальной вершины), а для неориентированного в два (для двух концов ребра). Список смежности, таким образом, требует памяти $O(|V|+ |E|)$. Проверка наличия ребра $(u, v)$ теперь требует просмотра списка вершины u (что может быть больше O(1) шагов, если вершины хранятся именно в списке, а не в множестве или словаре). Зато в этом представлении легко просмотреть всех соседей заданной вершины (что часто бывает необходимо). Список смежности для неориентированного графа симметричен (если u содержится в списке для v, то и v содержится в списке для u).

Матрица смежности или список смежности?

Что лучше? Ответ зависит от отношения между числом вершин $|V|$ и числом рёбер $|E|$. Заметим, что $|E|$ может быть довольно малым — порядка $|V|$ (если $|E|$ сильно меньше $|V|$, то граф уже вырожденный — в частности, содержит изолированные вершины). Или же довольно большим — порядка $|V|^2$ (если граф содержит все возможные рёбра). Графы с большим числом рёбер называют плотными (dense), с малым — разреженными (sparse).
Выбирая алгоритм, полезно понимать, с какими графами ему в основном придётся иметь дело. Важно это и при хранении: если хранить веб-граф, в котором больше восьми миллиардов вершин, в виде матрицы смежности, то занимать она будет миллионы терабайтов, что сравнимо с общей ёмкостью всех жёстких дисков в мире. И дальше, скорее всего, будет только хуже (матрица может расти быстрее, чем производство дисков). А вот хранить граф Интернета в виде списка смежности вполне разумно, поскольку хранить нужно несколько десятков миллиардов гиперссылок, каждая из которых будет занимать в списке всего несколько байтов, и такого размера диск поместится в карман. Такая разница происходит из-за того, что граф Интернета очень разрежен: страница содержит в среднем пару десятков ссылок на другие страницы — из нескольких миллиардов возможных.

Напоминание про множества и словари

Про множества

Задание множеств
A = set() # Пустое множество A = # Явное перечисление элементов A = set('hello') # Множество букв в строке B = [1, 2, 1, 2]; A = set(B) # Множество из списка или любого итерируемого объекта A = # Генератор множеств
Работа с элементами множеств
C = for elem in C: # Перебираем все элементы множества print(elem) sorted(C) # Список из отсортированных элементов множества 1 in C # Проверка принадлежности A.add(3) # Добавление элемента A.remove(3) # Удаление элемента, который есть в множестве A.discard(4) # Удаление элемента, которого может не быть в множестве A.pop() # Извлечение случайного элемента из множества с удалением его
Операции с множествами
len(A) # Количество элементов в множестве A | B # Возвращает множество, являющееся объединением множеств A и B. A |= B # Добавляет в множество A все элементы из множества B. A & B # Возвращает множество, являющееся пересечением множеств A и B. A &= B # Оставляет в множестве A только те элементы, которые есть в множестве B. A - B # Возвращает разность множеств A и B (A, но не B). A -= B # Удаляет из множества A все элементы, входящие в B. A ^ B # Возвращает симметрическую разность множеств A и B. A ^= B # Записывает в A симметрическую разность множеств A и B. A = B # Возвращает true, если B является подмножеством A. A < B # Эквивалентно A B # Эквивалентно A >= B and A != B

Про словари

Задание словарей
D = <> # Пустой словарь D = # Явное перечисление D = dict([(1, 'a'), (2, 'b')]) # Словарь из списка пар элементов D = dict(zip([1, 2], ['a', 'b'])) # Словарь из итерируемого объекта, возвращающего пары элементов (ключ-значение) D = # Генератор словарей
Работа с элементами словаря
len(D) # Количество элементов в словаре D[key] # Поиск по ключу, который есть в словаре key in D # Проверка принадлежности словарю D[key] = value # Установка или изменение значения del D[key] # Удаление ключа, который есть в словаре value = D.pop(key) # Удаления ключа вместе с возвращением значения value = D.pop(key, no_key_value) # Удаления ключа вместе с возвращением значения. Если ключа нет, то no_key_value key, value = D.popitem() # Извлечение из словаря пары (ключ, значение) с удалением ключа D.get(key, no_key_value) # Значение по ключу, no_key_value, если ключа нет D[key] = D.get(key, 0) + 1 # Самая простая реализация счётчика for key in D: # Перебираем все ключи print(key, D[key]) for key, value in D.items(): # Перебираем все пары (ключ, значение) print(key, value) for value in D.values(): # Перебор всех значений print(value) sorted(D) # Отсортированный список ключей sorted(D.values()) # Отсортированный список значений sorted(D.items()) # Отсортированный по ключу список пар (ключ, значение) sorted(D.items(), key=lambda x: x[1]) # Отсортированный по значению список пар (ключ, значение)

Список смежности и матрица смежности: туда и обратно

A: От матрицы смежности к списку ребер, неориентированный вариант

Простой неориентированный граф задан матрицей смежности, выведите его представление в виде списка ребер.

Входные данные включают число n — количество вершин в графе, а затем n строк по n чисел, каждое из которых равно 0 или 1, — его матрицу смежности. Выведите список ребер заданного графа (в любом порядке).

5 0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0
1 3 2 3 2 5

B: От списка ребер к матрице смежности, неориентированный вариант

Простой неориентированный граф задан списком ребер, выведите его представление в виде матрицы смежности.

Входные данные включают число n — количество вершин в графе, и число m — количество рёбер. Затем следует m пар чисел — ребра графа. Выведите матрицу смежности заданного графа.

5 3 1 3 2 3 2 5
0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0

C: От матрицы смежности к списку ребер, ориентированный вариант

Ориентированный граф задан матрицей смежности, выведите его представление в виде списка ребер.

Входные данные включают число n — количество вершин в графе, а затем n строк по n чисел, каждое из которых равно 0 или 1, — его матрицу смежности. Выведите список ребер заданного графа (в любом порядке).

5 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
2 5 3 1 3 2

D: От списка ребер к матрице смежности, ориентированный вариант

Простой ориентированный граф задан списком ребер, выведите его представление в виде матрицы смежности.

Входные данные включают число n — количество вершин в графе, и число m — количество рёбер. Затем следует m пар чисел — ребра графа. Выведите матрицу смежности заданного графа.

5 3 1 3 2 3 5 2
0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0

Как хранить графы в программе

В графе в отличие от корневого дерева нет корня и нет разницы между предками и потомками. Все рёбра равнозначны. Поэтому удобно хранить граф в виде словаря, ключами которого являются все вершины, а значениями — множество вершин, соединённых с данной. Если граф неориентированный, то каждое ребро (v, w) «хранится» сразу в двух множествах.

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

В спортивном программировании вершины графа — обычно последовательные числа от 1 до $n$. В этом случае граф лучше хранить как список списков или вектор векторов (в C++). Однако в этом разминочном листке предполагается использование питона и словаря множеств. Это удобно и быстро. Спортивные модификации алгоритмов будут в отдельном листке.

E: digraph_from_input

Directed_acyclic_graph.png

Программа получает на вход число рёбер в ориентированном графе N . Далее следует N строк, задающие рёбра графа. Каждая строка имеет вид откуда_ребро куда_ребро .

Программа должна сформировать словарь digraph смежности ориентированного графа и вывести его при помощи функции pprint из модуля pprint .

Реализуйте функцию digraph_from_input() , которая считывает граф в таком формате из стандартного ввода и возвращает словарь digraph .

8 A B B C B D B E C E D E E F G D
<'A': <'B'>, ‘B’: , ‘C’: , ‘D’: , ‘E’: , ‘F’: set(), ‘G’: >

F: graph_from_input

Indirected_acyclic_graph.png

Программа получает на вход число рёбер в неориентированном графе N . Далее следует N строк, задающие рёбра графа. Каждая строка имеет вид откуда_ребро куда_ребро .

Программа должна сформировать словарь graph смежности неориентированного графа и вывести его при помощи функции pprint из модуля pprint .

Реализуйте функцию graph_from_input() , которая считывает граф в таком формате из стандартного ввода и возвращает словарь graph .

8 A B B C B D B E C E D E E F G D
<'A': <'B'>, ‘B’: , ‘C’: , ‘D’: , ‘E’: , ‘F’: , ‘G’: >

G: Степень вершины

Indirected_acyclic_graph.png

Дан неориентированный граф.

Выведите степень каждой вершины. Вершины должны выводиться в лексикографическом порядке.

8 A B B C B D B E C E D E E F G D
A 1 B 4 C 2 D 3 E 4 F 1 G 1

H: Эйлеров цикл в связном графе

Indirected_acyclic_graph.png

Дан связный неориентированный граф.

Выведите YES , если в нём есть эйлеров цикл, и NO иначе.

Python в СРЦОД: 8. Матрица смежности и список ребер

Граф называется неориентированным, если по всем его ребрам можно ходить в обоих направлениях. Так как граф простой, то у него не должно быть петель (т.е. для любой вершины этого графа нет ребра в её саму). Считываем в матрицу смежности весь граф и проверяем, чтобы для любого i и j (i!=j) выполнялось a[i][j]==a[j][i], а если i==j, то a[i][j] всегда должно быть равно нулю.

По заданной квадратной матрице \(n times n\) —>n×n из нулей и единиц определите, может ли данная матрица быть матрицей смежности простого неориентированного графа.

Входные данные

На вход программы поступает число \(n\) \((1 \le n \le 100)\) – размер матрицы, а затем n строк по \(n\) чисел, каждое из которых равно 0 или 1, – сама матрица.

Выходные данные

Выведите «YES», если приведенная матрица может быть матрицей смежности простого неориентированного графа, и «NO» в противном случае.

Входные данные

5 0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *