{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":7163,"databundleVersionId":44582}],"dockerImageVersionId":31328,"isInternetEnabled":false,"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":"2026-04-21T04:55:44.24008Z","iopub.execute_input":"2026-04-21T04:55:44.240436Z","iopub.status.idle":"2026-04-21T04:55:44.253247Z","shell.execute_reply.started":"2026-04-21T04:55:44.240406Z","shell.execute_reply":"2026-04-21T04:55:44.252493Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\n\n# Check exact file paths\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T04:59:17.048626Z","iopub.execute_input":"2026-04-21T04:59:17.049225Z","iopub.status.idle":"2026-04-21T04:59:17.058146Z","shell.execute_reply.started":"2026-04-21T04:59:17.049182Z","shell.execute_reply":"2026-04-21T04:59:17.05734Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\n\nPATH = '/kaggle/input/competitions/kkbox-churn-prediction-challenge/'\n\nprint(\"Loading train and members...\")\ntrain   = pd.read_csv(PATH + 'train.csv.7z')\nmembers = pd.read_csv(PATH + 'members_v3.csv.7z')\n\nprint(f\"Users: {len(train):,}  |  Churn rate: {train['is_churn'].mean():.1%}\")\nprint(\"Done!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T05:00:17.366962Z","iopub.execute_input":"2026-04-21T05:00:17.367297Z","iopub.status.idle":"2026-04-21T05:00:17.470998Z","shell.execute_reply.started":"2026-04-21T05:00:17.367266Z","shell.execute_reply":"2026-04-21T05:00:17.469866Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport subprocess\nimport os\n\nPATH = '/kaggle/input/competitions/kkbox-churn-prediction-challenge/'\nWORK = '/kaggle/working/'\n\nprint(\"Extracting files...\")\nsubprocess.run(['7z', 'e', PATH+'train.csv.7z',      f'-o{WORK}', '-y'], capture_output=True)\nsubprocess.run(['7z', 'e', PATH+'members_v3.csv.7z', f'-o{WORK}', '-y'], capture_output=True)\nsubprocess.run(['7z', 'e', PATH+'transactions.csv.7z',f'-o{WORK}', '-y'], capture_output=True)\n\nprint(\"Files extracted:\")\nfor f in os.listdir(WORK):\n    if f.endswith('.csv'):\n        mb = os.path.getsize(WORK+f)/(1024*1024)\n        print(f\"  {f:40s} {mb:.0f} MB\")\n\nprint(\"\\nLoading...\")\ntrain   = pd.read_csv(WORK+'train.csv')\nmembers = pd.read_csv(WORK+'members_v3.csv')\n\nprint(f\"Users: {len(train):,}  |  Churn rate: {train['is_churn'].mean():.1%}\")\nprint(\"Done!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T05:02:04.632935Z","iopub.execute_input":"2026-04-21T05:02:04.633297Z","iopub.status.idle":"2026-04-21T05:03:41.20317Z","shell.execute_reply.started":"2026-04-21T05:02:04.633259Z","shell.execute_reply":"2026-04-21T05:03:41.202324Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"WORK = '/kaggle/working/'\n\ndf = train.merge(members, on='msno', how='left')\n\ndf['registration_init_time'] = pd.to_datetime(\n    df['registration_init_time'].astype(str), errors='coerce')\ndf['days_since_registered'] = (\n    pd.Timestamp('2017-03-01') - df['registration_init_time']).dt.days\ndf['bd'] = df['bd'].apply(lambda x: x if 10 < x < 70 else np.nan)\n\ntrans = pd.read_csv(WORK+'transactions.csv')\ntrans['membership_expire_date'] = pd.to_datetime(\n    trans['membership_expire_date'].astype(str), errors='coerce')\ntrans['days_until_expiry'] = (\n    trans['membership_expire_date'] - pd.Timestamp('2017-03-01')).dt.days\ntrans_latest = trans.sort_values('transaction_date').groupby('msno').last().reset_index()\n\ndf = df.merge(trans_latest[['msno','payment_plan_days','actual_amount_paid',\n    'is_auto_renew','is_cancel','days_until_expiry']], on='msno', how='left')\ndf = df.fillna(0)\ndel trans, trans_latest\n\nprint(f\"Tier 1+3 ready. Columns: {df.shape[1]}\")\npri","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T05:04:58.253586Z","iopub.execute_input":"2026-04-21T05:04:58.254575Z","iopub.status.idle":"2026-04-21T05:07:21.470076Z","shell.execute_reply.started":"2026-04-21T05:04:58.254503Z","shell.execute_reply":"2026-04-21T05:07:21.468787Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import subprocess\n\nprint(\"Extracting user_logs... (takes 2-3 mins)\")\nsubprocess.run(['7z', 'e', \n    '/kaggle/input/competitions/kkbox-churn-prediction-challenge/user_logs.csv.7z',\n    '-o/kaggle/working/', '-y'], capture_output=True)\n\nprint(\"Loading user_logs...\")\nlogs = pd.read_csv('/kaggle/working/user_logs.csv',\n                   usecols=['msno','total_secs','num_100','num_unq','num_25','date'])\n\nlogs_agg = logs.groupby('msno').agg(\n    total_secs_mean = ('total_secs', 'mean'),\n    num_100_mean    = ('num_100',    'mean'),\n    num_unq_mean    = ('num_unq',    'mean'),\n    active_days     = ('date',       'count'),\n    num_25_mean     = ('num_25',     'mean'),\n).reset_index()\n\nlogs_agg['completion_rate'] = (\n    logs_agg['num_100_mean'] / (logs_agg['num_25_mean'] + 1)).round(4)\n\ndel logs\ndf = df.merge(logs_agg, on='msno', how='left').fillna(0)\ndel logs_agg\n\nprint(f\"All 3 tiers ready! Total columns: {df.shape[1]}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T05:22:32.526323Z","iopub.execute_input":"2026-04-21T05:22:32.526775Z","iopub.status.idle":"2026-04-21T05:36:05.411081Z","shell.execute_reply.started":"2026-04-21T05:22:32.526735Z","shell.execute_reply":"2026-04-21T05:36:05.410123Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom xgboost import XGBClassifier\nfrom sklearn.metrics import roc_auc_score, f1_score\n\nfeature_cols = [\n    'city','bd','registered_via','days_since_registered',\n    'is_cancel','is_auto_renew','payment_plan_days',\n    'actual_amount_paid','days_until_expiry',\n    'total_secs_mean','num_100_mean','num_unq_mean',\n    'active_days','completion_rate'\n]\nfeature_cols = [c for c in feature_cols if c in df.columns]\n\nprint(f\"Training with {len(feature_cols)} features\")\n\nX = df[feature_cols]\ny = df['is_churn']\nX_train,X_test,y_train,y_test = train_test_split(\n    X, y, test_size=0.2, random_state=42)\n\nmodel = XGBClassifier(n_estimators=300, max_depth=6,\n    learning_rate=0.05, subsample=0.8,\n    eval_metric='logloss', random_state=42)\nmodel.fit(X_train, y_train,\n    eval_set=[(X_test,y_test)], verbose=50)\n\nauc = roc_auc_score(y_test, model.predict_proba(X_test)[:,1])\nf1  = f1_score(y_test, model.predict(X_test))\n\nprint(\"=\"*45)\nprint(f\"AUC: {auc:.3f}  {'PASS' if auc>=0.75 else 'needs work'}\")\nprint(f\"F1:  {f1:.3f}  {'PASS' if f1>=0.55 else 'needs work'}\")\nprint(\"=\"*45)\n\nimportance = pd.DataFrame({\n    'feature': feature_cols,\n    'importance': model.feature_importances_\n}).sort_values('importance', ascending=False)\nprint(\"\\nTop features:\")\nprint(importance.to_string(index=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T05:54:36.17854Z","iopub.execute_input":"2026-04-21T05:54:36.180866Z","iopub.status.idle":"2026-04-21T05:54:54.966443Z","shell.execute_reply.started":"2026-04-21T05:54:36.180771Z","shell.execute_reply":"2026-04-21T05:54:54.965594Z"}},"outputs":[],"execution_count":null}]}