|
|
|
|
| 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ı.") |