A análise de redes sociais é o processo de explorar vários sistemas usando a teoria das redes. Começou a ser amplamente utilizado apenas quando ficou claro que um grande número de redes existentes (sociais, econômicas, biológicas) têm propriedades universais: tendo estudado um tipo, você pode entender a estrutura de quaisquer outras redes e aprender a fazer previsões a partir delas.
Qualquer rede consiste em participantes individuais (pessoas ou coisas na rede) e nas relações entre eles. As redes são frequentemente visualizadas por meio de gráficos - estruturas compostas de muitos pontos e linhas que representam as conexões entre esses pontos. Os participantes são representados como nós da rede e seus relacionamentos são representados como linhas que os conectam. Essa visualização ajuda a obter uma avaliação qualitativa e quantitativa das redes:
Figura: 1. Gráfico direcional que descreve o giro do dinheiro entre os bancos que formam o mercado de câmbio (1). Os bancos da UE estão marcados em vermelho, América do Norte - em azul e outros países - em verde.
Figura: 2. Gráfico que mostra a cooperação dos parceiros de auditoria na Dinamarca em 2010-2014 (2) A partir
da análise de redes sociais, é possível analisar uma variedade de interações e processos de troca de recursos, tanto materiais como informativos. Por exemplo, ao analisar a rede de transações entre clientes do banco (onde os nós são os clientes do banco e as bordas são as transferências entre eles), é possível determinar o círculo de pessoas envolvidas em transações fraudulentas ou identificar violações de regulamentos internos por funcionários do banco.
Você também pode construir uma rede de relações de trabalho (a exemplo de vários tipos de comunicação entre os funcionários), o que pode ajudar a entender a estrutura social da organização e a posição de cada funcionário nessa estrutura. Diante dos dados sobre o tipo de comunicação de cada funcionário, pode-se inclusive analisar como características como liderança, mentoria e cooperação afetam sua carreira e, com base nos conhecimentos adquiridos, definir objetivos de carreira e propor programas de treinamento para alcançá-los.
Além disso, os eventos podem ser previstos usando o exemplo de redes. Por exemplo, existem modelos que estimam a probabilidade de falha de um software, alguns deles consideram as pessoas como fonte de previsões - afinal, são as pessoas que desenvolvem e testam os produtos antes do lançamento. Suas interações formam uma rede: você pode pensar nos desenvolvedores como nós e se eles trabalharam juntos no mesmo arquivo na mesma versão, como bordas de uma rede. Compreender as interações e as informações sobre falhas anteriores dirá muito sobre a confiabilidade do produto final e apontará os arquivos com maior probabilidade de falhar.
Agora vamos tentar aplicar a análise de redes sociais na prática. Para fazer isso, usaremos a linguagem de programação Python, ou melhor, a biblioteca networkx para trabalhar com gráficos, a biblioteca matplotlib para visualização e a biblioteca da comunidade para destacar as comunidades dentro da rede. Vamos importá-los:
1. import community
2. import networkx as nx
3. import matplotlib.cm as cm
4. import matplotlib.pyplot as plt
1. Importar dados e transformá-los em um gráfico
Como um conjunto de dados, vamos pegar uma correspondência de e-mail de uma grande universidade europeia, que contém informações anônimas sobre todas as mensagens de e-mail recebidas e enviadas entre membros de uma instituição de pesquisa (link). O conjunto de dados contém um arquivo txt onde cada linha lista pares de nós que estão relacionados entre si.
1. G = nx.read_edgelist('email-Eu-core.txt', create_using=nx.DiGraph())
2. print(' : {}'.format(G.number_of_nodes()))
3. print(' : {}'.format(G. number_of_edges()))
4. print(' : {}'.format(round(G.number_of_edges() / float(G.number_of_nodes()), 4)))
: 1005
: 25571
: 25.4438
No código acima, o conjunto de dados foi importado e convertido em um gráfico. Em seguida, deduzimos sequencialmente os principais parâmetros do grafo: o número de nós, arestas e o número médio de vizinhos dos nós no grafo. O último parâmetro reflete o quão próximos os nós estão conectados uns aos outros.
2. Principais características dos gráficos
Para entender como você pode trabalhar com cada gráfico específico, primeiro você precisa entender como ele funciona. Vamos dar uma olhada rápida nas características pelas quais você pode entender a estrutura de um gráfico.
Em primeiro lugar, considere os conceitos de conectividade e direcionalidade. Um gráfico é chamado de conectado se cada par de nós no gráfico estiver interconectado, ou seja, de qualquer nó, você pode chegar a qualquer outro nó. Se o gráfico for desconectado, ele pode ser dividido em subgráficos conectados ao máximo (chamados de componentes). Além disso, os gráficos podem ser direcionados e não direcionados. Isso é determinado pela presença da direcionalidade das conexões entre os dois participantes. Um exemplo de rede direcionada são as transações entre clientes de bancos, onde cada cliente pode receber e receber pagamentos.
No caso geral, em grafos direcionados, as conexões não são mútuas, portanto, para grafos direcionados, ao invés do conceito de conectividade, distinguem-se os conceitos de componentes de conectividade fraca e forte. Um componente é considerado fracamente conectado se ignorar a direção resulta em um gráfico conectado. Um componente de conectividade forte pode ser tal se todos os seus vértices forem mutuamente alcançáveis. Vamos dar uma olhada na estrutura do gráfico de nosso conjunto de dados de e-mail:
1. if nx.is_directed(G):
2. if nx.is_weakly_connected(G):
3. print(' .')
4. else:
5. print(' .')
6. else:
7. if nx.is_connected(G):
8. print(' .')
9. else:
10. print(' .')
O gráfico é direcional e consiste em vários componentes.
Aqui, verificamos a direcionalidade e a conectividade do gráfico e descobrimos que o gráfico do conjunto de dados é direcionado e contém vários componentes desconectados. Vamos dar uma olhada nos maiores componentes de conectividade forte e fraca:
1. G_weak = G.subgraph(max(nx.weakly_connected_components(G), key=len))
2. print(' : {}'.format(G_weak.number_of_nodes()))
3. print(' : {}'.format(G_weak.number_of_edges()))
4. print(' : {}'.format(round(G_weak.number_of_edges() / float(G_weak.number_of_nodes()), 4)))
5.
6. G_strong = G.subgraph(max(nx.strongly_connected_components(G), key=len))
7. print(' : {}'.format(G_strong.number_of_nodes()))
8. print(' : {}'.format(G_strong.number_of_edges()))
9. print(' : {}'.format(round(G_strong.number_of_edges() / float(G_strong.number_of_nodes()), 4)))
: 986
: 25552
: 25.9148
: 803
: 24729
: 30.7958
Assim, obtivemos as características principais para o componente com conexão fraca e para o componente com conexão forte nele incluído. Vamos ver quais conclusões podemos tirar neste estágio. Vemos que de 1.005 participantes, 986 pessoas se comunicam entre si, enquanto 183 deles enviaram e-mails para outras pessoas de forma unilateral e apenas 803 pessoas mantiveram comunicação bidirecional. Em 823 casos, a tentativa de estabelecer comunicação via e-mail falhou. Vemos também que os participantes mais ativos (incluídos no componente de conexão forte) se comunicam com uma média de 30 pessoas.
Vejamos outras características principais dos gráficos que definem sua estrutura. Os gráficos são considerados ponderados se as relações entre os nós refletem não apenas a própria existência da conexão, mas também um certo peso, refletindo a força dessa conexão. Nosso conjunto de dados com mensagens de e-mail não é ponderado, pois leva em consideração apenas o fato da correspondência, mas não o número de cartas enviadas.
Além disso, nós e links podem criar diferentes tipos de redes: monocotiledôneas, dicotiledôneas ou multinível. As redes monocotiledôneas consistem em um tipo de participantes e nas conexões entre eles. As redes bipartidas consistem em dois tipos diferentes de participantes, em que um dos tipos de nós está associado apenas ao outro tipo. As redes multinível também incluem dois tipos de participantes, mas os links podem conectar diferentes tipos de participantes e o mesmo tipo de participantes (por exemplo, relacionamentos entre gerentes e relacionamentos entre projetos). O conjunto de dados que tomamos para pesquisa é uma rede monocotiledônea, uma vez que consiste em apenas um tipo de participantes e as conexões entre eles.
3. Visualização do gráfico
Agora vamos tentar visualizar o conjunto de dados que pegamos. Para isso, precisamos da biblioteca matplotlib, que já importamos acima:
1. plt.figure(figsize=(15, 15))
2. plt.title('E-mails')
3. nx.draw(G, node_size=5)
4. plt.show()
A primeira linha define o tamanho da imagem futura, que recebe um nome específico. A terceira linha da função desenhar passa o gráfico e especifica o tamanho dos nós, após o qual o gráfico é exibido. Vamos dar uma olhada:
Fig. 3. Gráfico das interações do usuário com informações sobre todos os e-mails recebidos e enviados entre membros da instituição de pesquisa.
No gráfico resultante, vemos que há uma série de pontos que não estão conectados com o resto dos participantes da comunicação. Essas pessoas, sem ligação com o restante dos participantes, entraram no gráfico, pois enviaram cartas exclusivamente para si mesmas. Você também pode notar que na periferia há uma série de pontos que estão conectados com o resto do gráfico por algumas conexões de entrada - esses são os participantes que caíram no componente fracamente conectado do nosso gráfico, mas não caíram no componente forte conectado.
Vejamos também um gráfico que ilustra o forte componente de conexão - pessoas que têm comunicação bidirecional com outros membros da instituição de pesquisa:
Fig. 4. Componente de forte conectividade com informações sobre todos os e-mails recebidos e enviados entre membros da instituição de pesquisa...
4. Grau de nó e distribuição de graus de nós
Agora que conhecemos a estrutura do nosso gráfico e podemos visualizá-lo, vamos prosseguir para uma análise mais detalhada. Cada nó no gráfico tem um grau - o número de vizinhos mais próximos desse nó. Em redes direcionadas, pode-se distinguir tanto o grau de entrada (o número de conexões de entrada para o nó) quanto o grau de saída (o número de conexões de saída do nó). Se calcularmos o grau para cada nó do gráfico, podemos determinar a distribuição dos graus dos nós. Vamos dar uma olhada no gráfico de e-mail:
1. degree = dict(G.degree())
2. degree_values = sorted(set(degree.values()))
3. hist = [list(degree.values()).count(x) for x in degree_values]
4. plt.figure(figsize=(10, 10))
5. plt.plot(degree_values, hist, 'ro-')
6. plt.legend(['Degree'])
7. plt.xlabel('Degree')
8. plt.ylabel('Number of nodes')
9. plt.show()
Figura: 5. Distribuição de graus em um gráfico com informações sobre todos os e-mails de entrada e saída entre membros da instituição de pesquisa
No gráfico resultante, vemos a distribuição de graus de nós: um grande número de nós tem poucas conexões com vizinhos, mas há um pequeno número de nós grandes que têm várias conexões com outros participantes é enorme. Essa tendência é chamada de lei de distribuição de potência e é muito típica para grandes redes. Essa lei pode descrever a distribuição da população de diferentes cidades, a classificação dos sites na Internet e até a distribuição da riqueza entre as pessoas.
5. Caminho, diâmetro e distância média no gráfico
Agora vamos determinar o quão conectados estão os membros do nosso gráfico. Primeiro, vamos falar sobre os diferentes tipos de espaçamento entre nós.
Qualquer sequência de arestas que conecta os nós é chamada de caminho. Na maioria das vezes, a pesquisa considera um caminho simples, ou seja, um caminho sem ciclos e nós repetidos. O caminho mais curto entre dois nós é chamado de distância geodésica. O caminho mais curto mais longo em um gráfico é chamado de diâmetro, mas é muito sensível a desvios (uma cadeia em um gráfico multimilionário pode alterar seu diâmetro). Em grafos direcionados, o conceito de diâmetro pode ser utilizado apenas para o componente de conectividade forte, pois para calcular o diâmetro, é necessário que para cada par de nós haja um caminho entre eles. Em gráficos não direcionados, é suficiente que o componente em consideração esteja conectado.
Outra característica bastante informativa é a distância média entre os nós, que pode ser obtida pela média de todos os caminhos mais curtos do gráfico. A distância média é determinada pela estrutura do gráfico: se o gráfico for construído na forma de uma cadeia, será grande, mas quanto mais próximos os nós estiverem conectados, menor será a distância média. A distância média pode ser calculada tanto para o componente fortemente conectado quanto para o componente fracamente conectado:
1. print (': ', nx.diameter(G_strong))
2. print (' : ', nx.average_shortest_path_length(G_strong))
3. print (' : ', nx.average_shortest_path_length(G_weak))
: 6
: 2.5474824768713336
: 2.164486568301397
Vamos dar uma olhada nos resultados. O diâmetro neste caso mostra-nos a distância máxima entre dois estranhos, e aqui, como na conhecida teoria dos seis apertos de mão, essa distância é 6. A distância média nos componentes também é pequena, em média 2 “apertos de mão” são suficientes para dois estranhos. Um fenômeno interessante também pode ser visto aqui: a distância média no componente fortemente conectado é ligeiramente menor do que no componente fracamente conectado. Isso pode ser explicado pelo fato de que a direção das ligações não é levada em consideração para o componente fracamente conectado (apenas o próprio fato de sua presença). Por causa disso, a conexão no componente fraco aparece onde estava ausente para o componente forte.
6. Agrupamento e alocação de comunidades
Tendo descoberto as distâncias entre os participantes, vamos passar para outros fenômenos que refletem como os participantes no gráfico estão conectados uns aos outros: agrupamento e comunidades.
O coeficiente de cluster mostra que dois elementos do gráfico, conectados através do terceiro elemento, com alto grau de probabilidade estão conectados um ao outro. Mesmo se eles não estiverem vinculados, a probabilidade de que sejam vinculados no futuro é muito maior do que os outros dois nós tomados aleatoriamente. Esse fenômeno, denominado agrupamento ou transitividade, é extremamente comum em gráficos sociais.
Gráficos com alto grau de agrupamento são caracterizados pela presença de um número significativo de trigêmeos conectados (três nós conectados entre si). Eles são o bloco de construção de muitas redes sociais, onde o número de triângulos é muito grande. Freqüentemente, nem mesmo triângulos surgem, mas sim formações inteiras de agrupamentos, chamadas de comunidades, que estão mais intimamente relacionadas umas às outras do que ao resto do gráfico.
Vejamos o clustering e o coeficiente do cluster para o componente fracamente conectado:
1. print (': ', nx.transitivity(G_strong))
2. print (' : ', nx.average_clustering(G_strong))
: 0.2201493109315837
: 0.37270757578876434
Para um componente de forte conectividade, podemos obter as mesmas características, e também determinar o número de nós centrais (nós que estão mais intimamente relacionados aos demais) e o número de nós na periferia do gráfico:
1. print (': ', nx.transitivity(G_strong))
2. print (' : ', nx.average_clustering(G_strong))
3. print (' : ', len(nx.center(G_strong)))
4. print (' : ', len(nx.periphery(G_strong)))
: 0.2328022090200813
: 0.3905903756516427
: 46
: 3
No segundo caso, o coeficiente de agrupamento e agrupamento aumentou, refletindo que o componente conectado forte contém um número maior de trigêmeos relacionados. Vamos tentar juntos definir as principais comunidades no componente vagamente acoplado:
1. G_undirected = G_weak.to_undirected()
2. partition = community.best_partition(G_undirected)
3. communities = set(partition.values())
4. communities_dict = {c: [k for k, v in partition.items() if v == c] for c in communities}
5. highest_degree = {k: sorted(v, key=lambda x: G.degree(x))[-5:] for k, v in communities_dict.items()}
6. print(' : ', len(highest_degree))
7. print(' :', ', '.join([str(len(highest_degree[key])) for key in highest_degree]))
: 8
: 113, 114, 125, 131, 169, 188, 54, 92
Assim, na coluna com correspondência eletrônica, podem-se distinguir 8 comunidades, que estão mais relacionadas entre si do que com o restante da coluna. A menor das comunidades tem 54 membros e a maior tem 188 membros. Para redes com comunidades sobrepostas ou aninhadas, determinar o particionamento ideal pode ser mais difícil. Portanto, cada vez que você executa o código, a composição das comunidades pode ser diferente. Vamos ver a visualização da divisão que obtivemos:
1. pos = nx.spring_layout(G)
2. plt.figure(figsize=(15, 15))
3. cmap = cm.get_cmap('gist_rainbow', max(partition.values()) + 1)
4. nx.draw_networkx_nodes(G, pos, partition.keys(), node_size=20, cmap=cmap, node_color=list(partition.values()))
5. nx.draw_networkx_edges(G, pos, alpha=0.5)
6. plt.show()
Figura: 6. Exibição de várias comunidades em um componente vagamente acoplado com informações sobre todos os e-mails de entrada e saída entre membros da instituição de pesquisa.
No gráfico acima, vemos a distribuição dos participantes por comunidade, onde a cor dos nós descreve a pertença a uma determinada comunidade.
7. Reciprocidade de vínculos
Além das propriedades já descritas, existe também a reciprocidade em uma rede dirigida. Esta característica descreve qual porcentagem de links de saída tem um feedback, link de entrada. Para descobrir, usamos a função especial general_reciprocity da biblioteca networkx e observamos o nível de reciprocidade no gráfico e seus componentes:
1. print (' : ', nx.overall_reciprocity(G))
2. print (' : ', nx.overall_reciprocity(G_weak))
3. print (' : ', nx.overall_reciprocity(G_strong))
: 0.6933635759258535
: 0.6938791484032562
: 0.7169719762222492
Em 71% dos casos, os usuários receberam respostas às suas mensagens no componente de conectividade forte. Para o componente fracamente conectado e o gráfico inteiro como um todo, o nível é previsivelmente mais baixo.
8. Propriedades universais das redes
Para resumir, redes complexas, em geral, têm certas propriedades e algumas são características de muitas redes. Nossa análise do conjunto de dados de e-mail suporta bem essas tendências:
- . , , . : web-, , , (Wikipedia, Microsoft). , .
- . , , « ». : , .
- , , : 80% , . – , , . , , .
- , . , .
Analisamos e confirmamos todas as tendências listadas acima para um conjunto de dados com correspondência de e-mail: um grande componente conectado foi encontrado no gráfico, contendo mais de 80% de todos os nós. Dentro deste componente, a maioria dos nós era caracterizada por um pequeno número, entretanto, havia uma pequena porcentagem de participantes que tinha um grande número de vizinhos. Também vimos que o diâmetro e a distância média entre os participantes no gráfico são pequenos: a distância média no componente fracamente conectado contendo 986 participantes foi de apenas 2,1, o que significa que a maioria dos participantes está conectada entre si após apenas dois "apertos de mão". Além disso, o gráfico é caracterizado por um alto grau de reciprocidade: 69% de todos os participantes mantiveram contato bidirecional entre si.
Notas
O texto completo da pesquisa pode ser encontrado no livro de Nikolai Viktorovich Ursul "The Whole Truth About Forex" (2019).
A pesquisa está descrita no artigo "A Aplicação da Análise de Redes Sociais à Contabilidade e Auditoria" Slobodan Kacanski, Dean Lusher (2017, link ).