# Лабораторная работа 4. «Визуализация связей в виде графов» [назад](README.md) При выполнении данной лабораторной работы производится визуализация связей социальных сетей в виде графа. ## 1. Запрос перечня друзей пользователя Для выполнения работы импортируем необходимый пакет. ```python import vk_api ``` Как и при выполнении первой лабораторной работы создаем подключение, а далее находим друзей `user1` с помощью вызова метода `friends.get`. ```python friends_user1 = tools.get_all('friends.get', 100, {'user_id': user1}) ``` На выходе получим перечень `id` друзей пользователя. API VK позволяет дополнительно сразу запрашивать параметры пользователя такие как: ```python nickname, domain, sex, bdate, city, country, timezone, photo_50, photo_100, photo_200_orig, has_mobile, contacts, education, online, relation, last_seen, status, can_write_private_message, can_see_all_posts, can_post, universities ``` Необходимые параметры указываются в параметре `fields`. При этом для выполнения текущей задачи имеет ценность лишь список друзей. Поэтому дополнительный параметры указывать не будем. Итоговый код будет выглядеть следующим образом. ```python import vk_api import time import json import pickle user1 = 249771326 def auth_handler(): """ При двухфакторной аутентификации вызывается эта функция. """ # Код двухфакторной аутентификации key = input("Enter authentication code: ") # Если: True ‐ сохранить, False ‐ не сохранять. remember_device = True return key, remember_device def stop_f(items): print (items) def get_groups_users(friends_list, tools): friends_out = {} for friend in friends_list: try: friends_out[friend] = tools.get_all('friends.get', 100, {'user_id': friend}) except Exception: friends_out[friend] = [] time.sleep(1) return friends_out def main(): login, password = '<ВАШ ЛОГИН>', '<ВАШ ПАРОЛЬ>' vk_session = vk_api.VkApi( login, password, # функция для обработки двухфакторной аутентификации auth_handler=auth_handler ) try: vk_session.auth() except vk_api.AuthError as error_msg: print(error_msg) tools = vk_api.VkTools(vk_session) friend_list=[] friend_list.append(user1) friends_out = get_groups_users(friend_list, tools) print(friends_out) if __name__ == '__main__': main() ``` В результате исполнения этого данного получится JSON-response со списком друзей пользователя, `id` которого, был указан в переменной `user1`. ## 2. Формирование графа друзей пользователя Установим модуль networkX (необходима версия 1.9). ```python pip install networkx==1.9 ``` Далее реализуем функции для визуализации графа. ```python import networkx as nx def make_graph(friends_out, friends_friends): graph = nx.Graph() graph.add_node(user1, size = friends_out[user1]['count']) for i in friends_out[user1]['items']: try: graph.add_node(i, size=friends_friends[i]['count']) intersection = set(friends_out[user1]['items']).intersection( set(friends_friends[i]['items'])) graph.add_edge(user1, i, weight=len(intersection)) except Exception: print("err") return graph ``` Данная функция создает граф, вершинами которого являются узлы, содержащие `id` друзей, а также связывает между собой пользователя и его друга. Так как необходимо связать не только пользователя и друзей, а ещё и друзей между собой, то модифицируем код следующим образом. ```python def make_graph(friends_out, friends_friends): graph = nx.Graph() graph.add_node(user1, size = friends_out[user1]['count']) for i in friends_out[user1]['items']: try: graph.add_node(i, size = friends_friends[i]['count']) intersection = set(friends_out[user1]['items']).intersection( set(friends_friends[i]['items'])) graph.add_edge(user1, i, weight=len(intersection)) except Exception: print("err") for i in range(len(friends_out[user1]['items'])): id1 = friends_out[user1]['items'][i] for k in range(i+1, len(friends_out[user1]['items'])): id2 = friends_out[user1]['items'][k] try: intersection = set(friends_friends[id1]['items']).intersection( set(friends_friends[id2]['items'])) if len(intersection) > 0: graph.add_edge(id1, id2, weight=len(intersection)) except Exception: print("err friend") return graph ``` Здесь дополнительно добавляется переменная `friends_friends`, которая содержит в себе списки друзей друзей пользователя. Данная переменная появляется в результате вызова метода `get_groups_users` куда на вход подаем список `id` друзей пользователя. ```python friend_friends = get_groups_users(friends_out[user1]['items'], tools) ``` Количество запросов ограничено, поэтому с целью экономии предлагается использовать модуль `pickle`. Данный модуль реализует мощный алгоритм сериализации и десериализации объектов Python. "Pickling" – процесс преобразования объекта Python в поток байтов, а "unpickling" – обратная операция, в результате которой поток байтов преобразуется обратно в Python-объект. Поток байтов легко можно записать в файл, поэтому модуль `pickle` широко применяется для сохранения и загрузки сложных объектов в Python. Пример его использования выглядит следующим образом. ```python with open('friends_friends.pkl', 'wb') as output: pickle.dump(friend_friends, output, pickle.HIGHEST_PROTOCOL) ``` Этот пример для сохранения объекта. Следующий пример – для загрузки объекта. ```python with open('friends_friends.pkl', 'rb') as input: friends_friends = pickle.load(input) ``` Вызовем функцию `make_graph` в функции `main`. ```python g = make_graph(friends_out, friend_friends) ``` После формирования связей так же рекомендуется сохранить полученный граф при помощи Pickle. ## 3. Визуализация графа друзей пользователя Сформированный граф необходимо визуализировать. Для этого будем использовать модуль `matplotlib` (необходимая версия – 2.2.4). Реализуем функцию визуализации. ```python def plot_graph(graph, adjust_nodesize): #pos = nx.drawing.layout.circular_layout(graph) pos=nx.spring_layout(graph, k=0.1) # нормализуем размер вершины для визуализации. # Оптимальное значение параметра #vadjust_nodesize ‐ от 300 до 500 nodesize = [graph.node[i]['size']/adjust_nodesize for i in graph.nodes()] #нормализуем толщину ребра графа. Здесь хорошо подходит #нормализация по Standard Score edge_mean = numpy.mean([graph.edge[i[0]][i[1]]['weight'] for i in graph.edges()]) edge_std_dev = numpy.std([graph.edge[i[0]][i[1]]['weight'] for i in graph.edges()]) edgewidth = [ ((graph.edge[i[0]][i[1]]['weight'] ‐ edge_mean)/edge_std_dev/2) for i in graph.edges()] #создаем граф для визуализации nx.draw_networkx_nodes( graph, pos, node_size=nodesize, node_color='y', alpha=0.9 ) nx.draw_networkx_edges( graph, pos, width=edgewidth, edge_color='b' ) nx.draw_networkx_labels(graph, pos, fontsize=5) # сохраняем и показываем визуализированный граф plt.savefig('saved') plt.show() ``` Вызовем реализованную функцию в `main`. ```python plot_graph(g, 500) ``` В результате работы кода будет получено изображение графа друзей пользователя. Данный метод является довольно простым, т.к. при этом не происходит процесс кластеризации графа, для лучшего понимания его структуры. Однако этот пример удобен для понимания процесса работы с графами.