{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Concepts and ideas","metadata":{}},{"cell_type":"markdown","source":"## How zenith and azimuth gives a direction\n![image.png](attachment:664274b1-8b04-4ca8-b93b-13a0f9fb7ef2.png)","metadata":{},"attachments":{"664274b1-8b04-4ca8-b93b-13a0f9fb7ef2.png":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAAgYAAAFoCAYAAAAsKTMXAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAFvFSURBVHhe7Z0JuJXj+sbfzFKZQpMmlSIppUEpTjhKko7h4PyJMnWIlFJmSSiZMjQZIkQZQyhDJaSkWUVJAzJUxkzn/P2e1rvO19auPazhW993/67ru/aa9t5r77XW9z7v89zP/ZT47584IYQQQog/2SbxVQghhBBCgYEQQggh/ocCAyGEEEIkUWAghBBCiCQKDIQQQgiRRIGBEEIIIZIoMBBCCCFEEgUGQgghhEiiwEAIIYQQSRQYCCGEECKJAgMhhBBCJFFgIIQQQogkBR6i9Msvv7j//Oc/iWuFZ7vttnPbb7994lrh+OOPP9zvv/9erN+/8847Jy4VHv3t+tuLgv52/e2FRX+7/vaiksq/XdMVhRBCCJFEpQQhIg67kS2hvYEQIogyBkKEhM8//9y9+OKLiWubUrlyZXfUUUe5bbYpXCy/fPlyN3jwYNetWze33377JW7dyOrVq93rr7/uSpUq5Tp06JC4VQgRd5QxECIkTJgwwX3yySfurLPOSh5lypSxRf2HH34odFAA1DyXLVvmSpQoYdepY/722292uXz58m7KlClu8uTJdl0IIUCBgRAhoVq1au7SSy+1xZxj7dq17rrrrnM9evRwHTt2TDyqcFSoUME9//zzrnr16nadjMS0adPsMsHCbrvtZpeFEMKz7Z8nnusSl4UQWYQdfOnSpW3B3rBhgzvuuOPcrrvu6u6++26344472mPIHDz22GNu7NixbtWqVe6ggw5yCxYssHLB/vvvb4HExIkTXaNGjUyl/NVXX7kRI0bY906fPt117tzZLVq0yM2dO9cdc8wx9lh+12effebuuecet9NOO7kaNWrY44UQ8UQZAyFCAos/5QLS/UOGDHGzZs1y/fv3t3ICcPtNN93kWrRo4a644gpbyJ966ik3b948d//997vx48e7E044wUoD48aNs/YjNAR9+/Z1n376qWUd0BlcdNFFbtCgQfYzgUChVq1arlWrVq5Tp05u3bp1iXuEEHFEgYEQIWP+/Pm2cJMpaNKkSeJW59599103atQoN3z4cNevXz+35557unfeecdVrVrVbbvttq5p06buiCOO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Dv/444+3o6icdNJJrlOnTnZ55cqV7u9//7t9DTPLly9PXPor2D3/8ssviWuZ4/fff7dDCCHCjgKDCMNCxCJYnIVwm222cQMGDHDHHnusXWcCY9euXVNifsTzOv30012PHj3coEGDkscxxxzjjjzyyC0u8Pkxffp016JFCzd37tzELc6yHfDJJ59YoHPHHXfY9UyA8PPhhx92jRs3dsuWLUvcKoQQ4UWBQYTp2LGjGzp0qB3FYccdd7TWRy9GfPnll92tt95ql4vD+vXrXaNGjUzo2LNnTzuaNGlii/vf/vY30zkUlvr167u33norOTUSRo0aZV/3228/t/fee9vlTEEZBhEof6sQQuQCCgwizGGHHebOO+88O4pLhQoVbDeP+RGZiH79+lmwwFTGorLzzju7U045xZUqVcquf//995adwH2xV69e1i7Jbv+zzz5zH330kfv888/tcSyyeCuQtZg3b55btGhRMk2PGRPlAlo1YeLEie7qq692s2fPtmmT/Ewes3TpUrvt22+/tcfl5ZtvvrGMBVkGLgN/K2UUfx/fH2wF5faPP/7YzZkzx3355ZfJ+3baaSf76qH1k5+7cOFCBQxCiNChwCDCvP/++5bG5kgFLVu2dMOHDzevA8AZ8cMPP7TLRaF06dKbZAWY8vjFF1+4sWPHWpYCnnvuOXNwZHQ03gosvM8++6wZMWHb/Morr9gQKBbpX3/91T399NN233vvvWffv3r1alt8WYx98DBz5kwLGIYNG5YcHhWEn3PTTTfZ982fP99dcskldjuByVVXXeWuvPJKs4+mJNGlSxe7jwADMyiCD55f586d3YgRI+y+IAQzY8aMsZLMpEmTLKtDECGEEGFBgUGEYQFCOOjFg8WF3TY7fAYsoT1gJ8/PLu6ulx3+M888Y1mIiy66yO277752O0HCQw895KpXr24HO326JMhecB8ZEfQJ1apVM68FApaGDRta8OB1BVWqVLEhUTwWy2eoV6+eZVH+9a9/uRkzZvwluCGAIBCpUaOGfc+rr75qtxPEELBst9129jxpA6Xjg983evRoCyIojTCb4quvvkoGFEEISr7++mu3xx57mO4ALcSECRMS9wohRPZRYCAKzRVXXGEaACCVz449v5R8QSA9j86gffv2m+zgCRbWrFljizcL6IUXXmj1+s3hSwcFwWcj8qNkyZLurrvu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"}}},{"cell_type":"markdown","source":"# Merge Data","metadata":{}},{"cell_type":"code","source":"def get_test_df_from_a_batch (test_batch_df, sensors_df, test_meta_df, batch_number):\n    \"\"\"\n    Converts test_batch, test_meta and sensor_geometry into a 'test_df' dataframe containing features and targets\n    It filters 'auxiliary' field to only 'False' values, due to challenge explanation:\n    ' If True, the pulse was not fully digitized, is of lower quality, and was more likely to originate from noise.'\n    It uses polars dataframes only.\n    \"\"\"\n    test_batch_df = test_batch_df.filter(pl.col(\"auxiliary\") == False)\n    sensors_df = sensors_df.with_columns(pl.col('sensor_id').cast(pl.Int16, strict=False))\n    test_df = test_batch_df.join (sensors_df, how='left', on = 'sensor_id')\n    test_meta_batch_df = test_meta_df.filter(pl.col(\"batch_id\") == batch_number)\n    test_df = test_df.join (test_meta_batch_df, how='left', on = 'event_id')\n    test_df = test_df.drop (columns=['batch_id', 'auxiliary'])\n    test_df = test_df.drop (columns=['first_pulse_index'])\n    #test_df = test_df.with_columns(xy = pl.col('x') * pl.col('y'))\n    del test_meta_batch_df #memory\n    del test_batch_df #memory\n    print (f'\\nTest dataframe:\\n')\n    print (test_df)\n    return test_df","metadata":{"execution":{"iopub.status.busy":"2023-04-06T14:45:47.707917Z","iopub.execute_input":"2023-04-06T14:45:47.709149Z","iopub.status.idle":"2023-04-06T14:45:47.971290Z","shell.execute_reply.started":"2023-04-06T14:45:47.709088Z","shell.execute_reply":"2023-04-06T14:45:47.970100Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Predictions function","metadata":{}},{"cell_type":"code","source":"from tqdm import tqdm\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn.preprocessing import StandardScaler\n\nclass CustomDataset(Dataset):\n    def __init__(self, df):\n        self.df = df\n        self.features = self.df.to_numpy()\n        self.scaler = StandardScaler()\n        self.features = self.scaler.fit_transform(self.features)\n    \n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        features = self.features[idx] #maybe we need to apply the scaler here again\n        return torch.tensor(features, dtype=torch.float32)\n\n    \nclass CustomModel(torch.nn.Module):\n    def __init__(self, input_size, output_size):\n        super(CustomModel, self).__init__()\n        self.fc1 = torch.nn.Linear(input_size, 64)\n        self.fc2 = torch.nn.Linear(64, 32)\n        self.fc3 = torch.nn.Linear(32, 16)\n        self.fc4 = torch.nn.Linear(16, 8)\n        self.fc5 = torch.nn.Linear(8, output_size)\n\n    def forward(self, x):\n        x = torch.nn.functional.relu(self.fc1(x))\n        x = torch.nn.functional.relu(self.fc2(x))\n        x = torch.nn.functional.relu(self.fc3(x))\n        x = torch.nn.functional.relu(self.fc4(x))\n        x = self.fc5(x)\n        return x\n\n    \ndef predictions (test_dataset, batch_size, device, model_path):\n    \"\"\"\n    Predicts zenith and azimuth.\n    Parameters:\n    x_test - test set features\n    Output:\n    az_preds, ze_preds\n    \"\"\"   \n\n    test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)\n    model = CustomModel(input_size=test_dataset.features.shape[1], output_size=2)\n    model.load_state_dict(torch.load(model_path, map_location=torch.device('cpu')))\n    model.to(device)\n\n    predictions = []\n    with torch.no_grad():\n        for i, inputs in enumerate(test_loader):\n            inputs = inputs.to(device)\n            outputs = model(inputs)\n            predictions.extend(outputs.cpu().numpy())\n\n    return np.array(predictions)","metadata":{"execution":{"iopub.status.busy":"2023-04-06T14:40:12.298105Z","iopub.execute_input":"2023-04-06T14:40:12.298882Z","iopub.status.idle":"2023-04-06T14:41:15.346522Z","shell.execute_reply.started":"2023-04-06T14:40:12.298840Z","shell.execute_reply":"2023-04-06T14:41:15.345276Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model train","metadata":{}},{"cell_type":"markdown","source":"## Imports","metadata":{}},{"cell_type":"code","source":"%%time\nimport polars as pl\nimport numpy as np\nimport pandas as pd\nimport os, gc\n\nprint('\\nFinished loading imports.\\n')\n\nif torch.cuda.is_available():\n    import cudf\n    device = 'cuda'\nelse:\n    device = 'cpu'\n    \nprint(f\"Device for predictions is {device}.\\n\")\n\nscores_df = pd.DataFrame([]) \ninput_path = '/kaggle/input/'\nwork_path = '/kaggle/working/'\nscores_path = f'{work_path}scores.csv'\nsaved_model_path = ''","metadata":{"execution":{"iopub.status.busy":"2023-04-06T14:42:54.241738Z","iopub.execute_input":"2023-04-06T14:42:54.242187Z","iopub.status.idle":"2023-04-06T14:42:54.446927Z","shell.execute_reply.started":"2023-04-06T14:42:54.242149Z","shell.execute_reply":"2023-04-06T14:42:54.446015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load data","metadata":{}},{"cell_type":"code","source":"%%time\nprint('Loading files...')\nfor dirname, _, filenames in os.walk(input_path):\n    for filename in filenames:\n        filepath = os.path.join(dirname, filename)\n        if 'model.pt' in filepath:\n            saved_model_path = filepath\n            print(\"'model.pt' file path found and loaded.\")\n        elif 'score' in filepath:\n            scores_df = pl.read_csv (filepath).to_pandas()\n            print(\"'scores' file loaded.\")\n        elif 'sensor' in filepath:\n            sensors_df = pl.read_csv (filepath).lazy()\n            print(\"'sensor_geometry' file loaded.\")\n        elif 'test_meta' in filepath:\n            test_meta_filepath = filepath\n            print(\"'test_meta' file path found and loaded.\")","metadata":{"execution":{"iopub.status.busy":"2023-04-06T14:42:58.351187Z","iopub.execute_input":"2023-04-06T14:42:58.352389Z","iopub.status.idle":"2023-04-06T14:42:59.352184Z","shell.execute_reply.started":"2023-04-06T14:42:58.352344Z","shell.execute_reply":"2023-04-06T14:42:59.351211Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n# 9mins per batch if I feed the entire batch all at once. RAM runs out after a few batches.\nprint ('Starting predictions...\\n')\n\nsubmission_df = pl.DataFrame([]).lazy()\ncounts = 1\nfor dirname, _, filenames in os.walk(input_path):\n    for filename in filenames:\n        filepath = os.path.join(dirname, filename)\n        if ('batch' in filepath) and ('test' in dirname):\n            test_meta_df = pl.read_parquet(test_meta_filepath).lazy()\n            print(\"'test_meta' file loaded.\")\n            test_batch_df = pl.read_parquet (filepath).lazy()\n            print(f\"\\nLoading 'test_batch' file.\\n\")\n            print(test_batch_df.collect())\n            batch_number = int(filename.split('_')[1].split('.')[0])\n            print(f\"'test_batch_{batch_number}' file loaded.\\n\")\n            print(f\"\\n\\nPredicting values for batch id {batch_number} - batch {counts}\\n\\n\")\n            test_df = get_test_df_from_a_batch(test_batch_df.collect(), \n                                               sensors_df.collect(), \n                                               test_meta_df.collect(), \n                                               batch_number)\n            del test_batch_df #memory\n            del test_meta_df #memory\n            test_dataset = CustomDataset(test_df)\n            if len(saved_model_path) == 0:\n                print ('\\nNo model to make predictions. Aborting.\\n')\n                break\n            preds = predictions(test_dataset, batch_size = 128, device = device, model_path = saved_model_path)\n            print('Azimuth predictions:\\n', preds[:,0])\n            print('Zenith predictions:\\n', preds[:,1])\n            if type(preds[0]) == int:\n                print('Predictions are not valid. Skipping these values and continuing with next batch.')\n                break\n            batch_results = {'event_id': test_df['event_id'], 'azimuth': preds[:,0], 'zenith': preds[:,1]}\n            del test_df #memory\n            batch_results_df = pl.DataFrame(batch_results).lazy()\n            batch_results_df = batch_results_df.groupby('event_id').median()\n            if submission_df.width == 0:\n                submission_df = batch_results_df\n            else:\n                submission_df = pl.concat ([submission_df, batch_results_df])\n            del batch_results_df\n            gc.collect()\n            counts += 1 ","metadata":{"execution":{"iopub.status.busy":"2023-04-05T12:30:22.784683Z","iopub.execute_input":"2023-04-05T12:30:22.785171Z","iopub.status.idle":"2023-04-05T12:30:23.126715Z","shell.execute_reply.started":"2023-04-05T12:30:22.785134Z","shell.execute_reply":"2023-04-05T12:30:23.125418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Submissions","metadata":{}},{"cell_type":"code","source":"if type(submission_df) == pl.lazyframe.frame.LazyFrame:\n    df_aux = submission_df.collect()\nelse:\n    df_aux = submission_df\nsubmission_df = df_aux.with_columns(pl.when(pl.col(\"azimuth\") > 2*np.pi).then(2*3.141).otherwise(pl.col(\"azimuth\")).alias('azimuth'),\n                                    pl.when(pl.col(\"zenith\") > np.pi).then(3.141).otherwise(pl.col(\"zenith\")).alias('zenith'))\nsubmission_df.write_csv(f'{work_path}submission.csv')\nprint(submission_df)\n","metadata":{"execution":{"iopub.status.busy":"2023-04-04T21:40:07.451898Z","iopub.execute_input":"2023-04-04T21:40:07.453398Z","iopub.status.idle":"2023-04-04T21:40:07.479560Z","shell.execute_reply.started":"2023-04-04T21:40:07.453336Z","shell.execute_reply":"2023-04-04T21:40:07.478564Z"},"trusted":true},"execution_count":null,"outputs":[]}]}