{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":13275284,"sourceType":"datasetVersion","datasetId":8412817}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Read the Data\n","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\ndf = pd.read_csv('/kaggle/input/carvana-dataset/training.csv')\ndf.set_index('RefId', inplace=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:55.189496Z","iopub.execute_input":"2025-10-19T05:20:55.189882Z","iopub.status.idle":"2025-10-19T05:20:55.578991Z","shell.execute_reply.started":"2025-10-19T05:20:55.189858Z","shell.execute_reply":"2025-10-19T05:20:55.578124Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Drop Irrelevant Columns\n","metadata":{}},{"cell_type":"code","source":"cols_to_drop = [\n    'PurchDate', 'VehYear', 'Model', 'Trim', 'SubModel',\n    'WheelTypeID', 'BYRNO', 'VNZIP1', 'VNST', 'AUCGUART', 'PRIMEUNIT'\n]\n\ndf.drop(columns=cols_to_drop, inplace=True, errors='ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:55.580168Z","iopub.execute_input":"2025-10-19T05:20:55.580514Z","iopub.status.idle":"2025-10-19T05:20:55.596250Z","shell.execute_reply.started":"2025-10-19T05:20:55.580481Z","shell.execute_reply":"2025-10-19T05:20:55.595422Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train-Test Split\n","metadata":{}},{"cell_type":"code","source":"import numpy as np\n\ny = df.IsBadBuy\nX = df.drop('IsBadBuy', axis=1)\n\nfrom sklearn.model_selection import train_test_split\n\n\n# split into train and test sets\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=717)\n\nX_train.shape,X_test.shape\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:55.597178Z","iopub.execute_input":"2025-10-19T05:20:55.597511Z","iopub.status.idle":"2025-10-19T05:20:55.647619Z","shell.execute_reply.started":"2025-10-19T05:20:55.597489Z","shell.execute_reply":"2025-10-19T05:20:55.646478Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Clean Out-of-Range Values\n","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\ndef clean_out_of_range_values(df):\n    \"\"\"\n    پاک‌سازی مقادیر خارج از محدوده منطقی در ستون‌های عددی.\n    ورودی: DataFrame\n    خروجی: DataFrame تمیزشده\n    \"\"\"\n    valid_ranges = {\n        'VehicleAge': (0, 30),\n        'VehOdo': (0, 120000),\n        'MMRAcquisitionAuctionAveragePrice': (800, 46000),\n        'MMRAcquisitionAuctionCleanPrice': (1000, 46000),\n        'MMRAcquisitionRetailAveragePrice': (1000, 46000),\n        'MMRAcquisitonRetailCleanPrice': (1000, 46000),\n        'MMRCurrentAuctionAveragePrice': (300, 46000),\n        'MMRCurrentAuctionCleanPrice': (400, 46000),\n        'MMRCurrentRetailAveragePrice': (800, 46000),\n        'MMRCurrentRetailCleanPrice': (1000, 46000),\n        'VehBCost': (1000, 46000),\n        'WarrantyCost': (400, 8000)\n    }\n\n    df_clean = df.copy()\n    for col, (min_val, max_val) in valid_ranges.items():\n        if col in df_clean.columns:\n            df_clean[col] = df_clean[col].apply(\n                lambda x: x if pd.isna(x) or (min_val <= x <= max_val) else np.nan\n            )\n    return df_clean\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:55.650107Z","iopub.execute_input":"2025-10-19T05:20:55.650409Z","iopub.status.idle":"2025-10-19T05:20:55.659202Z","shell.execute_reply.started":"2025-10-19T05:20:55.650385Z","shell.execute_reply":"2025-10-19T05:20:55.657873Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train = clean_out_of_range_values(X_train)\nX_test = clean_out_of_range_values(X_test)\n\nX_train.shape, X_test.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:55.660188Z","iopub.execute_input":"2025-10-19T05:20:55.660455Z","iopub.status.idle":"2025-10-19T05:20:56.216398Z","shell.execute_reply.started":"2025-10-19T05:20:55.660435Z","shell.execute_reply":"2025-10-19T05:20:56.215021Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Feature Screening\n","metadata":{}},{"cell_type":"code","source":"def feature_screening(data, min_cv=0.1, mode_threshold=99, distinct_threshold=90):\n    \"\"\"\n    شناسایی ستون‌های غیرمفید در DataFrame:\n    \n    - ستون‌های عددی با ضریب تغییر کم (Coefficient of Variation پایین)\n    - ستون‌های دسته‌ای که بیش از حد غالب یک مقدار هستند\n    - ستون‌های دسته‌ای که درصد بالایی از مقادیر یکتا دارند\n\n    ورودی:\n        data : DataFrame\n        min_cv : حداقل ضریب تغییر برای ستون‌های عددی (default=0.1)\n        mode_threshold : درصد غالبیت یک مقدار در ستون‌های دسته‌ای (default=99)\n        distinct_threshold : درصد مقادیر یکتا در ستون‌های دسته‌ای (default=90)\n\n    خروجی:\n        screened_features : لیست ستون‌هایی که باید حذف شوند\n    \"\"\"\n    \n    processed_data = data.copy()\n\n    # انتخاب ستون‌های دسته‌ای و عددی\n    categorical = processed_data.select_dtypes(include=['object', 'category']).columns.tolist()\n    continuous = processed_data.select_dtypes(exclude=['object', 'category']).columns.tolist()\n\n    # 1️⃣ ستون‌های عددی با ضریب تغییر پایین\n    # ضریب تغییر = انحراف معیار / میانگین\n    cv_values = processed_data[continuous].std() / processed_data[continuous].replace(0, np.nan).mean()\n    screen_cv = cv_values[cv_values < min_cv].index.tolist()\n\n    # 2️⃣ ستون‌های دسته‌ای با غالبیت بیش از حد یک مقدار\n    mode_percentage = processed_data[categorical].apply(lambda x: x.value_counts(normalize=True, dropna=False).max() * 100)\n    screen_mode = mode_percentage[mode_percentage > mode_threshold].index.tolist()\n\n    # 3️⃣ ستون‌های دسته‌ای با درصد بالای مقادیر یکتا\n    distinct_percentage = processed_data[categorical].apply(lambda x: x.nunique() / len(x) * 100)\n    screen_distinct = distinct_percentage[distinct_percentage > distinct_threshold].index.tolist()\n\n    # ترکیب همه ستون‌های شناسایی‌شده\n    screened_features = list(set(screen_cv + screen_mode + screen_distinct))\n\n    return screened_features\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:56.218196Z","iopub.execute_input":"2025-10-19T05:20:56.218625Z","iopub.status.idle":"2025-10-19T05:20:56.228132Z","shell.execute_reply.started":"2025-10-19T05:20:56.218559Z","shell.execute_reply":"2025-10-19T05:20:56.226432Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"drop_list = feature_screening(X_train, min_cv=0.1, mode_threshold=99, distinct_threshold=90)\n\nX_train = X_train.drop(drop_list, axis=1)\nX_test = X_test.drop(drop_list, axis=1)\n\nX_train.shape, X_test.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:56.229425Z","iopub.execute_input":"2025-10-19T05:20:56.229912Z","iopub.status.idle":"2025-10-19T05:20:56.387439Z","shell.execute_reply.started":"2025-10-19T05:20:56.229787Z","shell.execute_reply":"2025-10-19T05:20:56.385064Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Outlier Detection (IsolationForest)\n","metadata":{}},{"cell_type":"code","source":"# import pandas as pd\n# from sklearn.ensemble import IsolationForest\n# from sklearn.preprocessing import StandardScaler, LabelEncoder\n\n# def outlier_handling(df: pd.DataFrame, contamination: float = 0.01, random_state: int = 42):\n#     \"\"\"\n#     شناسایی ایندکس داده‌های پرت با IsolationForest.\n\n#     پارامترها:\n#         df (pd.DataFrame): داده‌های ورودی\n#         contamination (float): نسبت تقریبی داده‌های پرت (default=0.01)\n#         random_state (int): عدد برای reproducibility (default=42)\n\n#     خروجی:\n#         pd.Index: ایندکس ردیف‌هایی که به عنوان پرت شناسایی شدند\n#     \"\"\"\n    \n#     # کپی داده‌ها برای جلوگیری از تغییرات روی داده اصلی\n#     df_temp = df.dropna().copy()\n    \n#     # شناسایی ستون‌های عددی و دسته‌ای\n#     numeric_cols = df_temp.select_dtypes(include=['int64', 'float64']).columns\n#     categorical_cols = df_temp.select_dtypes(include=['object', 'category']).columns\n\n#     # مقیاس‌بندی ستون‌های عددی\n#     if len(numeric_cols) > 0:\n#         scaler = StandardScaler()\n#         df_temp[numeric_cols] = scaler.fit_transform(df_temp[numeric_cols])\n    \n#     # کدگذاری ستون‌های دسته‌ای\n#     for col in categorical_cols:\n#         df_temp[col] = LabelEncoder().fit_transform(df_temp[col])\n\n#     # اجرای IsolationForest\n#     iso_forest = IsolationForest(contamination=contamination, random_state=random_state)\n#     iso_forest.fit(df_temp)\n\n#     # شناسایی ایندکس داده‌های پرت\n#     outlier_index = df_temp.index[iso_forest.predict(df_temp) == -1]\n\n#     return outlier_index\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:56.388446Z","iopub.execute_input":"2025-10-19T05:20:56.389031Z","iopub.status.idle":"2025-10-19T05:20:56.399346Z","shell.execute_reply.started":"2025-10-19T05:20:56.389004Z","shell.execute_reply":"2025-10-19T05:20:56.396962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom sklearn.ensemble import IsolationForest\nfrom sklearn.preprocessing import StandardScaler, OneHotEncoder\n\ndef outlier_handling(df, contamination=0.01, random_state=42):\n    \"\"\"\n    شناسایی ایندکس داده‌های پرت با IsolationForest\n    با استفاده از One-Hot Encoding برای ویژگی‌های دسته‌ای\n\n    ورودی:\n        df : DataFrame ورودی\n        contamination : نسبت تقریبی داده‌های پرت (default=0.01)\n        random_state : عدد برای reproducibility (default=42)\n\n    خروجی:\n        outlier_index : ایندکس ردیف‌هایی که به عنوان پرت شناسایی شدند\n    \"\"\"\n    df_temp = df.copy()\n    \n    # حذف مقادیر NaN\n    df_temp = df_temp.dropna()\n    \n    # تشخیص نوع داده‌ها\n    continuous_fields = df_temp.select_dtypes(include=['int64', 'float64']).columns.tolist()\n    categorical_fields = df_temp.select_dtypes(include=['object', 'category']).columns.tolist()\n    \n    # --- Scaling عددی ---\n    if continuous_fields:\n        scaler = StandardScaler()\n        df_temp[continuous_fields] = scaler.fit_transform(df_temp[continuous_fields])\n    \n    # --- One-Hot Encoding دسته‌ای ---\n    if categorical_fields:\n        ohe = OneHotEncoder(drop='first', sparse_output=False, handle_unknown='ignore')\n        encoded = ohe.fit_transform(df_temp[categorical_fields])\n        encoded_df = pd.DataFrame(encoded, \n                                  index=df_temp.index, \n                                  columns=ohe.get_feature_names_out(categorical_fields))\n        \n        # حذف ستون‌های قدیمی و اضافه‌کردن انکد شده‌ها\n        df_temp = pd.concat([df_temp.drop(columns=categorical_fields), encoded_df], axis=1)\n    \n    # --- مدل IsolationForest ---\n    clf = IsolationForest(contamination=contamination, random_state=random_state)\n    clf.fit(df_temp)\n    \n    # --- شناسایی داده‌های پرت ---\n    outlier_index = df_temp.index[clf.predict(df_temp) == -1]\n    \n    return outlier_index\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:56.401058Z","iopub.execute_input":"2025-10-19T05:20:56.401420Z","iopub.status.idle":"2025-10-19T05:20:56.433268Z","shell.execute_reply.started":"2025-10-19T05:20:56.401383Z","shell.execute_reply":"2025-10-19T05:20:56.431713Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"outlier_index = outlier_handling(X_train, contamination=0.01)\n\nX_train = X_train.drop(outlier_index.tolist())\ny_train = y_train.drop(outlier_index.tolist())\n\nX_train.shape, y_train.shape\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:20:56.436995Z","iopub.execute_input":"2025-10-19T05:20:56.437731Z","iopub.status.idle":"2025-10-19T05:21:02.210124Z","shell.execute_reply.started":"2025-10-19T05:20:56.437699Z","shell.execute_reply":"2025-10-19T05:21:02.208421Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Identify Rows with Many Price Nulls\n","metadata":{}},{"cell_type":"code","source":"def rows_with_many_price_nulls(df, price_null_threshold=4):\n    \"\"\"\n    شناسایی ایندکس ردیف‌هایی که بیش از price_null_threshold مقدار null\n    در ستون‌های قیمتی دارند\n    \"\"\"\n    # بررسی اینکه df تعریف شده باشه\n    if df is None or df.empty:\n        print(\"Warning: DataFrame ورودی تعریف نشده یا خالی است.\")\n        return []\n\n    # ستون‌های قیمتی\n    price_columns = [\n        'MMRAcquisitionAuctionAveragePrice',\n        'MMRAcquisitionAuctionCleanPrice',\n        'MMRAcquisitionRetailAveragePrice',\n        'MMRAcquisitonRetailCleanPrice',\n        'MMRCurrentAuctionAveragePrice',\n        'MMRCurrentAuctionCleanPrice',\n        'MMRCurrentRetailAveragePrice',\n        'MMRCurrentRetailCleanPrice'\n    ]\n    \n    # بررسی اینکه ستون‌ها در df موجود باشند\n    available_price_columns = [col for col in price_columns if col in df.columns]\n    if not available_price_columns:\n        print(\"Warning: هیچ یک از ستون‌های قیمتی در DataFrame موجود نیست.\")\n        return []\n    \n    # شناسایی ردیف‌ها\n    rows_to_drop = df[available_price_columns].isnull().sum(axis=1) >= price_null_threshold\n    return df[rows_to_drop].index.tolist()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.211547Z","iopub.execute_input":"2025-10-19T05:21:02.211924Z","iopub.status.idle":"2025-10-19T05:21:02.220561Z","shell.execute_reply.started":"2025-10-19T05:21:02.211901Z","shell.execute_reply":"2025-10-19T05:21:02.219252Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# شناسایی ردیف‌هایی که بیش از ۴ مقدار null در ستون‌های قیمتی دارند\ndrop_rows_price = rows_with_many_price_nulls(X_train, price_null_threshold=4)\n\n# دراپ ردیف‌ها از X_train و y_train\nif drop_rows_price:\n    X_train = X_train.drop(drop_rows_price)\n    y_train = y_train.drop(drop_rows_price)\n\n# نمایش تعداد ردیف‌های باقی‌مانده\nX_train.shape, y_train.shape\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.221893Z","iopub.execute_input":"2025-10-19T05:21:02.222186Z","iopub.status.idle":"2025-10-19T05:21:02.275792Z","shell.execute_reply.started":"2025-10-19T05:21:02.222165Z","shell.execute_reply":"2025-10-19T05:21:02.274500Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Missing Column Report\n","metadata":{}},{"cell_type":"code","source":"def missing_col_report(df, misscol=50):\n    \"\"\"\n    شناسایی ستون‌هایی که بیش از درصد مشخصی مقدار null دارند\n\n    ورودی:\n        df : DataFrame ورودی\n        misscol : درصد بیشینه null برای حفظ ستون\n\n    خروجی:\n        discard_missing_col : لیست ستون‌هایی که درصد null بالاتر از threshold دارند\n    \"\"\"\n    processed_data = df.copy()\n    \n    # محاسبه تعداد و درصد null هر ستون\n    missing_values_report = pd.DataFrame({\n        'Column': processed_data.columns,\n        'Missing Values': processed_data.isnull().sum(),\n        'Percentage Missing': processed_data.isnull().mean() * 100\n    })\n    \n    # ستون‌هایی که باید دراپ شوند\n    discard_missing_col = missing_values_report[missing_values_report['Percentage Missing'] > misscol].index.tolist()\n    \n    return discard_missing_col\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.277220Z","iopub.execute_input":"2025-10-19T05:21:02.277664Z","iopub.status.idle":"2025-10-19T05:21:02.283851Z","shell.execute_reply.started":"2025-10-19T05:21:02.277627Z","shell.execute_reply":"2025-10-19T05:21:02.282805Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# شناسایی ستون‌های با >50% null\ndiscard_missing_col = missing_col_report(X_train, misscol=50)\n\n# دراپ ستون‌ها از train و test\nX_train = X_train.drop(discard_missing_col, axis=1)\nX_test = X_test.drop(discard_missing_col, axis=1)\n\n# نمایش shape بعد از حذف\nX_train.shape, X_test.shape\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.285135Z","iopub.execute_input":"2025-10-19T05:21:02.285645Z","iopub.status.idle":"2025-10-19T05:21:02.395785Z","shell.execute_reply.started":"2025-10-19T05:21:02.285608Z","shell.execute_reply":"2025-10-19T05:21:02.394358Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Missing Row Report\n","metadata":{}},{"cell_type":"code","source":"def missing_row_report(df, missrow=50):\n    \"\"\"\n    شناسایی ردیف‌هایی که بیش از درصد مشخصی مقدار null دارند\n\n    ورودی:\n        df : DataFrame ورودی\n        missrow : درصد بیشینه null برای حفظ ردیف (0-100)\n\n    خروجی:\n        discard_missing_row : لیست ایندکس ردیف‌هایی که درصد null بالاتر از threshold دارند\n    \"\"\"\n    processed_data = df.copy()\n    \n    # محاسبه درصد null در هر ردیف\n    row_null_percent = processed_data.isnull().mean(axis=1) * 100\n    \n    # ردیف‌هایی که باید دراپ شوند\n    discard_missing_row = row_null_percent[row_null_percent > missrow].index.tolist()\n    \n    return discard_missing_row\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.397746Z","iopub.execute_input":"2025-10-19T05:21:02.398040Z","iopub.status.idle":"2025-10-19T05:21:02.404926Z","shell.execute_reply.started":"2025-10-19T05:21:02.398020Z","shell.execute_reply":"2025-10-19T05:21:02.403228Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# شناسایی ردیف‌هایی با >50% null\ndiscard_missing_row = missing_row_report(X_train, missrow=50)\n\n# دراپ ردیف‌ها از train و target\nif discard_missing_row:\n    X_train = X_train.drop(discard_missing_row)\n    y_train = y_train.drop(discard_missing_row)\n\nX_train.shape, y_train.shape\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.406050Z","iopub.execute_input":"2025-10-19T05:21:02.406329Z","iopub.status.idle":"2025-10-19T05:21:02.482020Z","shell.execute_reply.started":"2025-10-19T05:21:02.406308Z","shell.execute_reply":"2025-10-19T05:21:02.481125Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Impute Missing Values\n","metadata":{}},{"cell_type":"code","source":"def impute_remaining_missing(df):\n    \"\"\"\n    جایگزینی مقادیر null در DataFrame\n    - ستون‌های عددی -> میانه\n    - ستون‌های دسته‌ای -> مد\n    \"\"\"\n    # شناسایی ستون‌های عددی و دسته‌ای\n    numeric_cols = df.select_dtypes(include=['int64', 'float64']).columns\n    categorical_cols = df.select_dtypes(include=['object', 'category']).columns\n\n    # جایگزینی مقادیر null در ستون‌های عددی با میانه\n    df[numeric_cols] = df[numeric_cols].fillna(df[numeric_cols].median())\n\n    # جایگزینی مقادیر null در ستون‌های دسته‌ای با مد\n    for col in categorical_cols:\n        df[col] = df[col].fillna(df[col].mode()[0])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.483015Z","iopub.execute_input":"2025-10-19T05:21:02.483619Z","iopub.status.idle":"2025-10-19T05:21:02.489700Z","shell.execute_reply.started":"2025-10-19T05:21:02.483589Z","shell.execute_reply":"2025-10-19T05:21:02.488685Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# اعمال Impute روی X_train\nimpute_remaining_missing(X_train)\n\n# اعمال Impute روی X_test\nimpute_remaining_missing(X_test)\n\nX_train.shape, y_train.shape\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.490906Z","iopub.execute_input":"2025-10-19T05:21:02.491229Z","iopub.status.idle":"2025-10-19T05:21:02.654663Z","shell.execute_reply.started":"2025-10-19T05:21:02.491208Z","shell.execute_reply":"2025-10-19T05:21:02.653677Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Power Transformation (Box-Cox / Yeo-Johnson)\n","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import PowerTransformer\n\ndef power_transform_features(train, test, features):\n    \"\"\"\n    اعمال Power Transformation روی فیچرهای عددی انتخاب‌شده.\n    - برای داده‌های مثبت: Box-Cox\n    - برای داده‌های دارای صفر یا منفی: Yeo-Johnson\n    \n    ورودی‌ها:\n        train, test : DataFrame های آموزشی و آزمایشی\n        features : لیست ستون‌هایی که باید تبدیل شوند\n        \n    خروجی:\n        train, test : داده‌های تبدیل‌شده\n    \"\"\"\n    for feature in features:\n        if feature not in train.columns:\n            print(f\"⚠️ ستون '{feature}' در داده‌ی train یافت نشد، رد شد.\")\n            continue\n\n        # بررسی مقدارهای منفی یا صفر\n        has_non_positive = (train[feature] <= 0).any() or (test[feature] <= 0).any()\n\n        # انتخاب روش مناسب\n        method = 'yeo-johnson' if has_non_positive else 'box-cox'\n        transformer = PowerTransformer(method=method, standardize=False)\n\n        # اعمال ترنسفورم روی train و test\n        train[feature] = transformer.fit_transform(train[[feature]])\n        test[feature] = transformer.transform(test[[feature]])\n\n    return train, test\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.655678Z","iopub.execute_input":"2025-10-19T05:21:02.655912Z","iopub.status.idle":"2025-10-19T05:21:02.663753Z","shell.execute_reply.started":"2025-10-19T05:21:02.655892Z","shell.execute_reply":"2025-10-19T05:21:02.662457Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform_features = ['VehBCost', 'WarrantyCost']\n\nX_train, X_test = power_transform_features(X_train, X_test, transform_features)\n\nprint(\"Transformed shape:\", X_train.shape, X_test.shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:02.664865Z","iopub.execute_input":"2025-10-19T05:21:02.665205Z","iopub.status.idle":"2025-10-19T05:21:04.427375Z","shell.execute_reply.started":"2025-10-19T05:21:02.665176Z","shell.execute_reply":"2025-10-19T05:21:04.426315Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Identify Feature Types\n","metadata":{}},{"cell_type":"code","source":"continuous = X_train.select_dtypes(exclude=['object','category']).columns.tolist()\ncategorical = X_train.select_dtypes(include=['object','category']).columns.tolist()\n\nnominal = [\"Auction\", \"Make\", \"Color\", \"Transmission\", \"WheelType\",\n           \"Nationality\", \"Size\", \"TopThreeAmericanName\"]\n\nordinal = [i for i in categorical if i not in nominal]\n\n\nlen(continuous), len(categorical), len(ordinal), len(nominal)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:04.428271Z","iopub.execute_input":"2025-10-19T05:21:04.428623Z","iopub.status.idle":"2025-10-19T05:21:04.451459Z","shell.execute_reply.started":"2025-10-19T05:21:04.428600Z","shell.execute_reply":"2025-10-19T05:21:04.450021Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Build Full Preprocessing Pipeline\n","metadata":{}},{"cell_type":"code","source":"from sklearn.pipeline import Pipeline\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, StandardScaler\nfrom sklearn.feature_selection import RFECV\nfrom sklearn.discriminant_analysis import LinearDiscriminantAnalysis\nfrom sklearn.tree import DecisionTreeClassifier\n\n# ---- Preprocessing objects ----\none_hot_encoder = OneHotEncoder(drop='first', handle_unknown='ignore', sparse_output=False)\nordinal_encoder = OrdinalEncoder(handle_unknown='use_encoded_value', unknown_value=-1)\nscaler = StandardScaler()\n\n# ---- Feature selection ----\nwrapper = RFECV(\n    estimator=DecisionTreeClassifier(random_state=17),\n    step=1,\n    min_features_to_select=10,\n    cv=5,\n    n_jobs=-1\n)\n\n# ---- Pipelines ----\nnumerical_preprocessing_lda = Pipeline(steps=[\n    ('scaler', scaler),\n    ('lda', LinearDiscriminantAnalysis(n_components=1))\n])\n\nnominal_preprocessing = Pipeline(steps=[\n    ('onehot', one_hot_encoder)\n])\n\nordinal_preprocessing = Pipeline(steps=[\n    ('ordinal', ordinal_encoder)\n])\n\n# ---- Combine everything ----\npreprocessor_lda = ColumnTransformer(transformers=[\n    ('num', numerical_preprocessing_lda, continuous),\n    ('nom', nominal_preprocessing, nominal),\n    ('ord', ordinal_preprocessing, ordinal)\n], remainder='passthrough')\n\nprint(\"✅ Base preprocessor ready for all models.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:21:04.452890Z","iopub.execute_input":"2025-10-19T05:21:04.453184Z","iopub.status.idle":"2025-10-19T05:21:04.462179Z","shell.execute_reply.started":"2025-10-19T05:21:04.453151Z","shell.execute_reply":"2025-10-19T05:21:04.461324Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import pandas as pd\n# from sklearn.pipeline import Pipeline\n# from sklearn.compose import ColumnTransformer\n# from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, StandardScaler\n# from sklearn.feature_selection import RFECV\n# from sklearn.discriminant_analysis import LinearDiscriminantAnalysis\n# from sklearn.tree import DecisionTreeClassifier\n# import joblib\n\n# # ---- Preprocessing objects ----\n# one_hot_encoder = OneHotEncoder(drop='first', handle_unknown='ignore', sparse_output=False)\n# ordinal_encoder = OrdinalEncoder(handle_unknown='use_encoded_value', unknown_value=-1)\n# scaler = StandardScaler()\n\n# # ---- Feature selection wrapper ----\n# wrapper = RFECV(\n#     estimator=DecisionTreeClassifier(random_state=17),\n#     step=1,\n#     min_features_to_select=10,\n#     cv=5,\n#     n_jobs=-1\n# )\n\n# # ---- Pipelines for each type of feature ----\n# numerical_preprocessing_lda = Pipeline(steps=[\n#     ('scaler', scaler),\n#     ('lda', LinearDiscriminantAnalysis(n_components=1))\n# ])\n\n# nominal_preprocessing = Pipeline(steps=[\n#     ('onehot', one_hot_encoder)\n# ])\n\n# ordinal_preprocessing = Pipeline(steps=[\n#     ('ordinal', ordinal_encoder)\n# ])\n\n# # ---- Combine everything ----\n# preprocessor_lda = ColumnTransformer(transformers=[\n#     ('num', numerical_preprocessing_lda, continuous),\n#     ('nom', nominal_preprocessing, nominal),\n#     ('ord', ordinal_preprocessing, ordinal)\n# ], remainder='passthrough')\n\n# # ---- Final pipeline: preprocessing + feature selection ----\n# final_pipeline = Pipeline(steps=[\n#     ('preprocessor', preprocessor_lda),\n#     ('feature_selection', wrapper)\n# ])\n\n# # ---- Fit on training data ----\n# X_train_ready = final_pipeline.fit_transform(X_train, y_train)\n# X_test_ready = final_pipeline.transform(X_test)\n\n# # ---- Feature names construction ----\n# lda_name = ['LDA_component_1']\n# onehot_names = preprocessor_lda.named_transformers_['nom'].named_steps['onehot'] \\\n#     .get_feature_names_out(nominal).tolist()\n# ordinal_names = ordinal\n# all_feature_names = lda_name + onehot_names + ordinal_names\n\n# # ---- Keep only features selected by RFECV ----\n# mask = final_pipeline.named_steps['feature_selection'].support_\n# selected_feature_names = [name for name, keep in zip(all_feature_names, mask) if keep]\n\n# # ---- Create final DataFrames with target ----\n# X_train_ready_df = pd.DataFrame(X_train_ready, columns=selected_feature_names)\n# X_train_ready_df['target'] = y_train.values\n\n# X_test_ready_df = pd.DataFrame(X_test_ready, columns=selected_feature_names)\n# X_test_ready_df['target'] = y_test.values  # بعدا می‌تونی جدا کنی\n\n# # ---- Export to CSV ----\n# X_train_ready_df.to_csv(\"train_ready.csv\", index=False)\n# X_test_ready_df.to_csv(\"test_ready.csv\", index=False)\n\n# # ---- Save pipeline for later use ----\n# joblib.dump(final_pipeline, \"final_pipeline.pkl\")\n\n# print(\"✅ DataFrames ready and exported. Pipeline saved.\")\n# print(\"Train shape:\", X_train_ready_df.shape)\n# print(\"Test shape:\", X_test_ready_df.shape)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train & Evaluate Logistic Regression\n","metadata":{}},{"cell_type":"code","source":"# from sklearn.linear_model import LogisticRegression\n# from sklearn.metrics import accuracy_score, f1_score\n\n# pipeline_logreg = Pipeline(steps=[\n#     ('preprocessor', preprocessor_lda),\n#     ('feature_selection', wrapper),\n#     ('model', LogisticRegression(max_iter=500, random_state=17))\n# ])\n\n# pipeline_logreg.fit(X_train, y_train)\n# y_pred = pipeline_logreg.predict(X_test)\n\n# print(\"Model: Logistic Regression\")\n# print(\"Accuracy:\", accuracy_score(y_test, y_pred))\n# print(\"F1 Score:\", f1_score(y_test, y_pred))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:22:49.413029Z","iopub.execute_input":"2025-10-19T05:22:49.413478Z","iopub.status.idle":"2025-10-19T05:22:49.418612Z","shell.execute_reply.started":"2025-10-19T05:22:49.413445Z","shell.execute_reply":"2025-10-19T05:22:49.417237Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train & Evaluate Random Forest\n","metadata":{}},{"cell_type":"code","source":"# from sklearn.ensemble import RandomForestClassifier\n\n# pipeline_rf = Pipeline(steps=[\n#     ('preprocessor', preprocessor_lda),\n#     ('feature_selection', wrapper),\n#     ('model', RandomForestClassifier(n_estimators=200, random_state=17))\n# ])\n\n# pipeline_rf.fit(X_train, y_train)\n# y_pred = pipeline_rf.predict(X_test)\n\n# print(\"Model: Random Forest\")\n# print(\"Accuracy:\", accuracy_score(y_test, y_pred))\n# print(\"F1 Score:\", f1_score(y_test, y_pred))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:22:49.419731Z","iopub.execute_input":"2025-10-19T05:22:49.419968Z","iopub.status.idle":"2025-10-19T05:22:49.447917Z","shell.execute_reply.started":"2025-10-19T05:22:49.419950Z","shell.execute_reply":"2025-10-19T05:22:49.446644Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train & Evaluate SVM (RBF Kernel)\n","metadata":{}},{"cell_type":"code","source":"# from sklearn.svm import SVC\n\n# pipeline_svm = Pipeline(steps=[\n#     ('preprocessor', preprocessor_lda),\n#     ('feature_selection', wrapper),\n#     ('model', SVC(kernel='rbf', C=1, random_state=17))\n# ])\n\n# pipeline_svm.fit(X_train, y_train)\n# y_pred = pipeline_svm.predict(X_test)\n\n# print(\"Model: SVM (RBF)\")\n# print(\"Accuracy:\", accuracy_score(y_test, y_pred))\n# print(\"F1 Score:\", f1_score(y_test, y_pred))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:22:49.448903Z","iopub.execute_input":"2025-10-19T05:22:49.449139Z","iopub.status.idle":"2025-10-19T05:22:49.473175Z","shell.execute_reply.started":"2025-10-19T05:22:49.449121Z","shell.execute_reply":"2025-10-19T05:22:49.472050Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train & Evaluate KNN (k=7)\n","metadata":{}},{"cell_type":"code","source":"# from sklearn.neighbors import KNeighborsClassifier\n\n# pipeline_knn = Pipeline(steps=[\n#     ('preprocessor', preprocessor_lda),\n#     ('feature_selection', wrapper),\n#     ('model', KNeighborsClassifier(n_neighbors=7))\n# ])\n\n# pipeline_knn.fit(X_train, y_train)\n# y_pred = pipeline_knn.predict(X_test)\n\n# print(\"Model: KNN (k=7)\")\n# print(\"Accuracy:\", accuracy_score(y_test, y_pred))\n# print(\"F1 Score:\", f1_score(y_test, y_pred))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-19T05:22:49.473960Z","iopub.execute_input":"2025-10-19T05:22:49.474220Z","iopub.status.idle":"2025-10-19T05:22:49.511030Z","shell.execute_reply.started":"2025-10-19T05:22:49.474199Z","shell.execute_reply":"2025-10-19T05:22:49.509885Z"}},"outputs":[],"execution_count":null}]}