elliptic-event-aware-splitting / prepare_for_gephi.py
MucahitSylmz's picture
Gephi analizi için veri hazırlama scripti eklendi (prepare_for_gephi.py)"
995067e verified
"""
===============================================================================
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)