You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9.8 KiB

Лабораторная работа 4. «Визуализация связей в виде графов»

назад

При выполнении данной лабораторной работы производится визуализация связей социальных сетей в виде графа.

1. Запрос перечня друзей пользователя

Для выполнения работы импортируем необходимый пакет.

 import vk_api

Как и при выполнении первой лабораторной работы создаем подключение, а далее находим друзей user1 с помощью вызова метода friends.get.

friends_user1 = tools.get_all('friends.get', 100, {'user_id': user1})

На выходе получим перечень id друзей пользователя. API VK позволяет дополнительно сразу запрашивать параметры пользователя такие как:

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. При этом для выполнения текущей задачи имеет ценность лишь список друзей. Поэтому дополнительный параметры указывать не будем.

Итоговый код будет выглядеть следующим образом.

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).

 pip install networkx==1.9

Далее реализуем функции для визуализации графа.

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 друзей, а также связывает между собой пользователя и его друга. Так как необходимо связать не только пользователя и друзей, а ещё и друзей между собой, то модифицируем код следующим образом.

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 друзей пользователя.

friend_friends = get_groups_users(friends_out[user1]['items'], tools)

Количество запросов ограничено, поэтому с целью экономии предлагается использовать модуль pickle. Данный модуль реализует мощный алгоритм сериализации и десериализации объектов Python. "Pickling" процесс преобразования объекта Python в поток байтов, а "unpickling" обратная операция, в результате которой поток байтов преобразуется обратно в Python-объект. Поток байтов легко можно записать в файл, поэтому модуль pickle широко применяется для сохранения и загрузки сложных объектов в Python.

Пример его использования выглядит следующим образом.

with open('friends_friends.pkl', 'wb') as output:
  pickle.dump(friend_friends, output, pickle.HIGHEST_PROTOCOL)

Этот пример для сохранения объекта. Следующий пример для загрузки объекта.

with open('friends_friends.pkl', 'rb') as input:
  friends_friends = pickle.load(input)

Вызовем функцию make_graph в функции main.

 g = make_graph(friends_out, friend_friends)

После формирования связей так же рекомендуется сохранить полученный граф при помощи Pickle.

3. Визуализация графа друзей пользователя

Сформированный граф необходимо визуализировать. Для этого будем использовать модуль matplotlib (необходимая версия 2.2.4).

Реализуем функцию визуализации.

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.

plot_graph(g, 500)

В результате работы кода будет получено изображение графа друзей пользователя.

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