{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":53666,"databundleVersionId":6589269,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":10.798026,"end_time":"2025-04-08T17:19:48.34471","environment_variables":{},"exception":true,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-04-08T17:19:37.546684","version":"2.4.0"},"widgets":{"application/vnd.jupyter.widget-state+json":{"state":{"04a02fa7eb134b8ba71bfb7ac786a296":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"066742cb687d47e5951ef8dca76dba9c":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"140168e7812240ba9466d1dda5eed8ba":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_d40a783824ec4c95a57c0dbb91062a70","placeholder":"​","style":"IPY_MODEL_53a87c03b90b44c9bf4b20eab4f9a7c6","value":"100%"}},"243288b9694c4515a07c9ae094a94025":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"292ed40f89474a808b713abd79c6faa5":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2afbc94ce7c34e6cbddebc58ebc22c09":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2da40a6bf2734162b22cda62145810a6":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2e040b55749b4f87a3519dbdaba56c93":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"35856dd8abe54eaead0873b8b0c7f7df":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"38080ba4a8334c0b92780141c9fc50df":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"3ab5075b918b4f188e9df820982a7962":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_2afbc94ce7c34e6cbddebc58ebc22c09","max":5380,"min":0,"orientation":"horizontal","style":"IPY_MODEL_49dc7d146f844409b47cbc8a6dc83463","value":5380}},"3edf82cd2f6a4defbd50ab7bd5776819":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"495920747ef94e84890690c6ceeae1ec":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_68318c5fbcb9489289a905fd475d55e6","max":277,"min":0,"orientation":"horizontal","style":"IPY_MODEL_7b6f47188862495f8c1aebf24531bac5","value":277}},"49dc7d146f844409b47cbc8a6dc83463":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"4d30e4538e964eb7b2e13cd30718bba9":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"4f40895e3e1b4c3cab6fcec8b5086463":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"53a87c03b90b44c9bf4b20eab4f9a7c6":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"55205f7d637d44c898a95be214832267":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"5d5b3c57aec143929451c45e44970aac":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_837ca76e99cc451299eec274752bd94a","IPY_MODEL_7082d34f610641e4923c1789e598e4e6","IPY_MODEL_c7faee4ac907404e81ae906f4987d374"],"layout":"IPY_MODEL_292ed40f89474a808b713abd79c6faa5"}},"5dbc007ba2e14736a9a2a4858be7af06":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"627492dde2a44f7ba9e1a44089922c4b":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"68318c5fbcb9489289a905fd475d55e6":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"68cfde5646ad48c1b7785b8b6f55c37a":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"7082d34f610641e4923c1789e598e4e6":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_68cfde5646ad48c1b7785b8b6f55c37a","max":5380,"min":0,"orientation":"horizontal","style":"IPY_MODEL_9484b0e8f7ab44b3b986016dab5d5bdb","value":5380}},"791d37aa0c0f431a8ec6fa9c7823ae49":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"7b6f47188862495f8c1aebf24531bac5":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"7cd92a9c06bd48e5bf2e86f2a9071b4d":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_55205f7d637d44c898a95be214832267","placeholder":"​","style":"IPY_MODEL_c9abb62330a14c0b9b550d0b6b53913d","value":" 5380/5380 [00:55&lt;00:00, 114.29it/s]"}},"837ca76e99cc451299eec274752bd94a":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_4d30e4538e964eb7b2e13cd30718bba9","placeholder":"​","style":"IPY_MODEL_d75102dd68c84d71be16ba8d7a9b5d1e","value":"Matching detections: 100%"}},"88535c5987db4e82abb7794108718f0b":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"8e8cc658c17e4b86a3c60d7f553d8738":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"8f79fe67eb4d49689b322d9a59e4e11a":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9484b0e8f7ab44b3b986016dab5d5bdb":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"98cdfcf52b884bda8e8234292d784180":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"98ffd4cba55a496e97279af4151313d1":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_35856dd8abe54eaead0873b8b0c7f7df","placeholder":"​","style":"IPY_MODEL_5dbc007ba2e14736a9a2a4858be7af06","value":" 277/277 [00:13&lt;00:00, 19.53it/s]"}},"9e02d692f7264dae8afec2a4a714deaa":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"a585bccf3fe74604b82e7d83420953e9":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_2da40a6bf2734162b22cda62145810a6","placeholder":"​","style":"IPY_MODEL_04a02fa7eb134b8ba71bfb7ac786a296","value":"100%"}},"aa27b554f6f1490e9efd393807cb6ca7":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"b2728370d395459ebe1f9f8d7a6a3f5f":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"b39322f78b6149a8b277109225ae2043":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_c828a1930fbb4d3fa71fdf786bf1cda4","IPY_MODEL_d877370e4c42444d9a88460e06578634","IPY_MODEL_e0311a664a8443ebbbb6f56e1430fe8c"],"layout":"IPY_MODEL_4f40895e3e1b4c3cab6fcec8b5086463"}},"b3ad5f497ac54938b558e0096c65a591":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_fb8134f9ce764b2cbef172eb22fb8945","IPY_MODEL_3ab5075b918b4f188e9df820982a7962","IPY_MODEL_7cd92a9c06bd48e5bf2e86f2a9071b4d"],"layout":"IPY_MODEL_98cdfcf52b884bda8e8234292d784180"}},"b716677406d1410d86e0b4ff47eaa85d":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"bd0b5e7fd7bf4f72aabf65817884f130":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_140168e7812240ba9466d1dda5eed8ba","IPY_MODEL_d810e1e659c24ead83e521e83594b87d","IPY_MODEL_ed85114c140c42f78ca717e87979a99d"],"layout":"IPY_MODEL_8f79fe67eb4d49689b322d9a59e4e11a"}},"c7528fffe082477e8fd074e4ce9a1621":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"c7faee4ac907404e81ae906f4987d374":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_38080ba4a8334c0b92780141c9fc50df","placeholder":"​","style":"IPY_MODEL_243288b9694c4515a07c9ae094a94025","value":" 5380/5380 [00:54&lt;00:00, 108.91it/s]"}},"c828a1930fbb4d3fa71fdf786bf1cda4":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_8e8cc658c17e4b86a3c60d7f553d8738","placeholder":"​","style":"IPY_MODEL_791d37aa0c0f431a8ec6fa9c7823ae49","value":"100%"}},"c9abb62330a14c0b9b550d0b6b53913d":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"d40a783824ec4c95a57c0dbb91062a70":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"d75102dd68c84d71be16ba8d7a9b5d1e":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"d810e1e659c24ead83e521e83594b87d":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_c7528fffe082477e8fd074e4ce9a1621","max":277,"min":0,"orientation":"horizontal","style":"IPY_MODEL_b2728370d395459ebe1f9f8d7a6a3f5f","value":277}},"d877370e4c42444d9a88460e06578634":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_88535c5987db4e82abb7794108718f0b","max":277,"min":0,"orientation":"horizontal","style":"IPY_MODEL_ebb8342a7db943aa9def8c73d4e11688","value":277}},"dc60f6755f0d4ab2bb67dcbff4ad18b0":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_a585bccf3fe74604b82e7d83420953e9","IPY_MODEL_495920747ef94e84890690c6ceeae1ec","IPY_MODEL_98ffd4cba55a496e97279af4151313d1"],"layout":"IPY_MODEL_9e02d692f7264dae8afec2a4a714deaa"}},"e0311a664a8443ebbbb6f56e1430fe8c":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_627492dde2a44f7ba9e1a44089922c4b","placeholder":"​","style":"IPY_MODEL_066742cb687d47e5951ef8dca76dba9c","value":" 277/277 [07:22&lt;00:00,  1.96s/it]"}},"ebb8342a7db943aa9def8c73d4e11688":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"ed85114c140c42f78ca717e87979a99d":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_3edf82cd2f6a4defbd50ab7bd5776819","placeholder":"​","style":"IPY_MODEL_2e040b55749b4f87a3519dbdaba56c93","value":" 277/277 [00:01&lt;00:00, 252.17it/s]"}},"fb8134f9ce764b2cbef172eb22fb8945":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_b716677406d1410d86e0b4ff47eaa85d","placeholder":"​","style":"IPY_MODEL_aa27b554f6f1490e9efd393807cb6ca7","value":"Matching detections: 100%"}}},"version_major":2,"version_minor":0}}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"80e81888","cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport polars as pl\nfrom tqdm.auto import tqdm\nfrom matplotlib import pyplot as plt\nfrom sklearn.model_selection import GroupKFold\nfrom sklearn.metrics import mean_squared_error\nfrom scipy.signal import find_peaks\nimport gc\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.optim.lr_scheduler import CosineAnnealingLR\nfrom torchinfo import summary","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":5.813458,"end_time":"2025-04-08T17:19:46.013802","exception":false,"start_time":"2025-04-08T17:19:40.200344","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-11-05T16:05:03.466367Z","iopub.execute_input":"2025-11-05T16:05:03.466673Z","iopub.status.idle":"2025-11-05T16:05:03.471203Z","shell.execute_reply.started":"2025-11-05T16:05:03.466656Z","shell.execute_reply":"2025-11-05T16:05:03.470549Z"}},"outputs":[],"execution_count":null},{"id":"869a5e75","cell_type":"code","source":"df_series = pl.read_parquet(\"/kaggle/input/child-mind-institute-detect-sleep-states/train_series.parquet\")\ndf_events = pl.read_csv(\"/kaggle/input/child-mind-institute-detect-sleep-states/train_events.csv\")\ndf_events = df_events.with_columns(pl.col(\"event\").map_dict({\"wakeup\": 1.0, \"onset\": -1.0}, return_dtype=pl.Float32))\ndf_groupby = df_series.group_by(\"series_id\", maintain_order=True)","metadata":{"papermill":{"duration":0.074178,"end_time":"2025-04-08T17:19:46.100184","exception":true,"start_time":"2025-04-08T17:19:46.026006","status":"failed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"cf50419f","cell_type":"code","source":"dict_valid_ratio = dict()\nlist_feature_array = []\nlist_df_1min = []\n\nfor series_id, df in tqdm(df_groupby, total=df_series.get_column(\"series_id\").n_unique()):\n    df = (\n        df.join(\n            df_events.filter(pl.col(\"series_id\") == series_id).select(\"timestamp\", \"event\"),\n            on=\"timestamp\", how=\"left\"\n        ).with_columns(\n            pl.col(\"timestamp\").str.to_datetime(),\n            pl.col(\"event\").fill_null(0.0),\n        ).with_columns(\n            pl.col(\"timestamp\").dt.date().cast(str).alias(\"date\"),\n            pl.col(\"timestamp\").dt.time().cast(str).alias(\"time\"),\n        )\n    ).to_pandas()\n    \n    #  Handle duplicate data and calculate validity flag\n    df[\"timestamp\"] = df[\"timestamp\"].dt.tz_localize(None)\n    dup_count = df.groupby([\"anglez\", \"enmo\", \"time\"])[\"step\"].transform(\"count\")\n    df[\"valid_flag\"] = (dup_count == 1).astype(\"float32\")\n    dict_valid_ratio[series_id] = df[\"valid_flag\"].mean()\n    \n    #     Calculate rolling features and pivot the data\n    list_feature_array_tmp = []\n    df[\"log_anglez_std\"] = np.log(df[\"anglez\"].rolling(12, min_periods=1, center=True).std() + 1).astype(\"float32\")\n    df[\"log_enmo\"] = np.log(df[\"enmo\"] + 0.01).astype(\"float32\")\n    for feature in [\"log_anglez_std\", \"log_enmo\", \"valid_flag\"]:\n        df_pivot = df.pivot(index=[\"series_id\", \"date\"], columns=\"time\", values=feature)\n        feature_array = df_pivot.fillna(0).values\n        feature_array_1day_bedore = df_pivot.shift(1).fillna(0).values\n        feature_array_1day_after = df_pivot.shift(-1).fillna(0).values\n        feature_array = np.concatenate([feature_array_1day_bedore[:, -180*12:], feature_array, feature_array_1day_after[:, :180*12]], axis=1)\n        list_feature_array_tmp.append(feature_array)\n    list_feature_array.append(np.stack(list_feature_array_tmp, axis=1))\n    \n    #     Resample data to 1 minute intervals\n    dict_agg = {\"series_id\": \"first\", \"date\": \"first\", \"time\": \"first\", \"step\": \"mean\", \"event\": \"sum\", \"valid_flag\": \"max\"}\n    df_1min = df.resample(\"1min\", on=\"timestamp\").agg(dict_agg).reset_index()\n    df_1min[\"step\"] = df_1min[\"step\"].astype(\"int32\")\n    \n    #     Compute target values for events\n    values_event = df_1min[\"event\"].values\n    values_target = values_event.copy()\n    \n    #     window of 30 minutes (or steps) in both backward and forward directions\n    for j in range(30):\n        weight = np.exp(-j/2.8)\n        values_target[:-(j+1)] += weight*values_event[(j+1):]  # shift backward\n        if j > 0:\n            values_target[j:] += weight*values_event[:-j]  # shift forward\n    \n    df_1min[\"target\"] = values_target\n    list_df_1min.append(df_1min)","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"5e820b42","cell_type":"code","source":"del df_series, df_groupby\ngc.collect()","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"2f923f59","cell_type":"code","source":"# Normalizing the Array\n# This operation standardizes each feature's values across all timesteps and samples to be between 0 and 1\n\nX = np.concatenate(list_feature_array)\nX = (X - X.min(axis=(0, 2), keepdims=True)) / (X.max(axis=(0, 2), keepdims=True) - X.min(axis=(0, 2), keepdims=True))","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"29ac05c3","cell_type":"code","source":"X.shape","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"39c778b2","cell_type":"code","source":"df_1min = pd.concat(list_df_1min)\ndf_1min","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"28c31784","cell_type":"code","source":"del list_df_1min\ndel list_feature_array\ngc.collect()","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"b19093a8","cell_type":"code","source":"# reshape df_1min from a long format to a wide format \n\ndf_y = df_1min.pivot(index=[\"series_id\", \"date\"], columns=\"time\", values=\"target\").fillna(0)\ndf_mask = df_1min.pivot(index=[\"series_id\", \"date\"], columns=\"time\", values=\"valid_flag\").fillna(0)\ndf_mask.to_parquet(\"df_mask.parquet\")\n\ndf_y","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"e5b4781d","cell_type":"code","source":"class MyDataset(Dataset):\n    def __init__(self, X, Y, flag):\n        self.X = torch.FloatTensor(X)\n        if Y is not None:\n            self.Y = torch.FloatTensor(Y)\n        self.flag = torch.FloatTensor(flag)\n    \n    def __len__(self):\n        return self.X.shape[0]\n    \n    def __getitem__(self, idx):\n        if \"Y\" in dir(self):\n            return (self.X[idx], self.Y[idx], self.flag[idx])\n        else:\n            return (self.X[idx], torch.Tensor(), self.flag[idx])","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"d2d50c61","cell_type":"markdown","source":"Regression Model (1D-CNN UNet) https://www.kaggle.com/code/kmat2019/u-net-1d-cnn-with-keras","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]}},{"id":"c9042580","cell_type":"code","source":"# 1D convolutional layer,\n# batch normalization, \n# a ReLU activation function\n\nclass ConvBNReLU(nn.Module):\n    def __init__(self, in_channels, out_channels, kernel_size, stride, groups=1):\n        super().__init__()\n        \n        if stride == 1:\n            padding = \"same\"\n        else:\n            padding = (kernel_size - stride) // 2\n        self.layers = nn.Sequential(\n            nn.Conv1d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding, groups=groups),\n            nn.BatchNorm1d(out_channels),\n            nn.ReLU()\n        )\n    \n    def forward(self, x):\n        x_out = self.layers(x)\n        return x_out","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"0d05958f","cell_type":"code","source":"# Squeeze-and-Excitation (SE) block\n\nclass SEBlock(nn.Module):\n    def __init__(self, n_channels, se_ratio):\n        super().__init__()\n        \n        self.layers = nn.Sequential(\n            nn.AdaptiveAvgPool1d(output_size=1),  #  Global Average Pooling\n            nn.Conv1d(n_channels, n_channels//se_ratio, kernel_size=1),\n            nn.ReLU(),\n            nn.Conv1d(n_channels//se_ratio, n_channels, kernel_size=1),\n            nn.Sigmoid()\n        )\n\n    def forward(self, x):\n        x_out = torch.mul(x, self.layers(x))\n        return x_out\n","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"0504bb4e","cell_type":"code","source":"# residual block\n\nclass ResBlock(nn.Module):\n    def __init__(self, n_channels, kernel_size, se_ratio):\n        super().__init__()\n        \n        self.layers = nn.Sequential(\n            ConvBNReLU(n_channels, n_channels, kernel_size, stride=1),\n            ConvBNReLU(n_channels, n_channels, kernel_size, stride=1),\n            SEBlock(n_channels, se_ratio)\n        )\n    \n    def forward(self, x):\n        x_re = self.layers(x)\n        x_out = x + x_re\n        return x_out\n    ","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"8fda1c7c","cell_type":"code","source":"# U-Net model adapted for 1-dimensional data\n\nclass UNet1d(nn.Module):\n    def __init__(self, input_channels, initial_channels, initial_kernel_size,\n                 down_channels, down_kernel_size, down_stride, res_depth, res_kernel_size, se_ratio, out_kernel_size):\n        super().__init__()\n        self.down_kernel_size = down_kernel_size\n        self.down_stride = down_stride\n        \n        self.initial_layers = ConvBNReLU(input_channels, initial_channels, initial_kernel_size, stride=1, groups=input_channels)\n        \n        self.down_layers = nn.ModuleList()\n        for i in range(len(down_channels)):\n            if i == 0:\n                in_channels = initial_channels\n            else:\n                in_channels = down_channels[i-1] + input_channels\n            out_channels = down_channels[i]\n            kernel_size = down_kernel_size[i]\n            stride = down_stride[i]\n            \n            block = []\n            block.append(ConvBNReLU(in_channels, out_channels, kernel_size, stride))\n            for j in range(res_depth):\n                block.append(ResBlock(out_channels, res_kernel_size, se_ratio))\n            self.down_layers.append(nn.Sequential(*block))\n        \n        self.up_layers = nn.ModuleList()\n        for i in range(len(down_channels)-1, 0, -1):\n            in_channels = out_channels + down_channels[i]\n            out_channels = down_channels[i]\n            kernel_size = down_kernel_size[i]\n            self.up_layers.append(ConvBNReLU(in_channels, out_channels, kernel_size, stride=1))\n        \n        self.out_layers = nn.Conv1d(down_channels[1], 1, out_kernel_size, padding=\"same\")\n    \n    def forward(self, x):\n        outs = []\n        x_avg = x\n        x = self.initial_layers(x)\n        \n        for i in range(len(self.down_layers)):\n            x_out = self.down_layers[i](x)\n            if i == len(self.down_layers) - 1:\n                x = x_out\n            else:\n                outs.append(x_out)\n                kernel_size = self.down_kernel_size[i]\n                stride = self.down_stride[i]\n                padding = (kernel_size - stride) // 2\n                x_avg = F.avg_pool1d(x_avg, kernel_size, stride, padding)\n                x = torch.cat([x_out, x_avg], dim=1)\n        \n        for i in range(len(self.up_layers)):\n            scale_factor = self.down_stride[-i-1]\n            x = F.interpolate(x, scale_factor=scale_factor, mode=\"linear\")\n            x = torch.cat([x, outs[-i-1]], dim=1)\n            x = self.up_layers[i](x)\n        \n        x_out = self.out_layers(x)\n        x_out = x_out[:, 0, 180:-180]\n        \n        return x_out","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"3dc2dec9","cell_type":"code","source":"def train(model, data_loader, optimizer, criterion, device):\n    model.train()\n    for batch in data_loader:\n        X = batch[0].to(device)\n        Y = batch[1].to(device)\n        mask = batch[2].to(device)\n        preds = model(X) * mask\n        loss = criterion(preds, Y)\n\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"d44a8756","cell_type":"code","source":"def evaluate(model, data_loader, criterion, device):\n    model.eval()\n    n = 0\n    total_loss = 0.0\n    for batch in data_loader:\n        X = batch[0].to(device)\n        Y = batch[1].to(device)\n        mask = batch[2].to(device)\n        \n        with torch.no_grad():\n            preds = model(X) * mask\n        \n        loss = criterion(preds, Y)\n        total_loss += loss.item() * X.shape[0]\n        n += X.shape[0]\n    \n    avg_loss = total_loss / n\n    \n    return avg_loss","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"99900ef3","cell_type":"code","source":"def predict(model, data_loader, device):\n    model.eval()\n    preds_all = []\n    for batch in data_loader:\n        X = batch[0].to(device)\n        mask = batch[2].to(device)\n        \n        with torch.no_grad():\n            preds = model(X) * mask\n            \n        preds = preds.cpu().numpy()\n        preds_all.append(preds)\n    \n    preds_all = np.concatenate(preds_all)\n        \n    return preds_all","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"1044c60c","cell_type":"code","source":"n_splits = 10\nn_epochs = 15\nbatch_size = 16\n    \nY = df_y.values\nmask = df_mask.values\ngroups = df_y.index.get_level_values(\"series_id\").tolist()","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"dccac730","cell_type":"code","source":"preds_valid = np.zeros_like(Y)\ngkf = GroupKFold(n_splits=n_splits)\nfor fold, (idx_train, idx_valid) in enumerate(gkf.split(X, Y, groups=groups)):   \n    print(f\"fold: {fold}\")\n    X_train = X[idx_train]\n    Y_train = Y[idx_train]\n    mask_train = mask[idx_train]\n\n    X_valid = X[idx_valid]\n    Y_valid = Y[idx_valid]\n    mask_valid = mask[idx_valid]\n\n    # dataset\n    ds_train = MyDataset(X_train, Y_train, mask_train)\n    ds_valid = MyDataset(X_valid, Y_valid, mask_valid)\n\n    # dataloader\n    dl_train = DataLoader(\n        ds_train, \n        batch_size=batch_size,\n        shuffle=True, \n        num_workers=0, \n        pin_memory=True,\n        drop_last=True\n    )\n    dl_valid = DataLoader(\n        ds_valid,\n        batch_size=batch_size,\n        shuffle=False, \n        num_workers=0, \n        pin_memory=True, \n        drop_last=False\n    )\n\n    # build model\n    torch.manual_seed(0)\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    model = UNet1d(\n        input_channels=X.shape[1],\n        initial_channels=72,\n        initial_kernel_size=15,\n        down_channels=(72, 72, 72),\n        down_kernel_size=(12, 15, 15),\n        down_stride=(12, 9, 5),  # first element must be 12\n        res_depth=3,\n        res_kernel_size=15,\n        se_ratio=4,\n        out_kernel_size=21,\n    )\n    \n    if fold == 0:\n        print(summary(\n            model=model,\n            input_size=(batch_size, X.shape[1], X.shape[2]),\n            col_names=[\"input_size\", \"output_size\", \"num_params\"],\n            col_width=20\n        ))\n        \n    model.to(device)\n    optimizer = optim.Adam(model.parameters(), lr=1e-3)\n    scheduler = CosineAnnealingLR(optimizer, T_max=n_epochs)\n    criterion = nn.MSELoss()\n\n    best_loss = np.inf\n    for epoch in range(n_epochs):\n        train(model, dl_train, optimizer, criterion, device)\n        loss = evaluate(model, dl_valid, criterion, device)\n        scheduler.step()\n        if loss < best_loss:\n            best_loss = loss\n            torch.save(model.state_dict(), f\"model_{fold}.pth\")\n            print(f\"epoch: {epoch}\\tValid-Loss: {loss}\\tBEST\")\n        else:\n            print(f\"epoch: {epoch}\\tValid-Loss: {loss}\")\n\n    with torch.no_grad():\n        model.load_state_dict(torch.load(f\"model_{fold}.pth\"))\n        preds_valid[idx_valid] = predict(model, dl_valid, device)\n        \n    print(f\"Best_Loss: {best_loss}\")","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"dcd46e34","cell_type":"code","source":"rmse = mean_squared_error(Y, preds_valid, squared=False)\nprint(f\"RMSE: {rmse:.5f}\")","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"580a18cd","cell_type":"code","source":"plt.figure(figsize=(12, 3))\nplt.plot(np.linspace(0, 24, 60*24), preds_valid.mean(axis=0), label=\"Mean of Prediction\")\nplt.plot(np.linspace(0, 24, 60*24), df_y.mean().values, label=\"Mean of Target\")\nplt.xlim(0, 24)\nplt.xlabel(\"Hour(UTC)\")\nplt.legend()\nplt.show()","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"b6289f21","cell_type":"code","source":"df_pred = pd.DataFrame(preds_valid, index=df_y.index, columns=df_y.columns)\ndf_pred = df_pred.stack().reset_index(name=\"score\")\ndf_pred = pd.merge(\n    df_1min[[\"series_id\", \"date\", \"time\", \"step\", \"event\"]],\n    df_pred,\n    on=[\"series_id\", \"date\", \"time\"],\n    how=\"inner\"\n)\n\ndf_pred","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"8731b7cf","cell_type":"code","source":"list_df = []\nfor series_id, df in tqdm(df_pred.groupby(\"series_id\")):\n    for event in [\"onset\", \"wakeup\"]:\n        values_step = df[\"step\"].values\n        if event == \"onset\":\n            values_score = -df[\"score\"].values\n        else:\n            values_score = df[\"score\"].values\n\n        peak_idx = find_peaks(values_score, height=0.0, distance=8)[0]\n        df_peak = pd.DataFrame(values_step[peak_idx], columns=[\"step\"])\n        df_peak[\"series_id\"] = series_id\n        df_peak[\"event\"] = event\n        df_peak[\"score\"] = values_score[peak_idx]\n        list_df.append(df_peak)\n\n        \ndf_sub = pd.concat(list_df)\ndf_sub = df_sub.sort_values(\"score\", ascending=False).groupby(\"event\").head(100000)  # avoid Submission Scoring Error\ndf_sub = df_sub.sort_values([\"series_id\", \"step\"]).reset_index(drop=True)\ndf_sub = df_sub[[\"series_id\", \"step\", \"event\", \"score\"]].reset_index(names=\"row_id\")\ndf_sub.to_csv(\"df_sub.csv\", index=False)\ndf_sub","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"ce58b7d5","cell_type":"markdown","source":"post processing","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]}},{"id":"0a852460","cell_type":"code","source":"df_events = pd.read_csv(\"/kaggle/input/child-mind-institute-detect-sleep-states/train_events.csv\").dropna()\ndf_events[\"timestamp\"] = pd.to_datetime(df_events[\"timestamp\"], utc=True).dt.tz_localize(None)\ndf_events[\"time\"] = df_events[\"timestamp\"].dt.time.astype(str)\ndf_events[\"minute_mod15\"] = df_events[\"timestamp\"].dt.minute % 15\n\ndf_agg = df_events.groupby([\"time\", \"event\"], as_index=False).size()\ndf_agg[\"rate\"] = df_agg[\"size\"] / df_agg.groupby(\"event\")[\"size\"].transform(\"sum\") * (60*24)\ndf_time = df_agg.pivot(index=\"time\", columns=\"event\", values=\"rate\").fillna(0).reset_index()\ndf_time = df_time.merge(df_pred[[\"time\"]].drop_duplicates(), how=\"right\").fillna(0)\ndf_time = pd.concat([df_time]*3, ignore_index=True)\ndf_time[\"onset\"] = df_time[\"onset\"].rolling(60, center=True).mean()\ndf_time[\"wakeup\"] = df_time[\"wakeup\"].rolling(60, center=True).mean()\ndf_time = df_time.iloc[60*24:-60*24].reset_index(drop=True)\n\ndf_agg = df_events.groupby([\"minute_mod15\", \"event\"], as_index=False).size()\ndf_agg[\"rate\"] = df_agg[\"size\"] / df_agg.groupby(\"event\")[\"size\"].transform(\"sum\") * 15\ndf_minute = df_agg.pivot(index=\"minute_mod15\", columns=\"event\", values=\"rate\").reset_index()\n\ndf_time[[\"onset\", \"wakeup\"]] = df_time[[\"onset\", \"wakeup\"]].clip(0.1, 1.1) ** 0.13\ndf_minute[[\"onset\", \"wakeup\"]] = df_minute[[\"onset\", \"wakeup\"]].clip(0.5, 1.3) ** 0.06\n","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"64decced","cell_type":"code","source":"df_pred[\"minute_mod15\"] = df_pred[\"time\"].str[3:5].astype(int) % 15","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"aad7df29","cell_type":"code","source":"list_df = []\nfor series_id, df in tqdm(df_pred.groupby(\"series_id\")):\n    df = df.merge(df_time, how=\"left\", on=\"time\")\n    df = df.merge(df_minute, how=\"left\", on=\"minute_mod15\")\n    \n    df_tmp = df.copy()\n    df_tmp[\"score\"] = df_tmp[\"score\"].replace(0.0, np.nan)\n    df_tmp = df_tmp.groupby(\"time\")[\"score\"].mean()\n    df_tmp = pd.concat([df_tmp]*3).rolling(90, center=True, min_periods=1).mean()\n    df_tmp = df_tmp.iloc[60*24:-60*24].reset_index().rename({\"score\": \"score_mean\"}, axis=1)\n    df = df.merge(df_tmp, on=\"time\", how=\"left\")\n    \n    df[\"score\"] = 0.9*df[\"score\"] + 0.1*df[\"score_mean\"]\n    df[\"score\"] *= np.where(df[\"score\"]>0, df[\"wakeup_x\"], df[\"onset_x\"])\n    df[\"score\"] *= np.where(df[\"score\"]>0, df[\"wakeup_y\"], df[\"onset_y\"])\n    valid_ratio = dict_valid_ratio[series_id]\n    \n    for event in [\"onset\", \"wakeup\"]:\n        values_step = df[\"step\"].values\n        if event == \"onset\":\n            values_score = -df[\"score\"].values\n        else:\n            values_score = df[\"score\"].values\n        \n        # measure peaks\n        peak_idx = find_peaks(values_score, height=0.04, distance=60*16)[0]  # at least 16 hours interval\n        df_measure_peak = pd.DataFrame(values_step[peak_idx], columns=[\"step\"])\n        df_measure_peak[\"series_id\"] = series_id\n        df_measure_peak[\"event\"] = event\n        df_measure_peak[\"score\"] = values_score[peak_idx] * 4 * valid_ratio**0.15\n        \n        # minor peaks\n        peak_idx = find_peaks(values_score, height=0.0, distance=6)[0]\n        df_minor_peak = pd.DataFrame(values_step[peak_idx], columns=[\"step\"])\n        df_minor_peak[\"series_id\"] = series_id\n        df_minor_peak[\"event\"] = event\n        df_minor_peak[\"score\"] = values_score[peak_idx]\n        \n        df_peak = pd.concat([df_measure_peak, df_minor_peak]).drop_duplicates(subset=[\"step\"])\n        list_df.append(df_peak)\n        \n        \ndf_sub = pd.concat(list_df)\ndf_sub = df_sub.sort_values(\"score\", ascending=False).groupby(\"event\").head(100000)  # avoid Submission Scoring Error\ndf_sub = df_sub.sort_values([\"series_id\", \"step\"]).reset_index(drop=True)\ndf_sub = df_sub[[\"series_id\", \"step\", \"event\", \"score\"]].reset_index(names=\"row_id\")\ndf_sub.to_csv(\"df_sub_with_postprocess.csv\", index=False)\ndf_sub\n","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"29857a41","cell_type":"markdown","source":"Evaluation Function https://github.com/tubo213/kaggle-child-mind-institute-detect-sleep-states/blob/main/src/utils/metrics.py","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]}},{"id":"d8bbd7d6","cell_type":"code","source":"from bisect import bisect_left\nfrom typing import Dict, List, Tuple","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"25d4b0f6","cell_type":"code","source":"class ParticipantVisibleError(Exception):\n    pass\n\n# Set some placeholders for global parameters\nseries_id_column_name = \"series_id\"\ntime_column_name = \"step\"\nevent_column_name = \"event\"\nscore_column_name = \"score\"\nuse_scoring_intervals = False\ntolerances = {\n    \"onset\": [12, 36, 60, 90, 120, 150, 180, 240, 300, 360],\n    \"wakeup\": [12, 36, 60, 90, 120, 150, 180, 240, 300, 360],\n}\n\n\ndef score(\n    solution: pd.DataFrame,\n    submission: pd.DataFrame,\n    tolerances: Dict[str, List[float]],\n    series_id_column_name: str,\n    time_column_name: str,\n    event_column_name: str,\n    score_column_name: str,\n    use_scoring_intervals: bool = False,\n    verbose: bool = True,\n) -> Tuple[float, pd.DataFrame, pd.DataFrame]:\n    # Validate metric parameters\n    assert len(tolerances) > 0, \"Events must have defined tolerances.\"\n    assert set(tolerances.keys()) == set(solution[event_column_name]).difference(\n        {\"start\", \"end\"}\n    ), (\n        f\"Solution column {event_column_name} must contain the same events \"\n        \"as defined in tolerances.\"\n    )\n    assert pd.api.types.is_numeric_dtype(\n        solution[time_column_name]\n    ), f\"Solution column {time_column_name} must be of numeric type.\"\n\n    # Validate submission format\n    for column_name in [\n        series_id_column_name,\n        time_column_name,\n        event_column_name,\n        score_column_name,\n    ]:\n        if column_name not in submission.columns:\n            raise ParticipantVisibleError(\n                f\"Submission must have column '{column_name}'.\"\n            )\n\n    if not pd.api.types.is_numeric_dtype(submission[time_column_name]):\n        raise ParticipantVisibleError(\n            f\"Submission column '{time_column_name}' must be of numeric type.\"\n        )\n    if not pd.api.types.is_numeric_dtype(submission[score_column_name]):\n        raise ParticipantVisibleError(\n            f\"Submission column '{score_column_name}' must be of numeric type.\"\n        )\n\n    # Set these globally to avoid passing around a bunch of arguments\n    globals()[\"series_id_column_name\"] = series_id_column_name\n    globals()[\"time_column_name\"] = time_column_name\n    globals()[\"event_column_name\"] = event_column_name\n    globals()[\"score_column_name\"] = score_column_name\n    globals()[\"use_scoring_intervals\"] = use_scoring_intervals\n\n    return event_detection_ap(solution, submission, tolerances, verbose=verbose)\n\n\ndef find_nearest(xs: np.ndarray, value):\n    \"\"\"\n    Find the index of the closest value to x in the array xs.\n    \"\"\"\n    idx = np.searchsorted(xs, value, side=\"left\")\n    best_idx = None\n    best_error = float(\"inf\")\n    best_diff = float(\"inf\")\n\n    range_min = max(0, idx - 1)\n    range_max = min(len(xs), idx + 2)\n    for check_idx in range(\n        range_min, range_max\n    ):  # Check the exact, one before, and one after\n        error = abs(xs[check_idx] - value)\n        if error < best_error:\n            best_error = error\n            best_idx = check_idx\n            best_diff = xs[check_idx] - value\n\n    return best_idx, best_error, best_diff\n\n\ndef find_nearest_time_idx(sorted_gt_times, det_time, excluded_indices: set):\n    \"\"\"\n    search index of gt_times closest to det_time.\n\n    assumes gt_times is sorted in ascending order.\n    \"\"\"\n    # e.g. if gt_times = [0, 1, 2, 3, 4, 5] and det_time = 2.5, then idx = 3\n    sorted_gt_times = np.asarray(sorted_gt_times)\n    available_indices = np.asarray(\n        sorted(set(range(len(sorted_gt_times))) - excluded_indices), dtype=int\n    )\n    sorted_gt_times = sorted_gt_times[available_indices]\n    idx, error, diff = find_nearest(sorted_gt_times, det_time)\n    best_idx = available_indices[idx] if idx is not None else None\n\n    return best_idx, error, diff\n\n\ndef match_detections(\n    tolerance: float, ground_truths: pd.DataFrame, detections: pd.DataFrame\n) -> pd.DataFrame:\n    detections_sorted = detections.sort_values(\n        score_column_name, ascending=False\n    ).dropna()\n    is_matched = np.full_like(detections_sorted[event_column_name], False, dtype=bool)\n    diffs = np.full_like(\n        detections_sorted[event_column_name], float(\"inf\"), dtype=float\n    )\n    ground_truths_times = ground_truths.sort_values(time_column_name)[\n        time_column_name\n    ].to_list()\n    matched_gt_indices: set[int] = set()\n\n    for i, det in enumerate(detections_sorted.itertuples(index=False)):\n        det_time = getattr(det, time_column_name)\n\n        best_idx, best_error, best_diff = find_nearest_time_idx(\n            ground_truths_times, det_time, matched_gt_indices\n        )\n\n        if (best_idx is not None) and (best_error < tolerance):\n            is_matched[i] = True\n            diffs[i] = best_diff\n            matched_gt_indices.add(best_idx)\n\n    detections_sorted[\"matched\"] = is_matched\n    detections_sorted[\"diff\"] = diffs\n    return detections_sorted\n\n\ndef precision_recall_curve(\n    matches: np.ndarray, scores: np.ndarray, p: int\n) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:\n    if len(matches) == 0:\n        return [1], [0], []\n\n    # Sort matches by decreasing confidence\n    idxs = np.argsort(scores, kind=\"stable\")[::-1]\n    scores = scores[idxs]\n    matches = matches[idxs]\n\n    distinct_value_indices = np.where(np.diff(scores))[0]\n    threshold_idxs = np.r_[distinct_value_indices, matches.size - 1]\n    thresholds = scores[threshold_idxs]\n\n    # Matches become TPs and non-matches FPs as confidence threshold decreases\n    tps = np.cumsum(matches)[threshold_idxs]\n    fps = np.cumsum(~matches)[threshold_idxs]\n\n    precision = tps / (tps + fps)\n    precision[np.isnan(precision)] = 0\n    recall = (\n        tps / p\n    )  # total number of ground truths might be different than total number of matches\n\n    # Stop when full recall attained and reverse the outputs so recall is non-increasing.\n    last_ind = tps.searchsorted(tps[-1])\n    sl = slice(last_ind, None, -1)\n\n    # Final precision is 1 and final recall is 0\n    return np.r_[precision[sl], 1], np.r_[recall[sl], 0], thresholds[sl]\n\n\ndef average_precision_score(matches: np.ndarray, scores: np.ndarray, p: int) -> float:\n    precision, recall, _ = precision_recall_curve(matches, scores, p)\n    # Compute step integral\n    return -np.sum(np.diff(recall) * np.array(precision)[:-1])\n\n\ndef event_detection_ap(\n    solution: pd.DataFrame,\n    submission: pd.DataFrame,\n    tolerances: Dict[str, List[float]] = tolerances,\n    progress_bar: bool = True,\n    verbose: bool = True,\n) -> Tuple[float, pd.DataFrame, pd.DataFrame]:\n    # Ensure solution and submission are sorted properly\n    solution = solution.sort_values([series_id_column_name, time_column_name])\n    submission = submission.sort_values([series_id_column_name, time_column_name])\n\n    # Extract scoring intervals.\n    if use_scoring_intervals:\n        raise NotImplementedError(\"Scoring intervals not implemented.\")\n\n    # Extract ground-truth events.\n    ground_truths = solution.query(\"event not in ['start', 'end']\").reset_index(\n        drop=True\n    )\n\n    # Map each event class to its prevalence (needed for recall calculation)\n    class_counts = ground_truths.value_counts(event_column_name).to_dict()\n\n    # Create table for detections with a column indicating a match to a ground-truth event\n    detections = submission.assign(matched=False)\n\n    # Remove detections outside of scoring intervals\n    if use_scoring_intervals:\n        raise NotImplementedError(\"Scoring intervals not implemented.\")\n    else:\n        detections_filtered = detections\n\n    # Create table of event-class x tolerance x series_id values\n    aggregation_keys = pd.DataFrame(\n        [\n            (ev, tol, vid)\n            for ev in tolerances.keys()\n            for tol in tolerances[ev]\n            for vid in ground_truths[series_id_column_name].unique()\n        ],\n        columns=[event_column_name, \"tolerance\", series_id_column_name],\n    )\n\n    # Create match evaluation groups: event-class x tolerance x series_id\n    detections_grouped = aggregation_keys.merge(\n        detections_filtered, on=[event_column_name, series_id_column_name], how=\"left\"\n    ).groupby([event_column_name, \"tolerance\", series_id_column_name])\n    ground_truths_grouped = aggregation_keys.merge(\n        ground_truths, on=[event_column_name, series_id_column_name], how=\"left\"\n    ).groupby([event_column_name, \"tolerance\", series_id_column_name])\n\n    # Match detections to ground truth events by evaluation group\n    pbars = aggregation_keys.itertuples(index=False)\n    if progress_bar:\n        pbars = tqdm(pbars, total=len(aggregation_keys), desc=\"Matching detections\")\n        \n    detections_matched = []\n    for key in pbars:\n        dets = detections_grouped.get_group(key)\n        gts = ground_truths_grouped.get_group(key)\n        detections_matched.append(\n            match_detections(dets[\"tolerance\"].iloc[0], gts, dets)\n        )\n        \n    detections_matched = pd.concat(detections_matched)\n\n    # Compute AP per event x tolerance group\n    event_classes = ground_truths[event_column_name].unique()\n    ap_table = (\n        detections_matched.query(\"event in @event_classes\")\n        .groupby([event_column_name, \"tolerance\"])\n        .apply(\n            lambda group: average_precision_score(\n                group[\"matched\"].to_numpy(),\n                group[score_column_name].to_numpy(),\n                class_counts[group[event_column_name].iat[0]],\n            )\n        )\n        .reset_index()\n        .pivot(index=\"tolerance\", columns=\"event\", values=0)\n    )\n    \n    if verbose:\n        display(ap_table)\n        \n    # Average over tolerances, then over event classes\n    mean_ap = ap_table.mean().mean()\n\n    return mean_ap, ap_table, detections_matched","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"0d1617ca","cell_type":"code","source":"df_sub = pd.read_csv(\"df_sub.csv\")\nscore_all, df_score, df_result = score(\n    solution=df_events,\n    submission=df_sub,\n    tolerances=tolerances,\n    series_id_column_name=series_id_column_name,\n    time_column_name=time_column_name,\n    event_column_name=event_column_name,\n    score_column_name=score_column_name\n)\n\nprint(score_all)","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"9e5d38ad","cell_type":"code","source":"df_sub = pd.read_csv(\"df_sub_with_postprocess.csv\")\nscore_all, df_score, df_result = score(\n    solution=df_events,\n    submission=df_sub,\n    tolerances=tolerances,\n    series_id_column_name=series_id_column_name,\n    time_column_name=time_column_name,\n    event_column_name=event_column_name,\n    score_column_name=score_column_name\n)\n\nprint(score_all)","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true},"outputs":[],"execution_count":null}]}