Время прочтения: 6 мин.

Создание отчетов графовой аналитики является важной частью работы многих проектов и бизнес-процессов. Однако, процесс создания и настройки таких отчетов может быть сложным и требовать больших усилий. В этом посте рассмотрю, как использование языка программирования Python и его библиотек может значительно упростить генерацию отчетов графовой аналитики в формате PDF.

Для работы с PDF в Python есть множество библиотек, самые популярные из них: PyPDF2, ReportLab, FPDF.

Коротко приведу сравнение основных библиотек:

По совокупным критериям для дальнейшего разбора мной была выбрана библиотека ReportLab.

Одной из самых популярных библиотек для работы с графами в Python является NetworkX. Она предоставляет широкий набор инструментов для создания и анализа графов. Ее и буду использовать для создания графа и анализа данных.

В посте приведен код, необходимый для воспроизведения. Полный код доступен по ссылке.

Данные сгенерирую с помощью Python.  Пусть будет 500 операций, где будет информация об отправителе, получателе, сумме операции и флаг подозрительности операции.

df_dict = {}
count_operation = 500
for i in range(count_operation):
    df_dict[i] = {'reciver' : random.randint(1, count_operation/2),
                  'sender': random.randint(1, count_operation/2),
                  'sum_oper': random.randint(1000, 1000000),
                  'suspisios_transaction': random.randint(0, 1)}

Добавлю 100 переводов, где получателем будет клиент 1, а отправителем- любой другой клиент из основного датасета:

for i in range(100):
    df_dict[i] = {'reciver' : 1,
                  'sender': random.randint(1, count_operation/2),
                  'sum_oper': random.randint(1000, 1000000),
                  'suspisios_transaction': random.randint(0, 1)}
df =  pd.DataFrame().from_dict(df_dict).T

Получится вот такой датасет:

Создам направленный граф на основе сгенерированного датасета:

graph = nx.DiGraph()

for loc in range(len(df)):
    rec = df.iloc[loc].reciver
    send = df.iloc[loc].sender
    sum_o = df.iloc[loc].sum_oper
    flag = df.iloc[loc].suspisios_transaction

    graph.add_edge(rec, send, sum_oper = sum_o, suspisios_transaction=flag)

Отрисую граф с помощью библиотеки networkX. Для начала рассчитаю позиции вершин и отрисую граф с помощью networkx:

func_for_pos = nx.kamada_kawai_layout
nx.draw_networkx(graph, pos = func_for_pos(graph), ax=plt.subplots(figsize=(15,15))[1])

По результату отработки кода, получу граф:

Приступлю к написанию непосредственно кода отчета. Каждый отчет PDF можно персонализировать, выделяя наиболее важные вещи. В свой отчет я хочу добавить следующую информацию:

  • Количество вершин и узлов в графе
  • Представление графа с использованием аналитической раскладки
  • Degree centrality
  • Betweenness centrality
  • Pagerank
  • Выделение подозрительных операций
  • Количество связанных компонент

Начну создавать PDF-отчет. Создам первую страницу:

c = canvas.Canvas("Отчет по графовой аналитике.pdf")

Для того, чтобы в отчете корректно отображалась кириллица, необходимо скачать любой шрифт в формате .ttf (для отчета возьму: ‘DejaVuSerif.ttf’) и выполнить следующую настройку:

pdfmetrics.registerFont(TTFont('DejaVuSerif', 'DejaVuSerif.ttf'))

где в TTFont передам имя шрифта и путь до файла (если в текущей директории, то просто имя файла). В дальнейших установках шрифта можно обращаться просто по объявленному имени.

Добавлю заголовок – 14 размера шрифта:

c.setFont("DejaVuSerif", 14)
c.drawString(beg_str, 750, 'Отчет анализа операций с использованием графовой аналитики')

Теперь размещу информацию о количестве вершин и ребер в графе:

c.setFont("DejaVuSerif", 12) 
c.drawString(beg_str+20, 700, "Метрики графа:")
c.drawString(beg_str+40, 680, f"Количество вершин: {graph.number_of_nodes()}")
c.drawString(beg_str+40, 660, f"Количество ребер: {graph.number_of_edges()}")

Для сохранения отчета необходимо выполнять команду:

c.save()

В текущей директории создался PDF-отчет: «Отчет по графовой аналитике.pdf» с одной страницей:

Добавлю рисунок в отчет с отрисовкой графа с помощью аналитической раскладки графа Камада-Каваи, который сделан ранее.

Для того, чтобы добавить рисунок в отчет (самый простой, но не самый оптимальный способ) необходимо сохранить рисунок в текущей директории, отрисовать в отчете и затем удалить с помощью библиотеки os.

plt.savefig('network_plot.png')
c.drawImage('network_plot.png', beg_str-beg_str, 50, 600, 500)
os.remove('network_plot.png')

Добавлю заголовок:

c.setFont("DejaVuSerif", 14)
c.drawString(beg_str, 620, "Граф с использованием аналитической раскладки:")
c.drawString(beg_str, 600, f"{str(func_for_pos).split(' ')[1]}")

Добавлю небольшое описание алгоритма раскладки:

c.setFont("DejaVuSerif", 12)
c.drawString(beg_str, 560, "Алгоритм Камада-Каваи перемещает вершины в новые положения по одной,")
c.drawString(beg_str, 540, "так что общая энергия системы уменьшается с новой конфигурацией.")
c.drawString(beg_str, 520, "Расстояние между вершинами пропорционально длине кратчайшего пути.")

Добавлю информацию о том, какая вершина находится в центре:

cent_node = 1
c.drawString(beg_str, 70, f"Наиболее центральная вершина: {cent_node}")

В итоге получилась вот такая простая, но информативная страница:

Создам новую страницу:

c.showPage()

Добавлю заголовок с размером шрифта 14, затем верну 12 для основного текста:

c.setFont("DejaVuSerif", 14)
c.drawString(beg_str, 750, 'Метрики графа:')
c.setFont("DejaVuSerif", 12)

Рассчитаю 3 метрики графа и занесу их в отдельные датафреймы:

df_degrees = pd.DataFrame().from_dict(nx.degree(graph)).sort_values(1, ascending=False).rename(columns={0:'node', 1:'degree'})
bet = dict([(k, round(v,2)) for k,v in nx.betweenness_centrality(graph).items()])
pagerank = dict([(k, round(v,2)) for k,v in nx.pagerank(graph).items()])
df_betwenness = pd.DataFrame().from_dict(bet, orient='index').reset_index().sort_values(0, ascending=False).rename(columns={'index':'node', 0:'betweenness'})
df_pagerank = pd.DataFrame().from_dict(pagerank, orient='index').reset_index().sort_values(0, ascending=False).rename(columns={'index':'node', 0:'pagerank'})

Теперь отображу топ-7 вершин в каждой метрике в виде таблицы с заголовком:

table = Table([df_degrees.columns.tolist()] + df_degrees.iloc[:7].values.tolist())
table.wrapOn(c, 100, 70)
table.drawOn(c, beg_str, 570)
c.drawString(beg_str, 720, 'Cтепени вершин')

Аналогично для остальных таблиц, меняя только расположение на листе.

Получу вот такой раздел с таблицами:

Задам цвета:

•            ребрам – в соответствии с флагом подозрительности,

•            вершинам – в зависимости от степени. Если степень вершины больше 6, то красный, иначе – зеленый.

df['color_edge'] = df.suspisios_transaction.apply(lambda x: 'red' if x==1 else 'green')
color_node = df_degrees['degree'].apply(lambda x: 'red' if x>6 else 'green')

Отрисую граф с цветами ребер и вершин и также добавлю в отчет:

nx.draw_networkx(graph, pos = nx.kamada_kawai_layout(graph),
                 edge_color=list(df.color_edge),
                 node_color=color_node,
                 ax=plt.subplots(figsize=(15,15))[1])
plt.savefig('network_plot2.png')
c.drawImage('network_plot2.png', 0, 100, 600, 500)
c.drawString(beg_str, 550, "Граф, где красные вершины - степень>6, ребра - с флагом 1)")
os.remove('network_plot2.png')

Также добавлю строчку с количеством связанных компонент:

c.drawString(beg_str, 100, f"Количество связанных компонент в графе - {len([i for i in nx.connected_components(graph_bad_all.to_undirected())])}")

Получу вторую часть страницы:

Отберу вершины и ребра красного цвета. Нарисую 3 графа, состоящие:

  1. Только из красных вершин,
  2.  Только из красных ребер,
  3.  Совместно.

Новые графы размещу на новой странице, как и делала до этого. В результате получу последнюю страницу в отчете:

В этом посте я рассмотрела только базовые возможности библиотеки и создала простой отчет в формате PDF с помощью Python. Однако библиотека обладает богатым функционалом и предоставляет множество инструментов для создания профессиональных и настраиваемых PDF-документов.

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

Надеюсь, что этот пост поможет начать использовать Python для генерации отчетов в формате PDF и упростит работу с данными.