{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":81933,"databundleVersionId":9643020,"sourceType":"competition"}],"dockerImageVersionId":30918,"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":"2025-04-12T13:53:18.261619Z","iopub.execute_input":"2025-04-12T13:53:18.261962Z","iopub.status.idle":"2025-04-12T13:53:25.468996Z","shell.execute_reply.started":"2025-04-12T13:53:18.261932Z","shell.execute_reply":"2025-04-12T13:53:25.467805Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport random\nimport matplotlib.pyplot as plt\nfrom tqdm.notebook import tqdm\nimport glob\nimport scipy\n\nfrom concurrent.futures import ThreadPoolExecutor\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import cohen_kappa_score\nfrom sklearn.preprocessing import StandardScaler\nimport lightgbm as lgb\nimport optuna\nimport time\nfrom optuna.samplers import TPESampler\nfrom sklearn.impute import SimpleImputer, KNNImputer\nfrom scipy.optimize import minimize\nfrom collections import Counter\nfrom scipy import stats\nfrom sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:52.964300Z","iopub.execute_input":"2025-04-12T13:54:52.964733Z","iopub.status.idle":"2025-04-12T13:54:53.253259Z","shell.execute_reply.started":"2025-04-12T13:54:52.964699Z","shell.execute_reply":"2025-04-12T13:54:53.252207Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:53:43.475758Z","iopub.execute_input":"2025-04-12T13:53:43.476453Z","iopub.status.idle":"2025-04-12T13:53:43.482748Z","shell.execute_reply.started":"2025-04-12T13:53:43.476399Z","shell.execute_reply":"2025-04-12T13:53:43.480706Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def set_global_seed(seed=0):\n    np.random.seed(seed)\n    random.seed(seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:53:46.295353Z","iopub.execute_input":"2025-04-12T13:53:46.295762Z","iopub.status.idle":"2025-04-12T13:53:46.300599Z","shell.execute_reply.started":"2025-04-12T13:53:46.295732Z","shell.execute_reply":"2025-04-12T13:53:46.299203Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/train.csv')\ntest = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/test.csv')\ndata_dict = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/data_dictionary.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:07.088691Z","iopub.execute_input":"2025-04-12T13:54:07.089135Z","iopub.status.idle":"2025-04-12T13:54:07.204209Z","shell.execute_reply.started":"2025-04-12T13:54:07.089099Z","shell.execute_reply":"2025-04-12T13:54:07.202879Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:10.320262Z","iopub.execute_input":"2025-04-12T13:54:10.320710Z","iopub.status.idle":"2025-04-12T13:54:10.375175Z","shell.execute_reply.started":"2025-04-12T13:54:10.320678Z","shell.execute_reply":"2025-04-12T13:54:10.374151Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Understanding the datasets","metadata":{}},{"cell_type":"code","source":"train_cols = set(train.columns)\ntest_cols = set(test.columns)\ncolumns_not_in_test = sorted(list(train_cols - test_cols))\ndata_dict[data_dict['Field'].isin(columns_not_in_test)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:12.432277Z","iopub.execute_input":"2025-04-12T13:54:12.432727Z","iopub.status.idle":"2025-04-12T13:54:12.455048Z","shell.execute_reply.started":"2025-04-12T13:54:12.432693Z","shell.execute_reply":"2025-04-12T13:54:12.453689Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pciat_min_max = train.groupby('sii')['PCIAT-PCIAT_Total'].agg(['min', 'max'])\npciat_min_max = pciat_min_max.rename(\n    columns={'min': 'Minimum PCIAT total Score', 'max': 'Maximum total PCIAT Score'}\n)\npciat_min_max","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:15.578121Z","iopub.execute_input":"2025-04-12T13:54:15.578550Z","iopub.status.idle":"2025-04-12T13:54:15.601198Z","shell.execute_reply.started":"2025-04-12T13:54:15.578500Z","shell.execute_reply":"2025-04-12T13:54:15.599598Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_dict[data_dict['Field'] == 'PCIAT-PCIAT_Total']['Value Labels'].iloc[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:17.588441Z","iopub.execute_input":"2025-04-12T13:54:17.588953Z","iopub.status.idle":"2025-04-12T13:54:17.598671Z","shell.execute_reply.started":"2025-04-12T13:54:17.588883Z","shell.execute_reply":"2025-04-12T13:54:17.597412Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_with_sii = train[train['sii'].notna()][columns_not_in_test]\ntrain_with_sii[train_with_sii.isna().any(axis=1)].head().style.applymap(\n    lambda x: 'background-color: #FFC0CB' if pd.isna(x) else ''\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:19.456307Z","iopub.execute_input":"2025-04-12T13:54:19.456733Z","iopub.status.idle":"2025-04-12T13:54:19.550705Z","shell.execute_reply.started":"2025-04-12T13:54:19.456701Z","shell.execute_reply":"2025-04-12T13:54:19.549393Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"PCIAT_cols = [f'PCIAT-PCIAT_{i+1:02d}' for i in range(20)]\nrecalc_total_score = train_with_sii[PCIAT_cols].sum(\n    axis=1, skipna=True\n)\n(recalc_total_score == train_with_sii['PCIAT-PCIAT_Total']).all()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:21.766264Z","iopub.execute_input":"2025-04-12T13:54:21.766658Z","iopub.status.idle":"2025-04-12T13:54:21.776793Z","shell.execute_reply.started":"2025-04-12T13:54:21.766625Z","shell.execute_reply":"2025-04-12T13:54:21.775716Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def recalculate_sii(row):\n    if pd.isna(row['PCIAT-PCIAT_Total']):\n        return np.nan\n    max_possible = row['PCIAT-PCIAT_Total'] + row[PCIAT_cols].isna().sum() * 5\n    if row['PCIAT-PCIAT_Total'] <= 30 and max_possible <= 30:\n        return 0\n    elif 31 <= row['PCIAT-PCIAT_Total'] <= 49 and max_possible <= 49:\n        return 1\n    elif 50 <= row['PCIAT-PCIAT_Total'] <= 79 and max_possible <= 79:\n        return 2\n    elif row['PCIAT-PCIAT_Total'] >= 80 and max_possible >= 80:\n        return 3\n    return np.nan\n\ntrain['recalc_sii'] = train.apply(recalculate_sii, axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:24.291620Z","iopub.execute_input":"2025-04-12T13:54:24.291999Z","iopub.status.idle":"2025-04-12T13:54:25.917189Z","shell.execute_reply.started":"2025-04-12T13:54:24.291970Z","shell.execute_reply":"2025-04-12T13:54:25.915917Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mismatch_rows = train[\n    (train['recalc_sii'] != train['sii']) & train['sii'].notna()\n]\n\nmismatch_rows[PCIAT_cols + [\n    'PCIAT-PCIAT_Total', 'sii', 'recalc_sii'\n]].style.applymap(\n    lambda x: 'background-color: #FFC0CB' if pd.isna(x) else ''\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:30.586736Z","iopub.execute_input":"2025-04-12T13:54:30.587097Z","iopub.status.idle":"2025-04-12T13:54:30.614872Z","shell.execute_reply.started":"2025-04-12T13:54:30.587065Z","shell.execute_reply":"2025-04-12T13:54:30.613624Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train['sii'] = train['recalc_sii']\ntrain['complete_resp_total'] = train['PCIAT-PCIAT_Total'].where(\n    train[PCIAT_cols].notna().all(axis=1), np.nan\n)\n\nsii_map = {0: '0 (None)', 1: '1 (Mild)', 2: '2 (Moderate)', 3: '3 (Severe)'}\ntrain['sii'] = train['sii'].map(sii_map).fillna('Missing')\n\nsii_order = ['Missing', '0 (None)', '1 (Mild)', '2 (Moderate)', '3 (Severe)']\ntrain['sii'] = pd.Categorical(train['sii'], categories=sii_order, ordered=True)\n\ntrain.drop(columns='recalc_sii', inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:54:33.411974Z","iopub.execute_input":"2025-04-12T13:54:33.412327Z","iopub.status.idle":"2025-04-12T13:54:33.427291Z","shell.execute_reply.started":"2025-04-12T13:54:33.412297Z","shell.execute_reply":"2025-04-12T13:54:33.425818Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sii_counts = train['sii'].value_counts().reset_index()\ntotal = sii_counts['count'].sum()\nsii_counts['percentage'] = (sii_counts['count'] / total) * 100\n\nfig, axes = plt.subplots(1, 2, figsize=(14, 5))\n\n# SII\nsns.barplot(x='sii', y='count', data=sii_counts, palette='Blues_d', ax=axes[0])\naxes[0].set_title('Distribution of Severity Impairment Index (sii)', fontsize=14)\nfor p in axes[0].patches:\n    height = p.get_height()\n    percentage = sii_counts.loc[sii_counts['count'] == height, 'percentage'].values[0]\n    axes[0].text(\n        p.get_x() + p.get_width() / 2,\n        height + 5, f'{int(height)} ({percentage:.1f}%)',\n        ha=\"center\", fontsize=12\n    )\n\n# PCIAT_Total for complete responses\nsns.histplot(train['complete_resp_total'].dropna(), bins=20, ax=axes[1])\naxes[1].set_title('Distribution of PCIAT_Total', fontsize=14)\naxes[1].set_xlabel('PCIAT_Total for Complete PCIAT Responses')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:55:00.139825Z","iopub.execute_input":"2025-04-12T13:55:00.140491Z","iopub.status.idle":"2025-04-12T13:55:00.773024Z","shell.execute_reply.started":"2025-04-12T13:55:00.140451Z","shell.execute_reply":"2025-04-12T13:55:00.771613Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(train[train['complete_resp_total'] == 0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:55:04.066864Z","iopub.execute_input":"2025-04-12T13:55:04.067250Z","iopub.status.idle":"2025-04-12T13:55:04.075558Z","shell.execute_reply.started":"2025-04-12T13:55:04.067220Z","shell.execute_reply":"2025-04-12T13:55:04.074474Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"assert train['Basic_Demos-Age'].isna().sum() == 0\nassert train['Basic_Demos-Sex'].isna().sum() == 0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:55:05.908826Z","iopub.execute_input":"2025-04-12T13:55:05.909162Z","iopub.status.idle":"2025-04-12T13:55:05.914908Z","shell.execute_reply.started":"2025-04-12T13:55:05.909136Z","shell.execute_reply":"2025-04-12T13:55:05.913620Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Basic understanding**","metadata":{}},{"cell_type":"markdown","source":"# Pre-processing","metadata":{}},{"cell_type":"code","source":"### ---- Sensor Summary Feature Functions ---- ###\ndef describe_series_basic_stats(series, prefix):\n    \"\"\"Extract basic statistics from a time series.\"\"\"\n    return {\n        f\"{prefix}_mean\": series.mean(),\n        f\"{prefix}_std\": series.std(),\n        f\"{prefix}_min\": series.min(),\n        f\"{prefix}_max\": series.max(),\n        f\"{prefix}_range\": series.max() - series.min(),\n        f\"{prefix}_skew\": series.skew(),\n        f\"{prefix}_kurtosis\": series.kurtosis(),\n    }\n\ndef describe_all_axes(df):\n    \"\"\"Summarize stats across X, Y, Z axis.\"\"\"\n    all_stats = {}\n    for axis in ['X', 'Y', 'Z']:\n        all_stats.update(describe_series_basic_stats(df[axis], axis.lower()))\n    return all_stats\n\ndef describe_enmo(df):\n    \"\"\"Summarize ENMO (acceleration magnitude).\"\"\"\n    return describe_series_basic_stats(df['enmo'], 'enmo')\n\ndef describe_anglez(df):\n    \"\"\"Summarize device angle (anglez).\"\"\"\n    return describe_series_basic_stats(df['anglez'], 'anglez')\n\n### ---- Light Feature Engineering ---- ###\n\nlight_bins = [\n    (0, 5, 'Twilight'),\n    (5, 10, 'Minimal_Street_Lighting'),\n    (10, 50, 'Sunset'),\n    (50, 80, 'Family_Living_Room'),\n    (80, 100, 'Hallway'),\n    (100, 320, 'Very_Dark_Overcast_Day'),\n    (320, 500, 'Office_Lighting'),\n    (500, 1000, 'Sunrise_Sunset'),\n    (1000, 10000, 'Overcast_Day'),\n    (10000, 25000, 'Full_Daylight'),\n    (25000, 130000, 'Direct_Sunlight')\n]\n\ndef categorize_light(value):\n    \"\"\"Categorize light level (in lux) based on standard environmental conditions.\"\"\"\n    for low, high, label in light_bins:\n        if low <= value < high:\n            return label\n    return 'Unknown'\n\ndef describe_light(df):\n    \"\"\"Compute normalized proportions of time spent in each light category.\"\"\"\n    df['light_category'] = df['light'].apply(categorize_light)\n    category_counts = df['light_category'].value_counts(normalize=True).to_dict()\n    return {f\"light_{label}\": category_counts.get(label, 0) for _, _, label in light_bins}\n\n### ---- Streak Features (Activity / Inactivity Windows) ---- ###\n\ndef compute_streaks(series, condition, top_n=5):\n    \"\"\"Generic function to compute longest streaks satisfying a condition.\"\"\"\n    streak_lengths = []\n    current_streak = 0\n\n    for val in condition:\n        if val:\n            current_streak += 1\n        else:\n            if current_streak > 0:\n                streak_lengths.append(current_streak)\n            current_streak = 0\n\n    if current_streak > 0:\n        streak_lengths.append(current_streak)\n\n    streak_lengths = sorted(streak_lengths, reverse=True)[:top_n]\n    streak_lengths += [0] * (top_n - len(streak_lengths))\n    return streak_lengths\n\ndef longest_inactivity_streaks(df, window_size=100, threshold=10, top_n=5):\n    \"\"\"Detect long stretches of inactivity (rolling ENMO sum below threshold).\"\"\"\n    rolling_cumsum = df['enmo'].rolling(window=window_size).sum()\n    inactive_windows = rolling_cumsum <= threshold\n    return compute_streaks(df['enmo'], inactive_windows, top_n)\n\ndef longest_activity_streaks(df, window_size=100, threshold=5, top_n=5):\n    \"\"\"Detect long stretches of activity (rolling ENMO sum above threshold).\"\"\"\n    rolling_cumsum = df['enmo'].rolling(window=window_size).sum()\n    active_windows = rolling_cumsum > threshold\n    return compute_streaks(df['enmo'], active_windows, top_n)\n\n### ---- File Processing ---- ###\n\ndef process_file(filename, dirname):\n    \"\"\"Read a parquet file, extract features, and return feature dict with ID.\"\"\"\n    df = pd.read_parquet(os.path.join(dirname, filename, 'part-0.parquet'))\n    df.drop(['step'], axis=1, inplace=True)\n\n    features = {}\n\n    # Axes stats\n    features.update(describe_all_axes(df))\n\n    # ENMO & anglez\n    features.update(describe_enmo(df))\n    features.update(describe_anglez(df))\n\n    # Light analysis\n    features.update(describe_light(df))\n\n    # Activity ratio\n    features['enmo_active_ratio'] = (df['enmo'] > 0).mean()\n\n    # Longest inactivity/activity streaks\n    for i, val in enumerate(longest_inactivity_streaks(df, threshold=1)):\n        features[f'inact_streak_{i}'] = val\n\n    for i, val in enumerate(longest_activity_streaks(df, threshold=5)):\n        features[f'act_streak_{i}'] = val\n\n    sample_id = filename.split('=')[1]\n    return features, sample_id\n\n### ---- Bulk Processing ---- ###\n\ndef load_time_series(dirname) -> pd.DataFrame:\n    \"\"\"Process all files in directory and assemble final dataframe.\"\"\"\n    ids = os.listdir(dirname)\n\n    with ThreadPoolExecutor() as executor:\n        results = list(tqdm(executor.map(lambda fname: process_file(fname, dirname), ids), total=len(ids)))\n\n    features_list, sample_ids = zip(*results)\n\n    df = pd.DataFrame(features_list)\n    df['id'] = sample_ids\n\n    return df\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:55:26.687070Z","iopub.execute_input":"2025-04-12T13:55:26.687431Z","iopub.status.idle":"2025-04-12T13:55:26.706210Z","shell.execute_reply.started":"2025-04-12T13:55:26.687398Z","shell.execute_reply":"2025-04-12T13:55:26.705147Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ts = load_time_series(\"/kaggle/input/child-mind-institute-problematic-internet-use/series_train.parquet\")\ntest_ts = load_time_series(\"/kaggle/input/child-mind-institute-problematic-internet-use/series_test.parquet\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T13:55:36.063412Z","iopub.execute_input":"2025-04-12T13:55:36.063802Z","iopub.status.idle":"2025-04-12T14:00:45.909600Z","shell.execute_reply.started":"2025-04-12T13:55:36.063771Z","shell.execute_reply":"2025-04-12T14:00:45.907988Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def feature_engineering(df):\n\n    for col, (col_min, col_max) in min_max_dict.items():\n        df[col] = df[col].clip(lower=col_min, upper=col_max)\n\n    bins = [0, 6, 12, 18, 100]\n    labels = ['1 to 6', '7 to 12', '13 to 18', '19 to 100']\n    df['Age_Binned'] = pd.cut(df['Basic_Demos-Age'], bins=bins, labels=labels, right=True)\n    df['Age_Sex'] = df['Age_Binned'].astype(str) + '_' + df['Basic_Demos-Sex'].astype(str)\n    \n    df['BFP_BMI'] = df['BIA-BIA_Fat'] / df['BIA-BIA_BMI']\n    df['BFP_BMR'] = df['BIA-BIA_Fat'] * df['BIA-BIA_BMR']\n    df['BMR_Weight'] = df['BIA-BIA_BMR'] / df['Physical-Weight']\n    \n    df['Muscle_to_Fat'] = df['BIA-BIA_SMM'] / df['BIA-BIA_FMI']\n    df['Hydration_Status'] = df['BIA-BIA_TBW'] / df['Physical-Weight']\n    \n    df['PreInt_FGC_CU_PU'] = df['PreInt_EduHx-computerinternet_hoursday'] * df['FGC-FGC_CU'] * df['FGC-FGC_PU']\n    df['FGC_GSND_GSD_Age'] = df['FGC-FGC_GSND'] * df['FGC-FGC_GSD'] * df['Basic_Demos-Age']\n    df['SDS_Activity'] = df['BIA-BIA_Activity_Level_num'] * df['SDS-SDS_Total_T']\n    \n    df['CGasync_Score_Normalized'] = df['CGAS-CGAS_Score'] - df.groupby('Basic_Demos-Enroll_Season')['CGAS-CGAS_Score'].transform('mean')\n    df['Internet_Physical_Difference'] = df['PreInt_EduHx-computerinternet_hoursday'] - df['PAQ_A-PAQ_A_Total']\n   \n    df[df.select_dtypes(include='object').columns] = df.select_dtypes(include='object').astype('category')\n    return df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:00:54.820190Z","iopub.execute_input":"2025-04-12T14:00:54.820592Z","iopub.status.idle":"2025-04-12T14:00:54.831607Z","shell.execute_reply.started":"2025-04-12T14:00:54.820550Z","shell.execute_reply":"2025-04-12T14:00:54.830113Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/train.csv')\ntest = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/test.csv')\nsample = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/sample_submission.csv')\n\ntrain = pd.merge(train, train_ts, how=\"left\", on='id')\ntest = pd.merge(test, test_ts, how=\"left\", on='id')\n\n\nnumeric_cols = train[test.columns].select_dtypes(include='number').columns\nmin_max_dict = {col: (train[col].min(), train[col].max()) for col in numeric_cols}\n\ntrain = feature_engineering(train)\ntest = feature_engineering(test)\n\ntrain = train.drop('id', axis=1)\ntest  = test .drop('id', axis=1)   \n\ntrain = train.dropna(subset='sii')\n\ntarget = train['PCIAT-PCIAT_Total']\nsii_target = train['sii']\ntrain = train[test.columns]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:00:58.683188Z","iopub.execute_input":"2025-04-12T14:00:58.683557Z","iopub.status.idle":"2025-04-12T14:00:59.147084Z","shell.execute_reply.started":"2025-04-12T14:00:58.683527Z","shell.execute_reply":"2025-04-12T14:00:59.145901Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def map_pciat_to_sii(pciat_values):\n    return np.select(\n        [pciat_values <= 30, \n         (pciat_values > 30) & (pciat_values <= 49),\n         (pciat_values > 49) & (pciat_values <= 79),\n         pciat_values > 79],\n        [0, 1, 2, 3],\n        default=3  # For PCIAT values greater than 79\n    )\n    \ndef threshold_Rounder(oof_non_rounded, thresholds):\n    return np.where(oof_non_rounded < thresholds[0], 0,\n                    np.where(oof_non_rounded < thresholds[1], 1,\n                             np.where(oof_non_rounded < thresholds[2], 2, 3)))\n\ndef evaluate_predictions(thresholds, y_true, oof_non_rounded):\n    rounded_p = threshold_Rounder(oof_non_rounded, thresholds)\n    return -quadratic_weighted_kappa(y_true, rounded_p)\n\ndef quadratic_weighted_kappa(y_true, y_pred):\n    return cohen_kappa_score(y_true, y_pred, weights='quadratic')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:01:03.145889Z","iopub.execute_input":"2025-04-12T14:01:03.146254Z","iopub.status.idle":"2025-04-12T14:01:03.153251Z","shell.execute_reply.started":"2025-04-12T14:01:03.146223Z","shell.execute_reply":"2025-04-12T14:01:03.151998Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Implementation","metadata":{}},{"cell_type":"code","source":"def select_subset(df, target, subset_size=0.8):\n    df_subset = df.sample(frac=subset_size, random_state=42)\n    target_subset = target.loc[df_subset.index]\n    return df_subset, target_subset\n\n\ndef gaussian_noise_injection(df, target, noise_level, subset_size=0.2):\n\n    # Select a subset of data for augmentation\n    df_subset, target_subset = select_subset(df, target, subset_size)\n\n    # Split numeric and non-numeric columns\n    numeric_cols = df_subset.select_dtypes(include=['float64', 'int64'])\n    non_numeric_cols = df_subset.select_dtypes(exclude=['float64', 'int64'])\n\n    # Impute missing values in numeric columns\n    imputer = SimpleImputer(strategy='mean')\n    numeric_imputed = pd.DataFrame(imputer.fit_transform(numeric_cols), \n                                   columns=numeric_cols.columns, \n                                   index=numeric_cols.index)\n\n    # Add noise to numeric columns\n    augmented_numeric = numeric_imputed\n    for col in augmented_numeric.columns:\n        std_dev = augmented_numeric[col].std()\n        if std_dev > 0:  # Add noise only if variability exists\n            noise = np.random.normal(0, noise_level * std_dev, size=len(augmented_numeric))\n            augmented_numeric[col] += noise\n\n    # Concatenate back with non-numeric columns (align rows)\n    augmented_df = pd.concat([augmented_numeric, non_numeric_cols], axis=1)\n\n    # Ensure the column order matches the original subset\n    augmented_df = augmented_df[df_subset.columns]\n    return augmented_df, target_subset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:01:06.349986Z","iopub.execute_input":"2025-04-12T14:01:06.350358Z","iopub.status.idle":"2025-04-12T14:01:06.358805Z","shell.execute_reply.started":"2025-04-12T14:01:06.350325Z","shell.execute_reply":"2025-04-12T14:01:06.357334Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def augment_data_with_nans(X, target, threshold=0.1, subset_size=0.2):\n   \n    df_subset, target_subset = select_subset(X, target, subset_size)\n    X_augmented = df_subset.reset_index(drop=True).copy()\n    \n    # Identify columns that already contain NaN values\n    columns_with_nan = [col for col in X.columns if X[col].isna().sum() > 0]\n    \n    # Mask for non-NaN values in columns that contain NaNs\n    non_nan_mask = X_augmented[columns_with_nan].notna()\n    \n    # Randomly select which column to set to NaN (for each row) where there's a valid value\n    for col in columns_with_nan:\n        # Create a random mask for columns with valid values (non-NaN)\n        random_mask = np.random.rand(len(X_augmented)) < threshold  # Adjust probability as needed\n        \n        # Apply the mask to select rows and set that column's value to NaN\n        X_augmented.loc[random_mask, col] = np.nan\n    \n    return X_augmented, target_subset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:01:10.969533Z","iopub.execute_input":"2025-04-12T14:01:10.969963Z","iopub.status.idle":"2025-04-12T14:01:10.976984Z","shell.execute_reply.started":"2025-04-12T14:01:10.969930Z","shell.execute_reply":"2025-04-12T14:01:10.975647Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_confusion_matrix(y_true, y_pred, labels=None):\n    y_true = y_true.astype(np.int32)\n    y_pred = y_pred.astype(np.int32)\n    \n    if labels is None:\n        labels = sorted(set(y_true))\n\n    cm = confusion_matrix(y_true, y_pred, labels=labels)\n\n    disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=labels)\n    disp.plot(cmap='Blues', values_format='d')\n\n    plt.title(\"Confusion Matrix\")\n    plt.xlabel(\"Predicted Label\")\n    plt.ylabel(\"True Label\")\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:01:41.844485Z","iopub.execute_input":"2025-04-12T14:01:41.844903Z","iopub.status.idle":"2025-04-12T14:01:41.850747Z","shell.execute_reply.started":"2025-04-12T14:01:41.844869Z","shell.execute_reply":"2025-04-12T14:01:41.849638Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def cross_validate_model(params, X, y, sii_target, label='', save_models=True, pruning_callback=None, n_repeats=5, return_qwk=False):\n    features = X.columns\n    start_time = time.time()\n    oof = []\n    y_oof = []\n    qwk_list = []\n    model_list = []\n   \n    n = 0\n    for repeat in tqdm(range(n_repeats)):\n        random_seed = np.random.randint(0, 10000)  # Generate a random seed for each repeat\n        folds = StratifiedKFold(n_splits=5, shuffle=True, random_state=repeat)\n        \n        for fold, (idx_tr, idx_va) in enumerate(folds.split(X, sii_target)):\n            params['random_seeds'] = n\n            set_global_seed(n)\n            X_tr = X.iloc[idx_tr]\n            X_va = X.iloc[idx_va]\n            y_tr = y.iloc[idx_tr]\n            y_va = y.iloc[idx_va]\n            \n            \n            nan_prone_columns = [\n                col for col in X_tr.columns \n                if X_tr[col].isna().any()  # Has NaNs\n            ]\n    \n            # Step 1: Perform augmentation on X_tr\n            nan_augmented, nan_aug_target = augment_data_with_nans(X_tr, target, threshold=1, subset_size=0.2)\n            noise_augmented, noise_aug_target = gaussian_noise_injection(X_tr, y_tr, noise_level=0.02, subset_size=0.5)\n    \n            X_tr_augmented = pd.concat(\n                [nan_augmented, noise_augmented, X_tr[y_tr>49], X_tr[y_tr>49], X_tr[y_tr>49], X_tr[y_tr>79]],\n                ignore_index=True).reset_index(drop=True)\n            \n            y_tr_augmented = pd.concat(\n                [nan_aug_target, noise_aug_target, y_tr[y_tr>49], y_tr[y_tr>49], y_tr[y_tr>49], y_tr[y_tr>79]],\n                ignore_index=True).reset_index(drop=True)\n\n\n            X_tr_combined = pd.concat([X_tr, X_tr_augmented], ignore_index=True).reset_index(drop=True)\n            y_tr_combined = pd.concat([y_tr, y_tr_augmented], ignore_index=True).reset_index(drop=True)\n\n            shuffled_indices = np.random.permutation(X_tr_combined.index)\n            X_tr_combined = X_tr_combined.iloc[shuffled_indices].reset_index(drop=True)\n            y_tr_combined = y_tr_combined.iloc[shuffled_indices].reset_index(drop=True)\n\n            \n            dtrain = lgb.Dataset(X_tr_combined, label=y_tr_combined)\n            dvalid = lgb.Dataset(X_va, label=y_va)\n\n            model = lgb.train(\n                params,\n                dtrain,\n                valid_sets=[dtrain, dvalid],\n                num_boost_round=params['n_estimators'],\n            )\n\n            y_pred = model.predict(X_va)\n\n            if save_models:\n                model_list.append(model)\n            oof.append(y_pred)\n            y_oof.append(y_va)\n            \n            n +=1\n    elapsed_time = time.time() - start_time\n\n    y_oof_actuals = np.concatenate(y_oof)\n    oof_preds = np.concatenate(oof)\n    \n    # Post-processing: Map predictions\n    y_oof_sii = map_pciat_to_sii(y_oof_actuals)\n    oof_sii = map_pciat_to_sii(oof_preds)\n\n  \n    qwk = cohen_kappa_score(y_oof_sii, oof_sii, weights='quadratic')\n    mse = ((y_oof_actuals - oof_preds)**2).mean()  \n    print(f\"Overall QWK: {qwk:.3f}, MSE: {mse:.3f}, Time: {int((time.time() - start_time) / 60)} min\")\n\n    # Optimize thresholds\n    threshold_optimizer = minimize(evaluate_predictions, \n                                   x0=[34, 49, 62], \n                                   args=(y_oof_sii, oof_preds), \n                                   method='Nelder-Mead')\n    \n    optimized_preds = threshold_Rounder(oof_preds, threshold_optimizer.x)\n    optimized_qwk = cohen_kappa_score(y_oof_sii, optimized_preds, weights='quadratic')\n    accuracy = (y_oof_sii==optimized_preds).astype(np.float32).mean()\n    print(f\"Optimized QWK: {optimized_qwk:.3f}, Accuracy: {accuracy:.3f}, Thresholds: {threshold_optimizer.x}\")\n    \n    plot_confusion_matrix(y_oof_sii, oof_sii)\n    \n    if save_models:\n        saved_models[label] = {'features': features, 'model_list': model_list}\n\n    return optimized_qwk, threshold_optimizer.x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:01:46.336236Z","iopub.execute_input":"2025-04-12T14:01:46.336667Z","iopub.status.idle":"2025-04-12T14:01:46.352397Z","shell.execute_reply.started":"2025-04-12T14:01:46.336632Z","shell.execute_reply":"2025-04-12T14:01:46.351071Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"saved_models = {}\nresults = []\nfor i in range(1):\n    params = {'verbosity': -1,  'device': 'cpu', 'metric': 'mse', 'n_estimators':150, 'max_depth':5, 'max_bin': 15, 'boosting_type': 'gbdt', 'lambda_l1': 0.0012071403780584485, 'lambda_l2': 19.943477818207878, 'min_child_weight': 0.01586977190723854, 'learning_rate': 0.030512450456770007, 'num_leaves': 295, 'colsample_bytree': 0.8569995659929517, 'bagging_fraction': 0.587037100215173, 'feature_fraction': 0.8955475330753205, 'bagging_freq': 1}\n    qwk, qwk_thresholded = cross_validate_model(params, train, target, sii_target, label='trial', save_models=True, n_repeats=100)\n    print(qwk)\n    results.append(qwk)\nprint(f\"'mean {np.mean(results)}\")\nprint(f\"diff {max(results) - min(results)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:01:53.181638Z","iopub.execute_input":"2025-04-12T14:01:53.182041Z","iopub.status.idle":"2025-04-12T14:07:51.936961Z","shell.execute_reply.started":"2025-04-12T14:01:53.182010Z","shell.execute_reply":"2025-04-12T14:07:51.935890Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred = [model.predict(test)  for model in saved_models['trial']['model_list']]\n\nn = 16\ni = 500\nplt.hist(np.array(pred)[:, n][:i], bins=30, alpha=0.7)\n\n# Get the mode\nmode_val = stats.mode(np.array(pred)[:, n][:i].round())[0]  # mode.value[0]\n\n# Overlay the mode on the histogram\nplt.axvline(mode_val, color='k', linestyle='dashed', linewidth=2, label=f'Mode: {mode_val}')\nplt.axvline(np.array(pred)[:, n][:i].mean(), color='r', linestyle='dashed', linewidth=2, label=f'mean: {np.array(pred)[:, n][:i].mean()}')\n# Add a label\nplt.legend()\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:07:58.883653Z","iopub.execute_input":"2025-04-12T14:07:58.884060Z","iopub.status.idle":"2025-04-12T14:08:05.314360Z","shell.execute_reply.started":"2025-04-12T14:07:58.884029Z","shell.execute_reply":"2025-04-12T14:08:05.313130Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions = stats.mode(threshold_Rounder(np.array([model.predict(test) for model in saved_models['trial']['model_list']]), qwk_thresholded).astype(np.int32))[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:08:09.925771Z","iopub.execute_input":"2025-04-12T14:08:09.926141Z","iopub.status.idle":"2025-04-12T14:08:15.950837Z","shell.execute_reply.started":"2025-04-12T14:08:09.926110Z","shell.execute_reply":"2025-04-12T14:08:15.949956Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df = pd.read_csv('/kaggle/input/child-mind-institute-problematic-internet-use/sample_submission.csv')\n\nsubmission_df['sii'] = predictions\nsubmission_df.to_csv('submission.csv', index=False)\npd.read_csv('./submission.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T14:08:51.080786Z","iopub.execute_input":"2025-04-12T14:08:51.081152Z","iopub.status.idle":"2025-04-12T14:08:51.102265Z","shell.execute_reply.started":"2025-04-12T14:08:51.081124Z","shell.execute_reply":"2025-04-12T14:08:51.101290Z"}},"outputs":[],"execution_count":null}]}