{"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":35332,"databundleVersionId":3723648,"sourceType":"competition"},{"sourceId":3696790,"sourceType":"datasetVersion","datasetId":2211601},{"sourceId":14154237,"sourceType":"datasetVersion","datasetId":9021314}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport gc\nimport random\nimport numpy as np\nimport pandas as pd\nimport lightgbm as lgb\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import StratifiedKFold, cross_validate\nfrom sklearn.base import BaseEstimator, TransformerMixin\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.metrics import make_scorer\nfrom tqdm.auto import tqdm\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-20T16:50:31.492047Z","iopub.execute_input":"2025-12-20T16:50:31.493039Z","iopub.status.idle":"2025-12-20T16:50:38.927238Z","shell.execute_reply.started":"2025-12-20T16:50:31.493006Z","shell.execute_reply":"2025-12-20T16:50:38.926369Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class FeatureEngineeringTransformer(BaseEstimator, TransformerMixin):    \n    def __init__(self, num_features, cat_features):\n        self.num_features = num_features\n        self.cat_features = cat_features\n        self.num_agg_funcs = ['mean', 'std', 'min', 'max', 'last']\n        self.cat_agg_funcs = ['count', 'last', 'nunique']\n        \n    def fit(self, X, y=None):\n        return self\n    \n    def transform(self, X):\n        if 'customer_ID' not in X.columns:\n            raise ValueError(\"Data must contain 'customer_ID' column\")\n        \n        # Num feat engineer\n        num_agg = X.groupby(\"customer_ID\")[self.num_features].agg(self.num_agg_funcs)\n        num_agg.columns = ['_'.join(x) for x in num_agg.columns]\n        num_agg = num_agg.reset_index()\n        \n        # Cat feat engineer\n        cat_agg = X.groupby(\"customer_ID\")[self.cat_features].agg(self.cat_agg_funcs)\n        cat_agg.columns = ['_'.join(x) for x in cat_agg.columns]\n        cat_agg = cat_agg.reset_index()\n        \n        result = num_agg.merge(cat_agg, how='inner', on='customer_ID')\n        return result\n\ndef create_feature_pipeline():\n    train = pd.read_parquet('train_data.parquet')\n    features = train.drop(['customer_ID', 'S_2', 'target'], axis=1).columns.tolist()\n    cat_features = [\"B_30\", \"B_38\", \"D_114\", \"D_116\", \"D_117\", \"D_120\", \n                    \"D_126\", \"D_63\", \"D_64\", \"D_66\", \"D_68\"]\n    num_features = [col for col in features if col not in cat_features]\n    \n    feature_pipeline = Pipeline([\n        ('feature_engineering', FeatureEngineeringTransformer(num_features, cat_features))\n    ])\n    return feature_pipeline, num_features, cat_features","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T16:50:38.928877Z","iopub.execute_input":"2025-12-20T16:50:38.929473Z","iopub.status.idle":"2025-12-20T16:50:38.940555Z","shell.execute_reply.started":"2025-12-20T16:50:38.929432Z","shell.execute_reply":"2025-12-20T16:50:38.939425Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_data_with_pipeline():    \n    feature_pipeline, num_features, cat_features = create_feature_pipeline()\n    \n    train_raw = pd.read_parquet('train_data.parquet')\n    train_transformed = feature_pipeline.fit_transform(train_raw)\n    \n    train_labels = pd.read_csv('train_labels.csv')\n    train_final = train_transformed.merge(train_labels, how='inner', on='customer_ID')\n    \n    test_raw = pd.read_parquet('test_data.parquet')\n    test_final = feature_pipeline.transform(test_raw)\n    \n    train_final.to_parquet('train_fe.parquet', index=False)\n    test_final.to_parquet('test_fe.parquet', index=False)\n    return train_final, test_final, cat_features\n\ntrain, test, cat_features_original = preprocess_data_with_pipeline()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cat_features_transformed = [f\"{cf}_last\" for cf in cat_features_original]\ntrain[cat_features_transformed] = train[cat_features_transformed].fillna(\"missing\").astype(str)\ntest[cat_features_transformed] = test[cat_features_transformed].apply(\n    lambda x: x.cat.add_categories(\"missing\").fillna(\"missing\") \n    if x.dtype.name == 'category' \n    else x.fillna(\"missing\")\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T16:50:38.941557Z","iopub.execute_input":"2025-12-20T16:50:38.941883Z","iopub.status.idle":"2025-12-20T16:50:38.968404Z","shell.execute_reply.started":"2025-12-20T16:50:38.941852Z","shell.execute_reply":"2025-12-20T16:50:38.967449Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for cat_col in cat_features_transformed:\n    encoder = LabelEncoder()\n    encoder.fit(train[cat_col].tolist() + test[cat_col].tolist())\n    train[cat_col] = encoder.transform(train[cat_col])\n    test[cat_col] = encoder.transform(test[cat_col])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T16:50:38.970727Z","iopub.execute_input":"2025-12-20T16:50:38.971007Z","iopub.status.idle":"2025-12-20T16:50:54.614823Z","shell.execute_reply.started":"2025-12-20T16:50:38.970986Z","shell.execute_reply":"2025-12-20T16:50:54.613895Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# COMPETITION METRIC FROM Konstantin Yakovlev\n# https://www.kaggle.com/kyakovlev\n# https://www.kaggle.com/competitions/amex-default-prediction/discussion/327534\ndef amex_metric_mod(y_true, y_pred):\n\n    labels     = np.transpose(np.array([y_true, y_pred]))\n    labels     = labels[labels[:, 1].argsort()[::-1]]\n    weights    = np.where(labels[:,0]==0, 20, 1)\n    cut_vals   = labels[np.cumsum(weights) <= int(0.04 * np.sum(weights))]\n    top_four   = np.sum(cut_vals[:,0]) / np.sum(labels[:,0])\n\n    gini = [0,0]\n    for i in [1,0]:\n        labels         = np.transpose(np.array([y_true, y_pred]))\n        labels         = labels[labels[:, i].argsort()[::-1]]\n        weight         = np.where(labels[:,0]==0, 20, 1)\n        weight_random  = np.cumsum(weight / np.sum(weight))\n        total_pos      = np.sum(labels[:, 0] *  weight)\n        cum_pos_found  = np.cumsum(labels[:, 0] * weight)\n        lorentz        = cum_pos_found / total_pos\n        gini[i]        = np.sum((lorentz - weight_random) * weight)\n\n    return 0.5 * (gini[1]/gini[0] + top_four)\n\namex_scorer = make_scorer(amex_metric_mod, greater_is_better=True, needs_proba=True)\n\ndef seed_everything(seed):\n    random.seed(seed)\n    np.random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n\nseed_everything(42)\nn_folds = 5\nTARGET = 'target'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T16:50:54.615626Z","iopub.execute_input":"2025-12-20T16:50:54.615877Z","iopub.status.idle":"2025-12-20T16:51:15.849480Z","shell.execute_reply.started":"2025-12-20T16:50:54.615857Z","shell.execute_reply":"2025-12-20T16:51:15.848398Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"features = [col for col in train.columns if col not in ['customer_ID', TARGET]]\nX = train[features]\ny = train[TARGET]\n\nparams = {\n    'objective': 'binary',\n    'metric': \"binary_logloss\",\n    'boosting': 'dart',\n    'seed': 42,\n    'num_leaves': 100,\n    'learning_rate': 0.01,\n    'feature_fraction': 0.20,\n    'bagging_freq': 10,\n    'bagging_fraction': 0.50,\n    'n_jobs': 32,\n    'lambda_l2': 2,\n    'min_data_in_leaf': 40\n    }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T16:51:15.850628Z","iopub.execute_input":"2025-12-20T16:51:15.850882Z","iopub.status.idle":"2025-12-20T16:51:40.355795Z","shell.execute_reply.started":"2025-12-20T16:51:15.850861Z","shell.execute_reply":"2025-12-20T16:51:40.354692Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"lgb_model = lgb.LGBMClassifier(**params)\ncv = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=42)\n\nprint(\"=\"*50)\nprint(\"Running cross_validate with LightGBM\")\nprint(\"=\"*50)\n\ncv_results = cross_validate(\n    estimator=lgb_model,\n    X=X,\n    y=y,\n    cv=cv,\n    scoring={'amex': amex_scorer},\n    return_train_score=True,\n    return_estimator=True,\n    n_jobs=1,\n)\n\nprint(f\"Metrics for each folds: {cv_results['test_amex']}, OOF metric: {cv_results['test_amex'].mean():.6f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T16:51:40.356885Z","iopub.execute_input":"2025-12-20T16:51:40.357206Z","iopub.status.idle":"2025-12-20T16:51:40.365348Z","shell.execute_reply.started":"2025-12-20T16:51:40.357177Z","shell.execute_reply":"2025-12-20T16:51:40.364438Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_predictions_matrix = np.zeros((len(test), n_folds))\n\nfor fold_idx, model in enumerate(cv_results['estimator']):\n    print(f\"Предсказываем  для {fold_idx} фолдв\")\n    test_pred = model.predict_proba(test[features])[:, 1]\n    test_predictions_matrix[:, fold_idx] = test_pred\n\ntest_predictions_avg = test_predictions_matrix.mean(axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T16:51:40.366312Z","iopub.execute_input":"2025-12-20T16:51:40.366670Z","iopub.status.idle":"2025-12-20T17:02:47.286584Z","shell.execute_reply.started":"2025-12-20T16:51:40.366640Z","shell.execute_reply":"2025-12-20T17:02:47.285374Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df = pd.DataFrame({'customer_ID': test['customer_ID'], 'prediction': test_predictions_avg})\ntest_df.to_csv('submission.csv', index = False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T17:03:11.188013Z","iopub.execute_input":"2025-12-20T17:03:11.188795Z","iopub.status.idle":"2025-12-20T17:03:15.092426Z","shell.execute_reply.started":"2025-12-20T17:03:11.188764Z","shell.execute_reply":"2025-12-20T17:03:15.091202Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}