| """ |
| =============================================================================== |
| 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) |
| |
| |
| 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}") |
| |
| |
| print("\n2. Gephi düğüm tablosu oluşturuluyor...") |
| |
| nodes = pd.DataFrame() |
| nodes['Id'] = feat_df.iloc[:, 0] |
| nodes['Label'] = feat_df.iloc[:, 0] |
| nodes['timestep'] = feat_df.iloc[:, 1].astype(int) |
| |
| |
| 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) |
| |
| |
| class_numeric = {'1': 1, '2': 0, 'unknown': -1} |
| nodes['class_numeric'] = nodes['class_label'].map(class_numeric) |
| |
| |
| class_name = {'1': 'illicit', '2': 'licit', 'unknown': 'unknown'} |
| nodes['class_name'] = nodes['class_label'].map(class_name) |
| |
| |
| 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'] |
| |
| |
| |
| for i in range(2, 12): |
| 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()}") |
| |
| |
| print("\n3. Gephi kenar tablosu oluşturuluyor...") |
| |
| edges = pd.DataFrame() |
| edges['Source'] = edge_df['txId1'] |
| edges['Target'] = edge_df['txId2'] |
| edges['Type'] = 'Directed' |
| edges['Weight'] = 1.0 |
| |
| |
| 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'] |
| |
| |
| 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) |
| |
| |
| 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()}") |
| |
| |
| 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)") |
| |
| |
| 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) |
| |
| |
| 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()), |
| }) |
| |
| |
| 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}") |
| |
| |
| summary_df = pd.DataFrame(ts_summary) |
| summary_file = os.path.join(output_dir, 'timestep_summary.csv') |
| summary_df.to_csv(summary_file, index=False) |
| |
| |
| 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}%)") |
| |
| |
| 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}%)") |
| |
| |
| 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)") |
| |
| |
| 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) |
|
|