{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Hafta 2: Kod ve Veri için Test Stratejileri\n",
        "\n",
        "**Dersin Hedefleri:**\n",
        "1.  Yazılım testinin veri bilimi projelerindeki önemini kavramak.\n",
        "2.  Test Güdümlü Geliştirme (TDD) döngüsünü (Red-Green-Refactor) anlamak ve uygulamak.\n",
        "3.  `pytest` kütüphanesini kullanarak etkin birim testleri (unit tests) yazmak.\n",
        "4.  Veri bilimine özgü testler: Veri doğrulama (data validation) testleri ile veri kalitesini garanti altına almak.\n",
        "5.  Bu testleri, bir önceki hafta öğrendiğimiz Git iş akışlarına entegre etme mantığını anlamak.\n",
        "\n",
        "## 1. Neden Test Yazmalıyız?\n",
        "\n",
        "Bir önceki hafta, kodumuzun geçmişini yönetmeyi öğrendik. Peki yazdığımız kodun **doğru çalıştığından** nasıl emin olabiliriz?\n",
        "\n",
        "- **Güvenilirlik:** Testler, kodunuzun beklenen girdiler için beklenen çıktıları ürettiğini kanıtlar. Bu, özellikle karmaşık veri işleme pipeline'larında kritiktir.\n",
        "- **Regresyon Önleme:** Yeni bir özellik eklediğinizde veya mevcut kodu iyileştirdiğinizde (refactoring), farkında olmadan eski ve çalışan bir özelliği bozabilirsiniz. Kapsamlı bir test paketi, bu tür \"regresyon\" hatalarını anında yakalar.\n",
        "- **Canlı Dokümantasyon:** Testler, bir fonksiyonun nasıl kullanılması gerektiğini ve ne yapması gerektiğini gösteren en iyi dokümantasyondur. Kodun kendisi eskir ama testler güncel kalmak zorundadır.\n",
        "- **Refactoring Cesareti:** İyi test edilmiş bir kod tabanını iyileştirmek ve yeniden yapılandırmak çok daha kolay ve güvenlidir. Testler sizin güvenlik ağınızdır."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. Test Güdümlü Geliştirme (TDD)\n",
        "\n",
        "TDD, yazılım geliştirme sürecini tersine çeviren bir metodolojidir. Önce kodu yazıp sonra test etmek yerine, **önce test yazılır**.\n",
        "\n",
        "**TDD Döngüsü:**\n",
        "\n",
        "1.  **RED:** Yeni bir özellik için **başarısız olacak** bir test yaz. Testi çalıştır ve başarısız olduğunu gör. Bu, testin kendisinin doğru çalıştığını (yani olmayan bir şeyi \"başarılı\" göstermediğini) kanıtlar.\n",
        "2.  **GREEN:** Testi **başarılı kılacak en basit** kodu yaz. Bu aşamada kodun ne kadar \"güzel\" veya \"verimli\" olduğu önemli değildir. Amaç, sadece testi geçmektir. Testi tekrar çalıştır ve başarılı olduğunu gör.\n",
        "3.  **REFACTOR:** Artık çalışan bir kodun ve onu koruyan bir testin var. Kodunu daha okunabilir, daha verimli veya daha iyi tasarlanmış hale getirmek için güvenle yeniden yapılandır. Her adımdan sonra testleri çalıştırarak hiçbir şeyi bozmadığından emin ol.\n",
        "\n",
        "Bu döngü, daha modüler, daha test edilebilir ve daha sağlam bir kod tabanı oluşturmayı teşvik eder."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. `pytest` ile Pratik Uygulama\n",
        "\n",
        "`pytest`, Python için standart haline gelmiş, basit ve güçlü bir test çatısıdır.\n",
        "\n",
        "**Kurulum:**\n",
        "```bash\n",
        "pip install pytest\n",
        "```\n",
        "\n",
        "**Temel Kurallar:**\n",
        "- Test dosyalarının adları `test_*.py` veya `*_test.py` ile başlamalıdır.\n",
        "- Test fonksiyonlarının adları `test_*` ile başlamalıdır.\n",
        "- Testlerdeki doğrulamalar basit `assert` ifadeleri ile yapılır.\n",
        "\n",
        "### Uygulama: Veri Temizleme Fonksiyonunu Test Etme\n",
        "\n",
        "Senaryomuz: Bir hasta kayıt sisteminden gelen verileri temizleyen bir fonksiyon yazmamız gerekiyor. Bu verilerde hatalar olabilir (örn. negatif yaş, hatalı kan basıncı değeri).\n",
        "\n",
        "İlk olarak, test dosyamızı ve başarısız olacak testimizi (RED) oluşturalım."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Proje yapısını simüle edelim\n",
        "import os\n",
        "import shutil\n",
        "\n",
        "# Proje klasörlerini oluştur\n",
        "project_root = \"hafta2_projesi\"\n",
        "src_dir = os.path.join(project_root, \"src\")\n",
        "tests_dir = os.path.join(project_root, \"tests\")\n",
        "\n",
        "if os.path.exists(project_root):\n",
        "    shutil.rmtree(project_root)\n",
        "\n",
        "os.makedirs(src_dir)\n",
        "os.makedirs(tests_dir)\n",
        "\n",
        "# __init__.py dosyaları ile Python paketleri oluşturalım\n",
        "open(os.path.join(src_dir, \"__init__.py\"), 'w').close()\n",
        "open(os.path.join(tests_dir, \"__init__.py\"), 'w').close()\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "\n",
        "# --- RED: Başarısız olacak testi yaz ---\n",
        "# tests/test_data_cleaning.py\n",
        "test_file_content = \"\"\"\n",
        "import pytest\n",
        "import pandas as pd\n",
        "from src.data_cleaning import clean_patient_data\n",
        "\n",
        "def test_clean_patient_data_removes_negative_age():\n",
        "    # 1. Test için sentetik, sorunlu veri oluştur\n",
        "    dirty_data = pd.DataFrame({\n",
        "        'patient_id': [1, 2, 3],\n",
        "        'age': [34, -5, 45], # Negatif yaş hatası\n",
        "        'blood_pressure': ['120/80', '130/85', '110/70']\n",
        "    })\n",
        "    \n",
        "    # 2. Temizleme fonksiyonunu çağır\n",
        "    cleaned_data = clean_patient_data(dirty_data)\n",
        "    \n",
        "    # 3. Doğrula (Assert): Temizlenmiş veride negatif yaş kalmamalı\n",
        "    assert (cleaned_data['age'] >= 0).all()\n",
        "\"\"\"\n",
        "\n",
        "with open(os.path.join(tests_dir, \"test_data_cleaning.py\"), \"w\") as f:\n",
        "    f.write(test_file_content)\n",
        "\n",
        "# Henüz fonksiyonu yazmadığımız için bu test başarısız olacak.\n",
        "# İlk olarak src/data_cleaning.py dosyası boş bir fonksiyonla oluşturulur.\n",
        "cleaning_script_content_initial = \"\"\"\n",
        "import pandas as pd\n",
        "\n",
        "def clean_patient_data(df: pd.DataFrame) -> pd.DataFrame:\n",
        "    # Henüz bir şey yapmıyor\n",
        "    return df\n",
        "\"\"\"\n",
        "with open(os.path.join(src_dir, \"data_cleaning.py\"), \"w\") as f:\n",
        "    f.write(cleaning_script_content_initial)\n",
        "\n",
        "# Testi çalıştır (Normalde terminalden `pytest` komutu ile yapılır)\n",
        "# Burada subprocess ile simüle ediyoruz.\n",
        "# Not: Bu, fonksiyonun henüz negatif yaşları düzeltmediği için bir AssertionError fırlatacaktır.\n",
        "print(\"--- TDD Adım 1: RED ---\")\n",
        "# !pytest {project_root}  # Jupyter/IPython'da bu şekilde çalıştırılabilir.\n",
        "# subprocess ile çalıştırma:\n",
        "import subprocess\n",
        "result = subprocess.run([\"pytest\", project_root], capture_output=True, text=True)\n",
        "print(result.stdout)\n",
        "print(result.stderr)\n",
        "\n",
        "\n",
        "# --- GREEN: Testi geçecek en basit kod ---\n",
        "print(\"\\n--- TDD Adım 2: GREEN ---\")\n",
        "cleaning_script_content_green = \"\"\"\n",
        "import pandas as pd\n",
        "import numpy as np\n",
        "\n",
        "def clean_patient_data(df: pd.DataFrame) -> pd.DataFrame:\n",
        "    df_copy = df.copy()\n",
        "    # Negatif yaşları NaN ile değiştir (veya 0 ile, veya ortalama ile - şimdilik en basiti)\n",
        "    df_copy.loc[df_copy['age'] < 0, 'age'] = np.nan\n",
        "    return df_copy\n",
        "\"\"\"\n",
        "with open(os.path.join(src_dir, \"data_cleaning.py\"), \"w\") as f:\n",
        "    f.write(cleaning_script_content_green)\n",
        "\n",
        "# Testi tekrar çalıştır. Şimdi başarılı olmalı.\n",
        "result = subprocess.run([\"pytest\", project_root], capture_output=True, text=True)\n",
        "print(result.stdout)\n",
        "print(result.stderr)\n",
        "\n",
        "\n",
        "# --- REFACTOR: Kod iyileştirme ---\n",
        "print(\"\\n--- TDD Adım 3: REFACTOR ---\")\n",
        "# Negatif yaşları NaN yapmak yerine ortalama ile doldurma alternatif bir strateji olabilir.\n",
        "# Test ve kod bu yeni mantığa göre güncellenebilir.\n",
        "# Bu örnek için kodumuz yeterince basit, refactor adımını atlıyoruz ama mantık bu.\n",
        "print(\"Kodumuz basit olduğu için bu adımda değişiklik yapılmadı.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. Veri Doğrulama Testleri\n",
        "\n",
        "Geleneksel birim testleri fonksiyonların mantığını test eder. Ancak veri biliminde, **verinin kendisi de hatalar içerebilir**. Veri doğrulama testleri, veri kalitesini, yapısını ve istatistiksel özelliklerini kontrol eder.\n",
        "\n",
        "**Yaygın Veri Doğrulama Kontrolleri:**\n",
        "- **Şema Kontrolü:** Gerekli sütunlar var mı? Sütun adları doğru mu? Veri tipleri (int, float, object) beklendiği gibi mi?\n",
        "- **Boş Değer Kontrolü:** Belirli sütunlarda boş (null/NaN) değer olmamalı.\n",
        "- **Aralık Kontrolü:** Değerler mantıklı bir aralıkta mı? (örn: yaş > 0, yüzde 0-100 arası)\n",
        "- **Kategorik Değer Kontrolü:** Kategorik bir sütundaki değerler, izin verilen bir setin içinde mi? (örn: `cinsiyet` sütunu sadece 'Erkek', 'Kadın' içermeli)\n",
        "\n",
        "### Uygulama: `pydantic` ile Veri Şeması Doğrulama\n",
        "\n",
        "`pydantic` gibi kütüphaneler, Python veri sınıfları kullanarak veri şemalarını tanımlamayı ve doğrulamayı çok kolaylaştırır.\n",
        "\n",
        "**Kurulum:**\n",
        "```bash\n",
        "pip install pydantic\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "from pydantic import BaseModel, Field, ValidationError\n",
        "from typing import List\n",
        "\n",
        "# 1. Beklenen veri şemasını bir Pydantic modeli ile tanımla\n",
        "class PatientRecord(BaseModel):\n",
        "    patient_id: int\n",
        "    age: int = Field(gt=0, le=120) # gt: greater than, le: less than or equal\n",
        "    gender: str\n",
        "    blood_pressure: str\n",
        "\n",
        "# 2. Tüm DataFrame'i doğrulayacak bir model oluştur\n",
        "class PatientData(BaseModel):\n",
        "    records: List[PatientRecord]\n",
        "\n",
        "# 3. Sentetik Veri Oluşturma\n",
        "# a) Geçerli Veri\n",
        "valid_df = pd.DataFrame([\n",
        "    {'patient_id': 101, 'age': 45, 'gender': 'Female', 'blood_pressure': '120/80'},\n",
        "    {'patient_id': 102, 'age': 52, 'gender': 'Male', 'blood_pressure': '140/90'}\n",
        "])\n",
        "\n",
        "# b) Geçersiz Veri\n",
        "invalid_df = pd.DataFrame([\n",
        "    {'patient_id': 201, 'age': -10, 'gender': 'Male', 'blood_pressure': '130/85'}, # Negatif yaş\n",
        "    {'patient_id': 202, 'age': 30, 'gender': 'Other', 'blood_pressure': '115/75'} # Geçersiz 'gender' varsayımı\n",
        "])\n",
        "\n",
        "\n",
        "# 4. Doğrulama Fonksiyonu\n",
        "def validate_data_schema(df: pd.DataFrame) -> bool:\n",
        "    \"\"\"Validates a DataFrame against the PatientData Pydantic model.\"\"\"\n",
        "    try:\n",
        "        # Pydantic'in bekledeği format için DataFrame'i dict listesine çevir\n",
        "        data_dict = {\"records\": df.to_dict(orient=\"records\")}\n",
        "        PatientData.parse_obj(data_dict)\n",
        "        print(\"Veri şeması doğrulandı: Başarılı!\")\n",
        "        return True\n",
        "    except ValidationError as e:\n",
        "        print(\"Veri şeması hatası!\")\n",
        "        print(e)\n",
        "        return False\n",
        "\n",
        "print(\"--- Geçerli Veri Testi ---\")\n",
        "validate_data_schema(valid_df)\n",
        "\n",
        "print(\"\\n--- Geçersiz Veri Testi ---\")\n",
        "validate_data_schema(invalid_df)\n",
        "\n",
        "# Temizlik\n",
        "shutil.rmtree(project_root)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 5. Model Hiperparametre Optimizasyonu için Test Stratejileri\n",
        "\n",
        "Veri bilimi projelerinde model performansını optimize etmek için hiperparametre ayarlaması kritik öneme sahiptir. Grid Search ve Random Search gibi sistematik yaklaşımlar, hem objektif model karşılaştırması hem de tekrarlanabilir sonuçlar sağlar.\n",
        "\n",
        "### 5.1. Grid Search: Deterministik Hiperparametre Keşfi\n",
        "\n",
        "Grid Search, hiperparametre uzayında önceden tanımlanmış bir ızgara üzerinde kapsamlı arama yapar. Her parametre kombinasyonu test edilir ve çapraz doğrulama ile değerlendirilir.\n",
        "\n",
        "**Matematiksel Formalizasyon:**\n",
        "Hiperparametre uzayı Θ = {θ₁, θ₂, ..., θₚ} olsun. Grid Search için:\n",
        "- Her θᵢ için ayrı değer kümesi: Θᵢ = {θᵢ¹, θᵢ², ..., θᵢⁿⁱ}\n",
        "- Toplam arama uzayı: |Θ₁| × |Θ₂| × ... × |Θₚ| kombinasyon\n",
        "- Amaç: arg min_θ∈Θ L(θ) (kayıp fonksiyonu minimizasyonu)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import pandas as pd\n",
        "from sklearn.datasets import make_classification\n",
        "from sklearn.ensemble import RandomForestClassifier\n",
        "from sklearn.model_selection import GridSearchCV, RandomizedSearchCV, cross_val_score\n",
        "from sklearn.metrics import accuracy_score, precision_score, recall_score\n",
        "from sklearn.model_selection import train_test_split\n",
        "import pytest\n",
        "\n",
        "# Deneysel veri seti oluştur\n",
        "X, y = make_classification(n_samples=1000, n_features=10, n_informative=7,\n",
        "                          n_redundant=3, random_state=42)\n",
        "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
        "\n",
        "# Grid Search implementasyonu\n",
        "def test_grid_search_systematic():\n",
        "    \"\"\"Grid Search'ün sistematik hiperparametre tarama yaklaşımını test eder.\"\"\"\n",
        "\n",
        "    # Hiperparametre ızgarası tanımla\n",
        "    param_grid = {\n",
        "        'n_estimators': [50, 100, 200],\n",
        "        'max_depth': [3, 5, 7, None],\n",
        "        'min_samples_split': [2, 5, 10]\n",
        "    }\n",
        "\n",
        "    # Grid Search nesnesi oluştur\n",
        "    rf = RandomForestClassifier(random_state=42)\n",
        "    grid_search = GridSearchCV(\n",
        "        estimator=rf,\n",
        "        param_grid=param_grid,\n",
        "        cv=5,  # 5-katlı çapraz doğrulama\n",
        "        scoring='accuracy',\n",
        "        n_jobs=-1,\n",
        "        verbose=0\n",
        "    )\n",
        "\n",
        "    # Model eğitimi ve hiperparametre optimizasyonu\n",
        "    grid_search.fit(X_train, y_train)\n",
        "\n",
        "    # En iyi parametreleri al\n",
        "    best_params = grid_search.best_params_\n",
        "    best_score = grid_search.best_score_\n",
        "\n",
        "    # Test performansı\n",
        "    best_model = grid_search.best_estimator_\n",
        "    y_pred = best_model.predict(X_test)\n",
        "    test_accuracy = accuracy_score(y_test, y_pred)\n",
        "\n",
        "    # Doğrulama testleri\n",
        "    assert best_score > 0.7, \"Grid Search sonucu minimum performans kriterini karşılamalı\"\n",
        "    assert test_accuracy > 0.7, \"Test seti performansı kabul edilebilir seviyede olmalı\"\n",
        "    assert len(best_params) == 3, \"En iyi parametre seti tüm hiperparametreleri içermeli\"\n",
        "\n",
        "    print(f\"Grid Search Sonuçları:\")\n",
        "    print(f\"En iyi parametreler: {best_params}\")\n",
        "    print(f\"En iyi CV skoru: {best_score:.4f}\")\n",
        "    print(f\"Test doğruluğu: {test_accuracy:.4f}\")\n",
        "\n",
        "    return best_params, best_score, test_accuracy\n",
        "\n",
        "# Test fonksiyonunu çalıştır\n",
        "grid_results = test_grid_search_systematic()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 5.2. Random Search: Stokastik Hiperparametre Keşfi\n",
        "\n",
        "Random Search, hiperparametre uzayından rastgele örnekleme yapar. Bergstra & Bengio (2012) çalışması, yüksek boyutlu uzaylarda Random Search'ün Grid Search'ten daha etkili olabileceğini göstermiştir.\n",
        "\n",
        "**Teorik Avantajlar:**\n",
        "- Sürekli parametreler için daha uygun\n",
        "- Hesaplama maliyeti sabit (n iterasyon)\n",
        "- Yüksek boyutlu uzaylarda daha etkili keşif\n",
        "- Önemsiz parametrelere daha az zaman harcar"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "from scipy.stats import randint, uniform\n",
        "\n",
        "def test_random_search_efficiency():\n",
        "    \"\"\"Random Search'ün verimlilik ve keşif kapasitesini test eder.\"\"\"\n",
        "\n",
        "    # Sürekli ve ayrık parametreler için dağılım tanımla\n",
        "    param_distributions = {\n",
        "        'n_estimators': randint(50, 300),      # Ayrık dağılım\n",
        "        'max_depth': randint(3, 15),          # Ayrık dağılım\n",
        "        'min_samples_split': randint(2, 20),  # Ayrık dağılım\n",
        "        'min_samples_leaf': randint(1, 10),   # Ayrık dağılım\n",
        "        'max_features': uniform(0.1, 0.9)     # Sürekli dağılım\n",
        "    }\n",
        "\n",
        "    # Random Search nesnesi oluştur\n",
        "    rf = RandomForestClassifier(random_state=42)\n",
        "    random_search = RandomizedSearchCV(\n",
        "        estimator=rf,\n",
        "        param_distributions=param_distributions,\n",
        "        n_iter=50,  # 50 rastgele kombinasyon dene\n",
        "        cv=5,\n",
        "        scoring='accuracy',\n",
        "        n_jobs=-1,\n",
        "        verbose=0,\n",
        "        random_state=42\n",
        "    )\n",
        "\n",
        "    # Model eğitimi\n",
        "    random_search.fit(X_train, y_train)\n",
        "\n",
        "    # Sonuçları analiz et\n",
        "    best_params = random_search.best_params_\n",
        "    best_score = random_search.best_score_\n",
        "\n",
        "    # Test performansı\n",
        "    best_model = random_search.best_estimator_\n",
        "    y_pred = best_model.predict(X_test)\n",
        "    test_accuracy = accuracy_score(y_test, y_pred)\n",
        "\n",
        "    # Keşfedilen parametre uzayının çeşitliliğini değerlendir\n",
        "    results_df = pd.DataFrame(random_search.cv_results_)\n",
        "    param_variance = results_df[['param_n_estimators', 'param_max_depth']].var()\n",
        "\n",
        "    # Doğrulama testleri\n",
        "    assert best_score > 0.7, \"Random Search performans kriterini karşılamalı\"\n",
        "    assert test_accuracy > 0.7, \"Test performansı kabul edilebilir seviyede olmalı\"\n",
        "    assert param_variance.mean() > 0, \"Parametre uzayında yeterli çeşitlilik keşfedilmeli\"\n",
        "\n",
        "    print(f\"\\nRandom Search Sonuçları:\")\n",
        "    print(f\"En iyi parametreler: {best_params}\")\n",
        "    print(f\"En iyi CV skoru: {best_score:.4f}\")\n",
        "    print(f\"Test doğruluğu: {test_accuracy:.4f}\")\n",
        "    print(f\"Parametre çeşitliliği (varyans): {param_variance.mean():.2f}\")\n",
        "\n",
        "    return best_params, best_score, test_accuracy\n",
        "\n",
        "# Test fonksiyonunu çalıştır\n",
        "random_results = test_random_search_efficiency()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 5.3. Karşılaştırmalı Analiz ve Metodolojik Değerlendirme\n",
        "\n",
        "Grid Search ve Random Search yaklaşımlarının sistematik karşılaştırması, her metodun güçlü ve zayıf yönlerini ortaya koyar."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def test_search_methodology_comparison():\n",
        "    \"\"\"Grid Search ve Random Search metodolojilerini karşılaştırır.\"\"\"\n",
        "\n",
        "    # Aynı temel hiperparametre uzayı için her iki metodu test et\n",
        "    base_param_space = {\n",
        "        'n_estimators': [50, 100, 150],\n",
        "        'max_depth': [3, 5, 7],\n",
        "        'min_samples_split': [2, 5, 10]\n",
        "    }\n",
        "\n",
        "    # Grid Search\n",
        "    rf_grid = RandomForestClassifier(random_state=42)\n",
        "    grid_search = GridSearchCV(rf_grid, base_param_space, cv=3, scoring='accuracy')\n",
        "\n",
        "    # Random Search için eşdeğer dağılım\n",
        "    random_param_space = {\n",
        "        'n_estimators': [50, 100, 150],\n",
        "        'max_depth': [3, 5, 7],\n",
        "        'min_samples_split': [2, 5, 10]\n",
        "    }\n",
        "\n",
        "    rf_random = RandomForestClassifier(random_state=42)\n",
        "    random_search = RandomizedSearchCV(\n",
        "        rf_random, random_param_space, n_iter=27,  # Tüm kombinasyonları kapsa\n",
        "        cv=3, scoring='accuracy', random_state=42\n",
        "    )\n",
        "\n",
        "    # Her iki metodu eğit\n",
        "    import time\n",
        "\n",
        "    start_time = time.time()\n",
        "    grid_search.fit(X_train, y_train)\n",
        "    grid_time = time.time() - start_time\n",
        "\n",
        "    start_time = time.time()\n",
        "    random_search.fit(X_train, y_train)\n",
        "    random_time = time.time() - start_time\n",
        "\n",
        "    # Performans karşılaştırması\n",
        "    grid_performance = grid_search.best_score_\n",
        "    random_performance = random_search.best_score_\n",
        "\n",
        "    # Test sonuçları\n",
        "    assert abs(grid_performance - random_performance) < 0.05, \"Performans farkı minimal olmalı\"\n",
        "    assert grid_time > 0 and random_time > 0, \"Her iki metod da ölçülebilir sürede tamamlanmalı\"\n",
        "\n",
        "    print(f\"\\nMetodolojik Karşılaştırma:\")\n",
        "    print(f\"Grid Search - Performans: {grid_performance:.4f}, Süre: {grid_time:.2f}s\")\n",
        "    print(f\"Random Search - Performans: {random_performance:.4f}, Süre: {random_time:.2f}s\")\n",
        "    print(f\"Performans farkı: {abs(grid_performance - random_performance):.4f}\")\n",
        "\n",
        "    # Metodolojik öneriler\n",
        "    if random_performance >= grid_performance * 0.95 and random_time < grid_time:\n",
        "        print(\"Öneri: Bu veri seti için Random Search daha verimli\")\n",
        "    else:\n",
        "        print(\"Öneri: Grid Search daha kapsamlı keşif sağlıyor\")\n",
        "\n",
        "    return {\n",
        "        'grid': {'performance': grid_performance, 'time': grid_time},\n",
        "        'random': {'performance': random_performance, 'time': random_time}\n",
        "    }\n",
        "\n",
        "# Karşılaştırma testini çalıştır\n",
        "comparison_results = test_search_methodology_comparison()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 5.4. Pratik Uygulama Rehberi\n",
        "\n",
        "**Grid Search Kullanım Senaryoları:**\n",
        "- Düşük boyutlu hiperparametre uzayı (< 4 parametre)\n",
        "- Ayrık parametre değerleri\n",
        "- Kapsamlı keşif gereksinimi\n",
        "- Hesaplama kaynakları bol\n",
        "\n",
        "**Random Search Kullanım Senaryoları:**\n",
        "- Yüksek boyutlu hiperparametre uzayı (> 4 parametre)\n",
        "- Sürekli parametre dağılımları\n",
        "- Sınırlı hesaplama bütçesi\n",
        "- Hızlı prototipleme gereksinimi"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def hyperparameter_optimization_strategy(n_params, computation_budget, param_types):\n",
        "    \"\"\"Verilen kısıtlar için optimal hiperparametre optimizasyon stratejisi önerir.\"\"\"\n",
        "\n",
        "    if n_params <= 3 and computation_budget == 'high':\n",
        "        return \"Grid Search - Kapsamlı keşif için ideal\"\n",
        "    elif n_params > 4 or computation_budget == 'low':\n",
        "        return \"Random Search - Verimli keşif için ideal\"\n",
        "    elif 'continuous' in param_types:\n",
        "        return \"Random Search - Sürekli parametreler için daha uygun\"\n",
        "    else:\n",
        "        return \"Grid Search - Ayrık parametreler için sistematik yaklaşım\"\n",
        "\n",
        "# Strateji örnekleri\n",
        "print(\"\\nHiperparametre Optimizasyon Stratejisi Önerileri:\")\n",
        "print(\"Düşük boyut + Yüksek bütçe:\", hyperparameter_optimization_strategy(3, 'high', ['discrete']))\n",
        "print(\"Yüksek boyut + Düşük bütçe:\", hyperparameter_optimization_strategy(6, 'low', ['discrete']))\n",
        "print(\"Sürekli parametreler:\", hyperparameter_optimization_strategy(4, 'medium', ['continuous']))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Alıştırma 3: Kendi Veri Doğrulama Testinizi Yazın\n",
        "\n",
        "1.  Bir e-ticaret sitesinden geldiğini varsaydığınız siparişler için bir `pydantic` modeli (`OrderItem`) oluşturun.\n",
        "2.  Bu model şu alanları içermeli:\n",
        "    *   `order_id` (string)\n",
        "    *   `product_id` (string)\n",
        "    *   `quantity` (integer, 0'dan büyük olmalı)\n",
        "    *   `price_usd` (float, 0'dan büyük olmalı)\n",
        "    *   `status` (string, ve sadece 'shipped', 'pending', 'cancelled' değerlerini alabilmeli. `pydantic.validator` kullanmayı araştırın!)\n",
        "3.  Hem geçerli hem de geçersiz veriler içeren iki `pandas.DataFrame` oluşturun.\n",
        "4.  Bu DataFrame'leri `OrderItem` modelinize karşı doğrulayan bir fonksiyon yazın ve sonuçları gözlemleyin."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Haftanın Özeti\n",
        "\n",
        "Kod kalitesi ve güvenilirliğinin temel disiplini olan test yazma yöntemleri incelendi.\n",
        "\n",
        "- **TDD**, sağlam ve modüler kod geliştirme için güçlü bir metodoloji sunar.\n",
        "- **`pytest`**, Python'da test yazmayı basit ve etkili hale getirir.\n",
        "- **Veri Doğrulama**, veri bilimi projelerinin kritik bileşenidir ve `pydantic` gibi araçlar bu süreci kolaylaştırır.\n",
        "- **Grid Search ve Random Search**, model hiperparametre optimizasyonu için sistematik ve test edilebilir yaklaşımlar sunar.\n",
        "- **Hiperparametre optimizasyonu**, tekrarlanabilir ve objektif model değerlendirmesi için kritik öneme sahiptir.\n",
        "\n",
        "### Devam Eden Konular\n",
        "\n",
        "Kod versiyon yönetimi (Hafta 1) ve test stratejileri (Hafta 2) temellerinin ardından, **Özellik Mühendisliği (Feature Engineering)** konusu ele alınacaktır. Test süreçleri, yeni özellik yaratma aşamalarında mevcut pipeline'ın korunmasını sağlar.\n",
        "\n",
        "---\n",
        "\n",
        "### **Hafta 3: Özellik Mühendisliği ve Seçimi**\n",
        "```python\n",
        "# %% [markdown]"
      ]
    }
  ],
  "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
}