Автор оригинала: Norberto Ritzmann.
1. введение
Обработка графиков полезна для многих приложений, от социальных сетей до рекламы. В сценарии больших данных нам нужен инструмент для распределения этой нагрузки на обработку.
В этом уроке мы загрузим и изучим возможности графа с помощью Apache Spark в Java. Чтобы избежать сложных структур, мы будем использовать простой и высокоуровневый API Apache Spark graph: Graph Frame sAPI.
2. Графики
Прежде всего, давайте определим график и его компоненты. Граф-это структура данных, имеющая ребра и вершины. Ребра несут информацию , которая представляет отношения между вершинами.
Вершины являются точками в n -мерном пространстве, а ребра соединяют вершины в соответствии с их отношениями:
На изображении выше у нас есть пример социальной сети. Мы можем видеть вершины, представленные буквами, и ребра, несущие информацию о том, какие отношения существуют между вершинами.
3. Настройка Maven
Теперь давайте начнем проект с настройки конфигурации Maven.
Давайте добавим spark-graphx 2.11 , |/graph frames и spark-sql 2.11 :
org.apache.spark spark-graphx_2.11 2.4.4 graphframes graphframes 0.7.0-spark2.4-s_2.11 org.apache.spark spark-sql_2.11 2.4.4
Эти версии артефактов поддерживают Scala 2.11.
Кроме того, бывает так, что фреймы графа не находятся в Maven Central. Итак, давайте также добавим необходимый репозиторий Maven:
SparkPackagesRepo http://dl.bintray.com/spark-packages/maven
4. Конфигурация искры
Чтобы работать с графическими фреймами, нам нужно загрузить Hadoop и определить переменную окружения HADOOP_HOME .
В случае Windows в качестве операционной системы мы также загрузим соответствующую winutils.exe в папку HADOOP_HOME/bin .
Далее, давайте начнем наш код с создания базовой конфигурации:
SparkConf sparkConf = new SparkConf() .setAppName("SparkGraphFrames") .setMaster("local[*]"); JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
Нам также нужно будет создать сеанс Spark :
SparkSession session = SparkSession.builder() .appName("SparkGraphFrameSample") .config("spark.sql.warehouse.dir", "/file:C:/temp") .sparkContext(javaSparkContext.sc()) .master("local[*]") .getOrCreate();
5. Построение графика
Теперь мы все готовы начать с нашего основного кода. Итак, давайте определим сущности для наших вершин и ребер и создадим экземпляр Graph Frame .
Мы будем работать над отношениями между пользователями из гипотетической социальной сети.
5.1. Данные
Во-первых, для этого примера давайте определим обе сущности как User и Relationship :
public class User { private Long id; private String name; // constructor, getters and setters } public class Relationship implements Serializable { private String type; private String src; private String dst; private UUID id; public Relationship(String type, String src, String dst) { this.type = type; this.src = src; this.dst = dst; this.id = UUID.randomUUID(); } // getters and setters }
Далее давайте определим некоторые экземпляры User и Relationship :
Listusers = new ArrayList<>(); users.add(new User(1L, "John")); users.add(new User(2L, "Martin")); users.add(new User(3L, "Peter")); users.add(new User(4L, "Alicia")); List relationships = new ArrayList<>(); relationships.add(new Relationship("Friend", "1", "2")); relationships.add(new Relationship("Following", "1", "4")); relationships.add(new Relationship("Friend", "2", "4")); relationships.add(new Relationship("Relative", "3", "1")); relationships.add(new Relationship("Relative", "3", "4"));
5.2. Экземпляр графического фрейма
Теперь, чтобы создать и манипулировать нашим графом отношений, мы создадим экземпляр Graph Frame . Конструктор Graph Frame ожидает два экземпляра Dataset , первый из которых представляет вершины, а второй-ребра:
DatasetuserDataset = session.createDataFrame(users, User.class); Dataset
relationshipDataset = session.createDataFrame(relationships, Relation.class); GraphFrame graph = new GraphFrame(userDataframe, relationshipDataframe);
Наконец, мы запишем наши вершины и ребра в консоль, чтобы посмотреть, как это выглядит:
graph.vertices().show(); graph.edges().show();
+---+------+ | id| name| +---+------+ | 1| John| | 2|Martin| | 3| Peter| | 4|Alicia| +---+------+ +---+--------------------+---+---------+ |dst| id|src| type| +---+--------------------+---+---------+ | 2|622da83f-fb18-484...| 1| Friend| | 4|c6dde409-c89d-490...| 1|Following| | 4|360d06e1-4e9b-4ec...| 2| Friend| | 1|de5e738e-c958-4e0...| 3| Relative| | 4|d96b045a-6320-4a6...| 3| Relative| +---+--------------------+---+---------+
6. Операторы графа
Теперь, когда у нас есть экземпляр Graph Frame , давайте посмотрим, что мы можем с ним сделать.
6.1. Фильтр
Рамки графа позволяют нам фильтровать ребра и вершины по запросу.
Далее, давайте отфильтруем вершины по свойству name на User :
graph.vertices().filter("name = 'Martin'").show();
На консоли мы можем увидеть результат:
+---+------+ | id| name| +---+------+ | 2|Martin| +---+------+
Кроме того, мы можем напрямую фильтровать граф, вызывая ребра фильтра или вершины фильтра :
graph.filterEdges("type = 'Friend'") .dropIsolatedVertices().vertices().show();
Теперь, поскольку мы отфильтровали ребра, у нас все еще могут быть некоторые изолированные вершины. Итак, мы вызовем drop Isolated Vertices().
В результате у нас есть подграф, все еще являющийся графическим фреймом экземпляром, только с отношениями, имеющими статус “Друг”.:
+---+------+ | id| name| +---+------+ | 1| John| | 2|Martin| | 4|Alicia| +---+------+
6.2. Градусы
Еще одним интересным набором функций является набор операций degrees . Эти операции возвращают количество ребер инцидента на каждой вершине.
Операция degrees просто возвращает количество всех ребер каждой вершины. С другой стороны, в градусах подсчитывает только входящие ребра, а outDegrees подсчитывает только исходящие ребра.
Давайте подсчитаем входящие степени всех вершин в нашем графе:
graph.inDegrees().show();
В результате у нас есть фрейм графика , который показывает количество входящих ребер в каждую вершину, исключая те, у которых их нет:
+---+--------+ | id|inDegree| +---+--------+ | 1| 1| | 4| 3| | 2| 1| +---+--------+
7. Графовые алгоритмы
GraphFrames также предоставляет популярные алгоритмы, готовые к использованию — давайте рассмотрим некоторые из них.
7.1. PageRank
Алгоритм PageRank взвешивает входящие ребра в вершину и преобразует их в балл.
Идея заключается в том, что каждое входящее ребро представляет одобрение и делает вершину более релевантной в данном графе.
Например, в социальной сети, если за человеком следят разные люди, он будет высоко оценен.
Запуск алгоритма pagerank довольно прост:
graph.pageRank() .maxIter(20) .resetProbability(0.15) .run() .vertices() .show();
Чтобы настроить этот алгоритм, нам просто нужно предоставить:
- MaxIter – количество итераций ранга страницы для запуска – рекомендуется 20, слишком мало снизит качество, а слишком много снизит производительность
- Вероятность сброса – вероятность случайного сброса (альфа) – чем она ниже, тем больше будет разброс баллов между победителями и проигравшими – допустимые диапазоны от 0 до 1. Обычно 0,15 – это хороший результат
Ответ похож на Фрейм графика, хотя на этот раз мы видим дополнительный столбец, дающий ранг страницы каждой вершины:
+---+------+------------------+ | id| name| pagerank| +---+------+------------------+ | 4|Alicia|1.9393230468864597| | 3| Peter|0.4848822786454427| | 1| John|0.7272991738542318| | 2|Martin| 0.848495500613866| +---+------+------------------+
В нашем графике Алисия является наиболее релевантной вершиной, за которой следуют Мартин и Джон.
7.2. Подключенные Компоненты
Алгоритм связанных компонентов находит изолированные кластеры или изолированные подграфы. Эти кластеры представляют собой наборы связанных вершин в графе, где каждая вершина достижима из любой другой вершины в том же наборе.
Мы можем вызвать алгоритм без каких-либо параметров с помощью метода connected Components() :
graph.connectedComponents().run().show();
Алгоритм возвращает Фрейм графа , содержащий каждую вершину и компонент, с которым каждая из них связана:
+---+------+------------+ | id| name| component| +---+------+------------+ | 1| John|154618822656| | 2|Martin|154618822656| | 3| Peter|154618822656| | 4|Alicia|154618822656| +---+------+------------+
Наш граф имеет только один компонент — это означает, что у нас нет изолированных подграфов. Компонент имеет автоматически сгенерированный идентификатор, который в нашем случае равен 154618822656.
Хотя у нас здесь есть еще один столбец – идентификатор компонента, наш график все еще остается прежним.
7.3. Подсчет треугольников
Подсчет треугольников обычно используется для обнаружения сообщества и подсчета в графе социальной сети. Треугольник-это набор из трех вершин, где каждая вершина имеет отношение к двум другим вершинам в треугольнике.
В сообществе социальных сетей легко найти значительное количество треугольников, связанных друг с другом.
Мы можем легко выполнить подсчет треугольников непосредственно из нашего Графического фрейма экземпляра:
graph.triangleCount().run().show();
Алгоритм также возвращает Фрейм графика с количеством треугольников, проходящих через каждую вершину.
+-----+---+------+ |count| id| name| +-----+---+------+ | 1| 3| Peter| | 2| 1| John| | 2| 4|Alicia| | 1| 2|Martin| +-----+---+------+
8. Заключение
Apache Spark-отличный инструмент для вычисления соответствующего объема данных оптимизированным и распределенным способом. Кроме того, библиотека графических фреймов позволяет нам легко распределять операции с графами по Spark .
Как всегда, полный исходный код для примера доступен на GitHub .