{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":93783,"databundleVersionId":11331308,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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-05-30T12:05:20.295173Z","iopub.execute_input":"2025-05-30T12:05:20.295387Z","iopub.status.idle":"2025-05-30T12:05:23.11497Z","shell.execute_reply.started":"2025-05-30T12:05:20.29536Z","shell.execute_reply":"2025-05-30T12:05:23.114153Z"}},"outputs":[{"name":"stdout","text":"/kaggle/input/esa-adb-challenge/train.parquet\n/kaggle/input/esa-adb-challenge/sample_submission.parquet\n/kaggle/input/esa-adb-challenge/target_channels.csv\n/kaggle/input/esa-adb-challenge/test.parquet\n","output_type":"stream"}],"execution_count":1},{"cell_type":"code","source":"# Load channels 41-46 and ground truth from the training set\nchannel_names = [f\"channel_{x}\" for x in range(41,46 + 1)]\ncolumns_to_load = [\"id\"] + channel_names + [\"is_anomaly\"]\ndf = pd.read_parquet(\"/kaggle/input/esa-adb-challenge/train.parquet\", columns=columns_to_load)\ndf","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T12:05:23.116419Z","iopub.execute_input":"2025-05-30T12:05:23.116715Z","iopub.status.idle":"2025-05-30T12:05:25.588871Z","shell.execute_reply.started":"2025-05-30T12:05:23.116697Z","shell.execute_reply":"2025-05-30T12:05:25.5882Z"}},"outputs":[{"execution_count":2,"output_type":"execute_result","data":{"text/plain":"                id  channel_41  channel_42  channel_43  channel_44  \\\n0                0    0.812578    0.786344    0.771900    0.799178   \n1                1    0.812578    0.786344    0.771900    0.799178   \n2                2    0.821213    0.789557    0.770317    0.809411   \n3                3    0.819642    0.786344    0.770317    0.807050   \n4                4    0.821996    0.788753    0.770317    0.807837   \n...            ...         ...         ...         ...         ...   \n14728316  14728316    0.806299    0.799999    0.767944    0.792093   \n14728317  14728317    0.806299    0.790360    0.765569    0.792881   \n14728318  14728318    0.806299    0.795180    0.765569    0.792881   \n14728319  14728319    0.806299    0.796786    0.767152    0.796816   \n14728320  14728320    0.808654    0.792770    0.764778    0.796030   \n\n          channel_45  channel_46  is_anomaly  \n0           0.816855    0.765296           0  \n1           0.816855    0.765296           0  \n2           0.816006    0.765296           0  \n3           0.816855    0.766985           0  \n4           0.818551    0.761073           0  \n...              ...         ...         ...  \n14728316    0.821094    0.752628           0  \n14728317    0.821941    0.763607           0  \n14728318    0.822790    0.762762           0  \n14728319    0.820247    0.761073           0  \n14728320    0.823637    0.764451           0  \n\n[14728321 rows x 8 columns]","text/html":"<div>\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }\n\n    .dataframe tbody tr th {\n        vertical-align: top;\n    }\n\n    .dataframe thead th {\n        text-align: right;\n    }\n</style>\n<table border=\"1\" class=\"dataframe\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th></th>\n      <th>id</th>\n      <th>channel_41</th>\n      <th>channel_42</th>\n      <th>channel_43</th>\n      <th>channel_44</th>\n      <th>channel_45</th>\n      <th>channel_46</th>\n      <th>is_anomaly</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>0</th>\n      <td>0</td>\n      <td>0.812578</td>\n      <td>0.786344</td>\n      <td>0.771900</td>\n      <td>0.799178</td>\n      <td>0.816855</td>\n      <td>0.765296</td>\n      <td>0</td>\n    </tr>\n    <tr>\n      <th>1</th>\n      <td>1</td>\n      <td>0.812578</td>\n      <td>0.786344</td>\n      <td>0.771900</td>\n      <td>0.799178</td>\n      <td>0.816855</td>\n      <td>0.765296</td>\n      <td>0</td>\n    </tr>\n    <tr>\n      <th>2</th>\n      <td>2</td>\n      <td>0.821213</td>\n      <td>0.789557</td>\n      <td>0.770317</td>\n      <td>0.809411</td>\n      <td>0.816006</td>\n      <td>0.765296</td>\n      <td>0</td>\n    </tr>\n    <tr>\n      <th>3</th>\n      <td>3</td>\n      <td>0.819642</td>\n      <td>0.786344</td>\n      <td>0.770317</td>\n      <td>0.807050</td>\n      <td>0.816855</td>\n      <td>0.766985</td>\n      <td>0</td>\n    </tr>\n    <tr>\n      <th>4</th>\n      <td>4</td>\n      <td>0.821996</td>\n      <td>0.788753</td>\n      <td>0.770317</td>\n      <td>0.807837</td>\n      <td>0.818551</td>\n      <td>0.761073</td>\n      <td>0</td>\n    </tr>\n    <tr>\n      <th>...</th>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n    </tr>\n    <tr>\n      <th>14728316</th>\n      <td>14728316</td>\n      <td>0.806299</td>\n      <td>0.799999</td>\n      <td>0.767944</td>\n      <td>0.792093</td>\n      <td>0.821094</td>\n      <td>0.752628</td>\n      <td>0</td>\n    </tr>\n    <tr>\n      <th>14728317</th>\n      <td>14728317</td>\n      <td>0.806299</td>\n      <td>0.790360</td>\n      <td>0.765569</td>\n      <td>0.792881</td>\n      <td>0.821941</td>\n      <td>0.763607</td>\n      <td>0</td>\n    </tr>\n    <tr>\n      <th>14728318</th>\n      <td>14728318</td>\n      <td>0.806299</td>\n      <td>0.795180</td>\n      <td>0.765569</td>\n      <td>0.792881</td>\n      <td>0.822790</td>\n      <td>0.762762</td>\n      <td>0</td>\n    </tr>\n    <tr>\n      <th>14728319</th>\n      <td>14728319</td>\n      <td>0.806299</td>\n      <td>0.796786</td>\n      <td>0.767152</td>\n      <td>0.796816</td>\n      <td>0.820247</td>\n      <td>0.761073</td>\n      <td>0</td>\n    </tr>\n    <tr>\n      <th>14728320</th>\n      <td>14728320</td>\n      <td>0.808654</td>\n      <td>0.792770</td>\n      <td>0.764778</td>\n      <td>0.796030</td>\n      <td>0.823637</td>\n      <td>0.764451</td>\n      <td>0</td>\n    </tr>\n  </tbody>\n</table>\n<p>14728321 rows × 8 columns</p>\n</div>"},"metadata":{}}],"execution_count":2},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nplt.figure(figsize=(12,4))\nplt.plot(df['channel_41'], label='ch41')\n# mark anomalies\nanom_idx = df.index[df['is_anomaly']==1]\nplt.scatter(anom_idx, df.loc[anom_idx,'channel_41'], color='r', s=5, label='anomaly')\nplt.legend(); plt.title('Channel 41 with Anomaly Flags')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T12:05:25.589744Z","iopub.execute_input":"2025-05-30T12:05:25.590033Z","iopub.status.idle":"2025-05-30T12:06:09.246638Z","shell.execute_reply.started":"2025-05-30T12:05:25.590007Z","shell.execute_reply":"2025-05-30T12:06:09.246003Z"}},"outputs":[{"execution_count":3,"output_type":"execute_result","data":{"text/plain":"Text(0.5, 1.0, 'Channel 41 with Anomaly Flags')"},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"<Figure size 1200x400 with 1 Axes>","image/png":"iVBORw0KGgoAAAANSUhEUgAAA9UAAAGICAYAAABPxcMfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABuKElEQVR4nO3dd3xT9f7H8fdJ0sHe0AKVMkT2EAQrVEUrqAzxKjJUhooDcCHiZGMRGeJAERS8KojKz3nBiaIguBCcyBAKyEZlQ0dyfn+Epg0dNKHJSdLX8/HoozknZ3zOzPmc7/d8j2GapikAAAAAAOAzm9UBAAAAAAAQrkiqAQAAAADwE0k1AAAAAAB+IqkGAAAAAMBPJNUAAAAAAPiJpBoAAAAAAD+RVAMAAAAA4CeSagAAAAAA/ERSDQAAAACAn0iqAQAehmFo2LBhVodRbAzD0NixY60O44ykpaXJMAy9/PLLRR526tSpgQ8szCUmJmrgwIFBnefAgQOVmJgY1HkCAAKPpBoASoA///xTt912m+rVq6fY2FiVL19eHTp00FNPPaXjx49bHV5IWbFihQzDkGEY2r9/v9d369ev17333qsLLrhAsbGxMgxDaWlpQY9xyZIlAb9ZsGTJEhmGoZo1a8rlcgV0XuEu+2ZGfn/nn3++1eEBAALMYXUAAIDAWrx4sXr16qWYmBj1799fzZo1U0ZGhlasWKH7779fv/32m2bPnm11mCHB5XLpzjvvVJkyZXT06NE8369atUpPP/20mjRposaNG2vt2rUBj6lOnTo6fvy4oqKiPP2WLFmimTNnBjSxnj9/vhITE5WWlqbPP/9cKSkpAZtXpOjbt6+uvPJKr37VqlWzKBoAQLCQVANABNuyZYv69OmjOnXq6PPPP1d8fLznu6FDh2rTpk1avHixhRGGltmzZ2v79u265ZZb9NRTT+X5vkePHjpw4IDKlSunqVOnBiWpNgxDsbGxAZ9PbkePHtV7772nSZMmad68eZo/fz5JdRGce+65uuGGG6wOAwAQZFT/BoAI9sQTT+jIkSN66aWXvBLqbA0aNNDdd9+dp/+7776rZs2aKSYmRk2bNtVHH33k9f3WrVs1ZMgQnXPOOSpVqpSqVKmiXr165akK/fLLL8swDH399dcaPny4qlWrpjJlyujqq6/Wvn37vIZNTExUt27dtGLFCrVr106xsbGqV6+eXnnllTzxHThwQPfcc48SEhIUExOjBg0aaPLkyWdUTfmff/7Ro48+qvHjx6tixYr5DlO5cmWVK1fOr+kPHz5cVapUkWmann533nmnDMPQ008/7em3Z88eGYah559/XlLeZ6oHDhyomTNnSpJXNeNTzZ49W/Xr11dMTIzOO+88ff/990WO9Z133tHx48fVq1cv9enTR2+//bZOnDiRZ7jsZ/BPt79I0po1a3TFFVeofPnyKlu2rC699FJ98803XsNk7y8rVqzQXXfdpWrVqqlixYq67bbblJGRoQMHDqh///6qVKmSKlWqpJEjR3qtT0maOnWqLrjgAlWpUkWlSpVSmzZttGjRokKXd/PmzTIMQ08++WSe71auXCnDMPT6668XZdX5rKjxHj9+XHfddZeqVq2qcuXKqUePHtqxY0eedgMOHz6se+65R4mJiYqJiVH16tV12WWX6ccffwxI/AAAkmoAiGgffPCB6tWrpwsuuKDI46xYsUJDhgxRnz599MQTT+jEiRO65ppr9Pfff3uG+f7777Vy5Ur16dNHTz/9tG6//XYtXbpUF198sY4dO5Znmnfeead++uknjRkzRnfccYc++OCDfBtE27Rpk6699lpddtllmjZtmipVqqSBAwfqt99+8wxz7NgxXXTRRXrttdfUv39/Pf300+rQoYMeeughDR8+3Mc1lGPUqFGKi4vTbbfd5vc0CpOcnKx//vnHa1mWL18um82m5cuXe/WTpAsvvDDf6dx222267LLLJEmvvvqq5y+3BQsWaMqUKbrttts0ceJEpaWl6T//+Y8yMzOLFOv8+fPVqVMnxcXFqU+fPjp8+LA++OCDfIctyv7y22+/KTk5WT/99JNGjhypUaNGacuWLbr44ov17bff5pnmnXfeqY0bN2rcuHHq0aOHZs+erVGjRql79+5yOp1KTU1Vx44dNWXKlDzL/tRTT6l169YaP368UlNT5XA41KtXr0JrZNSrV08dOnTQ/Pnz810X5cqV01VXXXXa9Xbs2DHt37/f6+9067yo8Q4cOFDPPPOMrrzySk2ePFmlSpVS165d80zv9ttv1/PPP69rrrlGzz33nEaMGKFSpUpp3bp1p40fAOAnEwAQkQ4ePGhKMq+66qoijyPJjI6ONjdt2uTp99NPP5mSzGeeecbT79ixY3nGXbVqlSnJfOWVVzz95s2bZ0oyU1JSTJfL5el/7733mna73Txw4ICnX506dUxJ5ldffeXpt3fvXjMmJsa87777PP0mTJhglilTxtywYYPX/B988EHTbreb27Zt81qeMWPGnHa5f/rpJ9Nut5sff/yxaZqmOWbMGFOSuW/fvgLHmTJliinJ3LJly2mnn70sksznnnvONE3TPHDggGmz2cxevXqZNWrU8Ax31113mZUrV/asry1btpiSzHnz5nmGGTp0qJnfT3j2sFWqVDH/+ecfT//33nvPlGR+8MEHp41zz549psPhMOfMmePpd8EFF+S7HxV1f+nZs6cZHR1t/vnnn55+O3fuNMuVK2deeOGFnn7Z+0uXLl289pekpCTTMAzz9ttv9/TLysoya9eubV500UVeMZ26b2ZkZJjNmjUzL7nkEq/+derUMQcMGODpfuGFF0xJ5rp167zGrVq1qtdw+cle7/n9ffHFF57hBgwYYNapU8fneFevXm1KMu+55x6vYQcOHJhnH69QoYI5dOjQQuMFABQvSqoBIEIdOnRIknyurpySkqL69et7ulu0aKHy5ctr8+bNnn6lSpXyfM7MzNTff/+tBg0aqGLFivlWM7311lu9qignJyfL6XRq69atXsM1adJEycnJnu5q1arpnHPO8Zr3W2+9peTkZFWqVMmrRDAlJUVOp1NfffWVT8srSXfddZeuuOIKde7c2edxi6patWpq1KiRJ76vv/5adrtd999/v/bs2aONGzdKcpdUd+zYMd8q3UXVu3dvVapUydOdvU5zr8eCLFy4UDabTddcc42nX9++ffXhhx/q33//zTP86fYXp9OpTz75RD179lS9evU8w8XHx6tfv35asWKFZ1/NdvPNN3stf/v27WWapm6++WZPP7vdrrZt2+ZZptz75r///quDBw8qOTn5tNWfr7vuOsXGxnqVVn/88cfav39/kZ+TvvXWW/Xpp596/bVs2bLQcYoSb3Z1+iFDhniNe+edd+aZXsWKFfXtt99q586dRYoZAHDmaKgMACJU+fLlJbmfsfTFWWedladfpUqVvBKq48ePexqx2rFjh9dzrQcPHjztNLMTvlOTtKLMe+PGjfr5558LbFV57969+fYvyBtvvKGVK1fq119/9Wk8fyQnJ2vJkiWS3Mlz27Zt1bZtW1WuXFnLly9XjRo19NNPP6lfv35nNJ+iru/8vPbaa2rXrp3+/vtvTxXu1q1bKyMjQ2+99ZZuvfXWQueVPb/see3bt0/Hjh3TOeeck2e4xo0by+Vyafv27WratGmB06xQoYIkKSEhIU//U5fpf//7nyZOnKi1a9cqPT3d0/90NykqVqyo7t27a8GCBZowYYIkd9XvWrVq6ZJLLil03Gxnn322zw26FSXerVu3ymazqW7dul7jNmjQIM/0nnjiCQ0YMEAJCQlq06aNrrzySvXv39/rhgYAoHiRVANAhCpfvrxq1qzpc7Jot9vz7W+e0sDWvHnzdM899ygpKUkVKlSQYRjq06dPvo2FFWWaRR3O5XLpsssu08iRI/MdtmHDhvn2L8j999+vXr16KTo62tPQ2oEDByRJ27dvV0ZGhmrWrOnTNAvSsWNHzZkzR5s3b9by5cuVnJwswzDUsWNHLV++3PNO6Nyl9f4o6vo+1caNGz0Nmp199tl5vp8/f36epNrfeRWmoGnm1z/3fJYvX64ePXrowgsv1HPPPaf4+HhFRUVp3rx5WrBgwWnn279/f7311ltauXKlmjdvrvfff19DhgyRzRaYin1nGm9+rrvuOiUnJ+udd97RJ598oilTpmjy5Ml6++23dcUVVxTzEgAAJJJqAIho3bp10+zZs7Vq1SolJSUV23QXLVqkAQMGaNq0aZ5+J06c8CSjgVS/fn0dOXKk2F7xtH37di1YsCDfJObcc89Vy5Yti+3VWdnJ8qeffqrvv/9eDz74oCR3o2TPP/+8atasqTJlyqhNmzaFTudMqoYXZv78+YqKitKrr76aJ4FdsWKFnn76aW3bti3f0umCVKtWTaVLl9b69evzfPfHH3/IZrPlKYH21//93/8pNjZWH3/8sWJiYjz9582bV6TxL7/8clWrVk3z589X+/btdezYMd14443FElt+ihpvnTp15HK5tGXLFq+bHZs2bcp3uvHx8RoyZIiGDBmivXv36txzz9Vjjz1GUg0AAcIz1QAQwUaOHKkyZcrolltu0Z49e/J8/+eff+b7PubTsdvteUoin3nmGTmdTr9jLarrrrtOq1at0scff5znuwMHDigrK8un6b3zzjt5/nr37i1JeuWVV/J9zZK/6tatq1q1aunJJ59UZmamOnToIMmdbP/5559atGiRzj//fDkchd/zLlOmjCQV+02M+fPnKzk5Wb1799a1117r9Xf//fdLks+vlrLb7ercubPee+89r1eu7dmzRwsWLFDHjh09jyqcKbvdLsMwvPbDtLQ0vfvuu0Ua3+FwqG/fvnrzzTf18ssvq3nz5mrRokWxxJafosbbpUsXSdJzzz3n1f+ZZ57x6nY6nXkev6hevbpq1qzpVbUcAFC8KKkGgAhWv359LViwQL1791bjxo3Vv39/NWvWTBkZGVq5cqXeeustDRw40OfpduvWTa+++qoqVKigJk2aaNWqVfrss89UpUqV4l+IU9x///16//331a1bNw0cOFBt2rTR0aNH9csvv2jRokVKS0tT1apVizy9nj175umXXTJ9xRVXeE3r4MGDnkTm66+/liQ9++yzqlixoipWrJjva8JOlZycrIULF6p58+aeZ53PPfdclSlTRhs2bCjS89TZJdl33XWXunTpIrvdrj59+px2vMJ8++232rRpU4HLUKtWLZ177rmaP3++HnjgAZ+mPXHiRH366afq2LGjhgwZIofDoRdeeEHp6el64oknziju3Lp27arp06fr8ssvV79+/bR3717NnDlTDRo00M8//1ykaWS/pu2LL77Q5MmTiy22M4m3TZs2uuaaazRjxgz9/fffOv/88/Xll19qw4YNknJqLhw+fFi1a9fWtddeq5YtW6ps2bL67LPP9P3333vVKgEAFC+SagCIcD169NDPP/+sKVOm6L333tPzzz+vmJgYtWjRQtOmTdPgwYN9nuZTTz0lu92u+fPn68SJE+rQoYM+++wzT4laIJUuXVpffvmlUlNT9dZbb+mVV15R+fLl1bBhQ40bN87TqFUg/Pvvvxo1apRXv+xkpU6dOj4l1R07dvT0czgcSkpK0meffVak56n/85//6M4779TChQv12muvyTTNM06qs1u97t69e4HDdO/eXWPHjtXPP//sUwlu06ZNtXz5cj300EOaNGmSXC6X2rdvr9dee03t27c/o7hzu+SSS/TSSy/p8ccf1z333KO6detq8uTJSktLK3JS3aZNGzVt2lTr1q3T9ddfX2yxnWm8r7zyiuLi4vT666/rnXfeUUpKit544w2dc845io2NleQ+NoYMGaJPPvlEb7/9tlwulxo0aKDnnntOd9xxR0CXBQBKMsM8k5ZEAAAAIkzr1q1VuXJlLV261OpQCrV27Vq1bt1ar732WsBvAAAACsYz1QAAACf98MMPWrt2rfr37291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YUlSQkKCxZEAAAAAAEqSw4cPq0KFCgV+b5inS7tDgMvl0s6dO1WuXDkZhmF1OAU6dOiQEhIStH379kJfDo7Qx7aMHGzLyMB2jBxsy8jBtowcbMvIwbYsXqZp6vDhw6pZs6ZstoKfnA6LkmqbzabatWtbHUaRlS9fnp04QrAtIwfbMjKwHSMH2zJysC0jB9sycrAti09hJdTZaKgMAAAAAAA/kVQDAAAAAOAnkupiFBMTozFjxigmJsbqUHCG2JaRg20ZGdiOkYNtGTnYlpGDbRk52JbWCIuGygAAAAAACEWUVAMAAAAA4CeSagAAAAAA/ERSDQAAAACAn0iqAQAAAADwE0m1j2bOnKnExETFxsaqffv2+u677wod/q233lKjRo0UGxur5s2ba8mSJUGKFKfjy7acM2eOkpOTValSJVWqVEkpKSmn3fYIHl+Py2wLFy6UYRjq2bNnYANEkfi6HQ8cOKChQ4cqPj5eMTExatiwIefYEOHrtpwxY4bOOecclSpVSgkJCbr33nt14sSJIEWLgnz11Vfq3r27atasKcMw9O677552nGXLluncc89VTEyMGjRooJdffjngcaJwvm7Ht99+W5dddpmqVaum8uXLKykpSR9//HFwgkWh/Dkms3399ddyOBxq1apVwOIryUiqffDGG29o+PDhGjNmjH788Ue1bNlSXbp00d69e/MdfuXKlerbt69uvvlmrVmzRj179lTPnj3166+/BjlynMrXbbls2TL17dtXX3zxhVatWqWEhAR17txZO3bsCHLkOJWv2zJbWlqaRowYoeTk5CBFisL4uh0zMjJ02WWXKS0tTYsWLdL69es1Z84c1apVK8iR41S+bssFCxbowQcf1JgxY7Ru3Tq99NJLeuONN/Twww8HOXKc6ujRo2rZsqVmzpxZpOG3bNmirl27qlOnTlq7dq3uuece3XLLLSRkFvN1O3711Ve67LLLtGTJEq1evVqdOnVS9+7dtWbNmgBHitPxdVtmO3DggPr3769LL700QJFBJoqsXbt25tChQz3dTqfTrFmzpjlp0qR8h7/uuuvMrl27evVr3769edtttwU0Tpyer9vyVFlZWWa5cuXM//73v4EKEUXkz7bMysoyL7jgAvPFF180BwwYYF511VVBiBSF8XU7Pv/882a9evXMjIyMYIWIIvJ1Ww4dOtS85JJLvPoN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\n"},"metadata":{}}],"execution_count":3},{"cell_type":"code","source":"# XGBOOST\nimport pandas as pd\nimport numpy as np\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\nfrom xgboost import XGBClassifier\nfrom sklearn.metrics import precision_score, recall_score\nimport os\n\n\n\nfeatures = [f'channel_{i}' for i in range(41,47)]\nX = df[features].fillna(df[features].median())\ny = df['is_anomaly'].astype(int).values\n\nscaler = StandardScaler()\nX_scaled = scaler.fit_transform(X)\n\nX_tr, X_te, y_tr, y_te = train_test_split(\n    X_scaled, y,\n    test_size=0.2, stratify=y, random_state=42\n)\n\nmodel = XGBClassifier(\n    n_estimators=100,\n    learning_rate=0.1,\n    max_depth=4,\n    use_label_encoder=False,\n    eval_metric='logloss',\n    random_state=42\n)\nmodel.fit(X_tr, y_tr)\n\ny_pred = model.predict(X_te).astype(bool)\ny_true = y_te.astype(bool)\n\ndef extract_events(arr):\n    events, in_event = [], False\n    for i, v in enumerate(arr):\n        if v and not in_event:\n            start, in_event = i, True\n        elif not v and in_event:\n            events.append((start, i-1)); in_event = False\n    if in_event: events.append((start, len(arr)-1))\n    return events\n\ndef corrected_event_fbeta(y_true, y_pred, beta=0.5):\n    true_events = extract_events(y_true)\n    pred_events = extract_events(y_pred)\n\n    TP_e = sum(any(y_pred[s:e+1]) for s,e in true_events)\n    FN_e = len(true_events) - TP_e\n    FP_e = sum(\n        1 for s,e in pred_events\n        if not any(not (e < ts or s > te) for ts,te in true_events)\n    )\n    FP_t = np.sum((y_pred==1)&(y_true==0))\n    N_t  = np.sum(y_true==0)\n\n    Prec_e = TP_e/(TP_e+FP_e) if TP_e+FP_e>0 else 0\n    Prec_e_corr = Prec_e * (1 - FP_t/N_t)\n    Rec_e = TP_e/(TP_e+FN_e) if TP_e+FN_e>0 else 0\n\n    b2 = beta**2\n    Fbeta = ((1+b2)*Prec_e_corr*Rec_e)/(b2*Prec_e_corr + Rec_e) if (Prec_e_corr+Rec_e)>0 else 0\n\n    return TP_e, FP_e, FN_e, FP_t, N_t, Prec_e_corr, Rec_e, Fbeta\n\nTP_e, FP_e, FN_e, FP_t, N_t, P_corr, R_e, F05 = corrected_event_fbeta(y_true, y_pred, 0.5)\n\nprint(f\"Event‐wise True Positives  : {TP_e}\")\nprint(f\"Event‐wise False Positives : {FP_e}\")\nprint(f\"Event‐wise False Negatives : {FN_e}\")\nprint(f\"Sample‐wise FP_t           : {FP_t} / {N_t} nominal samples\")\nprint(f\"Corrected Precision_e      : {P_corr:.4f}\")\nprint(f\"Recall_e                   : {R_e:.4f}\")\nprint(f\"Corrected Event‐wise F0.5  : {F05:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T12:06:09.247467Z","iopub.execute_input":"2025-05-30T12:06:09.247718Z","iopub.status.idle":"2025-05-30T12:12:04.108691Z","shell.execute_reply.started":"2025-05-30T12:06:09.247698Z","shell.execute_reply":"2025-05-30T12:12:04.107932Z"}},"outputs":[{"name":"stdout","text":"Event‐wise True Positives  : 35050\nEvent‐wise False Positives : 215\nEvent‐wise False Negatives : 241323\nSample‐wise FP_t           : 226 / 2636844 nominal samples\nCorrected Precision_e      : 0.9938\nRecall_e                   : 0.1268\nCorrected Event‐wise F0.5  : 0.4198\n","output_type":"stream"}],"execution_count":4},{"cell_type":"code","source":"channel_names = [f\"channel_{x}\" for x in range(41,46 + 1)]\ncolumns_to_load = [\"id\"] + channel_names \ndf_test = pd.read_parquet(\"/kaggle/input/esa-adb-challenge/test.parquet\", columns=columns_to_load)\ndf_test","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T12:12:04.11043Z","iopub.execute_input":"2025-05-30T12:12:04.110762Z","iopub.status.idle":"2025-05-30T12:12:04.29454Z","shell.execute_reply.started":"2025-05-30T12:12:04.110743Z","shell.execute_reply":"2025-05-30T12:12:04.293866Z"}},"outputs":[{"execution_count":5,"output_type":"execute_result","data":{"text/plain":"              id  channel_41  channel_42  channel_43  channel_44  channel_45  \\\n0       14728321    0.807868    0.794377    0.764778    0.797604    0.820247   \n1       14728322    0.807868    0.794377    0.764778    0.797604    0.820247   \n2       14728323    0.808654    0.792770    0.763195    0.795242    0.821094   \n3       14728324    0.806299    0.791966    0.765569    0.796030    0.820247   \n4       14728325    0.809439    0.799195    0.763195    0.796816    0.821941   \n...          ...         ...         ...         ...         ...         ...   \n521275  15249596    0.801590    0.794377    0.763987    0.788945    0.819398   \n521276  15249597    0.803160    0.795180    0.764778    0.792093    0.818551   \n521277  15249598    0.804729    0.796786    0.763987    0.790520    0.816006   \n521278  15249599    0.806299    0.789557    0.763987    0.792881    0.816855   \n521279  15249600    0.804729    0.793574    0.763195    0.796030    0.817702   \n\n        channel_46  \n0         0.764451  \n1         0.764451  \n2         0.753472  \n3         0.763607  \n4         0.760229  \n...            ...  \n521275    0.760229  \n521276    0.762762  \n521277    0.763607  \n521278    0.757695  \n521279    0.756850  \n\n[521280 rows x 7 columns]","text/html":"<div>\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }\n\n    .dataframe tbody tr th {\n        vertical-align: top;\n    }\n\n    .dataframe thead th {\n        text-align: right;\n    }\n</style>\n<table border=\"1\" class=\"dataframe\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th></th>\n      <th>id</th>\n      <th>channel_41</th>\n      <th>channel_42</th>\n      <th>channel_43</th>\n      <th>channel_44</th>\n      <th>channel_45</th>\n      <th>channel_46</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>0</th>\n      <td>14728321</td>\n      <td>0.807868</td>\n      <td>0.794377</td>\n      <td>0.764778</td>\n      <td>0.797604</td>\n      <td>0.820247</td>\n      <td>0.764451</td>\n    </tr>\n    <tr>\n      <th>1</th>\n      <td>14728322</td>\n      <td>0.807868</td>\n      <td>0.794377</td>\n      <td>0.764778</td>\n      <td>0.797604</td>\n      <td>0.820247</td>\n      <td>0.764451</td>\n    </tr>\n    <tr>\n      <th>2</th>\n      <td>14728323</td>\n      <td>0.808654</td>\n      <td>0.792770</td>\n      <td>0.763195</td>\n      <td>0.795242</td>\n      <td>0.821094</td>\n      <td>0.753472</td>\n    </tr>\n    <tr>\n      <th>3</th>\n      <td>14728324</td>\n      <td>0.806299</td>\n      <td>0.791966</td>\n      <td>0.765569</td>\n      <td>0.796030</td>\n      <td>0.820247</td>\n      <td>0.763607</td>\n    </tr>\n    <tr>\n      <th>4</th>\n      <td>14728325</td>\n      <td>0.809439</td>\n      <td>0.799195</td>\n      <td>0.763195</td>\n      <td>0.796816</td>\n      <td>0.821941</td>\n      <td>0.760229</td>\n    </tr>\n    <tr>\n      <th>...</th>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n      <td>...</td>\n    </tr>\n    <tr>\n      <th>521275</th>\n      <td>15249596</td>\n      <td>0.801590</td>\n      <td>0.794377</td>\n      <td>0.763987</td>\n      <td>0.788945</td>\n      <td>0.819398</td>\n      <td>0.760229</td>\n    </tr>\n    <tr>\n      <th>521276</th>\n      <td>15249597</td>\n      <td>0.803160</td>\n      <td>0.795180</td>\n      <td>0.764778</td>\n      <td>0.792093</td>\n      <td>0.818551</td>\n      <td>0.762762</td>\n    </tr>\n    <tr>\n      <th>521277</th>\n      <td>15249598</td>\n      <td>0.804729</td>\n      <td>0.796786</td>\n      <td>0.763987</td>\n      <td>0.790520</td>\n      <td>0.816006</td>\n      <td>0.763607</td>\n    </tr>\n    <tr>\n      <th>521278</th>\n      <td>15249599</td>\n      <td>0.806299</td>\n      <td>0.789557</td>\n      <td>0.763987</td>\n      <td>0.792881</td>\n      <td>0.816855</td>\n      <td>0.757695</td>\n    </tr>\n    <tr>\n      <th>521279</th>\n      <td>15249600</td>\n      <td>0.804729</td>\n      <td>0.793574</td>\n      <td>0.763195</td>\n      <td>0.796030</td>\n      <td>0.817702</td>\n      <td>0.756850</td>\n    </tr>\n  </tbody>\n</table>\n<p>521280 rows × 7 columns</p>\n</div>"},"metadata":{}}],"execution_count":5},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import DataLoader, Dataset\nfrom sklearn.preprocessing import StandardScaler\n\n\ndata_file = '/kaggle/input/esa-adb-challenge/train.parquet'\nchannels = [f'channel_{i}' for i in range(41, 47)]\ncolumns = ['id'] + channels + ['is_anomaly']\n\ndf = pd.read_parquet(data_file, columns=columns)\n\n\ndf[channels] = df[channels].fillna(df[channels].median())\n\n\nX = df[channels].values\ny = df['is_anomaly'].values\n\n\nscaler = StandardScaler()\nX = scaler.fit_transform(X)\n\n\ntrain_ratio = 0.8\nsplit_idx = int(len(X) * train_ratio)\n\nX_train, y_train = X[:split_idx], y[:split_idx]\nX_val, y_val = X[split_idx:], y[split_idx:]\n\n\nclass WindowDataset(Dataset):\n    def __init__(self, X, y, window_size=256, stride=64):\n        self.X, self.y = [], []\n        for start in range(0, len(X) - window_size, stride):\n            self.X.append(X[start:start + window_size])\n            self.y.append(y[start:start + window_size])\n    def __len__(self):\n        return len(self.X)\n    def __getitem__(self, idx):\n        return torch.FloatTensor(self.X[idx]), torch.FloatTensor(self.y[idx])\n\nwindow_size, stride = 256, 64\ntrain_dataset = WindowDataset(X_train, y_train, window_size, stride)\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\n\n\nclass LSTMAnomalyDetector(nn.Module):\n    def __init__(self, num_features, hidden_size=64):\n        super().__init__()\n        self.lstm = nn.LSTM(num_features, hidden_size, batch_first=True)\n        self.fc = nn.Linear(hidden_size, 1)\n    def forward(self, x):\n        out, _ = self.lstm(x)\n        return self.fc(out).squeeze(-1)\n\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = LSTMAnomalyDetector(len(channels)).to(device)\n\n\ncriterion = nn.BCEWithLogitsLoss(pos_weight=torch.tensor(10.0).to(device))\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n\n\nfor epoch in range(25):\n    model.train()\n    epoch_loss = 0\n    for X_batch, y_batch in train_loader:\n        X_batch, y_batch = X_batch.to(device), y_batch.to(device)\n        optimizer.zero_grad()\n        outputs = model(X_batch)\n        loss = criterion(outputs, y_batch)\n        loss.backward()\n        optimizer.step()\n        epoch_loss += loss.item()\n    print(f\"Epoch {epoch+1}, Loss: {epoch_loss / len(train_loader):.4f}\")\n\n\nmodel.eval()\nval_preds = []\n\nchunk_size = 1024\nnum_val_points = len(X_val)\n\nwith torch.no_grad():\n    for start_idx in range(0, num_val_points, chunk_size):\n        end_idx = min(start_idx + chunk_size, num_val_points)\n        X_chunk = torch.FloatTensor(X_val[start_idx:end_idx]).unsqueeze(0).to(device)  # (1, seq_len, features)\n        preds_chunk = torch.sigmoid(model(X_chunk)).cpu().numpy().flatten()\n        val_preds.extend(preds_chunk)\n\nval_preds = np.array(val_preds)\n\n\ndef extract_events(arr):\n    events, in_event = [], False\n    for i, v in enumerate(arr):\n        if v and not in_event:\n            start, in_event = i, True\n        elif not v and in_event:\n            events.append((start, i-1))\n            in_event = False\n    if in_event:\n        events.append((start, len(arr)-1))\n    return events\n\ndef corrected_event_fbeta(y_true, y_pred, beta=0.5):\n    true_events = extract_events(y_true)\n    pred_events = extract_events(y_pred)\n\n    TP_e = sum(any(y_pred[s:e+1]) for s,e in true_events)\n    FN_e = len(true_events) - TP_e\n    FP_e = sum(\n        1 for s,e in pred_events\n        if not any(not (e < ts or s > te) for ts,te in true_events)\n    )\n    FP_t = np.sum((y_pred==1)&(y_true==0))\n    N_t  = np.sum(y_true==0)\n\n    Prec_e = TP_e/(TP_e+FP_e) if TP_e+FP_e>0 else 0\n    Prec_e_corr = Prec_e * (1 - FP_t/N_t)\n    Rec_e = TP_e/(TP_e+FN_e) if TP_e+FN_e>0 else 0\n\n    b2 = beta**2\n    Fbeta = ((1+b2)*Prec_e_corr*Rec_e)/(b2*Prec_e_corr + Rec_e) if (Prec_e_corr+Rec_e)>0 else 0\n\n    return Fbeta, Prec_e_corr, Rec_e\n\n\nbest_thresh, best_fbeta = 0.5, 0\nfor thresh in np.linspace(0.1, 0.9, 81):\n    preds_bin = (val_preds >= thresh).astype(int)\n    fbeta, prec_corr, rec_e = corrected_event_fbeta(y_val, preds_bin, beta=0.5)\n    if fbeta > best_fbeta:\n        best_fbeta, best_thresh, best_prec, best_rec = fbeta, thresh, prec_corr, rec_e\n\nprint(f\"\\nOptimal Threshold: {best_thresh:.2f}\")\nprint(f\"Corrected F0.5: {best_fbeta:.4f}, Precision_corr: {best_prec:.4f}, Recall: {best_rec:.4f}\")\n\n# Final comparison to baseline\nbaseline_fbeta = 0.4198\nprint(f\"Baseline XGBoost F0.5: {baseline_fbeta:.4f}\")\nprint(f\"Improvement over baseline: {best_fbeta - baseline_fbeta:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-10T19:38:16.862765Z","iopub.execute_input":"2025-06-10T19:38:16.863397Z","iopub.status.idle":"2025-06-10T19:46:48.738809Z","shell.execute_reply.started":"2025-06-10T19:38:16.86337Z","shell.execute_reply":"2025-06-10T19:46:48.738111Z"}},"outputs":[{"name":"stdout","text":"Epoch 1, Loss: 1.2469\nEpoch 2, Loss: 1.2364\nEpoch 3, Loss: 1.2344\nEpoch 4, Loss: 1.2331\nEpoch 5, Loss: 1.2271\nEpoch 6, Loss: 1.2274\nEpoch 7, Loss: 1.2342\nEpoch 8, Loss: 1.2405\nEpoch 9, Loss: 1.2298\nEpoch 10, Loss: 1.2321\nEpoch 11, Loss: 1.2302\nEpoch 12, Loss: 1.2242\nEpoch 13, Loss: 1.2207\nEpoch 14, Loss: 1.2218\nEpoch 15, Loss: 1.2236\nEpoch 16, Loss: 1.2269\nEpoch 17, Loss: 1.2351\nEpoch 18, Loss: 1.2226\nEpoch 19, Loss: 1.2217\nEpoch 20, Loss: 1.2283\nEpoch 21, Loss: 1.2245\nEpoch 22, Loss: 1.2233\nEpoch 23, Loss: 1.2354\nEpoch 24, Loss: 1.2335\nEpoch 25, Loss: 1.2307\n\nOptimal Threshold: 0.88\nCorrected F0.5: 0.7222, Precision_corr: 1.0000, Recall: 0.3421\nBaseline XGBoost F0.5: 0.4198\nImprovement over baseline: 0.3024\n","output_type":"stream"}],"execution_count":7},{"cell_type":"code","source":"\nimport numpy as np\nimport pandas as pd\nimport torch\n\n\ntest_file = '/kaggle/input/esa-adb-challenge/test.parquet'\nchannels = [f'channel_{i}' for i in range(41, 47)]\ncolumns = ['id'] + channels\n\n\ntest_df = pd.read_parquet(test_file, columns=columns)\n\n\ntest_df[channels] = test_df[channels].fillna(test_df[channels].median())\n\n\nX_test = scaler.transform(test_df[channels].values)\n\n\nmodel.eval()\nchunk_size = 1024\ntest_preds = []\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nwith torch.no_grad():\n    for start in range(0, len(X_test), chunk_size):\n        end = min(start + chunk_size, len(X_test))\n        X_chunk = torch.FloatTensor(X_test[start:end]).unsqueeze(0).to(device)\n        preds_chunk = torch.sigmoid(model(X_chunk)).cpu().numpy().flatten()\n        test_preds.extend(preds_chunk)\n\ntest_preds = np.array(test_preds)\n\n\noptimal_threshold = best_thresh  # Obtained during validation phase\ntest_preds_binary = (test_preds >= optimal_threshold).astype(int)\n\n\nsubmission_df = pd.DataFrame({\n    'id': test_df['id'],\n    'is_anomaly': test_preds_binary\n})\n\n\nsubmission_df.to_csv('submission.csv', index=False)\n\nprint(\"Submission file created successfully: submission.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-10T19:47:35.685421Z","iopub.execute_input":"2025-06-10T19:47:35.685708Z","iopub.status.idle":"2025-06-10T19:47:36.940628Z","shell.execute_reply.started":"2025-06-10T19:47:35.685687Z","shell.execute_reply":"2025-06-10T19:47:36.939853Z"}},"outputs":[{"name":"stdout","text":"Submission file created successfully: submission.csv\n","output_type":"stream"}],"execution_count":9}]}