|
|
|
|
|
import networkx as nx
|
|
import pandas as pd
|
|
import logging
|
|
|
|
|
|
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
|
|
|
|
|
|
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 = 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 = {}
|
|
|
|
|
|
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)
|
|
}
|
|
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
|
|
if not graph or graph.number_of_nodes() == 0:
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
partition = community_louvain.best_partition(graph, weight='weight')
|
|
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)
|
|
|
|
|
|
analysis_data = []
|
|
concepts_df = storage.load_dataframe('concepts', storage.CONCEPT_COLUMNS)
|
|
|
|
for node_id, metrics in centrality_metrics.items():
|
|
node_data = {
|
|
'concept_id': node_id,
|
|
'name': graph.nodes[node_id].get('name', 'N/A'),
|
|
'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
|
|
}
|
|
analysis_data.append(node_data)
|
|
|
|
if not analysis_data:
|
|
logging.warning("Ağ analizi sonucu veri üretilemedi.")
|
|
return None
|
|
|
|
analysis_df = pd.DataFrame(analysis_data)
|
|
|
|
|
|
if 'N/A' in analysis_df['name'].values and concepts_df is not None:
|
|
analysis_df = analysis_df.drop(columns=['name'])
|
|
analysis_df = pd.merge(analysis_df, concepts_df[['concept_id', 'name']], on='concept_id', how='left')
|
|
|
|
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ı.") |