- PVSM.RU - https://www.pvsm.ru -
Часть вторая, в которой Атосу все норм, а вот Графу де ля Фер чего-то не хватает
Вступление от авторов:
Добрый день! Сегодня мы продолжаем цикл статей, посвященный скорингу и использованию в оном теории графов. С первой статьей Вы можете ознакомиться здесь [3].
Все шуточные аллегории, вставки и прочее призваны немного разгрузить повествование и не позволить ему свалиться в нудную лекцию. Всем, кому не зайдет наш юмор, заранее приносим извинения
Цель данной статьи: не более, чем за 30 минут, описать основные способы хранения данных о графах и описать правила и принципы построения нашей модели для скоринга заемщика.
Термины и определения:
Аудиторы, нанятые ПАО «Король» для оценки кредитоспособности НПАО «Один за всех», столкнулись с некоторыми проблемами. С одной стороны, описать схему взаимодействия 10-15 компаний и провести первичную оценку взаимодействия между компаниями очень просто, достаточно иметь под рукой лист бумаги и ручку. Но, что делать, если у вас имеется информация о взаимодействии десятков или сотен тысяч компаний? Например, если Вам нужно описать взаимодействия Арамиса со всеми его пассиями или Д’артаньяна со всеми, с кем он дрался?
Как хранить данные об этих взаимодействиях?
Какие структуры данных и подходы использовать?
Аудиторам пришлось бы сажать за эту работу целый монашеский корпус писунов.
Мы так делать не будем и наделим наших аудиторов знаниями и технологиями будущего (отправим к ним Прометея в виде Т-800, который даст им свет знаний).
Ну, что ж, начнем отвечать на поставленные вопросы. Да будет свет!
Как мы уже писали и рисовали здесь [3], граф – это отношение 2-х множеств – множества узлов и множества ребер. Как же лучше хранить граф?
Прежде чем ответить на вопрос как хранить граф, нужно решить, а что конкретно мы хотим хранить и в каком виде.
По теории графов узлами графа могут быть любые объекты с любым набором параметров (этот факт пригодится нам позже, для продвинутых/адаптивных моделей расчета скорингового бала).
Так что же нам нужно хранить?
Как минимум, идентификатор узла и идентификаторы его соседей (с кем связан). Наличие этих данных уже позволяет осуществлять поиск в ширину и глубину.
Нужно ли хранить данные о ребрах графа? Да, если мы имеем дело со взвешенным графом. В нашем случае, мы имеем дело со взвешенным графом и в первой статье [3] мы нарисовали именно такой граф.
Достаточно ли этой информации? Нет.
Откуда взялись веса у ребер? В учебниках эта информация просто есть, кто-то ее собрал и обработал до нас. В нашем же позднем средневековье (именно тогда жили мушкетеры) никто не озаботился подсчетом весов ребер нашего графа. Мы должны сделать это сами. В этой и следующей статье мы не будем расписывать конкретную методику расчета веса ребра, это будет сделано в 4 статье. Сейчас нам важно определиться какую информацию о нашем графе мы будем хранить.
Итак, в нашем случае, есть три ключевых параметра, которые мы должны знать для корректного расчета скорингового балла:
Не спрашивайте откуда взялись в модели все эти числа, Д’артаньян так видит. В реальной жизни эти показатели будут рассчитываться тоже не нами (торговый оборот, долги, штрафы и пр. мы берем из уже существующих источников, мы ж не в средневековье, вроде).
Из всего вышеперечисленного получается, что мы должны хранить помимо идентификаторов узлов, параметры каждого узла и веса ребер, которые мы получаем на основании агрегации параметров узлов.
Итого, хранению подлежит следующая информация:
Отлично! Разобрались, что нам нужно хранить. Теперь — как хранить.
И опять небольшое отступление.
Как будет выглядеть наш процесс скоринга в упрощенном виде:
Исходя из этих двух этапов у нас есть три варианта:
Основные требования к данному процессу:
Если нужно долговременное хранение, то можно выбрать таблицу, с соответствующими значимыми полями. В NoSQL или оперативной памяти лучше хранить данные в виде списка или справочника (хеш-таблица) объектов.
{ 'Id': 1, 'Title': 'НПАО Один за всех', 'Rang': 4, 'Type': 'НПАО', 'Profit': 10000 }
С вершинами более-менее понятно. Как же лучше представить дуги/ребра графа? Для этого нужно понимать основной принцип любых аналитических операций над графом – обращение к любой дуге/ребру должно происходить очень быстро, желательно, чтобы время обращения было равно O(1). Сразу в голову приходить массив или матрица – структура, в которой к любому элементу можно обратиться быстро по индексу.
[i,j] – элемент матрицы обозначает дугу графа, где i- идентификатор начальной вершины, j — идентификатор конечной вершины, обращение и выбор начальной вершины происходит непосредственно по идентификатору начальной вершины. Пересечение i и j хранит вес ребра.
В таком представлении есть несколько минусов:
Следующий вариант хранения дуг/ребер – таблица, то есть набор столбцов и строк.
Например:

Такую структуру можно легко сохранить в реляционной БД и при выполнении SQL запросов выбирать нужные значения, но, когда речь идет об оперативной памяти, сложность поиска всех ребер вершины увеличивается и в общем случае занимает O(n) где n количество всех ребер графа.
Существует еще один очень хороший метод хранения графа для использования практически во всех системах, лишенный недостатков, описанных выше — это справочник ключ-значение или хеш-таблица. Получение всех ребер нужной вершины происходит с O(1) скоростью.
Существенный минус, что не все языки программирования поддерживают такую конструкцию.
Как же можно представить подобную структуру в разных системах?
В реляционной базе можно реализовать связь таблиц объектов и ребер (предыдущий пункт)

NoSQL
{ 'Id': 1, 'Title': 'НПАО Один за всех', 'Rang': 4, 'Type': 'НПАО', 'Profit': 10000, 'Relations': [{3,2}, {4,5}, … {n, -5}] }
При обращении к объекту по его ключу, сразу получаем набор его связей. Если у нас невзвешенный граф, вместо массива объектов можно передать массив идентификаторов соседей Relations: [3,4, … n]. В виде справочника ключ – значение, такой вариант похож на предыдущий. В справочнике ключ – значение можно хранить такой же объект, как в предыдущем примере, ключом, конечно, будет являться идентификатор вершины (может быть число, может быть строка и т.д., что позволяет конкретная системы разработки). Так же в справочнике можно хранить только массивы связей, а информацию о вершинах в другом справочнике.
Graf[1] = { 'Id': 1, 'Title': 'НПАО Один за всех', 'Rang': 4, 'Type': 'НПАО', 'Profit': 10000, 'Relations': [{3,2}, {4,5}, … {n, -5}] }
или
graf['one_for_all'] = 'Relations': [{3,2}, {4,5}, … {n, -5}]
Для нашего примера мы выбрали вариант хранения данных в оперативной памяти с созданием хеш-таблицы для скоринга данных. Промежуточные результаты будем записывать в файл на сервере.
Со структурами хранения худо – бедно определились, теперь пришло время разобраться, с чего начать построение нашей аналитической модели. Начнем с простого- определим взаимодействие с ближайшими соседями и с соседями соседей (друзья друзей).
Таким образом можно определить взаимодействие со всем связанными между собой вершинами. По нашим наблюдениям взаимодействие с соседями глубже 2 уровня представляет интерес только в особенных случаях и рассчитывается по другим методикам. Сложность этого расчета довольно велика 0(2^n).
Для расчета бала мы будем использовать немного измененный алгоритм поиска в глубину.
Доработка будет заключаться в следующем:
Ну, что ж, основные теоретические выкладки сделаны, пусть они и покажутся кому-то чем-то простым и банальным. Но для нас, гасконцев, это все важно и интересно, почти так же, как поступление в Королевские мушкетеры.
Переходим к практической реализации. Один за всех и все на одного!
Встретимся! Мы обязательно встретимся! Может быть через 10 лет или 20! Но встретимся!
Следующая статья близко!
Автор: MihhaCF
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/328013
Ссылки в тексте:
[1] AntipovSN: https://habr.com/ru/users/antipovsn/
[2] MihhaCF: https://habr.com/ru/users/mihhacf/
[3] здесь: https://habr.com/ru/post/464447/
[4] Хеш-таблица: https://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0
[5] Источник: https://habr.com/ru/post/464833/?utm_campaign=464833&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.