{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.12.12"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":116062,"databundleVersionId":14875579,"sourceType":"competition"},{"sourceId":14187577,"sourceType":"datasetVersion","datasetId":9045748},{"sourceId":287175447,"sourceType":"kernelVersion"},{"sourceId":292313527,"sourceType":"kernelVersion"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":1832.053542,"end_time":"2026-01-17T19:25:53.974197","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2026-01-17T18:55:21.920655","version":"2.6.0"},"widgets":{"application/vnd.jupyter.widget-state+json":{"state":{"0b854325c65d4eca947de90a910680f8":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"2389f88ab9c04f60b8403951a7f5b409":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"4b94207c65954ffc94ca2c9d341f0bbf":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":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,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"4f98217f3de34d3aa70028b1f4850854":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_8d70a8ffaf5346b68a07039cdf375d41","IPY_MODEL_b6652b0205814ef48bd0c76284418ca2","IPY_MODEL_d400b21c67224e01914b4d98fde514af"],"layout":"IPY_MODEL_c1dbbb7942dd44c28d7ceae5f1c1510e","tabbable":null,"tooltip":null}},"5aa427aeb96040fd9cc89ac202101372":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"6171cb7970d24de488a93d3757f46690":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":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,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"8d70a8ffaf5346b68a07039cdf375d41":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_4b94207c65954ffc94ca2c9d341f0bbf","placeholder":"​","style":"IPY_MODEL_5aa427aeb96040fd9cc89ac202101372","tabbable":null,"tooltip":null,"value":"100%"}},"9002b1a582eb481d80cddd84ccb4e5c3":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":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,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"b6652b0205814ef48bd0c76284418ca2":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_9002b1a582eb481d80cddd84ccb4e5c3","max":279437,"min":0,"orientation":"horizontal","style":"IPY_MODEL_2389f88ab9c04f60b8403951a7f5b409","tabbable":null,"tooltip":null,"value":279437}},"c1dbbb7942dd44c28d7ceae5f1c1510e":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":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,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"d400b21c67224e01914b4d98fde514af":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_6171cb7970d24de488a93d3757f46690","placeholder":"​","style":"IPY_MODEL_0b854325c65d4eca947de90a910680f8","tabbable":null,"tooltip":null,"value":" 279437/279437 [27:59&lt;00:00, 178.11it/s]"}}},"version_major":2,"version_minor":0}}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"b9034543","cell_type":"code","source":"import os\nimport gc\nimport pandas as pd\nimport numpy as np\nimport networkx as nx\nfrom tqdm.auto import tqdm\n\n# ==========================================\n# CONFIGURATION\n# ==========================================\nOBO_PATH = \"/kaggle/input/cafa-6-protein-function-prediction/Train/go-basic.obo\"\nSUBMISSION_INPUT = '/kaggle/input/cafa6-protein-function-enhanced-nb-v2/submission.tsv'\nSUBMISSION_OUTPUT = 'submission.tsv'\n\n# Threshold to remove weak noise.\nMIN_SCORE_THRESHOLD = 0.001 \n\n# ==========================================\n# 1. FAST GRAPH BUILDER (NetworkX)\n# ==========================================\ndef build_go_graph(go_obo_path):\n    print(f\"[1/4] Parsing OBO into DAG from {go_obo_path}...\")\n    go_graph = nx.DiGraph()\n    \n    if not os.path.exists(go_obo_path):\n        raise FileNotFoundError(f\"OBO not found: {go_obo_path}\")\n\n    with open(go_obo_path, \"r\") as f:\n        cur_id = None\n        for line in f:\n            line = line.strip()\n            if line == \"[Term]\":\n                cur_id = None\n            elif line.startswith(\"id: \"):\n                cur_id = line.split(\"id: \")[1].strip()\n                go_graph.add_node(cur_id)\n            elif line.startswith(\"is_a: \"):\n                pid = line.split()[1].strip()\n                if cur_id:\n                    go_graph.add_edge(cur_id, pid)\n            elif line.startswith(\"relationship: part_of \"):\n                parts = line.split()\n                if len(parts) >= 3:\n                    pid = parts[2].strip()\n                    if cur_id:\n                        go_graph.add_edge(cur_id, pid)\n\n    print(\"      -> Calculating Topological Order...\")\n    if not nx.is_directed_acyclic_graph(go_graph):\n        cycles = list(nx.simple_cycles(go_graph))\n        for cycle in cycles:\n            go_graph.remove_edge(cycle[0], cycle[1])\n\n    topo_order = list(nx.topological_sort(go_graph))\n    term_parents_map = {n: list(go_graph.successors(n)) for n in go_graph.nodes()}\n    \n    return topo_order, term_parents_map\n\n# ==========================================\n# 2. FAST PROPAGATION (HARD MAX)\n# ==========================================\ndef propagate_hard_max(df, topo_order, term_parents_map):\n    print(\"[3/4] Propagating Scores (Hard Max)...\")\n    print(\"      -> Grouping data...\")\n    \n    # Process by group to save memory and ensure isolation\n    groups = df.groupby('protein_id')\n    final_rows = []\n    \n    for pid, group in tqdm(groups):\n        # Map term -> score\n        scores = dict(zip(group['go_term'], group['score']))\n        \n        # Propagate from leaves to roots\n        for child in topo_order:\n            if child in scores:\n                child_score = scores[child]\n                if child in term_parents_map:\n                    for parent in term_parents_map[child]:\n                        current_parent_score = scores.get(parent, 0.0)\n                        # HARD MAX RULE: Parent score must be at least child score\n                        if child_score > current_parent_score:\n                            scores[parent] = child_score\n        \n        # Filter and collect\n        for term, score in scores.items():\n            if score >= MIN_SCORE_THRESHOLD:\n                final_rows.append((pid, term, score))\n\n    return pd.DataFrame(final_rows, columns=['protein_id', 'go_term', 'score'])\n\n# ==========================================\n# 3. MAIN PIPELINE\n# ==========================================\n\n# 1. Setup\ntopo_order, term_parents_map = build_go_graph(OBO_PATH)\n\nprint(f\"[2/4] Loading submission...\")\n# FIXED: names list now has 3 items to match your file\nsubmission = pd.read_csv(SUBMISSION_INPUT, sep='\\t', header=None, \n                         names=['protein_id', 'go_term', 'score'], \n                         dtype={'score': np.float32},\n                         on_bad_lines='skip') # Skip lines that don't match\n\n# 2. Propagate\nsubmission_improved = propagate_hard_max(submission, topo_order, term_parents_map)\n\n# 3. Save\nprint(f\"[4/4] Saving improved submission ({len(submission_improved)} rows)...\")\nsubmission_improved['score'] = submission_improved['score'].round(3)\nsubmission_improved.to_csv(SUBMISSION_OUTPUT, sep='\\t', index=False, header=False)\n\nprint(\"------------------------------------------------\")\nprint(f\"[Done] Saved to {SUBMISSION_OUTPUT}\")\nprint(\"------------------------------------------------\")\nprint(submission_improved.head())","metadata":{"execution":{"iopub.execute_input":"2026-01-17T18:55:25.208244Z","iopub.status.busy":"2026-01-17T18:55:25.207919Z","iopub.status.idle":"2026-01-17T19:25:51.637896Z","shell.execute_reply":"2026-01-17T19:25:51.637016Z"},"papermill":{"duration":1826.436603,"end_time":"2026-01-17T19:25:51.640775","exception":false,"start_time":"2026-01-17T18:55:25.204172","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"2938f7d6","cell_type":"code","source":"","metadata":{"papermill":{"duration":0.0016,"end_time":"2026-01-17T19:25:51.644339","exception":false,"start_time":"2026-01-17T19:25:51.642739","status":"completed"},"tags":[]},"outputs":[],"execution_count":null}]}