Начало работы с GraphFrames

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

Создаем граф GraphFrame

Для того чтобы создать граф GraphFrame, требуется два DataFrame, удовлетворяющие требованиям [1]:

  • DataFrame с вершинами (vertex), который должен содержать столбец id (идентификатор ID вершины);
  • DataFrame с ребрами (edge), который должен содержать столбцы src (из какого ID узла идет ребро) и dst (в какой ID узла попадает).

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

Граф GraphFrames (apache spark)
Ориентированный граф GraphFrame

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

Итак, чтобы создать граф DataFrame с вершинами ребрами должны быть переданы на вход объекту GraphFrame:

from graphframes import GraphFrame

v = spark.createDataFrame([
    (1, 'Anton', 23),
    (2, 'Anna', 27),
    (3, 'Andry', 24),
    (4, 'Alex', 32),
    (5, 'Boris', 55),
    (6, 'Vera', 64), ],
    ['id', 'name', 'age'])

e = spark.createDataFrame([
    (1, 2, 'friend'),
    (2, 1, 'friend'),
    (1, 3, 'friend'),
    (3, 1, 'friend'),
    (2, 3, 'friend'),
    (3, 2, 'friend'),
    (4, 2, 'follows'),
    (5, 6, 'follows'), ],
    ['src', 'dst', 'type'])

g = GraphFrame(v, e)

Основные манипуляции с графом

Чтобы узнать методы графа GraphFrames, вызовите функцию dir(g). Через граф можно обратиться к датафрейму с вершинами или ребрами с помощью методов vertices и edges, которые возвращают DataFrame. Код на Python для демонстрации:

>>> g.vertices.show()
+---+-----+---+
| id| name|age|
+---+-----+---+
|  1|Anton| 23|
|  2| Anna| 27|
|  3|Andry| 24|
|  4| Alex| 32|
|  5|Boris| 55|
|  6| Vera| 64|
+---+-----+---+

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

>>> has_subs = g.edges.filter("type = 'follows'").select('dst').distinct()
>>> g.vertices.join(follows, (g.vertices.id == has_subs.dst)).show()
+---+----+---+---+
| id|name|age|dst|
+---+----+---+---+
|  6|Vera| 64|  6|
|  2|Anna| 27|  2|
+---+----+---+---+

Метод degrees возвращает DataFrame, который содержит информацию о степени вершин — количестве входящих и исходящих ребер. Например, вершина под номер 1 (Антон) имеет степень, равной 4, так как в неё входят 2 ребра и из неё исходят 2 ребра; а вершины 5 (Борис) и 6 (Вера) имеют степень 1, так как из 5-й исходит 1 ребро, а в 6-ю входит 1 ребро. Убедимся в этом:

>>> g.degrees.show()
+---+------+
| id|degree|
+---+------+
|  6|     1|
|  5|     1|
|  1|     4|
|  3|     4|
|  2|     5|
|  4|     1|
+---+------+

А вот методы inDegrees и outDegrees выдадут только те вершины, которые имеют как минимум одно входящее и как минимум одно исходящее ребро, соответственно. Например, 5-я и 4-я вершины не имеют входящих ребер, следовательно, в inDegrees они не будут учтены; наоборот, 6-я вершина не имеет исходящих ребер, следовательно, она не будет учтена в outDegrees:

>>> g.inDegrees.show()
+---+--------+
| id|inDegree|
+---+--------+
|  6|       1|
|  1|       2|
|  3|       2|
|  2|       3|
+---+--------+
>>> g.outDegrees.show()
+---+---------+
| id|outDegree|
+---+---------+
|  5|        1|
|  1|        2|
|  3|        2|
|  2|        2|
|  4|        1|
+---+---------+

Компоненты связности GraphFrame

Компонентом связности называется подграф, несвязанный с другими подграфами. В нашем случае всего 2 подграфа: 5-6 и 1-2-3-4. Для нахождения компонентов связности используется метод connectedComponents, который возвращает DataFrame с дополнительным столбцом component. Однако перед его вызовом нужно создать директорию чекпоинтов, делается это следующим образом:

sc.setCheckpointDir('graphframes_cps')

Теперь уже можно найти компоненты связности исходного графа GraphFrames:

>>> g.connectedComponents().show()
+---+-----+---+---------+
| id| name|age|component|
+---+-----+---+---------+
|  1|Anton| 23|        1|
|  2| Anna| 27|        1|
|  3|Andry| 24|        1|
|  4| Alex| 32|        1|
|  5|Boris| 55|        5|
|  6| Vera| 64|        5|
+---+-----+---+---------+

Здесь числа 1 и 5 являются уникальными идентификаторами компонент, они могут быть и другими, главное чтобы они были уникальными. Заметим также, что нахождение компонент связности требует поиска в ширину и глубину, и с точки зрения нотации “O” большого займёт O(n+m) времени, где n — количество вершин, m — количество ребер [2]. Также можно определить компоненты сильной связности — это когда из вершины a можно добраться до b, так и из b в a [3]. В нашем примере есть только одна компонента сильной связности — 1-2-3. Находится компонента сильной связности с помощью метода stronglyConnectedComponents.

 

В следующей статье рассмотрим, как находить структурные шаблоны в графе (motif finding). Ещё больше подробностей о работе с графами в Apache Spark вы узнаете на нашем специализированном курсе «Графовые алгоритмы в Apache Spark » в лицензированном учебном центре обучения и повышения квалификации руководителей и ИТ-специалистов (менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data) в Москве:

Записаться на курс

Смотреть раcписание

Источники
  1. https://graphframes.github.io/graphframes/docs/_site/user-guide.html
  2. https://ru.wikipedia.org/wiki/Компонентасвязностиграфа
  3. https://ru.wikipedia.org/wiki/Компонентасильнойсвязности

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

Поиск по сайту