{
  "cells": [
    {
      "cell_type": "markdown",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Hafta 3: Özellik Mühendisliği ve Seçimi\n",
        "\n",
        "**Dersin Hedefleri:**\n",
        "1.  Özellik mühendisliğinin (Feature Engineering) makine öğrenmesi pipeline'ındaki merkezi rolünü anlamak.\n",
        "2.  Farklı veri tipleri (sayısal, kategorik, zaman damgalı) için yaygın özellik türetme tekniklerini uygulamak.\n",
        "3.  Eksik verileri (missing data) işlemek için stratejiler geliştirmek ve test etmek.\n",
        "4.  Özellik seçimi (feature selection) yöntemlerinin amacını ve temel türlerini öğrenmek.\n",
        "5.  Görselleştirmenin, özellik mühendisliği sürecindeki keşif ve doğrulama rolünü kullanmak.\n",
        "\n",
        "## 1. Özellik Mühendisliği Nedir?\n",
        "\n",
        "\"Garbage in, garbage out\" (Çöp girer, çöp çıkar) makine öğrenmesindeki en temel prensiplerden biridir. Bir modelin performansı, ne kadar karmaşık veya güçlü olursa olsun, büyük ölçüde kendisine verilen verinin kalitesine ve anlamlılığına bağlıdır.\n",
        "\n",
        "**Özellik Mühendisliği:** Ham veriyi, bir makine öğrenmesi modelinin problem çözme yeteneğini en üst düzeye çıkaracak bir formata dönüştürme sanatı ve bilimidir. Bu süreç, **domain bilgisi**, **yaratıcılık** ve **istatistiksel titizliğin** bir birleşimidir.\n",
        "\n",
        "İyi tasarlanmış özellikler:\n",
        "- Modelin veri içindeki desenleri (patterns) daha kolay öğrenmesini sağlar.\n",
        "- Daha basit modellerin bile yüksek performans göstermesine olanak tanır.\n",
        "- Modelin yorumlanabilirliğini artırabilir."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. Pratik Uygulamalar: Sayısal ve Kategorik Özellikler\n",
        "\n",
        "Bu bölümde, bir e-ticaret şirketinin müşteri veritabanını temsil eden sentetik bir veri seti üzerinde çalışacağız.\n",
        "\n",
        "### Sentetik Veri Üretimi\n",
        "\n",
        "Müşteri demografik bilgilerini, son aktivitelerini ve harcama alışkanlıklarını içeren gerçekçi bir veri seti oluşturalım."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "import numpy as np\n",
        "import seaborn as sns\n",
        "import matplotlib.pyplot as plt\n",
        "from datetime import datetime, timedelta\n",
        "\n",
        "# Veri üretimi için fonksiyon\n",
        "def generate_customer_data(num_customers=1000):\n",
        "    \"\"\"Generates a synthetic customer dataset.\"\"\"\n",
        "    np.random.seed(42)\n",
        "    \n",
        "    customer_ids = range(1, num_customers + 1)\n",
        "    \n",
        "    # Demografik özellikler\n",
        "    ages = np.random.randint(18, 70, size=num_customers)\n",
        "    # Bazı yaş değerlerini eksik bırakalım\n",
        "    ages = [age if np.random.rand() > 0.1 else np.nan for age in ages]\n",
        "    \n",
        "    cities = np.random.choice(['Istanbul', 'Ankara', 'Izmir', 'Bursa', 'Antalya'], \n",
        "                              size=num_customers, \n",
        "                              p=[0.4, 0.2, 0.2, 0.1, 0.1])\n",
        "    \n",
        "    # Kayıt tarihi\n",
        "    end_date = datetime.now()\n",
        "    start_date = end_date - timedelta(days=3*365)\n",
        "    registration_dates = [start_date + timedelta(seconds=np.random.randint(0, int((end_date - start_date).total_seconds()))) for _ in range(num_customers)]\n",
        "    \n",
        "    # Harcama bilgileri\n",
        "    total_spent = np.random.gamma(2, 150, size=num_customers).round(2)\n",
        "    # Yaş ile harcama arasında hafif bir pozitif korelasyon ekleyelim\n",
        "    total_spent += np.array([age * 1.5 if not np.isnan(age) else 0 for age in ages])\n",
        "    \n",
        "    # Son görülme (eksik değerler içerebilir)\n",
        "    last_seen_days_ago = np.random.randint(0, 365, size=num_customers)\n",
        "    last_seen_days_ago = [d if np.random.rand() > 0.15 else np.nan for d in last_seen_days_ago]\n",
        "    \n",
        "    # Hedef değişken (Churn olup olmadığı)\n",
        "    churn_probability = 1 / (1 + np.exp(-( (np.array(last_seen_days_ago, dtype=float) / 365 * 2) - (total_spent / 500) )))\n",
        "    churn = (np.random.rand(num_customers) < churn_probability).astype(int)\n",
        "\n",
        "    \n",
        "    df = pd.DataFrame({\n",
        "        'customer_id': customer_ids,\n",
        "        'age': ages,\n",
        "        'city': cities,\n",
        "        'registration_date': registration_dates,\n",
        "        'total_spent': total_spent,\n",
        "        'last_seen_days_ago': last_seen_days_ago,\n",
        "        'churn': churn\n",
        "    })\n",
        "    \n",
        "    return df\n",
        "\n",
        "customer_df = generate_customer_data(500)\n",
        "print(\"Veri Seti Önizlemesi:\")\n",
        "display(customer_df.head())\n",
        "print(\"\\nVeri Seti Bilgileri:\")\n",
        "customer_df.info()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.1. Eksik Veri İşleme (Missing Data Imputation)\n",
        "\n",
        "Modellerin çoğu, eksik (`NaN`) değerlerle çalışamaz. Bu değerleri stratejik olarak doldurmamız gerekir.\n",
        "\n",
        "**Stratejiler:**\n",
        "- **Sayısal Değerler:** Ortalama (mean), medyan (median) veya belirli bir sabit (örn: 0) ile doldurma. Medyan, aykırı değerlere (outliers) karşı daha dayanıklıdır.\n",
        "- **Kategorik Değerler:** En sık tekrar eden değer (mode) veya \"Bilinmiyor\" gibi yeni bir kategori ile doldurma."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Eksik değerlerin görselleştirilmesi\n",
        "plt.figure(figsize=(10, 6))\n",
        "sns.heatmap(customer_df.isnull(), cbar=False, cmap='viridis')\n",
        "plt.title('Eksik Değerlerin Konumu')\n",
        "plt.show()\n",
        "\n",
        "# Sayısal 'age' sütunu için medyan ile doldurma\n",
        "age_median = customer_df['age'].median()\n",
        "customer_df['age'].fillna(age_median, inplace=True)\n",
        "\n",
        "# 'last_seen_days_ago' için de medyan kullanalım\n",
        "last_seen_median = customer_df['last_seen_days_ago'].median()\n",
        "customer_df['last_seen_days_ago'].fillna(last_seen_median, inplace=True)\n",
        "\n",
        "print(\"Eksik değerler doldurulduktan sonraki durum:\")\n",
        "customer_df.info()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.2. Kategorik Veri Kodlama (Categorical Encoding)\n",
        "\n",
        "Modeller matematiksel işlemler yapar ve metin tabanlı kategorileri ('Istanbul', 'Ankara' vb.) doğrudan anlayamaz. Bunları sayısallaştırmamız gerekir.\n",
        "\n",
        "**One-Hot Encoding:** En yaygın yöntemdir. Her bir kategori için yeni bir ikili (0/1) sütun oluşturur. `pandas.get_dummies()` ile kolayca uygulanabilir."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 'city' sütununa one-hot encoding uygulama\n",
        "city_dummies = pd.get_dummies(customer_df['city'], prefix='city', drop_first=True)\n",
        "# drop_first=True: Multicollinearity'yi önlemek için bir referans kategori atar.\n",
        "\n",
        "# Yeni oluşturulan sütunları ana dataframe ile birleştirme\n",
        "customer_df = pd.concat([customer_df, city_dummies], axis=1)\n",
        "\n",
        "# Orijinal 'city' sütununu kaldırabiliriz\n",
        "customer_df.drop('city', axis=1, inplace=True)\n",
        "\n",
        "print(\"One-Hot Encoding sonrası DataFrame:\")\n",
        "display(customer_df.head())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.3. Zaman Damgalı Özellikler (Date/Time Features)\n",
        "\n",
        "`registration_date` gibi tarih sütunları, doğrudan model için anlamsızdır. Ancak içlerinden çok değerli bilgiler çıkarabiliriz."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 'registration_date' sütununu datetime objesine çevirelim (eğer değilse)\n",
        "customer_df['registration_date'] = pd.to_datetime(customer_df['registration_date'])\n",
        "\n",
        "# Yeni özellikler türetme\n",
        "customer_df['registration_year'] = customer_df['registration_date'].dt.year\n",
        "customer_df['registration_month'] = customer_df['registration_date'].dt.month\n",
        "customer_df['registration_day_of_week'] = customer_df['registration_date'].dt.dayofweek # Pazartesi=0, Pazar=6\n",
        "\n",
        "# Üyelik süresi (üyeliğin ne kadar eski olduğu)\n",
        "customer_df['membership_days'] = (datetime.now() - customer_df['registration_date']).dt.days\n",
        "\n",
        "# Orijinal tarih sütununu kaldırabiliriz\n",
        "customer_df.drop('registration_date', axis=1, inplace=True)\n",
        "\n",
        "print(\"Tarih özellikleri eklendikten sonra DataFrame:\")\n",
        "display(customer_df[['customer_id', 'registration_year', 'registration_month', 'registration_day_of_week', 'membership_days']].head())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Alıştırma 1: Etkileşim Özellikleri (Interaction Features)\n",
        "\n",
        "Bazen iki veya daha fazla özelliğin birleşimi, tek başlarına olduklarından daha fazla bilgi taşır. Bunlara **etkileşim özellikleri** denir.\n",
        "\n",
        "1.  `customer_df` DataFrame'ine `spent_per_day` adında yeni bir özellik ekleyin. Bu özellik, `total_spent` değerinin `membership_days` değerine bölünmesiyle hesaplanmalıdır. (Not: `membership_days` 0 ise ne yapılacağını düşünün, 0'a bölme hatasını önleyin!).\n",
        "2.  Bu yeni özelliğin `churn` ile olan ilişkisini görselleştirin. Örneğin, `churn` durumuna göre `spent_per_day` dağılımını bir kutu grafiği (`boxplot`) veya keman grafiği (`violinplot`) ile çizin. Bu özellik, churn'ü tahmin etmede faydalı görünüyor mu?"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Alıştırma 1 için çözüm alanı\n",
        "# 1. spent_per_day özelliğini oluştur\n",
        "customer_df['spent_per_day'] = customer_df['total_spent'] / (customer_df['membership_days'] + 1e-6) # 0'a bölmeyi önle\n",
        "\n",
        "# 2. Görselleştirme\n",
        "plt.figure(figsize=(12, 7))\n",
        "sns.boxplot(x='churn', y='spent_per_day', data=customer_df)\n",
        "plt.title('Günlük Harcama vs. Churn Durumu')\n",
        "plt.ylim(0, customer_df['spent_per_day'].quantile(0.95)) # Aykırı değerleri görselden çıkarmak için\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. Özellik Seçimi (Feature Selection)\n",
        "\n",
        "Çok fazla özellik eklemek her zaman iyi değildir. Bazı özellikler gürültü yaratabilir, modelin eğitilmesini yavaşlatabilir ve \"Boyutsallık Laneti\" (Curse of Dimensionality) problemine yol açabilir.\n",
        "\n",
        "**Özellik Seçimi:** Model performansını en üst düzeye çıkarmak için en anlamlı ve en bilgilendirici özellik alt kümesini seçme işlemidir.\n",
        "\n",
        "### Filtre Yöntemleri (Filter Methods)\n",
        "\n",
        "Bu yöntemler, özellikleri modelden bağımsız olarak, sadece istatistiksel özelliklerine göre sıralar veya filtreler. Hızlı ve basittirler. En yaygın olanı, hedef değişken ile olan **korelasyonu** incelemektir."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Model için son hazırlık: Hedef değişkeni ve ID'yi ayıralım\n",
        "X = customer_df.drop(['churn', 'customer_id'], axis=1)\n",
        "y = customer_df['churn']\n",
        "\n",
        "# Tüm özelliklerin hedef değişken (churn) ile olan korelasyonunu hesapla\n",
        "correlations = X.corrwith(y).sort_values(ascending=False)\n",
        "\n",
        "plt.figure(figsize=(10, 8))\n",
        "sns.barplot(x=correlations.values, y=correlations.index)\n",
        "plt.title('Özelliklerin Churn ile Korelasyonu')\n",
        "plt.xlabel('Korelasyon Katsayısı')\n",
        "plt.show()\n",
        "\n",
        "print(\"Korelasyon Değerleri:\")\n",
        "print(correlations)\n",
        "\n",
        "# Yorum: `last_seen_days_ago` ve `membership_days` churn'ü tahmin etmede en güçlü sinyallere sahip görünüyor.\n",
        "# Bu, domain bilgisiyle de tutarlıdır (uzun süredir uğramayan veya yeni olan müşterilerin churn etme olasılığı daha yüksek olabilir)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Alıştırma 2: Korelasyon Matrisi\n",
        "\n",
        "Sadece özelliklerin hedefle değil, birbirleriyle olan ilişkileri de önemlidir. Yüksek oranda korele olan iki özellik (örn. > 0.90) genellikle modele aynı bilgiyi verir ve bu durum multicollinearity gibi sorunlara yol açabilir.\n",
        "\n",
        "1. `X` DataFrame'indeki tüm özelliklerin birbirleriyle olan korelasyonunu hesaplayın (`.corr()` metodu).\n",
        "2. Bu korelasyon matrisini bir ısı haritası (`heatmap`) kullanarak görselleştirin.\n",
        "3. Birbirleriyle çok yüksek korelasyona sahip özellik çiftleri var mı? Varsa, bunlardan birini modelden çıkarmayı düşünür müydünüz? Neden?"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Alıştırma 2 için çözüm alanı\n",
        "\n",
        "# 1. Tüm özelliklerin birbirleriyle olan korelasyonunu hesaplama\n",
        "correlation_matrix = X.corr()\n",
        "\n",
        "# 2. Korelasyon matrisini ısı haritası ile görselleştirme\n",
        "plt.figure(figsize=(14, 10))\n",
        "sns.heatmap(correlation_matrix, annot=True, fmt='.2f', cmap='coolwarm', \n",
        "            center=0, square=True, linewidths=1, cbar_kws={\"shrink\": 0.8})\n",
        "plt.title('Özelliklerin Birbirleriyle Korelasyon Matrisi', fontsize=14)\n",
        "plt.tight_layout()\n",
        "plt.show()\n",
        "\n",
        "# 3. Yüksek korelasyonlu özellik çiftlerini bulma (eşik: 0.90)\n",
        "high_corr_threshold = 0.90\n",
        "high_corr_pairs = []\n",
        "\n",
        "for i in range(len(correlation_matrix.columns)):\n",
        "    for j in range(i+1, len(correlation_matrix.columns)):\n",
        "        if abs(correlation_matrix.iloc[i, j]) > high_corr_threshold:\n",
        "            high_corr_pairs.append({\n",
        "                'Feature 1': correlation_matrix.columns[i],\n",
        "                'Feature 2': correlation_matrix.columns[j],\n",
        "                'Correlation': correlation_matrix.iloc[i, j]\n",
        "            })\n",
        "\n",
        "if high_corr_pairs:\n",
        "    print(\"\\nYüksek Korelasyonlu Özellik Çiftleri (|korelasyon| > 0.90):\")\n",
        "    for pair in high_corr_pairs:\n",
        "        print(f\"  - {pair['Feature 1']} <-> {pair['Feature 2']}: {pair['Correlation']:.3f}\")\n",
        "    print(\"\\nYorum: Çok yüksek korelasyonlu özellik çiftlerinden birini modelden çıkarmayı düşünebiliriz.\")\n",
        "    print(\"Bu, multicollinearity'yi azaltır ve modelin daha kararlı ve yorumlanabilir olmasını sağlar.\")\n",
        "    print(\"Genellikle domain bilgisi veya hedef değişkenle daha düşük korelasyona sahip olanı çıkarırız.\")\n",
        "else:\n",
        "    print(\"\\nYüksek Korelasyonlu Özellik Çifti Bulunamadı (|korelasyon| > 0.90)\")\n",
        "    print(\"\\nYorum: Veri setimizde çok yüksek korelasyonlu özellik çiftleri yok.\")\n",
        "    print(\"Ancak, 0.70-0.90 arasındaki korelasyonlar da dikkat edilmesi gereken durumlardır.\")\n",
        "    print(\"Bu durumda, tüm özellikleri modelde tutabiliriz veya regularizasyon teknikleri kullanabiliriz.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. Explainable Boosting Machine (EBM): Yorumlanabilir Özellik Seçimi\n",
        "\n",
        "**EBM (Explainable Boosting Machine)**, Microsoft Research tarafından geliştirilen, hem yüksek performans hem de tam yorumlanabilirlik sunan bir Generalized Additive Model (GAM) türüdür.\n",
        "\n",
        "### EBM Nedir?\n",
        "\n",
        "Klasik makine öğrenmesi modellerinde genellikle bir **trade-off** vardır:\n",
        "- **Basit modeller** (Linear Regression, Decision Trees): Yorumlanabilir ama düşük performans\n",
        "- **Karmaşık modeller** (Random Forest, XGBoost, Neural Networks): Yüksek performans ama \"black box\"\n",
        "\n",
        "**EBM bu trade-off'u kırma iddiasındadır.** Gradient Boosting kadar güçlüdür, ancak tamamen yorumlanabilirdir.\n",
        "\n",
        "### Temel Özellikler:\n",
        "1. **Glasbox Model**: Her özelliğin katkısı ayrı ayrı görselleştirilebilir\n",
        "2. **Doğrusal Olmayan İlişkiler**: Her özellik için karmaşık, doğrusal olmayan şekil fonksiyonları öğrenir\n",
        "3. **Otomatik Etkileşim Tespiti**: Özellikler arası etkileşimleri (interactions) otomatik bulur ve modelleyebilir\n",
        "4. **Feature Importance**: Hangi özelliklerin önemli olduğunu kesin olarak gösterir\n",
        "5. **Local & Global Explanations**: Hem genel model davranışını hem de tek bir tahmini açıklayabilir"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 4.1. Matematiksel Temel: GAM ve Boosting'in Birleşimi\n",
        "\n",
        "EBM, şu matematiksel yapıya dayanır:\n",
        "\n",
        "$$g(E[y]) = \\beta_0 + \\sum_{i=1}^{n} f_i(x_i) + \\sum_{i,j} f_{ij}(x_i, x_j)$$\n",
        "\n",
        "**Basitçe açıklarsak:**\n",
        "- $g()$: Link fonksiyonu (classification için logit, regression için identity)\n",
        "- $\\beta_0$: Intercept (başlangıç değeri)\n",
        "- $f_i(x_i)$: Her bir özelliğin **bireysel shape fonksiyonu** (non-linear olabilir)\n",
        "- $f_{ij}(x_i, x_j)$: İki özellik arasındaki **pairwise etkileşim fonksiyonu**\n",
        "\n",
        "**Neden yorumlanabilir?**\n",
        "Her terim ($f_i$) ayrı ayrı hesaplanır ve görselleştirilebilir. Model, her özelliğin tahmini **nasıl etkilediğini** bize gösterir.\n",
        "\n",
        "**Nasıl eğitiliyor?**\n",
        "1. **Round-robin Boosting**: Her iterasyonda, bir özellik seçilir\n",
        "2. O özellik için küçük bir düzeltme (residual fit) yapılır\n",
        "3. Süreç, tüm özellikler için tekrarlanır (cyclic)\n",
        "4. Bu, yüzlerce/binlerce kez tekrar eder\n",
        "\n",
        "Bu yaklaşım, özelliklerin birbirinden bağımsız katkılarını öğrenmeyi sağlar."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 4.2. Ana Kısıtlamalar ve Dikkat Edilmesi Gerekenler\n",
        "\n",
        "**1. Etkileşim Limiti**\n",
        "- EBM varsayılan olarak sadece **pairwise (ikili) etkileşimleri** modeller\n",
        "- 3'lü veya daha fazla özellik etkileşimi desteklenmez\n",
        "- Çok karmaşık etkileşimler için deep learning gerekebilir\n",
        "\n",
        "**2. Büyük Veri Setlerinde Performans**\n",
        "- Boosting iteratif olduğu için eğitim **yavaş** olabilir (XGBoost'tan daha yavaş)\n",
        "- 100K+ satır ve 50+ özellikte eğitim saatler sürebilir\n",
        "- GPU desteği yok (sadece CPU)\n",
        "\n",
        "**3. Kategorik Özelliklerde Dikkat**\n",
        "- Çok fazla kategoriye sahip özellikler (high cardinality) problemli olabilir\n",
        "- One-hot encoding ile özellik sayısı patlaması yaşanabilir\n",
        "- EBM kategorik değişkenleri otomatik işleyebilir ama dikkatli kullanılmalı\n",
        "\n",
        "**4. Overfitting Riski**\n",
        "- Çok fazla boosting iteration ile model ezberleyebilir\n",
        "- `max_rounds`, `learning_rate`, `max_leaves` gibi hiperparametrelerin dikkatli ayarlanması gerekir\n",
        "- Validation set kullanarak early stopping önemli\n",
        "\n",
        "**5. Yorumlanabilirlik vs. Performans**\n",
        "- Bazı durumlarda XGBoost veya Neural Network daha iyi performans verebilir\n",
        "- EBM'in avantajı yorumlanabilirlik; eğer yorumlanabilirlik gerekli değilse, en iyi seçim olmayabilir"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 4.3. EBM ile Churn Tahmini: Pratik Uygulama\n",
        "\n",
        "Şimdi, hazırladığımız müşteri veri seti üzerinde EBM kullanarak:\n",
        "1. Model eğitimi yapacağız\n",
        "2. Özelliklerin önemini göreceğiz\n",
        "3. Her özelliğin tahmini nasıl etkilediğini görselleştireceğiz\n",
        "4. En önemsiz özellikleri elimine edeceğiz"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# EBM kütüphanesini yükle\n",
        "#!pip install -q interpret"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Gerekli kütüphaneleri import et\n",
        "from interpret.glassbox import ExplainableBoostingClassifier\n",
        "from sklearn.model_selection import train_test_split\n",
        "from sklearn.metrics import roc_auc_score, accuracy_score, classification_report\n",
        "\n",
        "# Veriyi train ve test olarak böl\n",
        "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)\n",
        "\n",
        "print(f\"Train set: {X_train.shape[0]} örneklem\")\n",
        "print(f\"Test set: {X_test.shape[0]} örneklem\")\n",
        "print(f\"Özellik sayısı: {X_train.shape[1]}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# EBM modelini oluştur ve eğit\n",
        "ebm = ExplainableBoostingClassifier(\n",
        "    random_state=42,\n",
        "    interactions=5,  # En iyi 10 pairwise etkileşimi otomatik bul\n",
        "    max_rounds=5000,  # Boosting iterasyon sayısı\n",
        "    learning_rate=0.01,  # Öğrenme hızı\n",
        "    early_stopping_rounds=50  # Validation set'te 50 round boyunca iyileşme olmazsa dur\n",
        ")\n",
        "\n",
        "# Modeli eğit\n",
        "ebm.fit(X_train, y_train)\n",
        "\n",
        "# Test seti üzerinde tahmin yap\n",
        "y_pred = ebm.predict(X_test)\n",
        "y_pred_proba = ebm.predict_proba(X_test)[:, 1]\n",
        "\n",
        "# Model performansını değerlendir\n",
        "print(\"Model Performansı:\")\n",
        "print(f\"Accuracy: {accuracy_score(y_test, y_pred):.4f}\")\n",
        "print(f\"ROC-AUC: {roc_auc_score(y_test, y_pred_proba):.4f}\")\n",
        "print(\"\\nClassification Report:\")\n",
        "print(classification_report(y_test, y_pred))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "#### Global Feature Importance: Hangi Özellikler En Önemli?\n",
        "\n",
        "EBM'in en güçlü özelliklerinden biri, her bir özelliğin modeldeki **global katkısını** kesin olarak gösterebilmesidir."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Global açıklamayı al\n",
        "from interpret import show\n",
        "\n",
        "ebm_global = ebm.explain_global()\n",
        "\n",
        "# Jupyter'da interaktif gösterim\n",
        "show(ebm_global)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Feature importance'ı manuel olarak görselleştirelim\n",
        "feature_importances = ebm_global.data()['scores']\n",
        "feature_names = ebm_global.data()['names']\n",
        "\n",
        "# DataFrame'e dönüştür ve sırala\n",
        "importance_df = pd.DataFrame({\n",
        "    'Feature': feature_names,\n",
        "    'Importance': feature_importances\n",
        "}).sort_values('Importance', ascending=False)\n",
        "\n",
        "# Görselleştir\n",
        "plt.figure(figsize=(10, 8))\n",
        "plt.barh(importance_df['Feature'][:15], importance_df['Importance'][:15])\n",
        "plt.xlabel('Global Importance Score')\n",
        "plt.title('Top 15 Özellik Önemi (EBM)')\n",
        "plt.gca().invert_yaxis()\n",
        "plt.tight_layout()\n",
        "plt.show()\n",
        "\n",
        "print(\"Tüm Özelliklerin Önem Skorları:\")\n",
        "print(importance_df)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "#### Shape Fonksiyonları: Özelliklerin Tahmini Nasıl Etkiliyor?\n",
        "\n",
        "EBM'in en değerli özelliği: Her bir özelliğin değerinin, tahmin üzerindeki **doğrusal olmayan etkisini** görebiliriz."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# En önemli 4 özelliğin shape fonksiyonlarını görselleştir\n",
        "top_features = importance_df['Feature'][:4].tolist()\n",
        "\n",
        "fig, axes = plt.subplots(2, 2, figsize=(14, 10))\n",
        "axes = axes.ravel()\n",
        "\n",
        "for idx, feature in enumerate(top_features):\n",
        "    # Özelliğin index'ini bul\n",
        "    feature_idx = list(X.columns).index(feature) if feature in X.columns else None\n",
        "    \n",
        "    if feature_idx is not None:\n",
        "        # Shape verilerini al\n",
        "        feature_data = ebm_global.data(feature_idx)\n",
        "        \n",
        "        # Grafik çiz\n",
        "        axes[idx].plot(feature_data['names'][1:], feature_data['scores'], marker='o', linewidth=2)\n",
        "        axes[idx].axhline(y=0, color='red', linestyle='--', alpha=0.3)\n",
        "        axes[idx].set_title(f'{feature}', fontsize=12, fontweight='bold')\n",
        "        axes[idx].set_xlabel('Feature Value')\n",
        "        axes[idx].set_ylabel('Log-odds Contribution')\n",
        "        axes[idx].grid(True, alpha=0.3)\n",
        "\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "#### Pairwise Etkileşimler: Hangi Özellikler Birlikte Çalışıyor?\n",
        "\n",
        "EBM, iki özellik arasındaki etkileşimleri otomatik tespit eder ve modelleyebilir."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Etkileşimleri bul\n",
        "interaction_features = [name for name in feature_names if ' x ' in name]\n",
        "interaction_scores = [importance_df[importance_df['Feature'] == name]['Importance'].values[0] \n",
        "                      for name in interaction_features]\n",
        "\n",
        "# Etkileşimleri görselleştir\n",
        "if interaction_features:\n",
        "    interaction_df = pd.DataFrame({\n",
        "        'Interaction': interaction_features,\n",
        "        'Importance': interaction_scores\n",
        "    }).sort_values('Importance', ascending=False)\n",
        "    \n",
        "    print(\"Tespit Edilen Pairwise Etkileşimler:\")\n",
        "    print(interaction_df)\n",
        "    \n",
        "    # Grafik\n",
        "    plt.figure(figsize=(10, 6))\n",
        "    plt.barh(interaction_df['Interaction'][:5], interaction_df['Importance'][:5])\n",
        "    plt.xlabel('Importance Score')\n",
        "    plt.title('Top 10 Pairwise Etkileşimler')\n",
        "    plt.gca().invert_yaxis()\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "else:\n",
        "    print(\"Model hiçbir anlamlı pairwise etkileşim tespit etmedi.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "#### Local Explanation: Tek Bir Tahminin Açıklanması\n",
        "\n",
        "EBM, sadece global değil, **local (bireysel) açıklamalar** da yapabilir. Yani \"Bu müşteri neden churn edecek?\" sorusuna yanıt verebilir."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Test setinden bir örnek seç\n",
        "sample_idx = 5\n",
        "sample = X_test.iloc[sample_idx:sample_idx+1]\n",
        "\n",
        "# Local açıklama\n",
        "ebm_local = ebm.explain_local(X_test, y_test)\n",
        "\n",
        "# İnteraktif gösterim (Jupyter'da çalışır)\n",
        "show(ebm_local, sample_idx)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "#### Feature Elimination: Önemsiz Özellikleri Çıkaralım\n",
        "\n",
        "Şimdi, önem skorlarına dayanarak **düşük katkılı özellikleri** modelden çıkaralım ve performansı karşılaştıralım."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Eşik değeri belirle: Importance < 0.01 olan özellikleri çıkar\n",
        "importance_threshold = 0.01\n",
        "\n",
        "# Çıkarılacak özellikleri belirle (sadece main features, interactions hariç)\n",
        "low_importance_features = importance_df[\n",
        "    (importance_df['Importance'] < importance_threshold) & \n",
        "    (~importance_df['Feature'].str.contains(' x '))\n",
        "]['Feature'].tolist()\n",
        "\n",
        "print(f\"Çıkarılacak {len(low_importance_features)} özellik:\")\n",
        "print(low_importance_features)\n",
        "\n",
        "# Yeni özellik seti oluştur\n",
        "X_train_reduced = X_train.drop(columns=low_importance_features)\n",
        "X_test_reduced = X_test.drop(columns=low_importance_features)\n",
        "\n",
        "print(f\"\\nÖnceki özellik sayısı: {X_train.shape[1]}\")\n",
        "print(f\"Yeni özellik sayısı: {X_train_reduced.shape[1]}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Yeni model eğit\n",
        "ebm_reduced = ExplainableBoostingClassifier(\n",
        "    random_state=42,\n",
        "    interactions=10,\n",
        "    max_rounds=5000,\n",
        "    learning_rate=0.01,\n",
        "    early_stopping_rounds=50\n",
        ")\n",
        "\n",
        "ebm_reduced.fit(X_train_reduced, y_train)\n",
        "\n",
        "# Performansı değerlendir\n",
        "y_pred_reduced = ebm_reduced.predict(X_test_reduced)\n",
        "y_pred_proba_reduced = ebm_reduced.predict_proba(X_test_reduced)[:, 1]\n",
        "\n",
        "print(\"Özellik Eliminasyonu Sonrası Model Performansı:\")\n",
        "print(f\"Accuracy: {accuracy_score(y_test, y_pred_reduced):.4f}\")\n",
        "print(f\"ROC-AUC: {roc_auc_score(y_test, y_pred_proba_reduced):.4f}\")\n",
        "\n",
        "# Karşılaştırma\n",
        "print(\"\\n\" + \"=\"*50)\n",
        "print(\"KARŞILAŞTIRMA:\")\n",
        "print(\"=\"*50)\n",
        "print(f\"Önceki Model - Özellik: {X_train.shape[1]}, ROC-AUC: {roc_auc_score(y_test, y_pred_proba):.4f}\")\n",
        "print(f\"Yeni Model   - Özellik: {X_train_reduced.shape[1]}, ROC-AUC: {roc_auc_score(y_test, y_pred_proba_reduced):.4f}\")\n",
        "print(f\"\\nPerformans Farkı: {roc_auc_score(y_test, y_pred_proba_reduced) - roc_auc_score(y_test, y_pred_proba):.4f}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "#### Alıştırma 3: EBM ile Deney Yapın\n",
        "\n",
        "1. `importance_threshold` değerini değiştirerek (örn: 0.05, 0.02, 0.005) farklı özellik sayılarıyla model eğitin\n",
        "2. Her durumda ROC-AUC skorunu kaydedin ve karşılaştırın\n",
        "3. Optimal özellik sayısını bulun (en az özellik, en yüksek performans)\n",
        "4. **Bonus:** En önemli 5 özelliği kullanarak yeni bir model eğitin ve performansı karşılaştırın"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Alıştırma 3 için çözüm alanı"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 4.4. EBM ile Öğrendiklerimiz: Özet\n",
        "\n",
        "**EBM'in Avantajları:**\n",
        "1. ✅ Tam yorumlanabilirlik: Her özelliğin katkısını kesin olarak görebiliriz\n",
        "2. ✅ Yüksek performans: Gradient Boosting seviyesinde doğruluk\n",
        "3. ✅ Otomatik etkileşim tespiti: Manuel feature engineering yükünü azaltır\n",
        "4. ✅ Objektif özellik seçimi: Hangi özelliklerin önemli olduğunu kesin olarak biliyoruz\n",
        "\n",
        "**Ne Zaman EBM Kullanmalıyız?**\n",
        "- Modelin kararlarını açıklamamız gerektiğinde (regülasyon, sağlık, finans)\n",
        "- Özellik mühendisliği ve seçimi için veri keşfi yaparken\n",
        "- Domain uzmanlarına model davranışını göstermek istediğimizde\n",
        "\n",
        "**Ne Zaman Dikkatli Olmalıyız?**\n",
        "- Çok büyük veri setlerinde (eğitim yavaş olabilir)\n",
        "- 3+ özellik etkileşimleri gerektiğinde\n",
        "- Yorumlanabilirlik öncelik değilse (XGBoost/NN daha hızlı olabilir)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Haftanın Özeti\n",
        "\n",
        "Bu hafta, ham veriyi modelin anlayabileceği ve en iyi şekilde öğrenebileceği bir formata dönüştürdük.\n",
        "- Eksik veri, kategorik veri ve zaman damgalı veri gibi yaygın zorluklarla nasıl başa çıkılacağını öğrendik.\n",
        "- Yaratıcı bir şekilde yeni özellikler (etkileşim, oran vb.) türetmenin önemini anladık.\n",
        "- Görselleştirmenin, özelliklerin değerini anlamada ne kadar güçlü bir araç olduğunu gördük.\n",
        "- Korelasyon gibi basit filtre yöntemleriyle en bilgilendirici özellikleri nasıl seçebileceğimize dair bir başlangıç yaptık.\n",
        "\n",
        "### Sonraki Adımlar\n",
        "\n",
        "Artık temizlenmiş ve zenginleştirilmiş bir özellik setimiz var. Bir sonraki hafta, bu özellik setini kullanarak bir model eğiteceğiz. Ancak bunu rastgele yapmayacağız. **Hafta 4: MLOps Prensipleri ve Deney Yönetimi**'nde, her bir model eğitimini, farklı parametreler ve özellik setleriyle yapılan, takip edilebilir ve karşılaştırılabilir bir deneye nasıl dönüştüreceğimizi öğreneceğiz. `MLflow` gibi araçlarla tanışacağız.\n",
        "\n",
        "---\n",
        "\n",
        "## **Hafta 4: MLOps Prensipleri ve Deney Yönetimi**```python"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python (cpasa_env)",
      "language": "python",
      "name": "cpasa_env"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}