Пишем Python-скрипт для работы с графом в Neo4j

Cypher Python Neo4j AuraDB графы примеры курсы обучение, обучение Neo4j примеры, курсы дата-аналитик Neo4j Python примеры обучение, обучение аналитике больших данных, Neo4j задачи на графах бизнес приложения примеры, Cypher Python Neo4j примеры курсы обучение, обучение большим данным, Школа Больших Данных Учебный Центр Коммерсант

Сегодня решим логистическую задачу поиска кратчайшего пути, создав граф знаний в Neo4j, развернутой в облачной платформе Aura DB и визуализируем найденный путь с помощью Python-библиотеки Networkx.

Работа с Neo4j в AuraDB

В прошлой статье мы упоминали, что для работы с популярной графовой СУБД Neo4j совсем необязательно устанавливать ее локально. Можно воспользоваться облачным сервисом AuraDB, тарифная политика предлагает вариант бесплатного использования. Однако, бесплатный тариф не включает библиотек  с графовыми алгоритмами, такие как Graph Data Science, о которой мы писали здесь, и APOC. Поэтому придется писать функции для работы с графами на внутреннем языке запросов Cypher. Но перед этим посмотрим, как обратиться к графовой базе данных из кода Python-приложения.

Как обычно, я буду писать и запускать Python-скрипт в интерактивной среде Google Colab. Программа будет выполнять следующие действия:

  1. Подключаться к облачному инстансу графовой базы данных Neo4j , развернутом в сервисе AuraDB;
  2. Создавать граф знаний, состоящий из вершин разных типов, например, магазин (Shop) и склад (Store), которые находятся на разном расстоянии друг от друга. Зададим расстояние между вершинами в километрах и определим время в минутах для преодоления этого расстояния.
  3. Искать кратчайший путь между заданными вершинами с указанием расстояния этого пути и времени его прохождения.

Сперва установим необходимые библиотеки и импортируем нужные модули с помощью следующего кода:

##############################################ячейка №1 в Google Colab#######################
#установка библиотек
!pip install neo4j
!pip install neo4jupyter

#импорт модулей
from neo4j import GraphDatabase
import logging
from neo4j.exceptions import ServiceUnavailable
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
import pandas as pd

Графовые алгоритмы. Бизнес-приложения

Код курса
GRAF
Ближайшая дата курса
по запросу
Продолжительность
24 ак.часов
Стоимость обучения
54 000 руб.

Далее напишем код создания направленного графа в Neo4j:

##############################################ячейка №2 в Google Colab - создание графа##############################################
# Определение класса
class App:

    # Конструктор класса
    def __init__(self, uri, user, password):
        # Инициализация драйвера Neo4j с переданными параметрами
        self.driver = GraphDatabase.driver(uri, auth=(user, password))        

    # Метод класса для закрытия соединения с базой данных Neo4j
    def close(self):
        # Не забудьте закрыть соединение драйвера, когда закончите работу с ним
        self.driver.close()

    # Метод класса для создания графа
    def create_graph(self):
        with self.driver.session() as session:
            # Выполнение запроса на создание графа
            session.execute_write(self._create_graph)
            
    # Статический метод, который содержит запрос на создание графа 
    @staticmethod
    def _create_graph(tx):
        tx.run("""
        CREATE (A:Store { name: 'A' })
        CREATE (B:Shop { name: 'B' })
        CREATE (C:Store { name: 'C' })
        CREATE (D:Store { name: 'D' })
        CREATE (E:Shop { name: 'E' })
        CREATE (F:Store { name: 'F' })
        CREATE (G:Store { name: 'G' })
        CREATE (H:Shop { name: 'H' })
        CREATE (I:Shop { name: 'I' })
        CREATE (A)-[:way { minutes: 90, km: 10 }]->(B)
        CREATE (B)-[:way { minutes: 80, km: 9 }]->(C)
        CREATE (C)-[:way { minutes: 75, km: 8 }]->(I)
        CREATE (A)-[:way { minutes: 55, km: 6 }]->(D)
        CREATE (D)-[:way { minutes: 35, km: 5 }]->(E)
        CREATE (D)-[:way { minutes: 30, km: 4 }]->(B)
        CREATE (E)-[:way { minutes: 10, km: 2 }]->(I)
        CREATE (A)-[:way { minutes: 60, km: 7 }]->(F)
        CREATE (F)-[:way { minutes: 270, km: 20 }]->(G)
        CREATE (F)-[:way { minutes: 7, km: 1 }]->(E)
        CREATE (G)-[:way { minutes: 40, km: 5 }]->(H)
        CREATE (H)-[:way { minutes: 20, km: 2 }]->(I)
        """)
        
# Основная программа
if __name__ == "__main__":
    # Aura запросы используют зашифрованное соединение с использованием схемы URI "neo4j+s"
    uri = "neo4j+s://.....здесь подключение к вашему экземпляру Neo4j........."
    user = "....здесь ваш пользователь......"
    password = "........здесь пароль вашего пользователя.........."
    # Создание экземпляра класса, передача параметров для инициализации драйвера Neo4j
    app = App(uri, user, password)
    # Вызов метода класса для создания графа
    app.create_graph()
    # Закрытие соединения с базой данных Neo4j
    app.close()

В этом Python-коде Cypher-скрипт для создания вершин и ребер является аргументом метода run() объекта tx в UDF-функции create_graph(self). Метод run() запускает Cypher-запрос в транзакции с автоматической фиксацией. Запрос отправляется, и заголовок результата будет получен немедленно, но содержимое результата будет извлекаться отложено, по мере использования клиентским приложением. В результате выполнения этого запроса в Neo4j создастся граф знаний, который в AuraDB визуализируется так:

Neo4j AuraDB, NoSQL графовая база данных
Интерфейс Neo4j в AuraDB

Далее напишем скрипт поиска кратчайшего пути, например, между точками A и I. Для визуализации найденного пути в области вывода Google Colab будем использовать библиотеку Networkx, которую мы ранее рассматривали здесь. Python-скрипт выглядит следующим образом:

##############################################ячейка №3 в Google Colab - поиск и визуализация кратчайшего пути между двумя точками###################################
# Определение класса
class App:

    # Конструктор класса
    def __init__(self, uri, user, password):
        self.driver = GraphDatabase.driver(uri, auth=(user, password))

    # Метод класса для закрытия соединения с базой данных Neo4j
    def close(self):
        # Don't forget to close the driver connection when you are finished with it
        self.driver.close()

    # Метод для поиска кратчайшего пути между двумя узлами графа
    def find_shortest_paths(self, from_node, to_node):
        with self.driver.session() as session:
            result = session.run("""
                MATCH (from:{from_label} {{ name: $from_name }}), (to:{to_label} {{ name: $to_name }}), 
                      path = (from)-[*]->(to) 
                RETURN distinct nodes(path) AS shortestPath, 
                    length(path),  
                    min(reduce(km = 0, r in relationships(path) | km+r.km)) AS totalDistance,   
                    min(reduce(minutes = 0, r in relationships(path) | minutes+r.minutes)) AS totalTime
                ORDER BY length(path), totalTime, totalDistance
            """.format(from_label=from_node['label'], to_label=to_node['label']),
            from_name=from_node['name'], to_name=to_node['name']) # запрос к БД

            result_list = list(result) # полученный результат в виде списка
            result.consume()  # добавляем эту строку, чтобы потреблять все результаты запроса

            paths = [] # список для хранения путей между узлами графа
            if result_list:
                for r in result_list:
                    path = r['shortestPath']
                    nodes = [p['name'] for p in path]
                    distance = r['totalDistance']
                    time = r['totalTime']

                    for i in range(len(nodes)-1):
                        source = nodes[i]
                        target = nodes[i+1]
                        paths.append({'Source': source, 'Target': target, 'Distance': distance, 'Time': time})
                    
                    df = pd.DataFrame(paths)
                    df = df.drop_duplicates() # удаление дубликатов

                    graph=nx.DiGraph()
                    graph.add_weighted_edges_from(df[['Source', 'Target', 'Distance']].values) # добавление весов на каждое ребро графа

                    # добавляем атрибут 'Time' к каждому ребру графа
                    nx.set_edge_attributes(graph, df.set_index(['Source', 'Target'])['Time'].to_dict(), 'Time')
                    pos = nx.spring_layout(graph, k=1000)
                    edges = [(u, v) for (u, v, d) in graph.edges(data=True) if d['weight'] > 1]

                    nx.draw_networkx_nodes(graph, pos, node_size=500)
                    nx.draw_networkx_labels(graph, pos)
                    nx.draw_networkx_edges(graph, pos, edgelist=edges, edge_color='b', arrows=True)

                    plt.show() # вывод графа с найденным путем

                    return print('Самый короткий путь между точками: ', nodes, 'расстояние ', distance, ' км, время', time, ' минут') #вывод результата
            else:
                print("Путей между этими точками не найдено")
                return pd.DataFrame()
      
# Основная программа
if __name__ == "__main__":
    # Aura запросы используют зашифрованное соединение с использованием схемы URI "neo4j+s"
    uri = "neo4j+s://.....здесь подключение к вашему экземпляру Neo4j........."
    user = "....здесь ваш пользователь......"
    password = "........здесь пароль вашего пользователя.........."
    # Создание экземпляра класса, передача параметров для инициализации драйвера Neo4j
    app = App(uri, user, password)
    # Вызов метода класса для поиска кратчайшего пути между начальным и конечным узлом
    from_node = {'name': 'D', 'label': 'Store'} #начальный узел
    to_node = {'name': 'I', 'label': 'Shop'} #конечный узел
    result = app.find_shortest_paths(from_node, to_node)
    print(result)
    # Закрытие соединения с базой данных Neo4j
    app.close()

Визуализация полученных результатов в Google Colab:

Python Networkx Google Colab, графы Python
Визуализация графа в Google Colab с помощью Networkx

Если заменить в Cypher-скрипте функцию min на max, будет выполняться поиск самого длинного пути. Рассмотренный скрипт можно запустить самостоятельно, скопировав его из моего репозитория Github. А пример модификации этого скрипта для демонстрации поиска графа финансовых транзакций между компаниями и частными лицами смотрите в новой статье

Поближе познакомиться с рассмотренными в этой статье и другими инструментами работы с графами для их практического применения в реальных проектах аналитики больших данных вы сможете на специализированных курсах нашего лицензированного учебного центра обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

Я даю свое согласие на обработку персональных данных и соглашаюсь с политикой конфиденциальности.

Источники

  1. https://neo4j.com/cloud/platform/aura-graph-database/
  2. https://neo4j.com/docs/api/python-driver/current/api.html
  3. https://github.com/AnnaVichugova/PythonApps/blob/main/Neo4j-Graph
Поиск по сайту