{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport warnings\nimport gzip\n\nwarnings.filterwarnings(\"ignore\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:04:47.601052Z","iopub.execute_input":"2026-06-15T14:04:47.601429Z","iopub.status.idle":"2026-06-15T14:04:49.155446Z","shell.execute_reply.started":"2026-06-15T14:04:47.601396Z","shell.execute_reply":"2026-06-15T14:04:49.154365Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TRAIN_PATH = \"/kaggle/input/competitions/avazu-ctr-prediction/train.gz\"\nSAMPLE_SIZE = 1000000\nRANDOM_SEED = 42","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:04:49.157564Z","iopub.execute_input":"2026-06-15T14:04:49.158223Z","iopub.status.idle":"2026-06-15T14:04:49.163291Z","shell.execute_reply.started":"2026-06-15T14:04:49.158190Z","shell.execute_reply":"2026-06-15T14:04:49.162205Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"COL_NOTES = {\n    \"id\":           \"Ad identifier (unique per row)\",\n    \"click\":        \"Target: 1 = clicked, 0 = not clicked\",\n    \"hour\":         \"YYMMDDHH format — encodes date AND hour\",\n    \"C1\":           \"Anonymized categorical\",\n    \"banner_pos\":   \"Position of the banner on the page\",\n    \"site_id\":      \"Site identifier\",\n    \"site_domain\":  \"Domain the site belongs to\",\n    \"site_category\":\"Category of the site\",\n    \"app_id\":       \"App identifier\",\n    \"app_domain\":   \"Domain of the app\",\n    \"app_category\": \"Category of the app\",\n    \"device_id\":    \"Device identifier (very high cardinality)\",\n    \"device_ip\":    \"IP address (very high cardinality)\",\n    \"device_model\": \"Device model\",\n    \"device_type\":  \"Type of device (phone/tablet/…)\",\n    \"device_conn_type\": \"Connection type (wifi/3g/…)\",\n    \"C14–C21\":      \"Anonymized categoricals\",\n}\n\nfor k,v in COL_NOTES.items():\n    print(f\"{k:20s}:   {v}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:04:49.164425Z","iopub.execute_input":"2026-06-15T14:04:49.164771Z","iopub.status.idle":"2026-06-15T14:04:49.186289Z","shell.execute_reply.started":"2026-06-15T14:04:49.164745Z","shell.execute_reply":"2026-06-15T14:04:49.185240Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 1: Loading Sample","metadata":{}},{"cell_type":"code","source":"with gzip.open(TRAIN_PATH,'rb') as f:\n    total_rows = sum(1 for _ in f)\n\nprint(\"Total number of rows:\", total_rows)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:04:49.187675Z","iopub.execute_input":"2026-06-15T14:04:49.188125Z","iopub.status.idle":"2026-06-15T14:05:42.908288Z","shell.execute_reply.started":"2026-06-15T14:04:49.188083Z","shell.execute_reply":"2026-06-15T14:05:42.907312Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"skip_prob = 1-(SAMPLE_SIZE/total_rows)\nskip_rows = np.random.RandomState(RANDOM_SEED).choice(\n    range(1,total_rows+1),\n    size = (total_rows-SAMPLE_SIZE),\n    replace=False\n)\nprint(skip_prob)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:05:42.911093Z","iopub.execute_input":"2026-06-15T14:05:42.911574Z","iopub.status.idle":"2026-06-15T14:05:50.026422Z","shell.execute_reply.started":"2026-06-15T14:05:42.911540Z","shell.execute_reply":"2026-06-15T14:05:50.025174Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(TRAIN_PATH,skiprows=skip_rows)\nprint(f\"Loaded sample of rows {df.shape[0]} and columns {df.shape[1]}\")\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:05:50.027607Z","iopub.execute_input":"2026-06-15T14:05:50.027889Z","iopub.status.idle":"2026-06-15T14:07:34.733346Z","shell.execute_reply.started":"2026-06-15T14:05:50.027864Z","shell.execute_reply":"2026-06-15T14:07:34.732506Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 2: Checks","metadata":{}},{"cell_type":"code","source":"df.dtypes","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:34.734428Z","iopub.execute_input":"2026-06-15T14:07:34.734750Z","iopub.status.idle":"2026-06-15T14:07:34.743264Z","shell.execute_reply.started":"2026-06-15T14:07:34.734725Z","shell.execute_reply":"2026-06-15T14:07:34.742136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:34.744846Z","iopub.execute_input":"2026-06-15T14:07:34.745272Z","iopub.status.idle":"2026-06-15T14:07:35.224760Z","shell.execute_reply.started":"2026-06-15T14:07:34.745230Z","shell.execute_reply":"2026-06-15T14:07:35.223842Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### STEP - 3 Target: click rate","metadata":{}},{"cell_type":"code","source":"click_rate = df['click'].mean()\nclick_rate","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:35.225848Z","iopub.execute_input":"2026-06-15T14:07:35.226196Z","iopub.status.idle":"2026-06-15T14:07:35.234216Z","shell.execute_reply.started":"2026-06-15T14:07:35.226168Z","shell.execute_reply":"2026-06-15T14:07:35.233149Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"Clicks {df['click'].sum()}\")\nprint(f\"Non clicks {(df['click']==0).sum()}\")\nprint(f\"\\n Imbalance ratio: 1:{(1-click_rate)/click_rate:.1f}  \")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:35.235305Z","iopub.execute_input":"2026-06-15T14:07:35.235641Z","iopub.status.idle":"2026-06-15T14:07:35.256022Z","shell.execute_reply.started":"2026-06-15T14:07:35.235604Z","shell.execute_reply":"2026-06-15T14:07:35.255155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(5, 3))\nax.bar(['Clicked','Not clicked'],df['click'].value_counts().sort_index(),color=[\"#5B8DB8\", \"#E87B5A\"])\nax.set_title(\"Click distribution\")\nax.set_ylabel(\"Count\")\nfor bar in ax.patches:\n    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1000,\n            f\"{bar.get_height():,.0f}\", ha=\"center\", fontsize=10)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:35.257209Z","iopub.execute_input":"2026-06-15T14:07:35.257513Z","iopub.status.idle":"2026-06-15T14:07:35.475020Z","shell.execute_reply.started":"2026-06-15T14:07:35.257470Z","shell.execute_reply":"2026-06-15T14:07:35.473812Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### STEP 4 — Parse the `hour` column","metadata":{}},{"cell_type":"code","source":"df['hour_str'] = df['hour'].astype(str)\ndf['date'] = pd.to_datetime(df['hour_str'], format=\"%y%m%d%H\")\ndf['hour_of_day'] = df['date'].dt.hour\ndf['day_of_week'] = df['date'].dt.dayofweek\ndf['date_only'] = df['date'].dt.date\ndf['day'] = df['date'].dt.day","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:09:20.983925Z","iopub.execute_input":"2026-06-15T14:09:20.984258Z","iopub.status.idle":"2026-06-15T14:09:21.876685Z","shell.execute_reply.started":"2026-06-15T14:09:20.984230Z","shell.execute_reply":"2026-06-15T14:09:21.875711Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Date range in sample:\")\nprint(f\"  From: {df['date'].min()}\")\nprint(f\"  To:   {df['date'].max()}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:36.344413Z","iopub.execute_input":"2026-06-15T14:07:36.344692Z","iopub.status.idle":"2026-06-15T14:07:36.354964Z","shell.execute_reply.started":"2026-06-15T14:07:36.344669Z","shell.execute_reply":"2026-06-15T14:07:36.353898Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ctr_by_hour = df.groupby('hour_of_day')['click'].mean()\nctr_by_hour.plot()\nplt.xticks(range(0, 24, 2))\nplt.title(\"Click rate by hour of day\")\nplt.ylabel(\"click rate\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:36.358681Z","iopub.execute_input":"2026-06-15T14:07:36.359006Z","iopub.status.idle":"2026-06-15T14:07:36.638303Z","shell.execute_reply.started":"2026-06-15T14:07:36.358970Z","shell.execute_reply":"2026-06-15T14:07:36.637391Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ctr_by_dayofweek = df.groupby(\"day_of_week\")['click'].mean()\nctr_by_dayofweek.plot()\nplt.xticks(range(0, 7, 1))\nplt.title(\"Click rate by day of week\")\nplt.ylabel(\"click rate\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:36.639231Z","iopub.execute_input":"2026-06-15T14:07:36.639515Z","iopub.status.idle":"2026-06-15T14:07:36.803135Z","shell.execute_reply.started":"2026-06-15T14:07:36.639488Z","shell.execute_reply":"2026-06-15T14:07:36.802185Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"peak_hour = ctr_by_hour.idxmax()\nprint(f\"\\nPeak CTR hour: {peak_hour}:00  ({ctr_by_hour[peak_hour]:.3%})\")\nprint(f\"Lowest CTR hour: {ctr_by_hour.idxmin()}:00  ({ctr_by_hour.min():.3%})\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:36.804304Z","iopub.execute_input":"2026-06-15T14:07:36.804650Z","iopub.status.idle":"2026-06-15T14:07:36.812293Z","shell.execute_reply.started":"2026-06-15T14:07:36.804623Z","shell.execute_reply":"2026-06-15T14:07:36.811475Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### STEP 5 — Cardinality of categorical columns","metadata":{}},{"cell_type":"code","source":"cat_cols = [c for c in df.columns if c not in\n            [\"id\",\"click\",\"hour\",\"hour_str\",\"date\",\"date_only\",\"hour_of_day\",\"day_of_week\"]]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:36.813431Z","iopub.execute_input":"2026-06-15T14:07:36.813760Z","iopub.status.idle":"2026-06-15T14:07:36.830369Z","shell.execute_reply.started":"2026-06-15T14:07:36.813727Z","shell.execute_reply":"2026-06-15T14:07:36.829273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cardinality = pd.DataFrame({\n    \"columns\":cat_cols,\n    \"unique_vals\": [df[c].nunique() for c in cat_cols]\n}).sort_values(by='unique_vals')\n\ncardinality","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:36.831554Z","iopub.execute_input":"2026-06-15T14:07:36.831834Z","iopub.status.idle":"2026-06-15T14:07:37.660140Z","shell.execute_reply.started":"2026-06-15T14:07:36.831809Z","shell.execute_reply":"2026-06-15T14:07:37.659048Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sns.barplot(data = cardinality,x=\"columns\",y='unique_vals',color=\"#7B6CC8\")\nplt.yscale('log')\nplt.xticks(rotation=45,ha='right')\nplt.xlabel(\"Number of unique values\")\nplt.title(\"Cardinality of categorical features\")\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:37.661616Z","iopub.execute_input":"2026-06-15T14:07:37.661932Z","iopub.status.idle":"2026-06-15T14:07:38.377812Z","shell.execute_reply.started":"2026-06-15T14:07:37.661903Z","shell.execute_reply":"2026-06-15T14:07:38.376913Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### STEP 6 — CTR per category (which features drive clicks most?)","metadata":{}},{"cell_type":"code","source":"feature_signal = {}\n\nfor col in cat_cols:\n    if df[col].nunique() < 5000:\n        ctr_per_cat = df.groupby(col)['click'].mean()\n        feature_signal[col] = ctr_per_cat.std()\n\nsignaldf = pd.Series(feature_signal).sort_values(ascending=False)\nsignaldf.round(4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:38.378945Z","iopub.execute_input":"2026-06-15T14:07:38.379323Z","iopub.status.idle":"2026-06-15T14:07:39.898098Z","shell.execute_reply.started":"2026-06-15T14:07:38.379286Z","shell.execute_reply":"2026-06-15T14:07:39.897179Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\" CTR by banner_pos \")\nprint(df.groupby(\"banner_pos\")[\"click\"].agg([\"mean\",\"count\"]).rename(columns={\"mean\":\"ctr\"}))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:39.899128Z","iopub.execute_input":"2026-06-15T14:07:39.899456Z","iopub.status.idle":"2026-06-15T14:07:39.934182Z","shell.execute_reply.started":"2026-06-15T14:07:39.899413Z","shell.execute_reply":"2026-06-15T14:07:39.933063Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\" CTR by device_type \")\n\nprint(df.groupby(\"device_type\")[\"click\"].agg([\"mean\",\"count\"]).rename(columns={\"mean\":\"ctr\"}))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:39.935230Z","iopub.execute_input":"2026-06-15T14:07:39.935533Z","iopub.status.idle":"2026-06-15T14:07:39.973275Z","shell.execute_reply.started":"2026-06-15T14:07:39.935507Z","shell.execute_reply":"2026-06-15T14:07:39.972424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\" CTR by device_conn_type \")\nprint(df.groupby(\"device_conn_type\")[\"click\"].agg([\"mean\",\"count\"]).rename(columns={\"mean\":\"ctr\"}))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:39.974398Z","iopub.execute_input":"2026-06-15T14:07:39.974714Z","iopub.status.idle":"2026-06-15T14:07:40.012267Z","shell.execute_reply.started":"2026-06-15T14:07:39.974688Z","shell.execute_reply":"2026-06-15T14:07:40.011198Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### STEP 7 — Site vs App: which channel drives more clicks?","metadata":{}},{"cell_type":"code","source":"print(df['app_id'].value_counts().head())\nprint(df['site_id'].value_counts().head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:40.013403Z","iopub.execute_input":"2026-06-15T14:07:40.013744Z","iopub.status.idle":"2026-06-15T14:07:40.176225Z","shell.execute_reply.started":"2026-06-15T14:07:40.013718Z","shell.execute_reply":"2026-06-15T14:07:40.175212Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"NULL_HASH = \"85f751fd\"  # Avazu uses this hash for the absent channel\ndf[\"is_app\"] = (df[\"app_id\"] != NULL_HASH).astype(int)\ndf[\"channel\"] = df[\"is_app\"].map({1: \"App\", 0: \"Site\"})\n \nprint(df.groupby(\"channel\")[\"click\"].agg([\"mean\",\"count\"]).rename(columns={\"mean\":\"ctr\"}))\n \nfig, ax = plt.subplots(figsize=(5, 3))\nchannel_ctr = df.groupby(\"channel\")[\"click\"].mean()\nax.bar(channel_ctr.index, channel_ctr.values, color=[\"#5B8DB8\",\"#E87B5A\"])\nax.set_title(\"CTR: Site vs App traffic\")\nax.set_ylabel(\"Click rate\")\nplt.tight_layout()\nplt.savefig(\"eda_step7_site_vs_app.png\", dpi=120)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:40.177544Z","iopub.execute_input":"2026-06-15T14:07:40.177921Z","iopub.status.idle":"2026-06-15T14:07:40.653479Z","shell.execute_reply.started":"2026-06-15T14:07:40.177894Z","shell.execute_reply":"2026-06-15T14:07:40.652499Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df['channel'].unique()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:40.654646Z","iopub.execute_input":"2026-06-15T14:07:40.654937Z","iopub.status.idle":"2026-06-15T14:07:40.713852Z","shell.execute_reply.started":"2026-06-15T14:07:40.654910Z","shell.execute_reply":"2026-06-15T14:07:40.712706Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### STEP 9 — Correlation between anonymized C-features and CTR","metadata":{}},{"cell_type":"code","source":"for col in ['site_id','app_id','device_id']:\n    vc = df[col].value_counts()\n    rare = (vc>10).sum()\n    print(f\"{col:20s}: {vc.shape[0]:6,} unique values, \"\n          f\"{rare:6,} appear <10 times ({rare/vc.shape[0]:.1%})\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:40.714811Z","iopub.execute_input":"2026-06-15T14:07:40.715064Z","iopub.status.idle":"2026-06-15T14:07:41.052365Z","shell.execute_reply.started":"2026-06-15T14:07:40.715039Z","shell.execute_reply":"2026-06-15T14:07:41.051218Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"c_cols = [col for col in df.columns if col.startswith(\"C\")]\n\nc_signal = {}\n\nfor col in c_cols:\n    if df[col].nunique() < 5000:\n        c_signal[col] = df.groupby(col)['click'].mean().std()\n\nc_signal","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:41.053498Z","iopub.execute_input":"2026-06-15T14:07:41.053816Z","iopub.status.idle":"2026-06-15T14:07:41.318523Z","shell.execute_reply.started":"2026-06-15T14:07:41.053782Z","shell.execute_reply":"2026-06-15T14:07:41.317562Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"c_signal_df = pd.Series(c_signal).sort_values(ascending=False)\nprint(c_signal_df.round(4))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:07:41.319705Z","iopub.execute_input":"2026-06-15T14:07:41.320070Z","iopub.status.idle":"2026-06-15T14:07:41.327278Z","shell.execute_reply.started":"2026-06-15T14:07:41.320037Z","shell.execute_reply":"2026-06-15T14:07:41.326353Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 11 — Time-based validation split","metadata":{}},{"cell_type":"code","source":"df['day'].unique()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:09:49.173021Z","iopub.execute_input":"2026-06-15T14:09:49.173365Z","iopub.status.idle":"2026-06-15T14:09:49.189146Z","shell.execute_reply.started":"2026-06-15T14:09:49.173335Z","shell.execute_reply":"2026-06-15T14:09:49.188091Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##### Since there are 10 days data in dataset, lets put 9 days ( from 21st to 29th ) as training and last day (30th) as testing","metadata":{}},{"cell_type":"code","source":"train = df[df['day']<=29]\nval = df[df['day']==30]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:12:16.822973Z","iopub.execute_input":"2026-06-15T14:12:16.823307Z","iopub.status.idle":"2026-06-15T14:12:17.114459Z","shell.execute_reply.started":"2026-06-15T14:12:16.823279Z","shell.execute_reply":"2026-06-15T14:12:17.113227Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"Length of training set: {len(train)}\")\nprint(f\"Length of validation set: {len(val)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:13:57.647966Z","iopub.execute_input":"2026-06-15T14:13:57.648312Z","iopub.status.idle":"2026-06-15T14:13:57.653721Z","shell.execute_reply.started":"2026-06-15T14:13:57.648283Z","shell.execute_reply":"2026-06-15T14:13:57.652621Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 12 — Baseline model (Logistic Regression)","metadata":{}},{"cell_type":"code","source":"from sklearn.linear_model import LogisticRegression\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.metrics import log_loss","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:15:52.652377Z","iopub.execute_input":"2026-06-15T14:15:52.652747Z","iopub.status.idle":"2026-06-15T14:15:52.994309Z","shell.execute_reply.started":"2026-06-15T14:15:52.652714Z","shell.execute_reply":"2026-06-15T14:15:52.993387Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BASE_FEATURES = [\n    \"C1\", \"banner_pos\", \"site_category\", \"app_category\",\n    \"device_type\", \"device_conn_type\", \"C15\", \"C16\", \"C18\",\n    \"hour_of_day\", \"day_of_week\"\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:16:03.287902Z","iopub.execute_input":"2026-06-15T14:16:03.288461Z","iopub.status.idle":"2026-06-15T14:16:03.293509Z","shell.execute_reply.started":"2026-06-15T14:16:03.288401Z","shell.execute_reply":"2026-06-15T14:16:03.292520Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train = train[BASE_FEATURES]\nX_val = val[BASE_FEATURES]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:25:30.612676Z","iopub.execute_input":"2026-06-15T14:25:30.612987Z","iopub.status.idle":"2026-06-15T14:25:30.690140Z","shell.execute_reply.started":"2026-06-15T14:25:30.612963Z","shell.execute_reply":"2026-06-15T14:25:30.689018Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for col in X_train.select_dtypes(\"object\").columns:\n    le = LabelEncoder()\n    X_train[col] = le.fit_transform(X_train[col].astype('str'))\n    X_val[col] = X_val[col].astype(str).map(\n        lambda x: le.transform([x])[0] if x in le.classes_ else -1\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:25:33.068303Z","iopub.execute_input":"2026-06-15T14:25:33.069073Z","iopub.status.idle":"2026-06-15T14:26:00.458068Z","shell.execute_reply.started":"2026-06-15T14:25:33.069024Z","shell.execute_reply":"2026-06-15T14:26:00.457020Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"Y_train = train['click']\nY_val = val['click']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:26:02.223257Z","iopub.execute_input":"2026-06-15T14:26:02.223641Z","iopub.status.idle":"2026-06-15T14:26:02.228581Z","shell.execute_reply.started":"2026-06-15T14:26:02.223612Z","shell.execute_reply":"2026-06-15T14:26:02.227529Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = LogisticRegression()\nmodel.fit(X_train,Y_train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:26:27.183544Z","iopub.execute_input":"2026-06-15T14:26:27.184575Z","iopub.status.idle":"2026-06-15T14:26:41.102465Z","shell.execute_reply.started":"2026-06-15T14:26:27.184536Z","shell.execute_reply":"2026-06-15T14:26:41.101689Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_preds = model.predict_proba(X_val)[:, 1]\nscore = log_loss(Y_val, val_preds)\nprint(f\"Baseline log loss: {score:.5f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:26:48.624164Z","iopub.execute_input":"2026-06-15T14:26:48.624513Z","iopub.status.idle":"2026-06-15T14:26:48.693905Z","shell.execute_reply.started":"2026-06-15T14:26:48.624485Z","shell.execute_reply":"2026-06-15T14:26:48.693206Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-15T14:09:26.928858Z","iopub.execute_input":"2026-06-15T14:09:26.929188Z","iopub.status.idle":"2026-06-15T14:09:26.950631Z","shell.execute_reply.started":"2026-06-15T14:09:26.929159Z","shell.execute_reply":"2026-06-15T14:09:26.949527Z"}},"outputs":[],"execution_count":null}]}