{"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":115660,"databundleVersionId":13805039,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:29:01.466772Z","iopub.execute_input":"2025-09-26T14:29:01.466957Z","iopub.status.idle":"2025-09-26T14:29:03.509017Z","shell.execute_reply.started":"2025-09-26T14:29:01.466940Z","shell.execute_reply":"2025-09-26T14:29:03.507982Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 1: Import thư viện\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom scipy.stats import pointbiserialr\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\npd.set_option(\"display.max_columns\", 100)\nsns.set_style(\"whitegrid\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:29:03.511239Z","iopub.execute_input":"2025-09-26T14:29:03.511610Z","iopub.status.idle":"2025-09-26T14:29:05.111857Z","shell.execute_reply.started":"2025-09-26T14:29:03.511584Z","shell.execute_reply":"2025-09-26T14:29:05.110953Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 2: Load dữ liệu\nTRAIN_PATH = \"/kaggle/input/detect-fraudulent-credit-card-transactions-btl/train.csv\"\nTEST_PATH = \"/kaggle/input/detect-fraudulent-credit-card-transactions-btl/test.csv\"\n\ntrain = pd.read_csv(TRAIN_PATH)\ntest = pd.read_csv(TEST_PATH)\n\nprint(\"Train shape:\", train.shape)\nprint(\"Test shape:\", test.shape)\ntrain.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:29:05.112818Z","iopub.execute_input":"2025-09-26T14:29:05.113201Z","iopub.status.idle":"2025-09-26T14:29:09.323712Z","shell.execute_reply.started":"2025-09-26T14:29:05.113178Z","shell.execute_reply":"2025-09-26T14:29:09.322837Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 3: Kiểm tra Class\nprint(train.info())\nprint(train[\"Class\"].value_counts())\nprint(train[\"Class\"].value_counts(normalize=True))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:29:09.324650Z","iopub.execute_input":"2025-09-26T14:29:09.324946Z","iopub.status.idle":"2025-09-26T14:29:09.387399Z","shell.execute_reply.started":"2025-09-26T14:29:09.324922Z","shell.execute_reply":"2025-09-26T14:29:09.386553Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 4: Thống kê Train,Duplicate,Missing\nprint(train.describe().T)\n\nprint(\"Duplicate rows:\", train.duplicated().sum())\nprint(\"Missing values:\\n\", train.isna().sum().sort_values(ascending=False).head(10))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:29:09.388362Z","iopub.execute_input":"2025-09-26T14:29:09.388631Z","iopub.status.idle":"2025-09-26T14:29:10.283415Z","shell.execute_reply.started":"2025-09-26T14:29:09.388610Z","shell.execute_reply":"2025-09-26T14:29:10.282608Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Dữ liệu sạch","metadata":{}},{"cell_type":"code","source":"# Cell 5: Phân phối Amount & Time\nfig, ax = plt.subplots(1,2, figsize=(14,5))\nsns.histplot(train[\"Amount\"], bins=100, ax=ax[0], kde=False)\nax[0].set_title(\"Distribution of Amount\")\nsns.histplot(np.log1p(train[\"Amount\"]), bins=100, ax=ax[1], kde=False)\nax[1].set_title(\"Distribution of log(Amount+1)\")\nplt.show()\n\nplt.figure(figsize=(12,5))\nsns.histplot(train[\"Time\"], bins=100, kde=False)\nplt.title(\"Distribution of Time\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:29:10.284363Z","iopub.execute_input":"2025-09-26T14:29:10.284674Z","iopub.status.idle":"2025-09-26T14:29:11.971752Z","shell.execute_reply.started":"2025-09-26T14:29:10.284647Z","shell.execute_reply":"2025-09-26T14:29:11.970725Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Phần lớn là giao dịch nhỏ\n- Dữ liệu thu thập trong 2 ngày, phân định ngày đêm khá rõ ràng","metadata":{}},{"cell_type":"code","source":"# Cell 6: Class-conditional plots\nfig, ax = plt.subplots(1,2, figsize=(14,5))\nsns.kdeplot(data=train, x=\"Amount\", hue=\"Class\", ax=ax[0], common_norm=False)\nax[0].set_title(\"Amount by Class\")\nsns.kdeplot(data=train, x=\"Time\", hue=\"Class\", ax=ax[1], common_norm=False)\nax[1].set_title(\"Time by Class\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:29:11.974023Z","iopub.execute_input":"2025-09-26T14:29:11.974314Z","iopub.status.idle":"2025-09-26T14:29:14.513885Z","shell.execute_reply.started":"2025-09-26T14:29:11.974291Z","shell.execute_reply":"2025-09-26T14:29:14.512933Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Class 1 (Fraud) và 0 (Non Fraud) tập trung ở giao dịch dưới 500 (Giao dịch nhỏ)\n- Fraud phân bổ đều theo thời gian, không theo giờ cao điểm\n- Non Fraud tập trung vào ban ngày","metadata":{}},{"cell_type":"code","source":"# Cell 7: Tương quan\nfeature_cols = [c for c in train.columns if c not in [\"id\", \"Class\"]]\ncorrs = []\nfor c in feature_cols:\n    r, p = pointbiserialr(train[c], train[\"Class\"])\n    corrs.append((c, r))\ncorr_df = pd.DataFrame(corrs, columns=[\"feature\",\"corr\"]).sort_values(\"corr\", ascending=False)\nprint(corr_df.head(10))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:29:14.514908Z","iopub.execute_input":"2025-09-26T14:29:14.515278Z","iopub.status.idle":"2025-09-26T14:29:14.672139Z","shell.execute_reply.started":"2025-09-26T14:29:14.515248Z","shell.execute_reply":"2025-09-26T14:29:14.671244Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- V11, V4, V2 chắc chắn mang tín hiệu.","metadata":{}},{"cell_type":"code","source":"# Cell 8: Time of day pattern\ntrain[\"hour\"] = (train[\"Time\"] % (24*3600)) // 3600\nplt.figure(figsize=(12,5))\nsns.countplot(data=train, x=\"hour\", hue=\"Class\")\nplt.title(\"Transaction counts by hour of day\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:29:14.673088Z","iopub.execute_input":"2025-09-26T14:29:14.673357Z","iopub.status.idle":"2025-09-26T14:29:15.179986Z","shell.execute_reply.started":"2025-09-26T14:29:14.673338Z","shell.execute_reply":"2025-09-26T14:29:15.179122Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Tương tự như trên, Fraud k kể ngày hay đêm","metadata":{}},{"cell_type":"markdown","source":"**Tiền xử lý**","metadata":{}},{"cell_type":"code","source":"# Cell 9: Feature Engineering - Amount log\ntrain[\"Amount_log\"] = np.log1p(train[\"Amount\"])\ntest[\"Amount_log\"]  = np.log1p(test[\"Amount\"])\n\n# Time được tính bằng giây từ giao dịch đầu tiên\n# => chuyển thành \"giờ trong ngày\" để xem pattern\ntrain[\"hour\"] = (train[\"Time\"] % (24*3600)) // 3600\ntest[\"hour\"]  = (test[\"Time\"] % (24*3600)) // 3600\n\n# cyclical encoding\ntrain[\"hour_sin\"] = np.sin(2*np.pi*train[\"hour\"]/24)\ntrain[\"hour_cos\"] = np.cos(2*np.pi*train[\"hour\"]/24)\ntest[\"hour_sin\"]  = np.sin(2*np.pi*test[\"hour\"]/24)\ntest[\"hour_cos\"]  = np.cos(2*np.pi*test[\"hour\"]/24)\n\n# flag ban đêm\ntrain[\"is_night\"] = train[\"hour\"].between(0,6).astype(int)\ntest[\"is_night\"]  = test[\"hour\"].between(0,6).astype(int)\n\n# Z-score Amount theo giờ\nstats = train.groupby(\"hour\")[\"Amount_log\"].agg([\"mean\",\"std\"]).reset_index()\nstats[\"std\"].replace(0, 1e-6, inplace=True)\n\ndef apply_zscore(df, stats):\n    df = df.merge(stats, on=\"hour\", how=\"left\")\n    return (df[\"Amount_log\"] - df[\"mean\"]) / df[\"std\"]\n\ntrain[\"Amount_log_z_h\"] = apply_zscore(train, stats)\ntest[\"Amount_log_z_h\"]  = apply_zscore(test, stats)\n\n# Mahalanobis distance tới class 0\nfrom sklearn.covariance import LedoitWolf\nfrom numpy.linalg import inv\n\nv_cols = [c for c in train.columns if c.startswith(\"V\")]\nmd_feats = v_cols + [\"Amount_log\"]\n\nX0 = train.loc[train[\"Class\"]==0, md_feats].values\nmu0 = X0.mean(axis=0)\ncov0 = LedoitWolf().fit(X0).covariance_\ninv_cov0 = inv(cov0)\n\ndef mahalanobis_rows(X, mu, inv_cov):\n    D = X - mu\n    return np.sqrt(np.einsum(\"ij,jk,ik->i\", D, inv_cov, D))\n\ntrain[\"md_neg\"] = mahalanobis_rows(train[md_feats].values, mu0, inv_cov0)\ntest[\"md_neg\"]  = mahalanobis_rows(test[md_feats].values,  mu0, inv_cov0)\n\n# Kiểm tra lại dữ liệu\nprint(\"Train shape:\", train.shape)\nprint(\"Test shape:\", test.shape)\nprint(\"New features added: Amount_log, hour, hour_sin, hour_cos, is_night, Amount_log_z_h, md_neg\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:30:08.463660Z","iopub.execute_input":"2025-09-26T14:30:08.463971Z","iopub.status.idle":"2025-09-26T14:30:09.619844Z","shell.execute_reply.started":"2025-09-26T14:30:08.463945Z","shell.execute_reply":"2025-09-26T14:30:09.618858Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 10: Chuẩn bị dữ liệu cho model\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.metrics import roc_auc_score, average_precision_score\n\n# Xác định feature và target\ndrop_cols = [\"id\", \"Time\", \"Amount\", \"Class\"]\nfeature_cols = [c for c in train.columns if c not in drop_cols]\n\nX = train[feature_cols]\ny = train[\"Class\"]\nX_test = test[feature_cols]\n\nprint(\"Feature count:\", len(feature_cols))\n\n# Cross-validation (5-fold)\ncv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\n\nroc_scores, pr_scores = [], []\ntest_pred = np.zeros(len(X_test))\n\nfor fold, (tr_idx, val_idx) in enumerate(cv.split(X, y), 1):\n    X_tr, X_val = X.iloc[tr_idx], X.iloc[val_idx]\n    y_tr, y_val = y.iloc[tr_idx], y.iloc[val_idx]\n\n    scaler = StandardScaler()\n    X_tr_s = scaler.fit_transform(X_tr)\n    X_val_s = scaler.transform(X_val)\n    X_test_s = scaler.transform(X_test)\n\n    clf = LogisticRegression(\n        max_iter=2000,\n        solver=\"lbfgs\",\n        class_weight=\"balanced\",\n        C=1.0\n    )\n    clf.fit(X_tr_s, y_tr)\n\n    # predict validation\n    y_pred_val = clf.predict_proba(X_val_s)[:, 1]\n    roc = roc_auc_score(y_val, y_pred_val)\n    pr = average_precision_score(y_val, y_pred_val)\n\n    roc_scores.append(roc)\n    pr_scores.append(pr)\n\n    print(f\"Fold {fold}: ROC AUC={roc:.4f}, PR AUC={pr:.4f}\")\n\n    # predict test (trung bình qua folds)\n    test_pred += clf.predict_proba(X_test_s)[:, 1] / cv.n_splits\n\nprint(\"\\nMean ROC AUC:\", np.mean(roc_scores))\nprint(\"Mean PR AUC:\", np.mean(pr_scores))\n\n# Submission file\ny_pred_label = (test_pred >= 0.5).astype(int)\nsub = pd.DataFrame({\"id\": test[\"id\"], \"class\": y_pred_label})\nsub.to_csv(\"submission.csv\", index=False)\nprint(\"Submission preview:\")\nprint(sub.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-26T14:34:39.361411Z","iopub.execute_input":"2025-09-26T14:34:39.361707Z","iopub.status.idle":"2025-09-26T14:34:54.803304Z","shell.execute_reply.started":"2025-09-26T14:34:39.361688Z","shell.execute_reply":"2025-09-26T14:34:54.802530Z"}},"outputs":[],"execution_count":null}]}