{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 1. IMPORT LIBRARIES\n# ============================================\n\nimport pandas as pd\nimport numpy as np\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.feature_extraction.text import TfidfVectorizer\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.metrics import roc_auc_score\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nprint(\"Libraries imported successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:20:47.924145Z","iopub.execute_input":"2026-08-19T13:20:47.924584Z","iopub.status.idle":"2026-08-19T13:20:52.065739Z","shell.execute_reply.started":"2026-08-19T13:20:47.92454Z","shell.execute_reply":"2026-08-19T13:20:52.06466Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 2. LOAD DATASETS\n# ============================================\n\nTRAIN_PATH = \"/kaggle/input/competitions/rsna-knee-abnormality-detection/train.csv\"\nTEST_PATH = \"/kaggle/input/competitions/rsna-knee-abnormality-detection/test.csv\"\n\ntrain = pd.read_csv(TRAIN_PATH)\ntest = pd.read_csv(TEST_PATH)\n\nprint(\"Train shape:\", train.shape)\nprint(\"Test shape :\", test.shape)\n\nprint(\"\\nTrain columns:\")\nprint(train.columns.tolist())\n\nprint(\"\\nTest columns:\")\nprint(test.columns.tolist())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:24:02.341232Z","iopub.execute_input":"2026-08-19T13:24:02.341604Z","iopub.status.idle":"2026-08-19T13:24:02.577779Z","shell.execute_reply.started":"2026-08-19T13:24:02.341574Z","shell.execute_reply":"2026-08-19T13:24:02.5766Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 3. DEFINE TARGET COLUMNS\n# ============================================\n\nTARGETS = [\n    \"ACL\",\n    \"MCL\",\n    \"Medial Meniscus\",\n    \"Lateral Meniscus\",\n    \"Medial OA\",\n    \"Lateral OA\",\n    \"PF OA\",\n    \"Effusion\",\n    \"Synovitis\",\n    \"Baker's\",\n    \"Contusion\",\n    \"Fracture\"\n]\n\nprint(\"Number of target classes:\", len(TARGETS))\nprint(TARGETS)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:24:25.681905Z","iopub.execute_input":"2026-08-19T13:24:25.682231Z","iopub.status.idle":"2026-08-19T13:24:25.688009Z","shell.execute_reply.started":"2026-08-19T13:24:25.682204Z","shell.execute_reply":"2026-08-19T13:24:25.68707Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 4. CHECK DATA\n# ============================================\n\nprint(\"Missing values in Report:\")\nprint(\"Train:\", train[\"Report\"].isna().sum())\n\nprint(\"\\nTraining label distribution:\")\ndisplay(train[TARGETS].mean().sort_values(ascending=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:24:34.653909Z","iopub.execute_input":"2026-08-19T13:24:34.65424Z","iopub.status.idle":"2026-08-19T13:24:34.683661Z","shell.execute_reply.started":"2026-08-19T13:24:34.654214Z","shell.execute_reply":"2026-08-19T13:24:34.682304Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 5. PREPARE TEXT DATA\n# ============================================\n\ntrain[\"Report\"] = train[\"Report\"].fillna(\"\").astype(str)\n\nX = train[\"Report\"]\ny = train[TARGETS].copy()\n\nprint(\"Number of training samples:\", len(X))\nprint(\"Number of test samples:\", len(test))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:24:51.07452Z","iopub.execute_input":"2026-08-19T13:24:51.074872Z","iopub.status.idle":"2026-08-19T13:24:51.085642Z","shell.execute_reply.started":"2026-08-19T13:24:51.074842Z","shell.execute_reply":"2026-08-19T13:24:51.084659Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 6. TRAIN / VALIDATION SPLIT\n# ============================================\n\nX_train, X_valid, y_train, y_valid = train_test_split(\n    X,\n    y,\n    test_size=0.20,\n    random_state=42\n)\n\nprint(\"Training samples  :\", len(X_train))\nprint(\"Validation samples:\", len(X_valid))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:24:59.830292Z","iopub.execute_input":"2026-08-19T13:24:59.830644Z","iopub.status.idle":"2026-08-19T13:24:59.841077Z","shell.execute_reply.started":"2026-08-19T13:24:59.830618Z","shell.execute_reply":"2026-08-19T13:24:59.840156Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 7. TF-IDF TEXT FEATURES\n# ============================================\n\nvectorizer = TfidfVectorizer(\n    max_features=50000,\n    ngram_range=(1, 2),\n    min_df=2,\n    sublinear_tf=True\n)\n\nX_train_tfidf = vectorizer.fit_transform(X_train)\nX_valid_tfidf = vectorizer.transform(X_valid)\n\nprint(\"Training TF-IDF shape :\", X_train_tfidf.shape)\nprint(\"Validation TF-IDF shape:\", X_valid_tfidf.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:25:07.992368Z","iopub.execute_input":"2026-08-19T13:25:07.992835Z","iopub.status.idle":"2026-08-19T13:25:09.556774Z","shell.execute_reply.started":"2026-08-19T13:25:07.992805Z","shell.execute_reply":"2026-08-19T13:25:09.555277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 8. TRAIN 12 CLASSIFIERS\n# ============================================\n\nmodels = {}\nvalidation_predictions = {}\n\nfor target in TARGETS:\n\n    print(f\"Training model for: {target}\")\n\n    # Filter out NaN values for the current target\n    is_not_nan = ~y_train[target].isnull()\n    X_train_filtered = X_train_tfidf[is_not_nan.values]\n    y_train_filtered = y_train[target][is_not_nan]\n\n    model = LogisticRegression(\n        max_iter=1000,\n        class_weight=\"balanced\",\n        C=1.0,\n        solver=\"liblinear\"\n    )\n\n    model.fit(\n        X_train_filtered,\n        y_train_filtered\n    )\n\n    models[target] = model\n\n    # Probability of abnormality being present\n    validation_predictions[target] = model.predict_proba(\n        X_valid_tfidf\n    )[:, 1]\n\nprint(\"\\nAll 12 models trained successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:25:17.555001Z","iopub.execute_input":"2026-08-19T13:25:17.5555Z","iopub.status.idle":"2026-08-19T13:25:17.670984Z","shell.execute_reply.started":"2026-08-19T13:25:17.555423Z","shell.execute_reply":"2026-08-19T13:25:17.669899Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 9. VALIDATION ROC-AUC\n# ============================================\n\nauc_scores = {}\n\nfor target in TARGETS:\n\n    # Filter out NaN values for the current target from y_valid\n    is_not_nan_valid = ~y_valid[target].isnull()\n    y_valid_filtered = y_valid[target][is_not_nan_valid]\n    validation_predictions_filtered = validation_predictions[target][is_not_nan_valid.values]\n\n    # Calculate ROC-AUC score only if there are valid samples\n    if len(y_valid_filtered) > 0:\n        score = roc_auc_score(\n            y_valid_filtered,\n            validation_predictions_filtered\n        )\n    else:\n        score = np.nan # Assign NaN if no valid samples for this target\n\n    auc_scores[target] = score\n\n    print(f\"{target:20s} : {score:.4f}\")\n\n\n# Filter out NaN scores before calculating the mean AUC\nvalid_auc_scores = [s for s in auc_scores.values() if not np.isnan(s)]\n\n# Calculate mean_auc only if there are valid scores\nif len(valid_auc_scores) > 0:\n    mean_auc = np.mean(valid_auc_scores)\nelse:\n    mean_auc = np.nan # Assign NaN if no valid AUC scores\n\nprint(\"\\n\" + \"=\" * 50)\nprint(f\"MACRO AVERAGE ROC-AUC : {mean_auc:.4f}\")\nprint(\"=\" * 50)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:25:26.664987Z","iopub.execute_input":"2026-08-19T13:25:26.665324Z","iopub.status.idle":"2026-08-19T13:25:26.715939Z","shell.execute_reply.started":"2026-08-19T13:25:26.665296Z","shell.execute_reply":"2026-08-19T13:25:26.714661Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# FIX: RELOAD THE CORRECT DATASETS\n# ============================================\n\nimport pandas as pd\nimport numpy as np\n\nTRAIN_PATH = \"/kaggle/input/competitions/rsna-knee-abnormality-detection/train.csv\"\nTEST_PATH = \"/kaggle/input/competitions/rsna-knee-abnormality-detection/test.csv\"\n\ntrain = pd.read_csv(TRAIN_PATH)\ntest = pd.read_csv(TEST_PATH)\n\nprint(\"Train shape:\", train.shape)\nprint(\"Test shape :\", test.shape)\n\nprint(\"\\nTrain columns:\")\nprint(train.columns.tolist())\n\nprint(\"\\nTest columns:\")\nprint(test.columns.tolist())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:27:02.623523Z","iopub.execute_input":"2026-08-19T13:27:02.623862Z","iopub.status.idle":"2026-08-19T13:27:02.757702Z","shell.execute_reply.started":"2026-08-19T13:27:02.623835Z","shell.execute_reply":"2026-08-19T13:27:02.756191Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# CHECK REPORT COLUMN\n# ============================================\n\nprint(\"Does train.csv contain Report?\",\n      \"Report\" in train.columns)\n\nprint(\"Does test.csv contain Report?\",\n      \"Report\" in test.columns)\n\nprint(\"\\nFirst training report:\")\nprint(train[\"Report\"].iloc[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:27:12.595995Z","iopub.execute_input":"2026-08-19T13:27:12.596496Z","iopub.status.idle":"2026-08-19T13:27:12.604625Z","shell.execute_reply.started":"2026-08-19T13:27:12.596424Z","shell.execute_reply":"2026-08-19T13:27:12.603327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 10. PREPARE FINAL TEXT FEATURES\n# ============================================\n\nimport pandas as pd # Ensure pandas is available if not in current scope\nfrom scipy.sparse import csr_matrix # Import for sparse matrix\n\n# Reload test.csv to ensure we have the base test DataFrame\nTEST_PATH = \"/kaggle/input/competitions/rsna-knee-abnormality-detection/test.csv\"\ntest = pd.read_csv(TEST_PATH)\n\n# Load test reports data to get the 'Report' column for the test set\nTEST_REPORTS_PATH = \"/content/test_reports.csv\" # <--- User needs to ensure this file exists or update the path\n\ntest_has_reports_column = False\ntry:\n    test_reports_df = pd.read_csv(TEST_REPORTS_PATH)\n    print(\"Columns in test_reports_df:\", test_reports_df.columns.tolist())\n\n    if 'Report' in test_reports_df.columns:\n        test_has_reports_column = True\n        # Merge the test_reports_df with the existing 'test' DataFrame\n        test = pd.merge(test, test_reports_df[['StudyInstanceUID', 'Report']], on='StudyInstanceUID', how='left')\n    else:\n        print(f\"Warning: The file '{TEST_REPORTS_PATH}' was found, but it does not contain a 'Report' column. Skipping merge.\")\n        test['Report'] = '' # Add empty 'Report' column to test df to prevent KeyError later\nexcept FileNotFoundError:\n    print(f\"Error: The file '{TEST_REPORTS_PATH}' was not found.\")\n    print(\"Please ensure the test reports file is uploaded to your Colab environment or update 'TEST_REPORTS_PATH' to the correct location.\")\n    test['Report'] = '' # Add empty 'Report' column to test df to prevent KeyError later\n\n# Make sure reports are strings\ntrain[\"Report\"] = train[\"Report\"].fillna(\"\").astype(str)\ntest[\"Report\"] = test[\"Report\"].fillna(\"\").astype(str)\n\nprint(\"Creating final TF-IDF features...\")\n\nfinal_vectorizer = TfidfVectorizer(\n    max_features=50000,\n    ngram_range=(1, 2),\n    min_df=2,\n    sublinear_tf=True\n)\n\nX_all_tfidf = final_vectorizer.fit_transform(train[\"Report\"])\n\nif test_has_reports_column:\n    X_test_tfidf = final_vectorizer.transform(test[\"Report\"])\nelse:\n    # If test reports were not loaded or 'Report' column is missing, create an empty sparse matrix\n    # with the same number of features as the trained vectorizer. This prevents NameError in subsequent cells.\n    num_features = len(final_vectorizer.vocabulary_) if hasattr(final_vectorizer, 'vocabulary_') else 0\n    X_test_tfidf = csr_matrix((len(test), num_features), dtype=float)\n    print(\"X_test_tfidf created as an empty sparse matrix due to missing/invalid test reports.\")\n\n\nprint(\"Training TF-IDF shape :\", X_all_tfidf.shape)\nprint(\"Test TF-IDF shape     :\", X_test_tfidf.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:28:03.624808Z","iopub.execute_input":"2026-08-19T13:28:03.625134Z","iopub.status.idle":"2026-08-19T13:28:05.335215Z","shell.execute_reply.started":"2026-08-19T13:28:03.625109Z","shell.execute_reply":"2026-08-19T13:28:05.33423Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 11. TRAIN FINAL MODELS\n# ============================================\n\nfinal_models = {}\n\nfor target in TARGETS:\n\n    print(f\"Training final model: {target}\")\n\n    # Filter out NaN values for the current target from the full training data\n    is_not_nan_all_train = ~train[target].isnull()\n    X_all_tfidf_filtered = X_all_tfidf[is_not_nan_all_train.values]\n    y_all_train_filtered = train[target][is_not_nan_all_train]\n\n    model = LogisticRegression(\n        max_iter=1000,\n        class_weight=\"balanced\",\n        C=1.0,\n        solver=\"liblinear\"\n    )\n\n    model.fit(\n        X_all_tfidf_filtered,\n        y_all_train_filtered\n    )\n\n    final_models[target] = model\n\nprint(\"\\nAll final models trained successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:28:22.020905Z","iopub.execute_input":"2026-08-19T13:28:22.02126Z","iopub.status.idle":"2026-08-19T13:28:22.148817Z","shell.execute_reply.started":"2026-08-19T13:28:22.021231Z","shell.execute_reply":"2026-08-19T13:28:22.146991Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 12. PREDICT TEST DATA\n# ============================================\n\ntest_predictions = pd.DataFrame()\n\nfor target in TARGETS:\n\n    print(f\"Predicting: {target}\")\n\n    test_predictions[target] = final_models[target].predict_proba(\n        X_test_tfidf\n    )[:, 1]\n\nprint(\"\\nPrediction shape:\", test_predictions.shape)\n\ndisplay(test_predictions.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:28:31.295019Z","iopub.execute_input":"2026-08-19T13:28:31.295374Z","iopub.status.idle":"2026-08-19T13:28:31.335281Z","shell.execute_reply.started":"2026-08-19T13:28:31.295345Z","shell.execute_reply":"2026-08-19T13:28:31.334537Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 13. CREATE SUBMISSION FILE\n# ============================================\n\nsubmission = pd.DataFrame()\n\n# First column\nsubmission[\"StudyInstanceUID\"] = test[\"StudyInstanceUID\"]\n\n# Add all 12 predictions\nfor target in TARGETS:\n    submission[target] = test_predictions[target]\n\nprint(\"Submission shape:\", submission.shape)\n\ndisplay(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:28:41.475215Z","iopub.execute_input":"2026-08-19T13:28:41.475602Z","iopub.status.idle":"2026-08-19T13:28:41.501306Z","shell.execute_reply.started":"2026-08-19T13:28:41.475572Z","shell.execute_reply":"2026-08-19T13:28:41.500259Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 14. VERIFY SUBMISSION\n# ============================================\n\nexpected_columns = [\"StudyInstanceUID\"] + TARGETS\n\nprint(\"Expected columns:\", len(expected_columns))\nprint(\"Actual columns  :\", len(submission.columns))\n\nprint(\"\\nColumns correct:\",\n      submission.columns.tolist() == expected_columns)\n\nprint(\"\\nMissing values:\")\nprint(submission.isna().sum())\n\nprint(\"\\nProbability range:\")\nprint(\"Minimum:\", submission[TARGETS].min().min())\nprint(\"Maximum:\", submission[TARGETS].max().max())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:28:50.420358Z","iopub.execute_input":"2026-08-19T13:28:50.421422Z","iopub.status.idle":"2026-08-19T13:28:50.438146Z","shell.execute_reply.started":"2026-08-19T13:28:50.421381Z","shell.execute_reply":"2026-08-19T13:28:50.437139Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# 15. SAVE SUBMISSION\n# ============================================\n\nSUBMISSION_PATH = \"/kaggle/working/submission.csv\"\n\nsubmission.to_csv(\n    SUBMISSION_PATH,\n    index=False\n)\n\nprint(\"Submission saved successfully!\")\nprint(SUBMISSION_PATH)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-19T13:33:38.975124Z","iopub.execute_input":"2026-08-19T13:33:38.975549Z","iopub.status.idle":"2026-08-19T13:33:38.983342Z","shell.execute_reply.started":"2026-08-19T13:33:38.97545Z","shell.execute_reply":"2026-08-19T13:33:38.982332Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}