{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.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":71549,"databundleVersionId":8561470,"sourceType":"competition"}],"dockerImageVersionId":30746,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# https://www.kaggle.com/code/metric/rsna-lumbar-metric-71549\nimport numpy as np\nimport pandas as pd\nimport pandas.api.types\nimport sklearn.metrics\n\n\nclass ParticipantVisibleError(Exception):\n    pass\n\n\ndef get_condition(full_location: str) -> str:\n    # Given an input like spinal_canal_stenosis_l1_l2 extracts 'spinal'\n    for injury_condition in ['spinal', 'foraminal', 'subarticular']:\n        if injury_condition in full_location:\n            return injury_condition\n    raise ValueError(f'condition not found in {full_location}')\n\n\ndef score(\n        solution: pd.DataFrame,\n        submission: pd.DataFrame,\n        row_id_column_name: str,\n        any_severe_scalar: float\n    ) -> float:\n    '''\n    Pseudocode:\n    1. Calculate the sample weighted log loss for each medical condition:\n    2. Derive a new any_severe label.\n    3. Calculate the sample weighted log loss for the new any_severe label.\n    4. Return the average of all of the label group log losses as the final score, normalized for the number of columns in each group.\n       This mitigates the impact of spinal stenosis having only half as many columns as the other two conditions.\n    '''\n    target_levels = ['normal_mild', 'moderate', 'severe']\n\n    # Run basic QC checks on the inputs\n    if not pandas.api.types.is_numeric_dtype(submission[target_levels].values):\n        raise ParticipantVisibleError('All submission values must be numeric')\n\n    if not np.isfinite(submission[target_levels].values).all():\n        raise ParticipantVisibleError('All submission values must be finite')\n\n    if solution[target_levels].min().min() < 0:\n        raise ParticipantVisibleError('All labels must be at least zero')\n    if submission[target_levels].min().min() < 0:\n        raise ParticipantVisibleError('All predictions must be at least zero')\n\n    solution['study_id'] = solution['row_id'].apply(lambda x: x.split('_')[0])\n    solution['location'] = solution['row_id'].apply(lambda x: '_'.join(x.split('_')[1:]))\n    solution['condition'] = solution['row_id'].apply(get_condition)\n    \n    del solution[row_id_column_name]\n    del submission[row_id_column_name]\n    assert sorted(submission.columns) == sorted(target_levels)\n\n    submission['study_id'] = solution['study_id']\n    submission['location'] = solution['location']\n    submission['condition'] = solution['condition']\n\n    condition_losses = []\n    condition_weights = []\n    for condition in ['spinal', 'foraminal', 'subarticular']:\n        condition_indices = solution.loc[solution['condition'] == condition].index.values\n        condition_loss = sklearn.metrics.log_loss(\n            y_true=solution.loc[condition_indices, target_levels].values,\n            y_pred=submission.loc[condition_indices, target_levels].values,\n            sample_weight=solution.loc[condition_indices, 'sample_weight'].values\n        )\n        condition_losses.append(condition_loss)\n        condition_weights.append(1)\n    \n    any_severe_spinal_labels = pd.Series(solution.loc[solution['condition'] == 'spinal'].groupby('study_id')['severe'].max())\n    any_severe_spinal_weights = pd.Series(solution.loc[solution['condition'] == 'spinal'].groupby('study_id')['sample_weight'].max())\n    any_severe_spinal_predictions = pd.Series(submission.loc[submission['condition'] == 'spinal'].groupby('study_id')['severe'].max())\n    any_severe_spinal_loss = sklearn.metrics.log_loss(\n        y_true=any_severe_spinal_labels,\n        y_pred=any_severe_spinal_predictions,\n        sample_weight=any_severe_spinal_weights\n    )\n    condition_losses.append(any_severe_spinal_loss)\n    condition_weights.append(any_severe_scalar)\n    return np.average(condition_losses, weights=condition_weights)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-07-19T12:35:25.692200Z","iopub.execute_input":"2024-07-19T12:35:25.692630Z","iopub.status.idle":"2024-07-19T12:35:25.709226Z","shell.execute_reply.started":"2024-07-19T12:35:25.692595Z","shell.execute_reply":"2024-07-19T12:35:25.708109Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/sample_submission.csv')\nsubmission_acc = pd.DataFrame()\nfor i in range(10):\n    submission['row_id'] = submission['row_id'].apply(lambda v:str(i)+v[1:])\n    submission_acc = pd.concat([submission_acc,submission])\nsubmission = submission_acc.reset_index(drop=True)\nsubmission.tail()","metadata":{"execution":{"iopub.status.busy":"2024-07-19T12:35:25.711442Z","iopub.execute_input":"2024-07-19T12:35:25.712153Z","iopub.status.idle":"2024-07-19T12:35:25.740932Z","shell.execute_reply.started":"2024-07-19T12:35:25.712113Z","shell.execute_reply":"2024-07-19T12:35:25.739841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"solution = submission.copy()\nr = np.random.randint(0,3,len(solution))# Random target_levels\nsolution['sample_weight'] = np.array([1,2,4])[r]\nsolution['target_levels'] = np.array(['normal_mild', 'moderate', 'severe'])[r]\nsolution['normal_mild'] = np.array([1,0,0])[r]\nsolution['moderate'] = np.array([0,1,0])[r]\nsolution['severe'] = np.array([0,0,1])[r]\nsolution.tail()","metadata":{"execution":{"iopub.status.busy":"2024-07-19T12:35:25.742385Z","iopub.execute_input":"2024-07-19T12:35:25.743120Z","iopub.status.idle":"2024-07-19T12:35:25.759099Z","shell.execute_reply.started":"2024-07-19T12:35:25.743078Z","shell.execute_reply":"2024-07-19T12:35:25.757930Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"score(solution,submission,'row_id',1.)","metadata":{"execution":{"iopub.status.busy":"2024-07-19T12:35:25.761446Z","iopub.execute_input":"2024-07-19T12:35:25.761798Z","iopub.status.idle":"2024-07-19T12:35:25.800053Z","shell.execute_reply.started":"2024-07-19T12:35:25.761769Z","shell.execute_reply":"2024-07-19T12:35:25.799015Z"},"trusted":true},"execution_count":null,"outputs":[]}]}