| """ |
| =============================================================================== |
| VERİ TEMİZLEME VE ÖN İŞLEME ANALİZİ |
| =============================================================================== |
| |
| Bu script veri setini derinlemesine inceleyip şu soruları cevaplar: |
| 1. Eksik değer var mı? |
| 2. Outlier (aykırı değer) var mı? |
| 3. Özellik dağılımları nasıl? |
| 4. Sınıf dengesizliği ne kadar ciddi? |
| 5. Hangi temizleme/ön işleme adımları sonuçları iyileştirir? |
| |
| KULLANIM: |
| python data_audit.py --data_dir ./dataset |
| =============================================================================== |
| """ |
|
|
| import os, sys, argparse, warnings |
| import numpy as np |
| import pandas as pd |
| from collections import Counter |
|
|
| import matplotlib |
| matplotlib.use('Agg') |
| import matplotlib.pyplot as plt |
| import seaborn as sns |
|
|
| from sklearn.preprocessing import StandardScaler, RobustScaler, MinMaxScaler |
| from sklearn.ensemble import RandomForestClassifier, IsolationForest |
| from sklearn.model_selection import train_test_split |
| from sklearn.metrics import f1_score, classification_report |
| from sklearn.feature_selection import mutual_info_classif |
| from imblearn.over_sampling import SMOTE, ADASYN |
| from imblearn.under_sampling import RandomUnderSampler |
| from imblearn.combine import SMOTETomek |
| import lightgbm as lgb |
|
|
| warnings.filterwarnings('ignore') |
| np.random.seed(42) |
|
|
| def main(data_dir): |
| FIGDIR = os.path.join(data_dir, '..', 'audit_figures') |
| os.makedirs(FIGDIR, exist_ok=True) |
| |
| |
| print("=" * 70) |
| print("1. VERİ YÜKLEME") |
| print("=" * 70) |
| |
| feat_df = pd.read_csv(os.path.join(data_dir, 'elliptic_txs_features.csv'), header=None) |
| class_df = pd.read_csv(os.path.join(data_dir, 'elliptic_txs_classes.csv')) |
| edge_df = pd.read_csv(os.path.join(data_dir, 'elliptic_txs_edgelist.csv')) |
| |
| txids = feat_df.iloc[:, 0].values |
| timesteps = feat_df.iloc[:, 1].values.astype(int) |
| features = feat_df.iloc[:, 2:].values |
| |
| label_map = {'1': 1, '2': 0, 'unknown': -1} |
| labels = np.array([label_map[str(c)] for c in class_df['class'].values]) |
| |
| labeled_mask = labels >= 0 |
| X = features[labeled_mask].astype(np.float64) |
| y = labels[labeled_mask] |
| ts = timesteps[labeled_mask] |
| |
| print(f" Toplam: {len(features)}, Etiketli: {len(X)}") |
| print(f" Özellik sayısı: {X.shape[1]}") |
| print(f" Sınıf dağılımı: illicit={y.sum()} ({y.mean()*100:.1f}%), licit={len(y)-y.sum()}") |
| |
| |
| print("\n" + "=" * 70) |
| print("2. EKSİK DEĞER ANALİZİ") |
| print("=" * 70) |
| |
| nan_count = np.isnan(X).sum() |
| inf_count = np.isinf(X).sum() |
| zero_cols = (X == 0).all(axis=0).sum() |
| |
| print(f" NaN sayısı: {nan_count}") |
| print(f" Inf sayısı: {inf_count}") |
| print(f" Tamamen sıfır olan sütun: {zero_cols}") |
| |
| |
| nan_per_col = np.isnan(X).sum(axis=0) |
| if nan_count > 0: |
| problematic = np.where(nan_per_col > 0)[0] |
| print(f" NaN içeren sütunlar: {len(problematic)}") |
| for col in problematic[:10]: |
| print(f" Sütun {col}: {nan_per_col[col]} NaN ({nan_per_col[col]/len(X)*100:.2f}%)") |
| else: |
| print(f" ✓ Eksik değer yok") |
| |
| |
| print("\n" + "=" * 70) |
| print("3. OUTLIER (AYKIRI DEĞER) ANALİZİ") |
| print("=" * 70) |
| |
| |
| Q1 = np.percentile(X, 25, axis=0) |
| Q3 = np.percentile(X, 75, axis=0) |
| IQR = Q3 - Q1 |
| lower = Q1 - 1.5 * IQR |
| upper = Q3 + 1.5 * IQR |
| |
| outlier_mask = (X < lower) | (X > upper) |
| outlier_per_col = outlier_mask.sum(axis=0) |
| outlier_per_row = outlier_mask.sum(axis=1) |
| total_outliers = outlier_mask.sum() |
| |
| print(f" Toplam outlier hücre: {total_outliers} ({total_outliers/(X.shape[0]*X.shape[1])*100:.2f}%)") |
| print(f" En az 1 outlier'ı olan satır: {(outlier_per_row > 0).sum()} ({(outlier_per_row > 0).mean()*100:.1f}%)") |
| print(f" Ortalama outlier/satır: {outlier_per_row.mean():.1f}") |
| |
| |
| top_outlier_cols = np.argsort(-outlier_per_col)[:10] |
| print(f"\n En çok outlier içeren 10 sütun:") |
| for col in top_outlier_cols: |
| print(f" Sütun {col}: {outlier_per_col[col]} outlier ({outlier_per_col[col]/len(X)*100:.1f}%)") |
| |
| |
| ill_outliers = outlier_per_row[y == 1].mean() |
| lic_outliers = outlier_per_row[y == 0].mean() |
| print(f"\n İllicit örneklerin ortalama outlier sayısı: {ill_outliers:.1f}") |
| print(f" Licit örneklerin ortalama outlier sayısı: {lic_outliers:.1f}") |
| if ill_outliers > lic_outliers * 1.2: |
| print(f" ⚠️ İllicit örnekler {ill_outliers/lic_outliers:.1f}x daha fazla outlier içeriyor!") |
| print(f" Bu, outlier temizliğinin illicit sınıfını orantısız etkileyebileceği anlamına gelir.") |
| |
| |
| print("\n" + "=" * 70) |
| print("4. ÖZELLİK DAĞILIMI ANALİZİ") |
| print("=" * 70) |
| |
| |
| local_features = X[:, :93] |
| agg_features = X[:, 93:] |
| |
| local_mean = np.mean(np.abs(local_features)) |
| agg_mean = np.mean(np.abs(agg_features)) |
| local_std = np.std(local_features) |
| agg_std = np.std(agg_features) |
| |
| print(f" Local özellikler (93 adet):") |
| print(f" Ortalama |değer|: {local_mean:.4f}") |
| print(f" Standart sapma: {local_std:.4f}") |
| print(f" Min: {local_features.min():.4f}, Max: {local_features.max():.4f}") |
| |
| print(f"\n Aggregated özellikler (72 adet):") |
| print(f" Ortalama |değer|: {agg_mean:.4f}") |
| print(f" Standart sapma: {agg_std:.4f}") |
| print(f" Min: {agg_features.min():.4f}, Max: {agg_features.max():.4f}") |
| |
| |
| variances = np.var(X, axis=0) |
| low_var = (variances < 1e-6).sum() |
| print(f"\n Varyansı çok düşük (< 1e-6) özellik: {low_var}") |
| |
| |
| corr_matrix = np.corrcoef(X.T) |
| np.fill_diagonal(corr_matrix, 0) |
| high_corr = (np.abs(corr_matrix) > 0.95).sum() // 2 |
| print(f" Yüksek korelasyonlu (|r|>0.95) özellik çifti: {high_corr}") |
| |
| |
| print("\n" + "=" * 70) |
| print("5. SINIF DENGESİZLİĞİ ANALİZİ") |
| print("=" * 70) |
| |
| ratio = (y == 0).sum() / max((y == 1).sum(), 1) |
| print(f" Licit/İllicit oranı: {ratio:.1f}:1") |
| print(f" Bu çok ciddi bir dengesizlik.") |
| |
| |
| print(f"\n Timestep bazında dengesizlik:") |
| for t in sorted(np.unique(ts))[:10]: |
| mask = ts == t |
| t_ill = y[mask].sum() |
| t_total = mask.sum() |
| t_rate = t_ill / max(t_total, 1) * 100 |
| print(f" TS {t:2d}: {t_total:5d} örnek, {t_ill:4d} illicit ({t_rate:.1f}%)") |
| print(f" ... (49 timestep toplam)") |
| |
| |
| print("\n" + "=" * 70) |
| print("6. TEMİZLEME VE ÖN İŞLEME ETKİ TESTİ") |
| print("=" * 70) |
| print(" Her adımın F1 skoruna etkisini test ediyoruz...") |
| print(" Temporal split kullanılıyor (TS 1-39 eğitim, 40-49 test)") |
| |
| tr_mask = ts <= 39 |
| te_mask = ts > 39 |
| |
| def eval_pipeline(X_tr, y_tr, X_te, y_te, name): |
| """LightGBM ile hızlı değerlendirme.""" |
| model = lgb.LGBMClassifier( |
| n_estimators=300, max_depth=10, learning_rate=0.1, |
| scale_pos_weight=10, random_state=42, n_jobs=-1, verbose=-1 |
| ) |
| model.fit(X_tr, y_tr) |
| pred = model.predict(X_te) |
| f1 = f1_score(y_te, pred, zero_division=0) |
| return f1 |
| |
| results = {} |
| |
| |
| print("\n [1/8] Baseline (ham veri)...") |
| f1_base = eval_pipeline(X[tr_mask], y[tr_mask], X[te_mask], y[te_mask], "baseline") |
| results['1. Ham Veri (Baseline)'] = f1_base |
| print(f" F1 = {f1_base:.4f}") |
| |
| |
| print(" [2/8] StandardScaler...") |
| scaler = StandardScaler() |
| X_ss = scaler.fit_transform(X[tr_mask]), scaler.transform(X[te_mask]) |
| f1_ss = eval_pipeline(X_ss[0], y[tr_mask], X_ss[1], y[te_mask], "standard") |
| results['2. StandardScaler'] = f1_ss |
| print(f" F1 = {f1_ss:.4f} (fark: {f1_ss-f1_base:+.4f})") |
| |
| |
| print(" [3/8] RobustScaler...") |
| rscaler = RobustScaler() |
| X_rs = rscaler.fit_transform(X[tr_mask]), rscaler.transform(X[te_mask]) |
| f1_rs = eval_pipeline(X_rs[0], y[tr_mask], X_rs[1], y[te_mask], "robust") |
| results['3. RobustScaler'] = f1_rs |
| print(f" F1 = {f1_rs:.4f} (fark: {f1_rs-f1_base:+.4f})") |
| |
| |
| print(" [4/8] NaN temizleme + StandardScaler...") |
| X_clean = np.nan_to_num(X, nan=0.0, posinf=0.0, neginf=0.0) |
| scaler2 = StandardScaler() |
| X_c = scaler2.fit_transform(X_clean[tr_mask]), scaler2.transform(X_clean[te_mask]) |
| f1_c = eval_pipeline(X_c[0], y[tr_mask], X_c[1], y[te_mask], "clean") |
| results['4. NaN Temizleme + Standard'] = f1_c |
| print(f" F1 = {f1_c:.4f} (fark: {f1_c-f1_base:+.4f})") |
| |
| |
| print(" [5/8] Outlier Clipping (IQR)...") |
| X_clipped = np.clip(X, lower, upper) |
| scaler3 = StandardScaler() |
| X_cl = scaler3.fit_transform(X_clipped[tr_mask]), scaler3.transform(X_clipped[te_mask]) |
| f1_cl = eval_pipeline(X_cl[0], y[tr_mask], X_cl[1], y[te_mask], "clipped") |
| results['5. Outlier Clipping + Standard'] = f1_cl |
| print(f" F1 = {f1_cl:.4f} (fark: {f1_cl-f1_base:+.4f})") |
| |
| |
| print(" [6/8] SMOTE Oversampling...") |
| try: |
| smote = SMOTE(random_state=42) |
| X_tr_sm, y_tr_sm = smote.fit_resample(X_clean[tr_mask], y[tr_mask]) |
| scaler4 = StandardScaler() |
| X_sm_tr = scaler4.fit_transform(X_tr_sm) |
| X_sm_te = scaler4.transform(X_clean[te_mask]) |
| f1_sm = eval_pipeline(X_sm_tr, y_tr_sm, X_sm_te, y[te_mask], "smote") |
| results['6. SMOTE + Standard'] = f1_sm |
| print(f" F1 = {f1_sm:.4f} (fark: {f1_sm-f1_base:+.4f})") |
| except Exception as e: |
| print(f" SMOTE başarısız: {e}") |
| results['6. SMOTE + Standard'] = 0 |
| |
| |
| print(" [7/8] Düşük Varyans Özellik Çıkarma...") |
| var_mask = variances > 1e-6 |
| X_var = X_clean[:, var_mask] |
| scaler5 = StandardScaler() |
| X_v = scaler5.fit_transform(X_var[tr_mask]), scaler5.transform(X_var[te_mask]) |
| f1_v = eval_pipeline(X_v[0], y[tr_mask], X_v[1], y[te_mask], "var_filter") |
| results[f'7. Düşük Varyans Çıkarma ({var_mask.sum()} feat)'] = f1_v |
| print(f" F1 = {f1_v:.4f} (fark: {f1_v-f1_base:+.4f})") |
| |
| |
| print(" [8/8] Tam Pipeline: Temizle + Clip + RobustScale + SMOTE...") |
| try: |
| X_full = np.nan_to_num(X, nan=0.0, posinf=0.0, neginf=0.0) |
| X_full = np.clip(X_full, lower, upper) |
| rscaler2 = RobustScaler() |
| X_full_tr = rscaler2.fit_transform(X_full[tr_mask]) |
| X_full_te = rscaler2.transform(X_full[te_mask]) |
| smote2 = SMOTE(random_state=42) |
| X_full_tr_sm, y_full_tr_sm = smote2.fit_resample(X_full_tr, y[tr_mask]) |
| f1_full = eval_pipeline(X_full_tr_sm, y_full_tr_sm, X_full_te, y[te_mask], "full") |
| results['8. Tam Pipeline'] = f1_full |
| print(f" F1 = {f1_full:.4f} (fark: {f1_full-f1_base:+.4f})") |
| except Exception as e: |
| print(f" Tam pipeline başarısız: {e}") |
| results['8. Tam Pipeline'] = 0 |
| |
| |
| print("\n" + "=" * 70) |
| print("7. ÖZET: TEMİZLEME ADIMLARININ ETKİSİ") |
| print("=" * 70) |
| |
| print(f"\n {'Adım':<45s} {'F1':>8s} {'Fark':>10s}") |
| print(" " + "-" * 65) |
| for name, f1 in sorted(results.items()): |
| diff = f1 - f1_base |
| marker = " ✓ EN İYİ" if f1 == max(results.values()) else "" |
| print(f" {name:<45s} {f1:>8.4f} {diff:>+10.4f}{marker}") |
| |
| best_name = max(results, key=results.get) |
| best_f1 = max(results.values()) |
| print(f"\n En iyi pipeline: {best_name}") |
| print(f" Baseline'dan iyileşme: {best_f1 - f1_base:+.4f} ({(best_f1-f1_base)/f1_base*100:+.1f}%)") |
| |
| |
| print("\nFigür oluşturuluyor...") |
| sns.set_theme(style='whitegrid', font_scale=1.0) |
| |
| fig, axes = plt.subplots(2, 2, figsize=(16, 12)) |
| |
| |
| names = list(results.keys()) |
| vals = list(results.values()) |
| colors = ['#FF6B6B' if v == f1_base else '#4ECDC4' if v == best_f1 else '#45B7D1' for v in vals] |
| axes[0,0].barh(names, vals, color=colors) |
| axes[0,0].set_xlabel('Illicit F1 Score') |
| axes[0,0].set_title('Ön İşleme Adımlarının F1 Etkisi\n(Kırmızı=baseline, Yeşil=en iyi)') |
| axes[0,0].axvline(x=f1_base, color='red', linestyle='--', alpha=0.5) |
| |
| |
| axes[0,1].hist(outlier_per_row[y==0], bins=30, alpha=0.6, label='Licit', color='green', density=True) |
| axes[0,1].hist(outlier_per_row[y==1], bins=30, alpha=0.6, label='Illicit', color='red', density=True) |
| axes[0,1].set_xlabel('Outlier Sayısı / Satır') |
| axes[0,1].set_ylabel('Yoğunluk') |
| axes[0,1].set_title('Sınıf Bazında Outlier Dağılımı') |
| axes[0,1].legend() |
| |
| |
| axes[1,0].bar(['Licit', 'Illicit'], [(y==0).sum(), (y==1).sum()], color=['green', 'red']) |
| axes[1,0].set_ylabel('Örnek Sayısı') |
| axes[1,0].set_title(f'Sınıf Dengesizliği ({ratio:.1f}:1)') |
| |
| |
| axes[1,1].hist(np.log10(variances + 1e-10), bins=50, color='steelblue') |
| axes[1,1].set_xlabel('log10(Varyans)') |
| axes[1,1].set_ylabel('Özellik Sayısı') |
| axes[1,1].set_title('Özellik Varyans Dağılımı') |
| axes[1,1].axvline(x=np.log10(1e-6), color='red', linestyle='--', label='Düşük varyans eşiği') |
| axes[1,1].legend() |
| |
| plt.tight_layout() |
| plt.savefig(os.path.join(FIGDIR, 'data_audit.png'), dpi=150, bbox_inches='tight') |
| plt.close() |
| print(f" ✓ {os.path.join(FIGDIR, 'data_audit.png')}") |
| |
| |
| pd.DataFrame([ |
| {'adim': k, 'f1': v, 'fark': v - f1_base} |
| for k, v in results.items() |
| ]).to_csv(os.path.join(FIGDIR, '..', 'audit_results.csv'), index=False) |
| |
| print("\n" + "=" * 70) |
| print("VERİ DENETİMİ TAMAMLANDI!") |
| print("=" * 70) |
|
|
|
|
| if __name__ == '__main__': |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--data_dir', type=str, default='./dataset') |
| args = parser.parse_args() |
| main(args.data_dir) |
|
|