Categorias
Artigos Home Python

Networkx

Analisando grafos com Python e a biblioteca Networkx

NetworkX é um pacote Python utilizado para manipulação de grafos e redes complexas. A documentação da biblioteca, além de exemplos mais aprofundados, estão disponíveis em: https://networkx.org/. Neste artigo, apresentaremos como manipular grafos usando essa biblioteca. A antes de nos aprofundarmos no Networkx, precisamos explorar o conceito de grafos.

Grafos

Grafos são estruturas que representam um conjunto de itens e o relacionamento entre eles. Um grafo G(V, E) é composto por vértices (também conhecidos como nós) que se conectar por meio das arestas (também conhecidos como links ou pares de nós).

Exemplo de grafo composto por seis vértices. Cada esfera representa um vértice, e as linhas que os conectam são as arestas que indicam um relacionamento entre eles. Fonte: User:AzaToth, Public domain, via Wikimedia Commons

Primeiros passos

Instalação

No Python 3, você pode instalá-lo com o comando:

pip3 install networkx

No Python 2, use:

pip install networkx

No Anaconda use:

conda install networkx

Criando um objeto networkx

Como apresentado anteriormente, um grafo é composto por vértices conectados por arestas. NetworkX permite que vértices possam ser strings de texto, números, etc.

O código a seguir cria um objeto networkx:

import networkx as nx
G = nx.Graph()

Adicionando vértices

Agora vamos adicionar dois vértices e criar uma aresta para conectá-los:

import networkx as nx
G = nx.Graph()

# adicionando vertices
G.add_node(1)
G.add_node(2)


# Visualizando os vertices
print(G.nodes())
 #[1, 2]

#adicionando arestas
G.add_edge(1, 2)

# total de vertices e arestas
n_vertices = G.number_of_nodes()
n_arestas = G.number_of_edges()

print('vertices: ', n_vertices, '\narestas: ', n_arestas)
#vertices:  2 
#arestas:  1

Note que ao executar o comando:

G.add_edge(4, 5)
print(G.nodes()) # [4, 5]

Networkx irá criar os vértices 4 e 5, e depois irá criar uma aresta os conectando.

Você pode visualizar as arestas acessando o atributo edges:

print(G.edges) # [(4, 5)]

Removendo vértices

Podemos remover um vértice usando a função remove_node( ):

# adicionando vertices
G.add_node(1)
G.add_node(2) 
print(G.nodes()) #[1, 2]

# remove o nó 2
G.remove_node(2)
print(G.nodes())
 #[1]

Desenhando o grafo

Podemos imprimir o grafo usando o comando draw. Veja:

nx.draw(G)
Grafo ligando o nó 1 ao 2. Fonte: próprio autor.

Podemos ainda adicionar arestas sem a necessidade de incluir cada um dos itens:

G.add_edge(3, 4)
G.add_edge(1, 3)
G.add_edge(5, 6)
G.add_edge(5, 9)
G.add_edge(7, 8)
G.add_edge(1, 8)
G.add_edge(1, 4)

nx.draw(G)  # imprimindo o grafo

Veja como ficará o grafo apresentado:

Plotagem do grafo com várias arestas. Nós são incluídos automaticamente pelo networkx. Fonte: próprio autor.

Podemos adicionar rótulos aos nós usando o atributo with_labels. Veja:

nx.draw(G, with_labels=True)
Adicionando rótulos com o atributo with_labels. Fonte: próprio autor.

Podemos ainda alterar a cor e o tamanho dos vértices com as propriedades node_size e node_color, respectivamente. No exemplo a seguir, iremos aumentar o tamanho dos nós e alterar a cor para vermelho.

nx.draw(G, with_labels=True, node_size=1200, node_color='red')
Alterando cor (de azul para vermelho) e tamanho dos vértices (para 1200). Fonte: próprio autor.

Subgrafos

Na figura apresentada acima, podemos perceber visualmente que há duas partes do grafo que não se conectam entre si. Denominamos essas partes como subgrafos. Podemos obtê-las utilizando o código:

subgrafos = [G.subgraph(s) for s in nx.connected_components(G)]

print(len(subgrafos))  # verificando a quantidade de subgrafos

Podemos ainda plotar os diferentes subgrafos com cores distintas, como por exemplo, verde e vermelho. Veja:

nx.draw(
    subgrafos[0],       # subgrafo na posição 0
    with_labels=True,   # mostrar rótulos
    node_size=800,      # aumentar tamanho
    node_color='red'    # cor vermelha
)

nx.draw(
    subgrafos[1],       # subgrafo na posição 0
    with_labels=True,   # mostrar rótulos
    node_size=800,      # aumentar tamanho
    node_color='green'  # cor verde
)

Isso irá gerar a seguinte figura:

Subgrafos impressos em cores diferentes. Fonte: próprio autor.

Perceba que alguns nós se sobrepõe. A definição da posição de impressão é aleatória. Entretanto, podemos utilizar algumas variações da função draw para alterar a forma de plotagem. Vamos ilustrar isso usando duas dessas variações: draw_circular e draw_random.

# grafo vermelho - circular
nx.draw_circular(
    subgrafos[0], 
    with_labels=True, 
    node_size=800, 
    node_color='red'
)

# grafo verde - posições aleatórias
nx.draw_random(
    subgrafos[1], 
    with_labels=True, 
    node_size=800, 
    node_color='green'
)
Grafo circular (vermelho) e grafo com posições definidas aleatoriamente (verde). Fonte: próprio autor.

Note pelo exemplo acima que a função draw_circular força os vértices a se posicionarem em forma de círculo. A outra função, draw_random, posiciona os pontos de forma aleatória.

Convertendo um CSV em um grafo

Em alguns casos, você precisará criar um grafo usando como entrada o conteúdo de um arquivo CSV. Podemos fazer isso usando a networkx e a biblioteca pandas através do método from_pandas_edgelist( ). Para ilustrar isso, vamos primeiro criar um arquivo CSV para ilustrar o grafo a seguir:

Grafo exemplo. Fonte: User:AzaToth, Public domain, via Wikimedia Commons

Observe como uma tabela com os pares de nós do grafo acima:

Nó 1Nó 2
12
15
23
25
34
45
46
Tabela de links do grafo exemplo. Nesta tabela, cada linha indica dois nós que se conectam. Por exemplo, o nó 1 se conecta ao nó 2. Na tabela, essa conexão é indicada na segunda linha.

O formato CSV (Comma Separated Values) é comumente utilizado para armazenamento de informações. Nesse arquivo, cada entrada é representada por uma linha, enquanto seus atributos são separados por vírgula (ou ponto e vírgula). Uma variação também bastante comum desse formato é o TSV (Tab-Separated Values), que utiliza tabulações para separar os atributos.

Veja como os pares de links poderiam ser representados em um arquivo CSV (aqui denominado grafo.csv):

Nó 1,Nó 2
1,2
1,5
2,3
2,5
3,4
4,5
4,6

Observe que a primeira linha é destinada ao cabeçalho, que indica o que irá descrever um representante para cada uma das entradas.

Carregando um CSV com pandas

Agora vamos utilizar a biblioteca pandas para carregar esse arquivo para um script Python.

import pandas as pd  # importa o pandas 

# carregar os nós
with open("grafo.csv") as f:
     pares = f.read().splitlines()

# pega os nós em duas listas separadas
node_1 = []
node_2 = []

# separando os nós em listas separadas
for i, cont in zip(pares, range(len(pares))):

     if cont == 0:
         continue  # ignora a primeira linha

     node_1.append(i.split(',')[0])
     node_2.append(i.split(',')[1])

# cria um dataframe PANDAS para armazenar os dados
df = pd.DataFrame({'node_1': node_1, 'node_2': node_2})

print(df)

'''
   node_1 node_2
 0      1      2
 1      1      5
 2      2      3
 3      2      5
 4      3      4
 5      4      5
 6      4      6
'''

A biblioteca pandas converte nosso CSV em um objeto de DataFrame. Observe que cada linha contém o índice da posição. Na primeira linha vemos os títulos das colunas (optamos por chamá-los de “node_1” e “node_2” no lugar de “Nó 1” e “Nó 2” para evitar possíveis problemas com codificação).

Convertendo um DataFrame do pandas em um grafo do networkx

Agora vamos carregar o grafo usando networkx e imprimi-lo usando a biblioteca Matplotlib.

Podemos converter um DataFrame do pandas para um grafo do networkx usando o método nx.from_pandas_edgelist( ). Esse método deve receber como entrada: (i) um dataframe; (ii) o nome da coluna com o primeiro nó; (iii) o nome da coluna com o segundo nó; (iv) o método de criação

Observe como podemos converter o DataFrame criado anteriormente em um grafo:

import networkx as nx
import matplotlib.pyplot as plt

G = nx.from_pandas_edgelist(
    df, 
    "node_1", 
    "node_2", 
    create_using=nx.Graph()
)

# plota o grafo
nx.draw(G, with_labels=True,  node_size = 1000)
plt.show()

Ao executar esse código obteremos a seguinte imagem:

Grafo criado a partir de um arquivo CSV. Fonte: próprio autor.

Salvando grafos em CSV

Podemos salvar um grafo em um arquivo CSV usando a função write_edgelist( ):

gravando um grafo como arquivo csv
 nx.write_edgelist(
     G,                # grafo
     "grafo2.csv",     # nome do arquivo
     delimiter=",",    # separador
     data=False,       # se True, grava dados do peso das arestas
     encoding='utf-8'  # codificação
 )

Lendo arquivos CSV como grafos

Vimos anteriormente como gerar grafos a partir de arquivos CSV passando por DataFrames gerados com pandas. Entretanto, podemos ler um arquivo CSV diretamente usando networkx:

G2 = nx.read_edgelist("grafo2.csv", delimiter=",")

Resumo

DescriçãoComando
Iniciar um grafoG = nx.Graph()
Criar um novo vértice (nó)G.add_node(1)
G.add_node(2)
Ver todos os vérticesG.nodes()
Cria uma arestaG.add_edge(1, 2)
Contar total de vérticesG.number_of_nodes()
Contar total de arestasG.number_of_edges()
Remover vérticeG.remove_node(2)
Desenhandonx.draw(G)
Criando um grafo a partir de um DataFrame do pandas.from_pandas_edgelist(
dataframe,
node_1,
node_2,
create_using=nx.Graph()
)
Salvando um grafo em CSVnx.write_edgelist(
grafo,
nome,
delimitador
)
Lendo um grafo a partir de um CSVG2 = nx.read_edgelist(
“[nome-arquivo].csv”,
delimiter=”,”
)
Principais comandos para manipulação de grafos com networkx.

Principais atributos e métodos para manipulação de grafos com networkx

add_edge
add_edges_from
add_node
add_nodes_from
add_weighted_edges_from
adj
adjacency
adjlist_inner_dict_factory
adjlist_outer_dict_factory
clear
clear_edges
copy
degree
edge_attr_dict_factory
edge_subgraph
edges
get_edge_data
graph
graph_attr_dict_factory
has_edge
has_node
is_directed
is_multigraph
name
nbunch_iter
neighbors
node_attr_dict_factory
node_dict_factory
nodes
number_of_edges
number_of_nodes
order
remove_edge
remove_edges_from
remove_node
remove_nodes_from
size
subgraph
to_directed
to_directed_class
to_undirected
to_undirected_class
update

Cursos profissionalizantes à partir de R$ 22,90

Leia também

  • Fator de impacto e Qualis de periódicos
    Atualizado em 2024
  • Como ler um arquivo mmCIF
    usando Python
  • O guia de sobrevivência de expressões regulares em Python
    Este material faz parte do livro “Python para Bioinformática” (ainda não publicado). Para mais informações, consulte o autor. Expressões regulares são ferramentas poderosas para buscas de padrões em textos. Logo, todo o bioinformata deve conhecê-las. Neste artigo, você irá conhecer os principais metacaracteres e irá aprender a manipular expressões regulares usando Python. Bons estudos! Expressões…
  • SVD – Decomposição por Valores Singulares com Python
    Este material faz parte do livro “Python para Bioinformática” (ainda não publicado). Para mais informações, consulte o autor. Decomposição por valores singulares, do inglês Singular Value Decomposition (SVD) é uma técnica da álgebra linear usada para recuperação de informações, redução dimensional e remoção de ruídos. Nessa técnica, uma matriz A de tamanho MxN é decomposta…
  • Como ler arquivos com JavaScript de forma síncrona
    asyn & await
  • Parâmetros do DataTables
    Veja como ordenar colunas, alterar a quantidade de itens em uma página e alterar o alinhamento no DataTables
  • Exportando tabelas do DataTables
    DataTables é uma biblioteca JavaScript fantástica para lidar com tabelas dinâmicas em páginas da web. Entretanto, podemos estender ainda mais suas funcionalidades, inserindo botões que permitem a exportação dos dados da tabela em múltiplos formatos, como por exemplo, tabular (para cópia), CSV, Excel, PDF e até mesmo HTML para impressão. Veja um exemplo: Observe o…
  • Método every()
    Usando o método every() para ler todos os itens de um array JavaScript
  • Análise de Correlação
    Correlação de Pearson
  • Curso de Escrita Científica
    Aprenda a escrever monografias, TCCs, teses, dissertações, projetos e artigos científicos

Por Diego Mariano

Doutor em Bioinformática pela Universidade Federal de Minas Gerais com atuação na área de ciência de dados e aprendizado de máquina aplicados ao aperfeiçoamento de enzimas usadas na produção de biocombustíveis. Mestre em Bioinformática, também pela UFMG, atuando na área de desenvolvimento de sistemas Web para montagem de genomas. Atualmente realiza estágio pós-doutoral no Departamento de Ciência da Computação da UFMG com foco em desenvolvimento de sistemas Web para Bioinformática, análise exploratória e visualização de dados. Tem conhecimentos nas linguagens: PHP, JavaScript, Python, R, Perl, HTML, CSS e SQL.

error

Compartilhe este post!

Facebook
YouTube
LinkedIn
Instagram