{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","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":"none","dataSources":[{"sourceId":105399,"databundleVersionId":12733338,"sourceType":"competition"},{"sourceId":12581792,"sourceType":"datasetVersion","datasetId":7902293}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# ---------------------------\n#  Import libraries\n# ---------------------------\nimport pandas as pd","metadata":{"_uuid":"97311fef-fdd5-4cb7-bde7-88d4d594d1f4","_cell_guid":"7215f34d-fce0-4172-9c8c-71ba6b31bf16","trusted":true,"collapsed":false,"execution":{"iopub.status.busy":"2025-07-21T14:16:19.336913Z","iopub.execute_input":"2025-07-21T14:16:19.337275Z","iopub.status.idle":"2025-07-21T14:16:34.439137Z","shell.execute_reply.started":"2025-07-21T14:16:19.337246Z","shell.execute_reply":"2025-07-21T14:16:34.438223Z"},"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ---------------------------\n#  Helper function: rank to score\n# ---------------------------\ndef rank_to_score(sr, eps=1e-5):\n    n = sr.max()\n    return 1.0 - (sr - 1) / (n + eps)\n\n# ---------------------------\n#  Helper function: score to rank\n# ---------------------------\ndef score_to_rank(s):\n    return s.rank(method='first', ascending=False).astype(int)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ---------------------------\n#  iBlend function\n# ---------------------------\ndef iBlend(path_to_ds, file_short_names, sls):\n\n    # Internal helper function: read submissions\n    def read_subm(sls, i):\n        tnm = sls[\"subm\"][i][\"name\"]\n        filename = f\"{sls['path']}{tnm}.csv\"\n        df = pd.read_csv(filename).rename(columns={'target': tnm, sls[\"target\"]: tnm})\n        del df[\"ranker_id\"]\n        return df\n\n    # Internal helper function: blending logic\n    def tida(sls):\n        dfs_subm = [read_subm(sls, i) for i in range(len(sls[\"subm\"]))]\n\n        # Merge all submissions by Id\n        df_subms = dfs_subm[0]\n        for df in dfs_subm[1:]:\n            df_subms = pd.merge(df_subms, df, on='Id')\n\n        cols = [col for col in df_subms.columns if col != \"Id\"]\n        short_name_cols = [c.replace(sls[\"prefix\"], '') for c in cols]\n        corrects = sls[\"subwts\"]\n        weights = [subm['weight'] for subm in sls[\"subm\"]]\n\n        # Compute sorted order\n        def alls(x, cs=cols):\n            tes = {c: x[c] for c in cs}.items()\n            subms_sorted = [\n                t[0].replace(sls[\"prefix\"], '')\n                for t in sorted(tes, key=lambda k: k[1], reverse=(sls[\"sort\"] == 'desc'))\n            ]\n            return subms_sorted\n\n        # Compute weighted ensemble\n        def correct(x, cs=cols, w=weights, cw=corrects):\n            ic = [x['alls'].index(c) for c in short_name_cols]\n            cS = [x[cols[j]] * (w[j] + cw[ic[j]]) for j in range(len(cols))]\n            return sum(cS)\n\n        df_subms['alls'] = df_subms.apply(alls, axis=1)\n        df_subms[sls[\"target\"]] = df_subms.apply(correct, axis=1)\n\n        schema_rename = {old_nc: new_shnc for old_nc, new_shnc in zip(cols, short_name_cols)}\n        df_subms = df_subms.rename(columns=schema_rename).rename(columns={sls[\"target\"]: \"ensemble\"})\n\n        submission_cols = ['Id', 'ensemble']\n        df_submission = df_subms[submission_cols].rename(columns={\"ensemble\": sls[\"target\"]})\n        return df_submission\n\n    # Load a sample submission template\n    sample_subm = pd.read_csv(f\"{path_to_ds}{file_short_names[1]}.csv\")\n\n    # Generate ascending and descending submissions, then blend\n    def ensemble_tida(sls, submission=sample_subm):\n        sls['sort'] = 'desc'\n        dfD = tida(sls)\n        sls['sort'] = 'asc'\n        dfA = tida(sls)\n\n        target, d, a = sls['target'], sls['desc'], sls['asc']\n        submission[target] = ((dfD[target] * d) + (dfA[target] * a)).round().astype(int)\n        return submission\n\n    # Final blended submission\n    submission = ensemble_tida(sls)\n    return submission","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ---------------------------\n#  Main function\n# ---------------------------\ndef main():\n    # Paths and filenames\n    path_to_ds = '/kaggle/input/20-juli-2025-flightrank/submission '\n    file_short_names = ['0.48507', '0.48425', '0.49343']\n\n    # Parameters configuration\n    params = {\n        'path': path_to_ds,\n        'sort': \"asc\\\\desc\",\n        'target': \"selected\",\n        'q_rows': 6_897_776,\n        'prefix': \"subm_\",\n        'desc': 0.44,\n        'asc': 0.54,\n        'subwts': [+0.11, -0.04, -0.07],\n        'subm': [\n            {'name': file_short_names[0], 'weight': 0.30},\n            {'name': file_short_names[1], 'weight': 0.20},\n            {'name': file_short_names[2], 'weight': 0.50},\n        ]\n    }\n\n    # Generate blended submission\n    df_submission = iBlend(path_to_ds, file_short_names, params)\n    df_submission.to_csv('/kaggle/working/submission_tida.csv', index=False)\n\n    # Load additional submissions for simple ensemble\n    df2 = pd.read_csv(f\"{path_to_ds}0.43916.csv\")\n    df3 = pd.read_csv(f\"{path_to_ds}0.42163.csv\")\n    df4 = pd.read_csv(f\"{path_to_ds}0.41226.csv\")\n\n    # List of dataframes\n    dfs = [df_submission, df2, df3, df4]\n\n    # Convert ranks to scores for ensemble\n    score_frames = []\n    for i, df in enumerate(dfs):\n        tmp = df[['Id', 'ranker_id', 'selected']].copy()\n        tmp['score'] = tmp.groupby('ranker_id')['selected'].transform(rank_to_score)\n        score_frames.append(tmp[['Id', 'ranker_id', 'score']].rename(columns={'score': f'score_{i}'}))\n\n    # Merge scores\n    merged = score_frames[0]\n    for frame in score_frames[1:]:\n        merged = merged.merge(frame, on=['Id', 'ranker_id'], how='left')\n\n    # Ensemble weights\n    weights = [0.997, 0.001, 0.001, 0.001]\n    score_cols = [f'score_{i}' for i in range(4)]\n    w = pd.Series(weights, index=score_cols)\n\n    # Compute weighted score mean\n    merged['score_mean'] = (merged[score_cols] * w).sum(axis=1) / w.sum()\n\n    # Convert scores back to ranks\n    merged['selected'] = merged.groupby('ranker_id')['score_mean'].transform(score_to_rank)\n\n    # Final submission file\n    final_submission = merged[['Id', 'ranker_id', 'selected']]\n    final_submission.to_csv(\"/kaggle/working/submission.csv\", index=False, float_format='%.0f')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ---------------------------\n#  Execute main function\n# ---------------------------\nif __name__ == \"__main__\":\n    main()","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}