Análise de redes complexas em Python com NetworkX e PyVis

Hora de colocar a mão na massa!

Victoria Aires
Computando Arte
7 min readNov 17, 2021

--

Photo by NASA on Unsplash

No meu artigo anterior aqui no blog, falei um pouco sobre redes complexas e como elas podem ser uma ferramenta útil para analisar diferentes tipos de problemas, oferecendo insights ricos e muito relacionados com suas aplicações no “mundo real”. Para quem perdeu ou gostaria de relembrar, segue o artigo no link abaixo.

Mas na prática, como podemos trabalhar com dados de redes complexas? Falando especificamente sobre a linguagem Python, a biblioteca NetworkX é uma ótima opção. Trata-se de uma ferramenta para criação, manipulação e estudo de redes complexas, que facilita bastante obter insights e análises. Na minha visão, considero-a como uma biblioteca muito completa, e de fácil aprendizado quando comparada a outras opções que já pude testar.

No entanto, o ponto fraco do NetworkX é o módulo de visualização dos grafos, que pode ser muito complexo para compreender e ainda resultar em visualizações confusas e estáticas. Nesse sentido, um excelente complemento é a biblioteca PyVis, voltada para visualizações interativas de redes complexas, com a vantagem de integrar-se com o NetworkX. O melhor dos dois mundos! Neste artigo, veremos um exemplo prático combinando ambas as bibliotecas.

1. A base de dados

Para exemplificar uma aplicação de análise de redes complexas com Python, vamos utilizar uma base de dados disponível no Kaggle, denominada Stack Overflow Tag Network. Esta base modela a correlação entre tags de tecnologias dentro da plataforma do Stack Overflow. Possui a seguinte estrutura:

  • Vértices: as tags, como html, python, android, docker, etc.
  • Arestas: o número de vezes em que duas tags foram associadas.

Como o número de vezes em que duas tags aparecem juntas pode variar, teremos um grafo ponderado, ou seja, há um peso ou valor associado à relação entre os vértices. Apesar de ser uma base de dados antiga, datada de 2017, ela é simples e interessante o suficiente para servir de exemplo à nossa aplicação.

Na base de dados, vamos utilizar apenas o arquivo stack_network_links.csv. Esse arquivo contém a lista das arestas do grafo, através das colunas source, target e value. Isso significa que, para cada linha, temos uma aresta entre os vértices source e target, e esse relacionamento tem a intensidade representada por value.

Amostra da lista de arestas disponibilizada na base de dados.

2. Carregando os dados

Para criar um grafo vazio com a biblioteca NetworkX, basta executar o comando abaixo.

No nosso caso, não criaremos um grafo vazio: vamos carregar diretamente a lista de arestas disponibilizada na base de dados, como explicado anteriormente. Isso pode ser feito de acordo com o código a seguir. A função nx.parse_edgelist recebe como entrada uma lista de arestas. Podemos especificar dados extras, como peso das arestas, através do parâmetro data .

O resultado final é um objeto do tipo Graph , contendo os vértices e arestas representados na base de dados. É possível conferir quais são esses conjuntos através dos métodos G.nodes() e G.edges() . Como nossas arestas possuem um dado extra (o peso), podemos visualizá-lo através do método G.edges.data('weight') .

Os primeiros 5 vértices e 5 arestas do grafo, com seus respectivos pesos.

3. Obtendo insights

Com nosso grafo devidamente carregado, podemos analisar sua estrutura em busca de insights sobre os nós e os relacionamentos entre eles. Lembrando que, nesse caso, os vértices são tags de tecnologias, e as arestas indicam o número de vezes que um par de tags foi relacionada. Assim, métricas de redes complexas devem ser capazes de capturar informações interessantes nesse contexto.

No artigo anterior, comentei sobre métricas de centralidade, que demonstram os nós mais influentes em uma rede, segundo um critério específico. Também vimos o conceito de comunidades, estruturas que capturam um conjunto de vértices com um forte relacionamento entre si.

A seguir, vamos ver como trabalhar com esses conceitos e métricas utilizando as ferramentas da biblioteca NetworkX.

Métricas de centralidade

O NetworkX disponibiliza uma série de métricas de centralidade, e você pode conferi-las neste link. Para nosso exemplo, analisaremos três delas: centralidade de grau ponderado (ou weighted degree), intermediação (ou betweenness) e autovetor (ou eigenvector).

Centralidade de grau ponderado: essa métrica representa a soma de todos os pesos das arestas ligadas a um dado nó. Assim, os nós mais influentes são aqueles que recebem mais ligações, ou as ligações com maiores intensidades.

No NetworkX, essa métrica pode ser calculada da seguinte forma: weighted_degree = G.degree(weight='weight') . Essa função retornará um dicionário onde as chaves são os vértices, e os valores são seus respectivos graus ponderados. Ordenando pelos maiores valores, temos o top 10 dos vértices mais influentes. Nota-se que são termos, em sua maioria, muito relacionados a ferramentas para desenvolvimento web em 2017.

Os 10 vértices mais influentes pela métrica de centralidade de grau ponderado.

Centralidade de intermediação: nessa métrica, são considerados mais influentes os nós que aparecem mais vezes no caminho mínimo entre os outros nós da rede. Ou seja, os nós mais centrais são aqueles que funcionam como pontes, conectando os demais na estrutura da rede.

Para calcular a intermediação no NetworkX, basta executar betweenness = nx.betweenness_centrality(G, weight='weight') . O retorno da função, novamente, é um dicionário contendo o score de betweenness de cada nó.

Ranking dos 10 vértices mais influentes pela centralidade de intermediação.

Analisando os top 10 nós mais influentes pela intermediação, observamos tags que podem estar relacionadas a diferentes contextos: sistemas operacionais, bancos de dados, e web em geral. Como são termos que não são exclusivos a um nicho, faz sentido que eles sejam considerados como as principais pontes da rede.

Centralidade de autovetor: o conceito dessa métrica é que vértices mais influentes compartilham suas influências com seus vizinhos. Basicamente, a ideia é a seguinte: nós de grau alto são muito relevantes, e compartilham essa relevância através de suas conexões, que compartilham com os vizinhos e assim sucessivamente.

Assim como nos exemplos anteriores, é simples computar a centralidade de autovetor, através do método eigenvector = nx.eigenvector_centrality(G, weight='weight') . O ranking dessa métrica, como podemos ver abaixo, é composto por alguns dos vértices mais influentes por grau ponderado, porém essa influência foi compartilhada com outros termos “bem relacionados”, como javascript, mysql, e html, todos relevantes no contexto de desenvolvimento web.

Top 10 vértices mais influentes pela centralidade de autovetor.

Comunidades

Falando sobre algoritmos para detecção de comunidades, a biblioteca NetworkX fornece diferentes opções, que você pode explorar aqui. Para nosso exemplo, utilizaremos a função greedy_modularity_communities , baseado em uma métrica chamada modularidade.

A modularidade descreve a qualidade dos grupos em um grafo. Quanto mais coeso, isto é, mais o grupo é conectado entre si, maior a modularidade. Esta métrica é usada por algoritmos para descobrir as comunidades dentro de uma dada rede.

O método implementado pelo NetworkX é o algoritmo de Clauset-Newman-Moore de heurística gulosa para modularidade. Em outras palavras, o algoritmo aplica um critério simples para selecionar o conjunto de comunidades com a melhor modularidade. Para executá-lo na biblioteca, basta executar o código abaixo.

No nosso exemplo, o algoritmo descobriu 14 comunidades. Cada uma delas está relacionada a um mesmo contexto: desenvolvimento web, sistemas operacionais, programação para Android e iOS, desenvolvimento ágil, e assim sucessivamente.

4. Visualizando o grafo

Como comentei no início do artigo, o NetworkX oferece suporte ao desenho dos grafos. Porém, o resultado final costuma não ser satisfatório, sendo o principal ponto fraco da biblioteca. Para ilustrar, executei o código a seguir para desenhar nossa rede da melhor maneira possível.

O resultado foi uma visualização muito confusa, com sobreposição dos textos de cada vértice e um grande emaranhado de arestas, organizados de qualquer maneira.

Uma visualização gerada pelo NetworkX para nossa rede.

Para sanar esse ponto fraco, podemos utilizar a biblioteca PyVis, que recebe um grafo NetworkX como entrada e retorna uma visualização interativa. Podemos fazer ainda melhor: a biblioteca é capaz de colorir os vértices de acordo com um atributo, e no nosso caso vamos utilizar as comunidades. Para isso, vamos criar o atributo group no nosso grafo.

Caso você opte por executar o código em um notebook Jupyter, incluindo o Google Colab, as poucas linhas a seguir são suficientes para gerar um arquivo HTML contendo uma visualização interativa da nossa rede, onde vértices da mesma cor pertencem à mesma comunidade.

Você pode conferir o resultado abaixo, onde passamos por todas as comunidades. Levando em consideração que a base de dados é antiga, podemos visualizar tendências de tecnologias da época. Algumas associações ficam bem claras, como as comunidades formada por tecnologias da Apple, Android, sistemas operacionais, desenvolvimento web e até mesmo Excel.

Passeando pelas comunidades da rede de tags.

Conclusão

Caso você tenha interesse em modelar seu problema utilizando ciência de redes, a biblioteca NetworkX é uma excelente opção para isso. Além da integração com Python, ela fornece diversos recursos para você explorar e obter insights sobre os relacionamentos na rede.

Já a biblioteca PyVis é uma alternativa fácil de usar para gerar visualizações interativas. Como ela gera um HTML, você pode facilmente incorporá-lo em websites, aplicações e dashboards. Assim, qualquer pessoa que consultar sua análise pode interagir e entender melhor os componentes da rede!

Caso você queira conferir nossa rede de tags de tecnologia, basta acessar este notebook. E caso tenha experiências e conhecimentos sobre outras formas de trabalhar com análise e visualização de redes, deixa aqui nos comentários! :D

--

--