File size: 6,682 Bytes
64b5d29 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# src/analysis/network_analysis.py
import networkx as nx
import pandas as pd
import logging
# Topluluk tespiti için Louvain metodu (önce 'pip install python-louvain community' yapılmalı)
try:
import community.community_louvain as community_louvain
community_lib_available = True
except ImportError:
logging.warning("'community' (python-louvain) kütüphanesi bulunamadı. Topluluk tespiti yapılamayacak. Kurulum için: pip install python-louvain community")
community_lib_available = False
# Yerel modüller
from src.data_management import storage
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def calculate_centrality(graph: nx.Graph) -> dict:
"""
Graf üzerindeki düğümler için merkeziyet metriklerini hesaplar.
Args:
graph (nx.Graph): Analiz edilecek NetworkX grafı.
Returns:
dict: {node_id: {'degree': float, 'betweenness': float, 'eigenvector': float (veya None)}}
formatında metrikleri içeren sözlük.
"""
metrics = {}
if not graph or graph.number_of_nodes() == 0:
return metrics
try:
degree_centrality = nx.degree_centrality(graph)
except Exception as e:
logging.error(f"Degree Centrality hesaplanırken hata: {e}")
degree_centrality = {}
try:
betweenness_centrality = nx.betweenness_centrality(graph)
except Exception as e:
logging.error(f"Betweenness Centrality hesaplanırken hata: {e}")
betweenness_centrality = {}
try:
# Eigenvector centrality bağlantısız (disconnected) graflarda veya bazı durumlarda hata verebilir
# max_iter artırılabilir veya hata yakalanabilir
eigenvector_centrality = nx.eigenvector_centrality(graph, max_iter=500, tol=1e-06)
except Exception as e:
logging.warning(f"Eigenvector Centrality hesaplanırken hata (graf bağlantısız olabilir): {e}")
eigenvector_centrality = {} # Hata durumunda boş bırak
# Metrikleri birleştir
for node in graph.nodes():
metrics[node] = {
'degree_centrality': degree_centrality.get(node, 0.0),
'betweenness_centrality': betweenness_centrality.get(node, 0.0),
'eigenvector_centrality': eigenvector_centrality.get(node, None) # Hata durumunda None olabilir
}
logging.info("Merkeziyet metrikleri hesaplandı.")
return metrics
def detect_communities(graph: nx.Graph) -> dict | None:
"""
Louvain algoritması kullanarak graf üzerindeki toplulukları tespit eder.
Args:
graph (nx.Graph): Analiz edilecek NetworkX grafı.
Returns:
dict | None: {node_id: community_id} formatında bölümleme sözlüğü veya hata/kütüphane yoksa None.
"""
if not community_lib_available:
return None # Kütüphane yoksa hesaplama yapma
if not graph or graph.number_of_nodes() == 0:
return None # Boş graf
# Louvain metodu yönlendirilmemiş graflarda daha iyi çalışır.
# Eğer graf yönlü ise, yönlendirilmemişe çevir (veya uyarı ver).
# Bizim grafımız zaten yönlendirilmemiş (nx.Graph).
# Ağırlıklı kenarları kullanabilir (varsayılan weight='weight')
try:
partition = community_louvain.best_partition(graph, weight='weight') # Kenar ağırlıklarını dikkate al
num_communities = len(set(partition.values()))
logging.info(f"Louvain ile topluluk tespiti tamamlandı. {num_communities} topluluk bulundu.")
return partition
except Exception as e:
logging.exception(f"Topluluk tespiti sırasında hata oluştu: {e}")
return None
def get_network_analysis_results(graph: nx.Graph) -> pd.DataFrame | None:
"""
Merkeziyet ve topluluk analizlerini yapar ve sonuçları bir DataFrame'de birleştirir.
Args:
graph (nx.Graph): Analiz edilecek NetworkX grafı.
Returns:
pd.DataFrame | None: 'concept_id', 'name', 'degree_centrality', 'betweenness_centrality',
'eigenvector_centrality', 'community_id' sütunlarını içeren DataFrame
veya hata durumunda None.
"""
if not graph or graph.number_of_nodes() == 0:
logging.warning("Analiz için boş veya geçersiz graf sağlandı.")
return None
logging.info("Ağ analizi metrikleri hesaplanıyor...")
centrality_metrics = calculate_centrality(graph)
community_partition = detect_communities(graph)
# Sonuçları bir DataFrame'e dönüştür
analysis_data = []
concepts_df = storage.load_dataframe('concepts', storage.CONCEPT_COLUMNS) # İsimler için yükle
for node_id, metrics in centrality_metrics.items():
node_data = {
'concept_id': node_id,
'name': graph.nodes[node_id].get('name', 'N/A'), # Graf düğümünden al
'degree_centrality': metrics.get('degree_centrality'),
'betweenness_centrality': metrics.get('betweenness_centrality'),
'eigenvector_centrality': metrics.get('eigenvector_centrality'),
'community_id': community_partition.get(node_id, -1) if community_partition else -1 # Topluluk yoksa -1
}
analysis_data.append(node_data)
if not analysis_data:
logging.warning("Ağ analizi sonucu veri üretilemedi.")
return None
analysis_df = pd.DataFrame(analysis_data)
# Eğer graf düğümlerinde isim yoksa, concepts_df'ten almayı dene (yedek)
if 'N/A' in analysis_df['name'].values and concepts_df is not None:
analysis_df = analysis_df.drop(columns=['name']) # Eski 'name' sütununu sil
analysis_df = pd.merge(analysis_df, concepts_df[['concept_id', 'name']], on='concept_id', how='left')
# Sütun sırasını ayarla
cols = ['concept_id', 'name'] + [col for col in analysis_df.columns if col not in ['concept_id', 'name']]
analysis_df = analysis_df[cols]
logging.info("Ağ analizi sonuçları DataFrame'e dönüştürüldü.")
return analysis_df
def save_network_analysis(analysis_df: pd.DataFrame):
""" Ağ analizi sonuçlarını Parquet dosyasına kaydeder. """
if analysis_df is not None and not analysis_df.empty:
storage.save_dataframe(analysis_df, storage.NETWORK_ANALYSIS_FILENAME)
logging.info(f"Ağ analizi sonuçları '{storage.NETWORK_ANALYSIS_FILENAME}.parquet' olarak kaydedildi.")
else:
logging.warning("Kaydedilecek ağ analizi sonucu bulunamadı.") |