mineracaografo

Trabalho para disciplina Mineração Grafos

https://github.com/wagnercoliveira/mineracaografo

Science Score: 44.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (4.8%) to scientific vocabulary
Last synced: 6 months ago · JSON representation ·

Repository

Trabalho para disciplina Mineração Grafos

Basic Info
  • Host: GitHub
  • Owner: WagnerCOliveira
  • Language: Jupyter Notebook
  • Default Branch: main
  • Size: 7 MB
Statistics
  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • Open Issues: 0
  • Releases: 0
Created 7 months ago · Last pushed 6 months ago
Metadata Files
Readme Citation

README.md

Tabela de conteúdos

Tecnologias Utilizadas

As seguintes ferramentas foram usadas na construção do projeto:

Criação Virtualenv

~~~bash python3 -m venv .venv ~~~

Acessando Virtualenv - WSL, Linux

~~~bash source .venv/bin/activate ~~~

Acessando Virtualenv - Windows

~~~bash .venv/Scripts/activate.bat ~~~

Instalação de Pacotes

~~~bash python -m pip install -r requirements.txt ~~~

Baixando e Descompactador de Arquivos

Este é um script simples em Python feito para baixar um arquivo .zip de uma URL, mostrar uma barrinha de progresso, descompactar o conteúdo em uma pasta e, por fim, apagar o arquivo .zip que foi baixado.

Como Funciona?

O script realiza os seguintes passos:

  1. Verifica a URL: Ele pega a URL do arquivo que você quer baixar.
  2. Cria a Pasta: Se a pasta de destino (por padrão, datasets) não existir, o script a cria para você.
  3. Baixa o Arquivo: Ele inicia o download do arquivo e mostra uma barra de progresso para você acompanhar o andamento.
  4. Descompacta Tudo: Assim que o download termina, ele extrai todos os arquivos de dentro do .zip para a pasta de destino.
  5. Limpa a Bagunça: Para não deixar arquivos desnecessários ocupando espaço, ele remove o arquivo .zip original.

Como Usar

  1. O código do arquivo downloader_datasets.py.

~~~Python main:
def main():
# Altere a URL aqui se precisar
url_arquivo = 'https://networksciencebook.com/translations/en/resources/networks.zip'

   # Altere o nome da pasta de destino aqui se quiser  
   nome_diretorio = 'datasets'      

   try:  
       baixar_arquivo(url_arquivo, nome_diretorio)  
   except Exception as e:  
       print(f"Erro ao baixar ou descompactar arquivo: {e}")

~~~

  1. è só executar o script pelo terminal:

~~~bash python downloader_datasets.py ~~~

E pronto! O script fará todo o trabalho para você. Se algo der errado, ele mostrará uma mensagem de erro.

Atividade 1 - Disciplina de Mineração em Grafo

12 de Julho 2025

Prof. Hygor Piaget M. Melo

Fazer o download do conjunto de redes empiricas https://networksciencebook.com/translations/en/resources/data.html

Realizar uma análise de estatística descritiva nas 10 redes empíricas. Apresentar em um relatório no dia 25 de Julho 2025 e discutido durante a aula.

  • A estatística descritiva deve contar com pelo menos:
    • Tamanho da rede;
    • Número de links;
    • Grau médio;
    • Distribuição de graus;
    • Média das distâncias entre pares:
    • Distância média entre todos os pares de nós na rede.
    • Diâmetro:
    • A maior distância entre todos os pares de nós na rede, representando o tamanho geral da rede.
    • Escolher uma rede das 10 redes empiricas e calcular a centradilidade dos nodos usando:
    • Autovetor
    • Katz
    • PageRank
    • Betwenness
    • Calcular a media delas assim como plotar o histograma.
    • Desenhar o subgrafo com os 1000 nodos com maior PageRank.
    • A cor do nodo deve representar o seu valor de PageRank. Usar a rede do item (g).
    • Desafio: Repetir as análises de a) - h) mas com uma rede encontrada por você.

Análise Estatística Descritiva de Redes Empíricas Utilizando NetworkX

I. Introdução

O objetivo principal realizar uma análise estatística descritiva abrangente de uma rede empírica, utilizando a biblioteca NetworkX em Python. A análise visa caracterizar as propriedades estruturais da rede para inferir sua eficiência, robustez e potencial para o fluxo de informações. Serão calculadas e interpretadas métricas específicas da rede, incluindo o tamanho da rede (número de nós), o número de links (arestas), o grau médio, a distribuição de graus, a média das distâncias entre pares e o diâmetro da rede.

Para cada rede foi criado um notebook com o proprio nome do dataset, e dentro de cata notebook tem os códigos e explicações, com excessão da rede aeroporto que foi criada apartir de um arquivo .csv.

Dataset | Notebook :-------------------- | :--------------- actor.edgelist.txt | actor.edgelist.ipynb citation.edgelist.txt | citation.edgelist.ipynb collaboration.edgelist.txt | collaboration.edgelist.ipynb email.edgelist.txt | email.edgelist.ipynb internet.edgelist.txt | internet.edgelist.ipynb metabolic.edgelist.txt | metabolic.edgelist.ipynb phonecalls.edgelist.txt | phonecalls.edgelist.ipynb powergrid.edgelist.txt | powergrid.edgelist.ipynb protein.edgelist.txt | protein.edgelist.ipynb www.edgelist.txt | www.edgelist.ipynb resumoanual2025.csv | aeroportos.ipynb iata-icao.csv | aeroportos_geomap.ipynb

Para o item desafio foi gerado um geomapa com as geolocalizações dos aeroportos de origem e destino com papel dos (nodes) e suas viagens com sendo suas arestas, mais abaixo tem suas explicações, como também o seu notebook explica alguns pontos importantes.

Geo Mapa

II. Metodologia: Carregamento e Representação da Rede

O ponto de partida para qualquer análise de rede empírica é o carregamento dos dados da rede em um objeto de grafo NetworkX. Esta etapa é crucial, pois a representação correta da estrutura da rede é fundamental para a precisão das análises subsequentes. O NetworkX oferece flexibilidade para ler dados de diversos formatos comuns de rede, como listas de arestas, listas de adjacências.

~~~Python

import networkx as nx

G = nx.read_edgelist('datasets/actor.edgelist.txt')

~~~

III. Métricas Descritivas da Rede e Análise

As métricas descritivas fornecem um panorama quantitativo das características estruturais de uma rede. A Tabela 1 oferece um resumo das métricas que serão detalhadas nas seções seguintes.

Tabela 1: Resumo das Métricas Descritivas da Rede

| Métrica | Valor Calculado | Unidade | Breve Descrição/Interpretação | | :---- | :---- | :---- | :---- | | Tamanho da Rede | N | nós | Escala total do sistema | | Número de Links | M | links | Conectividade total da rede | | Grau Médio | Z | passos | Conectividade média por nó | | Média das Distâncias entre Pares | X | passos | Eficiência média de comunicação/transporte | | Diâmetro da Rede | D | passos | Máxima separação entre dois nós; potencial de gargalos |

A. Tamanho da Rede (Número de Nós)

O tamanho da rede, representado pelo número de nós (ou vértices), N, é a medida mais fundamental da escala de um sistema de rede. Ele quantifica o total de entidades ou atores individuais presentes na rede em estudo.

O NetworkX oferece funções diretas para obter o número de nós: G.number_of_nodes() ou, de forma mais concisa, len(G). Para a rede empírica analisada, assume-se que possui

N nós.

~~~Python

numnodes = G.numberofnodes()
print(f"Tamanho da Rede (Número de Nós): {num
nodes} nós") ~~~

A escala da rede tem implicações diretas na viabilidade e na escolha dos métodos analíticos. Redes com um número muito grande de nós, como as redes sociais globais, podem tornar cálculos exatos para certas métricas, como os caminhos mais curtos ou o diâmetro, computacionalmente proibitivos. Nesses cenários, torna-se necessário empregar algoritmos de aproximação, como os disponíveis para o diâmetro , ou estratégias de amostragem. A escolha entre métodos exatos e aproximados envolve uma ponderação entre a precisão dos resultados e os recursos computacionais disponíveis.

B. Número de Links (Arestas)

O número de links (ou arestas/laços), M, representa a contagem total de conexões ou relacionamentos entre os nós na rede. Esta métrica quantifica o nível geral de interação ou conectividade dentro do sistema.

O NetworkX fornece a função G.number_of_edges() para obter o número total de arestas. Alternativamente, o método G.size() oferece um resultado idêntico.9 Para a rede em análise, assume-se que contém

M links.

~~~Python

numedges = G.numberofedges()
print(f"Número de Links (Arestas): {num
edges} links") ~~~

O número bruto de links, quando considerado em relação ao número de nós, oferece uma indicação imediata da esparsidade ou densidade da rede. Uma rede com muitos nós, mas relativamente poucas arestas, é considerada esparsa, o que pode sugerir conexões mais fracas ou menos numerosas, impactando o fluxo de informações. Em contraste, uma rede com um alto número de arestas em relação aos seus nós é densa, indicando uma forte interconectividade e, potencialmente, processos de difusão mais rápidos. Essa relação entre as contagens básicas de nós e arestas e uma propriedade de rede de nível superior, como a densidade ou o nível de conectividade, é fundamental para a compreensão inicial da estrutura.

C. Grau Médio (Average Degree)

O grau médio, ⟨k⟩, é a média do número de conexões por nó na rede, oferecendo um resumo conciso do nível de conectividade geral da rede.

Para redes não direcionadas, o grau médio é calculado como

2M/N, uma vez que cada aresta contribui para o grau de dois nós, ou simplesmente como a soma de todos os graus dividida pelo número de nós. Em redes direcionadas, o grau médio de entrada (in-degree), o grau médio de saída (out-degree) e o grau total médio são matematicamente equivalentes a M/N. Contudo, para uma compreensão mais aprofundada de redes direcionadas, é frequentemente mais relevante teoricamente examinar as distribuições de in-degree e out-degree separadamente.

É importante notar que o NetworkX não possui uma função direta average_degree() que retorne um único valor flutuante representando o grau médio de toda a rede. A função nx.average_degree_connectivity() calcula uma métrica diferente e mais complexa: o grau médio do vizinho mais próximo para nós de um grau

k específico. Esta é uma medida de assortatividade (correlação de graus) e não deve ser confundida com o grau médio simples. O grau médio deve ser calculado manualmente, somando todos os graus dos nós e dividindo pelo número de nós, ou utilizando as fórmulas 2M/N (para grafos não direcionados) ou M/N (para grafos direcionados).

~~~Python

Cálculo do grau médio para um grafo não direcionado

Para grafos direcionados, o cálculo seria G.numberofedges() / G.numberofnodes()

if G.isdirected():
avg
degree = G.numberofedges() / G.numberofnodes()
print(f"Grau Médio (Rede Direcionada): {avgdegree:.2f} passos")
else:
avg
degree = (2 * G.numberofedges()) / G.numberofnodes()
print(f"Grau Médio (Rede Não Direcionada): {avg_degree:.2f} passos")

~~~

A distinção entre o grau médio e a conectividade média de grau (nx.average_degree_connectivity) é fundamental para evitar interpretações errôneas. Enquanto o grau médio fornece uma medida global da conectividade, a conectividade média de grau é uma métrica mais avançada que explora a correlação entre os graus dos nós conectados. Explicitar que

nx.average_degree_connectivity não é o "Grau médio" solicitado, mas sim uma medida de assortatividade, demonstra uma compreensão aprofundada das nuances da biblioteca e orienta a metodologia para resultados precisos.

D. Distribuição de Graus (Degree Distribution)

A distribuição de graus, P(k), quantifica a probabilidade de um nó selecionado aleatoriamente na rede ter exatamente k conexões. Esta é uma descrição poderosa da heterogeneidade da rede, revelando padrões como a presença de nós "hub" altamente conectados ou uma distribuição mais uniforme de conexões. Para redes direcionadas, é essencial analisar separadamente a distribuição de in-degree (

Pin​(k)), que representa as conexões de entrada, e a distribuição de out-degree (Pout​(k)), que representa as conexões de saída. Essas distribuições frequentemente revelam papéis funcionais distintos para os nós, como receptores de informação versus emissores de informação.

Metodologia de Cálculo e Visualização:
O processo de cálculo envolve primeiro a obtenção do grau (ou in-degree/out-degree para grafos direcionados) de cada nó na rede. Em seguida, a frequência de cada valor de grau único é contada para construir a distribuição.
As técnicas de visualização são cruciais para a compreensão e comunicação dos resultados da análise da distribuição de graus.

  • Histograma: Um histograma dos graus fornece uma representação visual da frequência (ou probabilidade) de nós com um determinado grau. Isso oferece uma percepção imediata da forma da distribuição e dos valores de grau mais comuns.
  • Gráfico Log-Log: Plotar a distribuição de graus em uma escala log-log é essencial para identificar o comportamento de lei de potência (scale-free). Nesses tipos de distribuição, os pontos de dados tendem a se aproximar de uma linha reta em um gráfico log-log, indicando a presença de poucos nós "hub" altamente conectados e muitos nós com poucas conexões. Isso contrasta fortemente com redes aleatórias, que tipicamente exibem uma distribuição de Poisson (uma curva em forma de sino).

~~~Python

degree_sequence = sorted([d for n, d in G.degree()], reverse=True) # Para grafo não direcionado

Para grafo direcionado, usar:

degreesequence = sorted([d for n, d in G.outdegree()], reverse=True) # Exemplo para out-degree

degreecounts = collections.Counter(degreesequence)
deg, cnt = zip(*degree_counts.items())

Filtrar contagens zero para o gráfico log-log (logaritmo de zero é indefinido)

positivecntindices = np.array(cnt) > 0
logx = np.log10(np.array(deg)[positivecntindices])
logy = np.log10(np.array(cnt)[positivecntindices])

Histogram Plot

plt.figure(figsize=(10, 6))
plt.bar(deg, cnt, width=0.8, color='b')
plt.title("Histograma da Distribuição de Graus")
plt.xlabel("Grau (k)")
plt.ylabel("Número de Nós")
plt.show()

Log-Log Plot

plt.figure(figsize=(10, 6))
plt.plot(logx, logy, 'o', color='r', alpha=0.7)
plt.title("Distribuição de Graus (Escala Log-Log)")
plt.xlabel("log10(Grau)")
plt.ylabel("log10(Número de Nós)")
plt.grid(True, which="both", ls="-", alpha=0.2)
plt.show() ~~~

Tabela 2: Distribuição de Frequência de Graus

| Grau (k) | Número de Nós com Grau k (Frequência Absoluta) | Fração de Nós com Grau k (Frequência Relativa, P(k)) | | :---- | :---- | :---- | | k1​ | C1​ | P(k1​) | | k2​ | C2​ | P(k2​) | | ... | ... | ... | | kn​ | Cn​ | P(kn​) |

A Tabela 2 fornece os dados numéricos brutos que fundamentam as representações gráficas da distribuição de graus. Ela permite uma inspeção precisa da frequência de cada valor de grau específico, o que é crucial para verificar os padrões observados nos gráficos, como as contagens exatas de nós "hub" ou a frequência de nós de baixo grau.

E. Média das Distâncias entre Pares (Average Shortest Path Length - ASPL)

A média das distâncias entre pares, denotada como ⟨L⟩, é definida como o número médio de passos (arestas) ao longo dos caminhos mais curtos para todos os pares possíveis de nós na rede. Serve como uma medida crucial da eficiência da rede no transporte de informações ou massa. Um ASPL menor geralmente indica uma rede mais eficiente, interconectada e facilmente navegável, onde a informação pode viajar rapidamente entre quaisquer dois pontos.

O NetworkX fornece a função nx.average_shortest_path_length(G, weight=None, method=None) para este cálculo. No entanto, é crucial observar que esta função é definida para grafos conectados. Ela levanta um NetworkXError se o grafo não for conectado (para grafos não direcionados) ou não for fortemente conectado (para grafos direcionados).

Manuseio de Grafos Desconectados: Se a rede for desconectada, o ASPL para o grafo inteiro é indefinido. Nesses casos, a prática padrão é calcular o LCC para o maior componente conectado. O relatório deve primeiro identificar esses componentes usando nx.connected_components(G) para grafos não direcionados e em seguida, achar o maior componente conectado com a função MAX e parametro key=len, logo após criase um subgrafo com o maior componente conectado e por fim calcula o ASPL.

~~~Python

def mediadistanciasentrepares(lcc): """Média das Distâncias entre Pares"""

if nx.is_connected(lcc_):        
    print("A rede é conectada.")
    avg_path_length_estimate = nx.average_shortest_path_length(lcc_)   
    return print(f"Média das Distâncias entre Pares: {avg_path_length_estimate:.2f} passos")  
else:  
    print("A rede é desconectada.")        
    componentes = nx.connected_components(lcc_)
    maior_comp_nodes = max(componentes, key=len)

    print('Cria um subgrafo com o MAX correspondente para analise')
    componente_maior = lcc_.subgraph(maior_comp_nodes).copy()

    print(f"Novo subgrafo criado com {componente_maior.number_of_nodes()} nós e {componente_maior.number_of_edges()} arestas.")
    avg_path_length_estimate = nx.average_shortest_path_length(componente_maior)
    return print(f"Média das Distâncias entre Pares: {avg_path_length_estimate:.2f} passos")

~~~

Assumindo que a rede é conectada ou que o ASPL é calculado para seus componentes, a média das distâncias entre pares.

F. Diâmetro da Rede (Diameter)

O diâmetro de uma rede é definido como o caminho mais curto mais longo entre quaisquer dois nós na rede. Ele representa a separação máxima ou a "distância" que a informação ou influência deve percorrer dentro da rede. É uma métrica crítica para entender a compactação geral da rede, sua eficiência e potenciais gargalos. Um diâmetro menor geralmente indica uma rede mais interconectada e eficiente.

O NetworkX fornece a função nx.diameter(G, e=None, usebounds=False, weight=None). Assim como o ASPL, nx.diameter() é definida para grafos conectados e levanta um NetworkXError se o grafo não for conectado (para grafos não direcionados) ou não for fortemente conectado (para grafos direcionados).

Manuseio de Grafos Desconectados: Para grafos desconectados, o diâmetro é tecnicamente infinito. Na análise de redes prática, o diâmetro é tipicamente calculado para o maior componente conectado, pois ele representa o "alcance" máximo dentro da parte mais extensa da rede. O NetworkX também oferece nx.algorithms.approximation.distance_measures.diameter(), que calcula um limite inferior para o diâmetro e pode ser usado para grafos muito grandes onde o cálculo exato é inviável, mas também levanta um erro para grafos desconectados.

~~~Python

if nx.isconnected(G):
diameter = nx.diameter(G)
print(f"Diâmetro da Rede: {diameter} passos")
else:
print("A rede é desconectada. O diâmetro é indefinido para a rede completa. Calculando para o maior componente conectado.")
largest
cc = max(nx.connectedcomponents(G), key=len)
subgraph
lcc = G.subgraph(largestcc).copy()
if len(subgraph
lcc) > 1:
diameterlcc = nx.diameter(subgraphlcc)
print(f" Diâmetro do Maior Componente Conectado (N={len(subgraphlcc)}): {diameterlcc} passos")
else:
print("O maior componente conectado possui apenas um nó; diâmetro não aplicável.") ~~~

O diâmetro da rede é D passos. Um diâmetro grande, especialmente em relação ao tamanho da rede, pode indicar uma rede menos eficiente onde a informação ou os recursos podem levar um número significativo de passos para atravessar de um extremo ao outro. Isso destaca potenciais gargalos estruturais ou áreas onde o fluxo de comunicação pode ser lento. Por outro lado, um diâmetro pequeno, particularmente em redes grandes, reforça a propriedade de "mundo pequeno" (discutida com o ASPL) e sugere alta navegabilidade, onde quaisquer dois nós podem ser alcançados rapidamente. Essa conexão entre o diâmetro numérico e as implicações práticas para o design da rede, os protocolos de comunicação e o desempenho geral é um aspecto crucial.

G. Escolher uma rede das 10 redes empiricas e calcular a centradilidade dos nodos usando: Autovetor, Katz, PageRank, Betwenness. Calcular a media delas assim como plotar o histograma.

  • ### Escolhida a rede phonecalls

Centradilidade Autovetor.

Mede a influência de um vertice na rede. atravez de dois fatores:

  • Seu grau
  • Importância dos seus vizinhos.

~~~Python eigenvectorcentrality = nx.eigenvectorcentrality(lcc_) ~~~

Centradilidade Katz.

Tem o mesmo principio do autovetor, porem, a diferença é que, inicialmente, defini-se uma pequena quantidade de centralidade para cada vértice da rede.

Apresentou o segunte erro:

erro: (PowerIterationFailedConvergence(...), 'power iteration failed to converge within 100 iterations')

Aprendido: A centralidade de Katz não é calculada com uma fórmula simples e direta. Em vez disso, ela é encontrada usando um processo iterativo chamado Iteração de Potência (Power Iteration). O algoritmo começa com uma estimativa inicial para os scores de centralidade e refina essa estimativa repetidamente a cada passo (ou "iteração"). Ele para quando os scores mudam muito pouco entre uma iteração e a seguinte (o que significa que eles "convergiram" para uma solução).

Código

Solucionando erro power iteration failed to converge definindo um alpha menor que o valor padrão de 1, que não atendeu e apresentou esse erro.

A resolução foi preciso obter a matriz de adjacência em um formato que o SciPy, foi calculado o maior autovalor (utilizando linalg.eigs), k=1 significa que queremos o maior. 'LM' significa 'Largest Magnitude' (maior magnitude), o resultado é um array, então pegamos o primeiro elemento, usamos a parte real para evitar pequenos componentes e problemas de precisão numérica. O alpha calcula um número um pouco menor que seu máximo teórico, e é usando 99% do valor máximo por segurança.

~~~Python

Obtenha a matriz de adjacência em um formato que o SciPy

A = nx.toscipysparsearray(lcc, nodelist=lcc_.nodes())

Calculo do maior autovalor (raio espectral)

raio_espectral = np.real(linalg.eigs(A, k=1, which='LM')[0][0])

O alpha

alpha = (1 / raioespectral) * 0.99
print(f"Raio Espectral Calculado: {raio
espectral:.4f}") print(f"Alpha seguro para usar: {alpha:.4f}")

4. Rode novamente a centralidade de Katz com o alpha calculado para maixo de 1000 iterações.

katzcentrality = nx.katzcentrality(lcc, alpha=alpha, maxiter=1000) ~~~

Centradilidade PageRank.

~~~Python pagerankcentrality = nx.pagerank(lcc) ~~~

Centradilidade Betwenness.

~~~Python betweennesscentrality = nx.betweennesscentrality(lcc_, k=100) ~~~

Visualização e Organização dos dados das Centralidades

Foi organizado os Dados e Cálculo da Média, criando um DataFrame do pandas para melhor visualização e manipulação, o calculo da média sobre as quatro medidas de centralidade para cada nodo, foi ordenando os resultados pela média para facilitar a interpretação, feito um arredondando para 4 casas decimais.

~~~Python

Criando um DataFrame do pandas

dfcentrality = pd.DataFrame({ 'Autovetor': pd.Series(eigenvectorcentrality), 'Katz': pd.Series(katzcentrality), 'PageRank': pd.Series(pagerankcentrality), 'Betweenness': pd.Series(betweenness_centrality) })

Calculando a média

dfcentrality['Média'] = dfcentrality.mean(axis=1)

Ordenando os resultados

dfcentralitysorted = dfcentrality.sortvalues(by='Média', ascending=False)

Resultado

print("Tabela de Centralidades dos Nodos:") print(dfcentralitysorted.round(4).head()) ~~~

Configurações para histograma

O Histograma foi configurando o estilo do plot usando seaborn-v0_8-whitegrid, com 5 bins , color e edgecolor que se destacam com o estilo escolhido, utilizado a escala logaritmica no eixo y para melhorar a visualização, adicionando títulos e legendas correspondentes as médias centralizadas.

~~~Python

Plot do Histograma

Configurando o estilo do plot

plt.style.use('seaborn-v08-whitegrid') plt.figure(figsize=(10, 6)) plt.hist(dfcentrality['Média'], bins=5, color='skyblue', edgecolor='black')

Adicionando títulos e legendas

plt.title('Histograma da Média das Centralidades', fontsize=16) plt.xlabel('Média das Centralidades', fontsize=12) plt.ylabel('Frequência (Número de Nodos)', fontsize=12) plt.yscale('log')

Exibindo o gráfico

plt.show() ~~~

H. Desenhar o subgrafo com os 1000 nodos com maior PageRank. A cor do nodo deve representar o seu valor de PageRank. Usar a rede do item (g).

Abaixo segue a descrição do código:

Converte a variavel pagerank_centrality para uma Serie do pandas para facilitar a ordenação, ordenando os nodos pelo valor de PageRank em ordem decrescente e pegando os 1000 primeiros. É criando o subgrafo que contém apenas os nodos da lista top1000nodes, para a visualização foi usando um fundo escuro para destacar as cores, e foi utilizando o layout 'spring' trata os nodos como massas e as arestas como molas, pegando os valores de PageRank apenas para os nodos do subgrafo, usando um mapa de cores vibrante (plasma), adicionando uma barra de cores para mapear cores a valores de PageRank, foi remove os eixos para melhor representar o plot.

~~~Python

Convertendo o dicionário para uma Series do pandas para facilitar a ordenação

pagerankseries = pd.Series(pagerankcentrality)

Ordenando os nodos pelo valor de PageRank em ordem decrescente e pegando os 1000 primeiros

top1000nodes = pagerank_series.nlargest(1000).index.tolist() print("Top 1000 nodos com maior PageRank identificados.")

Criação do Subgrafo

S = G.subgraph(top1000nodes) print(f"Subgrafo criado com {S.numberofnodes()} nodos e {S.numberofedges()} arestas.")

Visualização do Subgrafo

print("Preparando a visualização do subgrafo...") plt.style.use('dark_background') # Usando um fundo escuro para destacar as cores fig, ax = plt.subplots(figsize=(16, 16))

Posição dos nodos para a visualização

pos = nx.spring_layout(S, seed=42, iterations=50)

Pegando os valores de PageRank apenas para os nodos do subgrafo

nodecolors = [pagerankcentrality[node] for node in S.nodes()]

Desenhando o grafo

nodes = nx.drawnetworkxnodes( S, pos, nodesize=50, nodecolor=nodecolors, cmap=plt.cm.plasma # Usando um mapa de cores vibrante (plasma) ) edges = nx.drawnetworkxedges(S, pos, alpha=0.1, edgecolor='gray')

Adicionando uma barra de cores para mapear cores a valores de PageRank

sm = plt.cm.ScalarMappable(cmap=plt.cm.plasma, norm=plt.Normalize(vmin=min(nodecolors), vmax=max(nodecolors))) sm.A = [] cbar = plt.colorbar(sm, ax=ax, shrink=0.8) cbar.setlabel('Valor de PageRank', rotation=270, labelpad=20, fontsize=14)

Configurações finais do plot

ax.set_title('Subgrafo dos 1000 Nodos com Maior PageRank', fontsize=20) plt.axis('off') # Remove os eixos print("Plot gerado. Exibindo a imagem...") plt.show() ~~~

I. Desafio: Repetir as análises de A - H mas com uma rede encontrada por você.

Retirado dataset deste link dos voos no ano de 2025, que por sua vez utiliza dados dos aeroportos de origem e destino no mundo, foi criado uma rede que corelaciona os voos dos aeroportos de origem a seus destinos respectivamente.

Para Utilização deste dataset, foi utilizado o pandas para adequação e criação do Grafo, foi lido o arquivo, separado as colunas alvo e criado um grafo, e por sua vez executados os passos de A até H.

~~~Python

Lendo Arquivo

df = pd.readcsv('resumoanual_2025.csv', sep=';', encoding='utf-8')

Separando as colunas para criação do Grafo

df2025 = df[ [ 'AEROPORTO DE ORIGEM (SIGLA)', 'AEROPORTO DE DESTINO (SIGLA)', 'AEROPORTO DE ORIGEM (PAS)' ] ] df2025

Criando um grafo vazio

G = nx.Graph()

Adicione vértices e arestas ao grafo criando anteriormente.

for index, row in df.iterrows(): G.addnode(row['AEROPORTO DE ORIGEM (SIGLA)']) G.addnode(row['AEROPORTO DE DESTINO (SIGLA)']) G.add_edge(row['AEROPORTO DE ORIGEM (SIGLA)'], row['AEROPORTO DE DESTINO (SIGLA)'])

~~~

GeoMapa

Foi adicionado novo notebook aeroportos_geomap.ipynb com a formação de geomapa pintando a "origem" e "destino" de todos os voos do gafro criado com dataset de Aeroportos.

No notebook contem uma analise exploratória de um novo dataset neste arquivo csv iata-icao.csv que contem a posição geografica de todos os aeroportos do mundo, e também com as sigras IATA e ICAO, cujo o significado IATA (International Air Transport Association) é uma associação comercial que define padrões para a aviação comercial e de passageiros, enquanto a ICAO (International Civil Aviation Organization) é uma agência da ONU que cria as regras de aviação civil internacional e os códigos alfanuméricos usados em planos de voo e comunicações técnicas.

Foi escolhido esse dataset para referenciar a sigla ICAO com a sigla do dado extraido do Ibge e mesclando as colunas necessárias em um dataframe para ser criado um novo Grafo preparado para receber as pocições geograficas e montar um mapa mundial com as rotas do voos.

Foi utilizado a bibliotecas matplotlib em conjunto com a bibioteca mpl_toolkits, a classe Basemap que renderiza as geolocalizações dos aeroportos com os nós e arestas.

Owner

  • Name: Wagner COliveira
  • Login: WagnerCOliveira
  • Kind: user

SysAdmin | SRE | Infra as Code | Linux

Citation (citation.edgelist.ipynb)

{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b3d511c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "import time\n",
    "import pandas as pd\n",
    "import networkx as nx  \n",
    "import collections  \n",
    "import matplotlib.pyplot as plt  \n",
    "import numpy as np\n",
    "import scipy.sparse.linalg as linalg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2f448035",
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nx.read_edgelist('datasets/citation.edgelist.txt')\n",
    "print(G)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3dad9562",
   "metadata": {},
   "source": [
    "### A. Tamanho da Rede\n",
    "O NetworkX oferece funções diretas para obter o número de nós: G.number\\_of\\_nodes() ou, de forma mais concisa, len(G).\n",
    "\n",
    "N nós."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98b47796",
   "metadata": {},
   "outputs": [],
   "source": [
    "num_nodes = G.number_of_nodes()  \n",
    "print(f\"Tamanho da Rede (Número de Nós): {num_nodes} nós\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b731fca",
   "metadata": {},
   "source": [
    "### B. Número de Links (Arestas)\n",
    "\n",
    "O NetworkX fornece a função G.number\\_of\\_edges() para obter o número total de arestas. \n",
    "Alternativamente, o método G.size() oferece um resultado idêntico.\n",
    "\n",
    "M links."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f7a25cea",
   "metadata": {},
   "outputs": [],
   "source": [
    "num_edges = G.number_of_edges()  \n",
    "print(f\"Número de Links (Arestas): {num_edges} links\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "564ec7c5",
   "metadata": {},
   "source": [
    "### C. Grau Médio (Average Degree)\n",
    "\n",
    "O grau médio, ⟨k⟩, é a média do número de conexões por nó na rede, oferecendo um resumo conciso do nível de conectividade geral da rede.\n",
    "\n",
    "Para redes não direcionadas, o grau médio é calculado como 2M/N, uma vez que cada aresta contribui para o grau de dois nós, ou simplesmente como a soma de todos os graus dividida pelo número de nós.\n",
    "\n",
    "Em redes direcionadas, o grau médio de entrada (in-degree), o grau médio de saída (out-degree) e o grau total médio são matematicamente equivalentes a M/N\n",
    "\n",
    "* Cálculo do grau médio para um grafo não direcionado  \n",
    "* Verifica de o grafo é direcionado G.is_directed()\n",
    "* Para grafos direcionados, o cálculo seria G.number_of_edges() / G.number_of_nodes()  \n",
    "* Para grafos não direcionados, o cálculo seria (2 * G.number_of_edges()) / G.number_of_nodes()  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "76a1f26d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cálculo do grau médio para um grafo não direcionado  \n",
    "# Para grafos direcionados, o cálculo seria G.number_of_edges() / G.number_of_nodes()  \n",
    "if G.is_directed():  \n",
    "    avg_degree = G.number_of_edges() / G.number_of_nodes()  \n",
    "    print(f\"Grau Médio (Rede Direcionada): {avg_degree:.2f} passos\")  \n",
    "else:  \n",
    "    avg_degree = (2 * G.number_of_edges()) / G.number_of_nodes()  \n",
    "    print(f\"Grau Médio (Rede Não Direcionada): {avg_degree:.2f} passos\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69c0bda8",
   "metadata": {},
   "source": [
    "### D. Distribuição de Graus (Degree Distribution)\n",
    "\n",
    "A distribuição de graus, P(k), quantifica a probabilidade de um nó selecionado aleatoriamente na rede ter exatamente k conexões.\n",
    "\n",
    "Metodologia de Cálculo e Visualização:  \n",
    "\n",
    "O processo de cálculo envolve primeiro a obtenção do grau (ou in-degree/out-degree para grafos direcionados) de cada nó na rede. Em seguida, a frequência de cada valor de grau único é contada para construir a distribuição.\n",
    "\n",
    "#### Código\n",
    "\n",
    "##### Montagem dados para gráfico\n",
    "\n",
    "* Calcula e organiza os dados de graus do seu grafo, \n",
    "  * obtém uma lista de pares (nó, grau) para cada nó no grafo,  \n",
    "  * que extrai apenas o valor do grau (d) de cada par, \n",
    "  * Ordena essa lista de graus em ordem decrescente. \n",
    "  * O resultado é uma lista chamada degree_sequence que contém os graus de todos os nós do grafo, do maior para o menor.\n",
    "* A classe **collections.Counter** é uma maneira de contar a frequência de itens em uma lista.\n",
    "  * Cria um dicionário onde as chaves são os graus (k) e os valores são o número de nós que têm esse grau. Ex.: se 10 nós têm grau 3, o dicionário terá a entrada {3: 10}\n",
    "* Gerando duas tuplas com os graus unicos, e das contagens corespondentes.\n",
    "  * Descompacta o dicionário com '*' pegando os items de gedree_counts, e a função zip gerando uma lista de tuplas com grau (deg) e contagem(cnt).\n",
    "\n",
    "##### Preparando dados\n",
    "\n",
    "* Seleciona apenas as contagens que são maiores que zero. Isso evita erros de cálculo.\n",
    "\n",
    "##### Historograma\n",
    "* Este histograma mostra quantos nós existem para cada grau possível no grafo.\n",
    "\n",
    "##### Distribuição\n",
    "* O principal objetivo é identificar a distribuição de graus por número de nós.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ab7911b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Monata uma lista com os graus dos nós em ordem decrescente.\n",
    "\n",
    "degree_sequence = sorted([d for n, d in G.degree()], reverse=True)\n",
    "degree_counts = collections.Counter(degree_sequence)  \n",
    "deg, cnt = zip(*degree_counts.items())\n",
    "\n",
    "# Filtrar contagens zero para o gráfico log-log (logaritmo de zero é indefinido)  \n",
    "positive_cnt_indices = np.array(cnt) > 0  \n",
    "logx = np.log10(np.array(deg)[positive_cnt_indices])  \n",
    "logy = np.log10(np.array(cnt)[positive_cnt_indices])\n",
    "\n",
    "# Histogram Plot  \n",
    "plt.figure(figsize=(10, 6))  \n",
    "plt.bar(deg, cnt, width=0.8, color='b')  \n",
    "plt.title(\"Histograma da Distribuição de Graus\")  \n",
    "plt.xlabel(\"Grau (k)\")  \n",
    "plt.ylabel(\"Número de Nós\")  \n",
    "plt.show()\n",
    "\n",
    "# Log-Log Plot  \n",
    "plt.figure(figsize=(10, 6))  \n",
    "plt.plot(logx, logy, 'o', color='r', alpha=0.7)  \n",
    "plt.title(\"Distribuição de Graus (Escala Log-Log)\")  \n",
    "plt.xlabel(\"log10(Grau)\")  \n",
    "plt.ylabel(\"log10(Número de Nós)\")  \n",
    "plt.grid(True, which=\"both\", ls=\"-\", alpha=0.2)  \n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "332f365c",
   "metadata": {},
   "source": [
    "### E. Média das Distâncias entre Pares (Average Shortest Path Length)\n",
    "\n",
    "A média das distâncias entre pares (ASPL), denotada como ⟨L⟩, é definida como o número médio de passos (arestas) ao longo dos caminhos mais curtos para todos os pares possíveis de nós na rede. Serve como uma medida crucial da eficiência da rede no transporte de informações ou massa. Um ASPL menor geralmente indica uma rede mais eficiente, interconectada e facilmente navegável, onde a informação pode viajar rapidamente entre quaisquer dois pontos.\n",
    "\n",
    "#### Código\n",
    "\n",
    "* A função media_distancias_entre_pares calcula a média das distâncias de caminho mais curto entre todos os pares de nós em um grafo. Ela foi projetada para lidar com dois cenários: quando o grafo é conectado e quando ele é desconectado.\n",
    "  * Grafo Conectado\n",
    "    * calcula direto utilizando a função nx.average_shortest_path_length()\n",
    "\n",
    "  * Grafo Desconectado\n",
    "    * Como não é possível calcular a distância entre nós que não estão no mesmo componente, é calculado o maior componente conectado.\n",
    "    * Faz o calculo da função nx.average_shortest_path_length() com o maior componente conectado.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c3f1ba5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def media_distancias_entre_pares(lcc_):\n",
    "    \"\"\"Média das Distâncias entre Pares\"\"\"\n",
    "\n",
    "    if nx.is_connected(lcc_):        \n",
    "        print(\"A rede é conectada.\")\n",
    "        avg_path_length_estimate = nx.average_shortest_path_length(lcc_)   \n",
    "        return print(f\"Média das Distâncias entre Pares: {avg_path_length_estimate:.2f} passos\")  \n",
    "    else:  \n",
    "        print(\"A rede é desconectada.\")        \n",
    "        componentes = nx.connected_components(lcc_)\n",
    "        maior_comp_nodes = max(componentes, key=len)\n",
    "        \n",
    "        print('Cria um subgrafo com o MAX correspondente para analise')\n",
    "        componente_maior = lcc_.subgraph(maior_comp_nodes).copy()\n",
    "        \n",
    "        print(f\"Novo subgrafo criado com {componente_maior.number_of_nodes()} nós e {componente_maior.number_of_edges()} arestas.\")\n",
    "        avg_path_length_estimate = nx.average_shortest_path_length(componente_maior)\n",
    "        return print(f\"Média das Distâncias entre Pares: {avg_path_length_estimate:.2f} passos\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ee2661a",
   "metadata": {},
   "source": [
    "### Amostragem\n",
    "\n",
    "* É criado um subgrado com 20% do grafo original, como amostragem, para diminuir o tempo de processamento da função media_distancias_entre_pares.\n",
    "\n",
    "  * Criar o subgrafo a partir da lista de nós\n",
    "    * Usar .copy() para ter um grafo independente\n",
    "  * Criando amostragem 20% dos Nós do LCC\n",
    "    * Pega a lista de todos os nós que estão no LCC\n",
    "    * Calcula quantos nós correspondem a 20% do LCC\n",
    "    * Usamos int() para garantir que o resultado seja um número inteiro\n",
    "    * Seleciona aleatoriamente 'num_nodes_to_sample' nós da lista, sem reposição\n",
    "  * Criar o Subgrafo Final com os 20% dos Nós\n",
    "    * Cria o subgrafo final a partir do LCC, usando apenas os nós amostrados\n",
    "    * Este novo grafo conterá os nós amostrados e TODAS as arestas que existiam ENTRE ELES no LCC"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed5590ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "lcc_nodes = list(max(nx.connected_components(G), key=len)) \n",
    "\n",
    "# Criando Subgrafo \n",
    "\n",
    "LCC_subgraph = G.subgraph(lcc_nodes).copy() \n",
    "print(f\"Número de nós no grafo original (G): {G.number_of_nodes()}\")\n",
    "print(f\"Número de nós no Maior Componente Conectado (LCC): {LCC_subgraph.number_of_nodes()}\")\n",
    "print(\"-\" * 30)\n",
    "\n",
    "# Criando Amostragem \n",
    "\n",
    "lcc_node_list = list(LCC_subgraph.nodes())\n",
    "num_nodes_to_sample = int(LCC_subgraph.number_of_nodes() * 0.20)\n",
    "sampled_nodes = random.sample(lcc_node_list, k=num_nodes_to_sample)\n",
    "print(f\"Vamos amostrar 20% do LCC, o que corresponde a {num_nodes_to_sample} nós.\")\n",
    "print(\"-\" * 30)\n",
    "\n",
    "# Subgrafo Final com os 20% dos Nós\n",
    "\n",
    "LCC_20_percent = LCC_subgraph.subgraph(sampled_nodes).copy()\n",
    "print(f\"Novo subgrafo criado com {LCC_20_percent.number_of_nodes()} nós e {LCC_20_percent.number_of_edges()} arestas.\")\n",
    "\n",
    "inicio_par = time.time()\n",
    "\n",
    "media_distancias_entre_pares(LCC_20_percent)\n",
    "fim_par = time.time()\n",
    "print(f\"Tempo de execução paralela: {(fim_par - inicio_par)/60:.4f} minutos\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db74e586",
   "metadata": {},
   "source": [
    "### F. Diâmetro da Rede (Diameter)\n",
    "\n",
    "O diâmetro de uma rede é definido como o caminho mais curto mais longo entre quaisquer dois nós na rede. Ele representa a separação máxima ou a \"distância\" que a informação ou influência deve percorrer dentro da rede. É uma métrica crítica para entender a compactação geral da rede, sua eficiência e potenciais gargalos. Um diâmetro menor geralmente indica uma rede mais interconectada e eficiente."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "417ca19b",
   "metadata": {},
   "outputs": [],
   "source": [
    "lcc_ = nx.subgraph(G, lcc_nodes)\n",
    "\n",
    "def diametro(lcc_):\n",
    "    \"\"\"Média das Distâncias entre Pares\"\"\"\n",
    "\n",
    "    if nx.is_connected(lcc_):        \n",
    "        print(\"A rede é conectada.\")\n",
    "        diam_estimate = nx.diameter(lcc_)\n",
    "        return print(f\"Estimativa do Diâmetro da Rede: {diam_estimate:.2f}\")  \n",
    "    else:  \n",
    "        print(\"A rede é desconectada. O diâmetro é indefinido para a rede completa. Calculando para o maior componente conectado.\")  \n",
    "        largest_cc = max(nx.connected_components(lcc_), key=len)  \n",
    "        subgraph_lcc = lcc_.subgraph(largest_cc).copy()  \n",
    "        if len(subgraph_lcc) > 1:  \n",
    "            diameter_lcc = nx.diameter(subgraph_lcc)  \n",
    "            print(f\"  Diâmetro do Maior Componente Conectado (N={len(subgraph_lcc)}): {diameter_lcc} passos\")  \n",
    "        else:  \n",
    "            print(\"O maior componente conectado possui apenas um nó; diâmetro não aplicável.\")\n",
    "       \n",
    "\n",
    "print(diametro(LCC_20_percent))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5bb7036a",
   "metadata": {},
   "source": [
    "### G. Escolher uma rede das 10 redes empiricas e calcular a centradilidade dos nodos usando: Autovetor, Katz, PageRank, Betwenness. Calcular a media delas assim como plotar o histograma.\n",
    "\n",
    "* Escolhida a rede phonecalls\n",
    "\n",
    "#### Centradilidade Autovetor.\n",
    "\n",
    "Mede a influência de um vertice na rede. atravez de dois fatores:\n",
    "\n",
    "* Seu grau \n",
    "* Importância dos seus vizinhos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "777cae83",
   "metadata": {},
   "outputs": [],
   "source": [
    "eigenvector_centrality = nx.eigenvector_centrality(lcc_)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e36b4837",
   "metadata": {},
   "source": [
    "### Centradilidade Katz.\n",
    "\n",
    "Tem o mesmo principio do autovetor, porem, a diferença é que, inicialmente, defini-se uma pequena quantidade de centralidade para cada vértice da rede.\n",
    "\n",
    "Apresentou o segunte erro:\n",
    "\n",
    "erro: (PowerIterationFailedConvergence(...), 'power iteration failed to converge within 100 iterations')\n",
    "\n",
    "Aprendido: A centralidade de Katz não é calculada com uma fórmula simples e direta. Em vez disso, ela é encontrada usando um processo iterativo chamado Iteração de Potência (Power Iteration). O algoritmo começa com uma estimativa inicial para os scores de centralidade e refina essa estimativa repetidamente a cada passo (ou \"iteração\"). Ele para quando os scores mudam muito pouco entre uma iteração e a seguinte (o que significa que eles \"convergiram\" para uma solução).\n",
    "\n",
    "#### Código\n",
    "\n",
    "Solucionando erro **power iteration failed to converge** definindo um alpha menor que já que o valor padrão de 1 não atendeu e apresentou esse erro.\n",
    "\n",
    "* Obtenha a matriz de adjacência em um formato que o SciPy\n",
    "  * Use to_scipy_sparse_array para versões mais novas do NetworkX\n",
    "* Calcule o maior autovalor (raio espectral)\n",
    "  * k=1 significa que queremos o maior. 'LM' significa 'Largest Magnitude' (maior magnitude).\n",
    "  * O resultado é um array, então pegamos o primeiro elemento.\n",
    "  * Usamos a parte real para evitar pequenos componentes e problemas de precisão numérica.\n",
    "* Definindo o alpha\n",
    "  * um pouco menor que seu máximo teórico\n",
    "  * Usando 99% do valor máximo por segurança\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "95fbb6ee",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Obtenha a matriz de adjacência em um formato que o SciPy\n",
    "\n",
    "A = nx.to_scipy_sparse_array(lcc_, nodelist=lcc_.nodes())\n",
    "\n",
    "# Calculo do maior autovalor (raio espectral)\n",
    "\n",
    "raio_espectral = np.real(linalg.eigs(A, k=1, which='LM')[0][0])\n",
    "\n",
    "# O alpha\n",
    "\n",
    "alpha = (1 / raio_espectral) * 0.99  \n",
    "print(f\"Raio Espectral Calculado: {raio_espectral:.4f}\")\n",
    "print(f\"Alpha seguro para usar: {alpha:.4f}\")\n",
    "\n",
    "# 4. Rode novamente a centralidade de Katz com o alpha calculado para maixo de 1000 iterações.\n",
    "\n",
    "katz_centrality = nx.katz_centrality(lcc_, alpha=alpha, max_iter=1000) \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6efe840",
   "metadata": {},
   "source": [
    "### Centradilidade PageRank."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0fa82d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "pagerank_centrality = nx.pagerank(lcc_)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39a69a32",
   "metadata": {},
   "source": [
    "### Centradilidade Betwenness."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fe920351",
   "metadata": {},
   "outputs": [],
   "source": [
    "betweenness_centrality = nx.betweenness_centrality(lcc_, k=100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4a9657e",
   "metadata": {},
   "source": [
    "### Visualização e Organização dos dados das Centralidades\n",
    "\n",
    "* Organização dos Dados e Cálculo da Média\n",
    "  * Criando um DataFrame do pandas para melhor visualização e manipulação\n",
    "  * Calculando a média das quatro medidas de centralidade para cada nodo\n",
    "  * Ordenando os resultados pela média para facilitar a interpretação\n",
    "    * Arredondando para 4 casas decimais\n",
    "    * imprimindo o head do dataframe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8a88f6da",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Criando um DataFrame do pandas\n",
    "\n",
    "df_centrality = pd.DataFrame({\n",
    "    'Autovetor': pd.Series(eigenvector_centrality),\n",
    "    'Katz': pd.Series(katz_centrality),\n",
    "    'PageRank': pd.Series(pagerank_centrality),\n",
    "    'Betweenness': pd.Series(betweenness_centrality)\n",
    "})\n",
    "\n",
    "# Calculando a média\n",
    "df_centrality['Média'] = df_centrality.mean(axis=1)\n",
    "\n",
    "# Ordenando os resultados\n",
    "df_centrality_sorted = df_centrality.sort_values(by='Média', ascending=False)\n",
    "\n",
    "\n",
    "# Resultado\n",
    "print(\"Tabela de Centralidades dos Nodos:\")\n",
    "print(df_centrality_sorted.round(4).head()) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3bf5d472",
   "metadata": {},
   "source": [
    "Foram feita algumas configurações para histograma\n",
    "\n",
    "* Histograma\n",
    "  * Configurando o estilo do plot usando seaborn-v0_8-whitegrid\n",
    "  * Configurado histograma para 100 bins , color e edgecolor que se destacam com o estilo escolhido. Utilizando logaritmo no eixo y para melhorar a visualização.\n",
    "  * Adicionando títulos e legendas correspondentes as médias centralizadas.\n",
    "  * Exibindo o gráfico"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b04007d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plot do Histograma\n",
    "# Configurando o estilo do plot\n",
    "plt.style.use('seaborn-v0_8-whitegrid')\n",
    "\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.hist(df_centrality['Média'], bins=100, color='skyblue', edgecolor='black')\n",
    "\n",
    "# Adicionando títulos e legendas\n",
    "plt.title('Histograma da Média das Centralidades', fontsize=16)\n",
    "plt.xlabel('Média das Centralidades', fontsize=12)\n",
    "plt.ylabel('Frequência (Número de Nodos)', fontsize=12)\n",
    "plt.yscale('log')\n",
    "\n",
    "# Exibindo o gráfico\n",
    "plt.show()\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82e20f91",
   "metadata": {},
   "source": [
    "### Desenhar o subgrafo com os 1000 nodos com maior PageRank. \n",
    "\n",
    "A cor do nodo deve representar o seu valor de PageRank. Usar a rede do item (g).\n",
    "\n",
    "\n",
    "* Convertendo o dicionário para uma Series do pandas para facilitar a ordenação    \n",
    "* Ordenando os nodos pelo valor de PageRank em ordem decrescente e pegando os 1000 primeiros\n",
    "* Criação do Subgrafo\n",
    "  * Criando o subgrafo que contém apenas os nodos da lista top_1000_nodes\n",
    "* Visualização do Subgrafo\n",
    "  * Usando um fundo escuro para destacar as cores\n",
    "* Posição dos nodos para a visualização (pode demorar um pouco para 1000 nodos)\n",
    "* O layout 'spring' trata os nodos como massas e as arestas como molas\n",
    "* Pegando os valores de PageRank apenas para os nodos do subgrafo\n",
    "* Desenhando o grafo\n",
    "  * Usando um mapa de cores vibrante (plasma)\n",
    "* Adicionando uma barra de cores para mapear cores a valores de PageRank\n",
    "* Configurações finais do plot\n",
    "  * Remove os eixos"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ce4d8f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Convertendo o dicionário para uma Series do pandas para facilitar a ordenação\n",
    "pagerank_series = pd.Series(pagerank_centrality)\n",
    "\n",
    "# Ordenando os nodos pelo valor de PageRank em ordem decrescente e pegando os 1000 primeiros\n",
    "top_1000_nodes = pagerank_series.nlargest(1000).index.tolist()\n",
    "print(\"Top 1000 nodos com maior PageRank identificados.\")\n",
    "\n",
    "# --- Criação do Subgrafo ---\n",
    "# Criando o subgrafo que contém apenas os nodos da lista top_1000_nodes\n",
    "S = G.subgraph(top_1000_nodes)\n",
    "print(f\"Subgrafo criado com {S.number_of_nodes()} nodos e {S.number_of_edges()} arestas.\")\n",
    "\n",
    "# --- Visualização do Subgrafo ---\n",
    "print(\"Preparando a visualização do subgrafo...\")\n",
    "plt.style.use('dark_background') # Usando um fundo escuro para destacar as cores\n",
    "fig, ax = plt.subplots(figsize=(16, 16))\n",
    "\n",
    "# Posição dos nodos para a visualização (pode demorar um pouco para 1000 nodos)\n",
    "# O layout 'spring' trata os nodos como massas e as arestas como molas\n",
    "pos = nx.spring_layout(S, seed=42, iterations=50)\n",
    "\n",
    "# Pegando os valores de PageRank apenas para os nodos do subgrafo\n",
    "node_colors = [pagerank_centrality[node] for node in S.nodes()]\n",
    "\n",
    "# Desenhando o grafo\n",
    "nodes = nx.draw_networkx_nodes(\n",
    "    S,\n",
    "    pos,\n",
    "    node_size=50,\n",
    "    node_color=node_colors,\n",
    "    cmap=plt.cm.plasma # Usando um mapa de cores vibrante (plasma)\n",
    ")\n",
    "edges = nx.draw_networkx_edges(S, pos, alpha=0.1, edge_color='gray')\n",
    "\n",
    "# Adicionando uma barra de cores para mapear cores a valores de PageRank\n",
    "sm = plt.cm.ScalarMappable(cmap=plt.cm.plasma, norm=plt.Normalize(vmin=min(node_colors), vmax=max(node_colors)))\n",
    "sm._A = []\n",
    "cbar = plt.colorbar(sm, ax=ax, shrink=0.8)\n",
    "cbar.set_label('Valor de PageRank', rotation=270, labelpad=20, fontsize=14)\n",
    "\n",
    "# Configurações finais do plot\n",
    "ax.set_title('Subgrafo dos 1000 Nodos com Maior PageRank', fontsize=20)\n",
    "plt.axis('off') # Remove os eixos\n",
    "print(\"Plot gerado. Exibindo a imagem...\")\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "MineracaoGrafo",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

GitHub Events

Total
  • Push event: 7
  • Create event: 1
Last Year
  • Push event: 7
  • Create event: 1

Dependencies

requirements.txt pypi
  • ipykernel *
  • jupyterlab *
  • matplotlib *
  • networkx *
  • numpy *
  • pandas *
  • scipy *
  • tqdm *