""" =============================================================================== GEPHI İÇİN VERİ HAZIRLAMA =============================================================================== Bu script, Kaggle'daki Elliptic Bitcoin veri setindeki 3 dosyayı alır ve Gephi'nin doğrudan açabileceği formata dönüştürür. KAYNAK: https://www.kaggle.com/datasets/ellipticco/elliptic-data-set - elliptic_txs_features.csv (203,769 düğüm × 167 sütun) - elliptic_txs_classes.csv (203,769 düğüm × 2 sütun: txId, class) - elliptic_txs_edgelist.csv (234,355 kenar × 2 sütun: txId1, txId2) ÇIKTI: gephi_data/ ├── gephi_nodes_full.csv # Tüm düğümler (Gephi node table) ├── gephi_edges_full.csv # Tüm kenarlar (Gephi edge table) ├── gephi_nodes_ts{N}.csv # Her timestep için ayrı düğüm dosyası ├── gephi_edges_ts{N}.csv # Her timestep için ayrı kenar dosyası └── timestep_summary.csv # Her timestep'in özet istatistikleri GEPHİ'DE AÇMAK İÇİN: 1. Gephi'yi aç → File → Import Spreadsheet 2. Önce gephi_nodes_full.csv'yi "Nodes table" olarak import et 3. Sonra gephi_edges_full.csv'yi "Edges table" olarak import et 4. Partition → "class" sütununa göre renklendir (illicit=kırmızı, licit=yeşil) 5. Layout → ForceAtlas2 veya Fruchterman-Reingold uygula 6. Filters → "timestep" sütununa göre filtrele TİMESTEP BAZLI ANALİZ: - Her timestep için ayrı dosyalar da üretiliyor - Gephi'de Dynamic Network olarak da yüklenebilir (Timeline özelliği) KULLANIM: python prepare_for_gephi.py --data_dir ./elliptic_data =============================================================================== """ import os import sys import numpy as np import pandas as pd import argparse import networkx as nx def main(data_dir): output_dir = os.path.join(data_dir, 'gephi_data') os.makedirs(output_dir, exist_ok=True) print("=" * 70) print("ELLIPTIC BITCOIN → GEPHI DÖNÜŞÜMÜ") print("=" * 70) # ─── DOSYALARI OKU ─── print("\n1. Dosyalar okunuyor...") features_path = os.path.join(data_dir, 'elliptic_txs_features.csv') classes_path = os.path.join(data_dir, 'elliptic_txs_classes.csv') edges_path = os.path.join(data_dir, 'elliptic_txs_edgelist.csv') for p in [features_path, classes_path, edges_path]: if not os.path.exists(p): print(f" HATA: {p} bulunamadı!") print(f" Kaggle'dan indirip {data_dir} klasörüne koyun:") print(f" https://www.kaggle.com/datasets/ellipticco/elliptic-data-set") sys.exit(1) feat_df = pd.read_csv(features_path, header=None) class_df = pd.read_csv(classes_path) edge_df = pd.read_csv(edges_path) print(f" Özellikler: {feat_df.shape}") print(f" Sınıflar: {class_df.shape}") print(f" Kenarlar: {edge_df.shape}") # ─── DÜĞÜM TABLOSU (Gephi Nodes) ─── print("\n2. Gephi düğüm tablosu oluşturuluyor...") nodes = pd.DataFrame() nodes['Id'] = feat_df.iloc[:, 0] # Gephi "Id" sütunu (zorunlu) nodes['Label'] = feat_df.iloc[:, 0] # Gephi "Label" sütunu nodes['timestep'] = feat_df.iloc[:, 1].astype(int) # Sınıf bilgisi ekle nodes = nodes.merge(class_df, left_on='Id', right_on='txId', how='left') nodes.drop('txId', axis=1, inplace=True) nodes.rename(columns={'class': 'class_label'}, inplace=True) # Sayısal sınıf (Gephi renklendirme için) class_numeric = {'1': 1, '2': 0, 'unknown': -1} nodes['class_numeric'] = nodes['class_label'].map(class_numeric) # Okunabilir sınıf adı class_name = {'1': 'illicit', '2': 'licit', 'unknown': 'unknown'} nodes['class_name'] = nodes['class_label'].map(class_name) # Ağ derecesi hesapla out_deg = edge_df.groupby('txId1').size().reset_index(name='out_degree') out_deg.columns = ['Id', 'out_degree'] in_deg = edge_df.groupby('txId2').size().reset_index(name='in_degree') in_deg.columns = ['Id', 'in_degree'] nodes = nodes.merge(out_deg, on='Id', how='left') nodes = nodes.merge(in_deg, on='Id', how='left') nodes['out_degree'] = nodes['out_degree'].fillna(0).astype(int) nodes['in_degree'] = nodes['in_degree'].fillna(0).astype(int) nodes['total_degree'] = nodes['out_degree'] + nodes['in_degree'] # İlk birkaç önemli özelliği ekle (Gephi'de görselleştirme için) # Not: Elliptic'te özellik isimleri gizli, ama ilk özellik genellikle amount ile ilişkili for i in range(2, 12): # İlk 10 özellik nodes[f'feature_{i}'] = feat_df.iloc[:, i] print(f" Düğüm sayısı: {len(nodes)}") print(f" Sınıf dağılımı:") print(f" illicit: {(nodes['class_name'] == 'illicit').sum()}") print(f" licit: {(nodes['class_name'] == 'licit').sum()}") print(f" unknown: {(nodes['class_name'] == 'unknown').sum()}") # ─── KENAR TABLOSU (Gephi Edges) ─── print("\n3. Gephi kenar tablosu oluşturuluyor...") edges = pd.DataFrame() edges['Source'] = edge_df['txId1'] # Gephi "Source" (zorunlu) edges['Target'] = edge_df['txId2'] # Gephi "Target" (zorunlu) edges['Type'] = 'Directed' # Yönlü graf edges['Weight'] = 1.0 # Kenar ağırlığı # Kenarın timestep'ini belirle (kaynak düğümün timestep'i) node_ts = dict(zip(nodes['Id'], nodes['timestep'])) edges['source_timestep'] = edges['Source'].map(node_ts) edges['target_timestep'] = edges['Target'].map(node_ts) edges['timestep'] = edges['source_timestep'] # Kenarın zamanı = kaynağın zamanı # Kenarın sınıf bilgisi (kaynak veya hedef illicit mi?) node_class = dict(zip(nodes['Id'], nodes['class_name'])) edges['source_class'] = edges['Source'].map(node_class) edges['target_class'] = edges['Target'].map(node_class) # İllicit-illicit bağlantılar edges['both_illicit'] = ((edges['source_class'] == 'illicit') & (edges['target_class'] == 'illicit')).astype(int) edges['any_illicit'] = ((edges['source_class'] == 'illicit') | (edges['target_class'] == 'illicit')).astype(int) print(f" Kenar sayısı: {len(edges)}") print(f" İki ucu da illicit olan kenarlar: {edges['both_illicit'].sum()}") print(f" En az bir ucu illicit olan kenarlar: {edges['any_illicit'].sum()}") # ─── TAM DOSYALARI KAYDET ─── print("\n4. Tam dosyalar kaydediliyor...") nodes_file = os.path.join(output_dir, 'gephi_nodes_full.csv') edges_file = os.path.join(output_dir, 'gephi_edges_full.csv') nodes.to_csv(nodes_file, index=False) edges.to_csv(edges_file, index=False) print(f" ✓ {nodes_file} ({os.path.getsize(nodes_file) / 1e6:.1f} MB)") print(f" ✓ {edges_file} ({os.path.getsize(edges_file) / 1e6:.1f} MB)") # ─── TIMESTEP BAZLI DOSYALAR ─── print("\n5. Timestep bazlı dosyalar oluşturuluyor...") ts_summary = [] for t in sorted(nodes['timestep'].unique()): ts_nodes = nodes[nodes['timestep'] == t] ts_node_ids = set(ts_nodes['Id']) ts_edges = edges[edges['Source'].isin(ts_node_ids) & edges['Target'].isin(ts_node_ids)] n_illicit = (ts_nodes['class_name'] == 'illicit').sum() n_licit = (ts_nodes['class_name'] == 'licit').sum() n_unknown = (ts_nodes['class_name'] == 'unknown').sum() n_labeled = n_illicit + n_licit illicit_rate = n_illicit / max(n_labeled, 1) # NetworkX ile ağ metrikleri G = nx.DiGraph() G.add_nodes_from(ts_nodes['Id'].values) G.add_edges_from(zip(ts_edges['Source'].values, ts_edges['Target'].values)) G_u = G.to_undirected() n_components = nx.number_connected_components(G_u) largest_cc = max(nx.connected_components(G_u), key=len) cc_ratio = len(largest_cc) / max(len(ts_nodes), 1) degs = [d for _, d in G.degree()] avg_degree = np.mean(degs) if degs else 0 density = nx.density(G) if len(ts_nodes) > 1 else 0 ts_summary.append({ 'timestep': t, 'n_nodes': len(ts_nodes), 'n_edges': len(ts_edges), 'n_illicit': n_illicit, 'n_licit': n_licit, 'n_unknown': n_unknown, 'illicit_rate': round(illicit_rate, 4), 'density': round(density, 6), 'avg_degree': round(avg_degree, 2), 'n_components': n_components, 'largest_cc_ratio': round(cc_ratio, 4), 'illicit_edges_both': int(ts_edges['both_illicit'].sum()), 'illicit_edges_any': int(ts_edges['any_illicit'].sum()), }) # Timestep dosyalarını kaydet ts_nodes.to_csv(os.path.join(output_dir, f'gephi_nodes_ts{t:02d}.csv'), index=False) ts_edges.to_csv(os.path.join(output_dir, f'gephi_edges_ts{t:02d}.csv'), index=False) status = "🔴" if illicit_rate > 0.18 else "🟢" print(f" TS {t:2d}: {len(ts_nodes):5d} nodes, {len(ts_edges):5d} edges, " f"illicit={n_illicit:4d} ({illicit_rate*100:5.1f}%) {status}") # ─── TIMESTEP ÖZET ─── summary_df = pd.DataFrame(ts_summary) summary_file = os.path.join(output_dir, 'timestep_summary.csv') summary_df.to_csv(summary_file, index=False) # ─── KRİZ ANALİZİ ─── print("\n" + "=" * 70) print("6. KRİZ ANALİZİ — Gephi'de Odaklanılacak Dönemler") print("=" * 70) print("\n KRİZ DÖNEMLERİ (illicit > %18):") crisis_ts = summary_df[summary_df['illicit_rate'] > 0.18] for _, row in crisis_ts.iterrows(): print(f" TS {row['timestep']:2d}: illicit={row['illicit_rate']*100:.1f}%, " f"nodes={row['n_nodes']}, edges={row['n_edges']}") print("\n SAKİN DÖNEMLER (illicit < %5):") calm_ts = summary_df[summary_df['illicit_rate'] < 0.05] for _, row in calm_ts.iterrows(): print(f" TS {row['timestep']:2d}: illicit={row['illicit_rate']*100:.1f}%, " f"nodes={row['n_nodes']}, edges={row['n_edges']}") print("\n GEPHİ KARŞILAŞTIRMA ÖNERİSİ:") print(" Aşağıdaki timestep çiftlerini Gephi'de yan yana karşılaştırın:") print(" Bu karşılaştırma, kriz öncesi ve sonrası ağ yapısının nasıl") print(" değiştiğini görsel olarak gösterecektir.") if len(crisis_ts) > 0: peak_ts = crisis_ts.loc[crisis_ts['illicit_rate'].idxmax(), 'timestep'] print(f"\n 1. KRİZ ZİRVESİ: TS {peak_ts} (illicit={crisis_ts.loc[crisis_ts['illicit_rate'].idxmax(), 'illicit_rate']*100:.1f}%)") # Zirve öncesi sakin dönem bul pre_crisis = summary_df[(summary_df['timestep'] < peak_ts) & (summary_df['illicit_rate'] < 0.05)] if len(pre_crisis) > 0: pre_ts = pre_crisis.iloc[-1]['timestep'] print(f" 2. KRİZ ÖNCESİ: TS {pre_ts} (illicit={pre_crisis.iloc[-1]['illicit_rate']*100:.1f}%)") # Zirve sonrası sakin dönem bul post_crisis = summary_df[(summary_df['timestep'] > peak_ts) & (summary_df['illicit_rate'] < 0.05)] if len(post_crisis) > 0: post_ts = post_crisis.iloc[0]['timestep'] print(f" 3. KRİZ SONRASI: TS {post_ts} (illicit={post_crisis.iloc[0]['illicit_rate']*100:.1f}%)") print(f"\n Gephi'de bu 3 timestep'i ayrı ayrı açıp karşılaştırın:") print(f" - gephi_nodes_ts{peak_ts:02d}.csv + gephi_edges_ts{peak_ts:02d}.csv (KRİZ)") print(f" - gephi_nodes_ts{pre_ts:02d}.csv + gephi_edges_ts{pre_ts:02d}.csv (KRİZ ÖNCESİ)") print(f" - gephi_nodes_ts{post_ts:02d}.csv + gephi_edges_ts{post_ts:02d}.csv (KRİZ SONRASI)") # ─── GEPHİ AYARLARI ÖNERİSİ ─── print("\n" + "=" * 70) print("7. GEPHİ AYAR ÖNERİLERİ") print("=" * 70) print(""" ADIM 1 — Import: File → Import Spreadsheet → gephi_nodes_full.csv (as Nodes table) File → Import Spreadsheet → gephi_edges_full.csv (as Edges table) ADIM 2 — Renklendirme: Appearance → Nodes → Partition → class_name illicit → Kırmızı (#FF4444) licit → Yeşil (#44BB44) unknown → Gri (#CCCCCC) ADIM 3 — Boyutlandırma: Appearance → Nodes → Ranking → total_degree → Size: 5-30 (Daha çok bağlantısı olan düğümler daha büyük görünür) ADIM 4 — Layout: Layout → ForceAtlas2 Ayarlar: Scaling=100, Gravity=1.0, Prevent Overlap=✓ ~1000 iterasyon çalıştır ADIM 5 — Filtreleme (Timestep bazlı): Filters → Attributes → Range → timestep İstediğiniz timestep aralığını seçin ADIM 6 — İstatistikler: Statistics → Network Overview → Run Modularity, Average Path Length, Clustering Coefficient Bu metrikler kriz öncesi/sonrası karşılaştırma için kullanılacak ADIM 7 — Export: File → Export → PNG/SVG (yayın kalitesinde) """) print("\n" + "=" * 70) print("TAMAMLANDI!") print(f"Çıktı klasörü: {output_dir}") print(f"Toplam dosya: {len(os.listdir(output_dir))}") print("=" * 70) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Elliptic Bitcoin → Gephi dönüşümü') parser.add_argument('--data_dir', type=str, default='./elliptic_data', help='Elliptic CSV dosyalarının bulunduğu klasör') args = parser.parse_args() main(args.data_dir)