{"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":99552,"databundleVersionId":13190393,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport pydicom\nimport warnings\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nwarnings.filterwarnings(\"ignore\")\n\nfrom glob import glob","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:45.031975Z","iopub.execute_input":"2025-07-30T18:47:45.032243Z","iopub.status.idle":"2025-07-30T18:47:45.037133Z","shell.execute_reply.started":"2025-07-30T18:47:45.03222Z","shell.execute_reply":"2025-07-30T18:47:45.036156Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Set plot style\nsns.set(style=\"whitegrid\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:45.038184Z","iopub.execute_input":"2025-07-30T18:47:45.038378Z","iopub.status.idle":"2025-07-30T18:47:45.070959Z","shell.execute_reply.started":"2025-07-30T18:47:45.03836Z","shell.execute_reply":"2025-07-30T18:47:45.070114Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load train.csv\ntrain_df = pd.read_csv('../input/rsna-intracranial-aneurysm-detection/train.csv')\nprint(\"Train shape:\", train_df.shape)\ndisplay(train_df.head())\ndisplay(train_df.describe())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:45.072146Z","iopub.execute_input":"2025-07-30T18:47:45.072391Z","iopub.status.idle":"2025-07-30T18:47:45.156039Z","shell.execute_reply.started":"2025-07-30T18:47:45.072369Z","shell.execute_reply":"2025-07-30T18:47:45.155405Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Basic info\nprint(\"\\nUnique modalities:\", train_df['Modality'].unique())\nprint(\"Missing values:\\n\", train_df.isnull().sum())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:45.156683Z","iopub.execute_input":"2025-07-30T18:47:45.156867Z","iopub.status.idle":"2025-07-30T18:47:45.165684Z","shell.execute_reply.started":"2025-07-30T18:47:45.156849Z","shell.execute_reply":"2025-07-30T18:47:45.164833Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 1. Demographic Distributions","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(12, 5))\n# Age distribution by sex\nplt.subplot(1, 2, 1)\nsns.histplot(data=train_df, x='PatientAge', hue='PatientSex', multiple='stack', bins=20)\nplt.title('Age Distribution by Sex')\nplt.xlabel('Age')\nplt.ylabel('Count')\n\n# Sex distribution\nplt.subplot(1, 2, 2)\nsns.countplot(x='PatientSex', data=train_df)\nplt.title('Sex Distribution')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:45.167543Z","iopub.execute_input":"2025-07-30T18:47:45.167736Z","iopub.status.idle":"2025-07-30T18:47:45.7109Z","shell.execute_reply.started":"2025-07-30T18:47:45.167719Z","shell.execute_reply":"2025-07-30T18:47:45.710026Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Modality Distribution","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(8, 5))\nsns.countplot(y='Modality', data=train_df)\nplt.title('Modality Distribution')\nplt.xlabel('Count')\nplt.ylabel('Modality')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:45.711769Z","iopub.execute_input":"2025-07-30T18:47:45.712047Z","iopub.status.idle":"2025-07-30T18:47:45.863584Z","shell.execute_reply.started":"2025-07-30T18:47:45.712024Z","shell.execute_reply":"2025-07-30T18:47:45.862822Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Aneurysm Present Distribution","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(6, 6))\ntrain_df['Aneurysm Present'].value_counts().plot.pie(autopct='%1.1f%%', labels=['Absent', 'Present'])\nplt.title('Aneurysm Present Distribution')\nplt.ylabel('')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:45.864304Z","iopub.execute_input":"2025-07-30T18:47:45.864556Z","iopub.status.idle":"2025-07-30T18:47:45.998769Z","shell.execute_reply.started":"2025-07-30T18:47:45.864537Z","shell.execute_reply":"2025-07-30T18:47:45.998115Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Location Label Distributions","metadata":{}},{"cell_type":"code","source":"location_cols = train_df.columns[4:-1]  # Location columns\nlabel_counts = train_df[location_cols].sum().sort_values(ascending=False)\n\nplt.figure(figsize=(10, 8))\nsns.barplot(x=label_counts.values, y=label_counts.index)\nplt.title('Aneurysm Counts by Location')\nplt.xlabel('Count')\nplt.ylabel('Location')\nplt.show()\n\n# Multi-label analysis: Number of aneurysms per positive case\npositive_df = train_df[train_df['Aneurysm Present'] == 1]\npositive_df['Num_Locations'] = positive_df[location_cols].sum(axis=1)\nsns.countplot(x='Num_Locations', data=positive_df)\nplt.title('Number of Aneurysm Locations per Positive Case')\nplt.xlabel('Number of Locations')\nplt.ylabel('Count')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:45.999376Z","iopub.execute_input":"2025-07-30T18:47:45.999793Z","iopub.status.idle":"2025-07-30T18:47:46.391116Z","shell.execute_reply.started":"2025-07-30T18:47:45.999763Z","shell.execute_reply":"2025-07-30T18:47:46.390389Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5. Correlations (e.g., between locations)","metadata":{}},{"cell_type":"code","source":"corr = train_df[location_cols].corr()\nplt.figure(figsize=(12, 10))\nsns.heatmap(corr, annot=True, cmap='coolwarm', fmt='.2f')\nplt.title('Correlation Heatmap of Location Labels')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:46.391813Z","iopub.execute_input":"2025-07-30T18:47:46.392027Z","iopub.status.idle":"2025-07-30T18:47:47.03224Z","shell.execute_reply.started":"2025-07-30T18:47:46.392006Z","shell.execute_reply":"2025-07-30T18:47:47.031663Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 6. Explore train_localizers.csv","metadata":{}},{"cell_type":"code","source":"localizers_df = pd.read_csv('../input/rsna-intracranial-aneurysm-detection/train_localizers.csv')\nprint(\"\\nLocalizers shape:\", localizers_df.shape)\ndisplay(localizers_df.head())\n\n# Distribution of aneurysms per series\naneurysms_per_series = localizers_df.groupby('SeriesInstanceUID').size()\nsns.histplot(aneurysms_per_series, bins=10)\nplt.title('Number of Annotated Aneurysms per Series')\nplt.xlabel('Number of Aneurysms')\nplt.ylabel('Count')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:47.032725Z","iopub.execute_input":"2025-07-30T18:47:47.032857Z","iopub.status.idle":"2025-07-30T18:47:47.253506Z","shell.execute_reply.started":"2025-07-30T18:47:47.032845Z","shell.execute_reply":"2025-07-30T18:47:47.252508Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 7. Sample DICOM Visualization","metadata":{}},{"cell_type":"code","source":"# Find a sample series (e.g., first one with aneurysm)\nsample_series = train_df[train_df['Aneurysm Present'] == 1]['SeriesInstanceUID'].iloc[0]\ndicom_path = f'../input/rsna-intracranial-aneurysm-detection/series/{sample_series}/*.dcm'\ndicom_files = sorted(glob(dicom_path))\n\nif dicom_files:\n    # Load middle slice\n    mid_idx = len(dicom_files) // 2 + 1\n    ds = pydicom.dcmread(dicom_files[mid_idx])\n    \n    plt.figure(figsize=(8, 8))\n    plt.imshow(ds.pixel_array, cmap='gray')\n    plt.title(f'Sample DICOM Slice from Series: {sample_series}')\n    plt.axis('off')\n    plt.show()\nelse:\n    print(\"No DICOM files found for sample series.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:47.25406Z","iopub.execute_input":"2025-07-30T18:47:47.254228Z","iopub.status.idle":"2025-07-30T18:47:47.544517Z","shell.execute_reply.started":"2025-07-30T18:47:47.254214Z","shell.execute_reply":"2025-07-30T18:47:47.543581Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 8. Segmentations Check (NIfTI files)","metadata":{}},{"cell_type":"code","source":"seg_paths = glob('../input/rsna-intracranial-aneurysm-detection/segmentations/*/*.nii')\nprint(\"\\nNumber of segmentation files:\", len(seg_paths))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T18:47:47.545326Z","iopub.execute_input":"2025-07-30T18:47:47.545609Z","iopub.status.idle":"2025-07-30T18:47:48.20036Z","shell.execute_reply.started":"2025-07-30T18:47:47.545587Z","shell.execute_reply":"2025-07-30T18:47:48.199682Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Visualization (WIP !!!)","metadata":{}},{"cell_type":"code","source":"import json\nimport random\nimport numpy as np\nimport nibabel as nib\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nfrom random import sample\nfrom scipy.ndimage import zoom\n\n# Set random seed for reproducibility\nrandom.seed(22)\n\n# Modified plot_around_label to visualize aneurysm location with rotated seg, label name, and modality\ndef plot_around_label(row_number, num_around=2, localizers_df=localizers_df, train_df=train_df):\n    row = localizers_df.iloc[row_number]\n    a = row['SeriesInstanceUID']\n    b = row['SOPInstanceUID']\n    c = row['coordinates']\n    d = row['location']\n    \n    # Get Modality from train_df\n    modality = train_df[train_df['SeriesInstanceUID'] == a]['Modality'].iloc[0] if not train_df[train_df['SeriesInstanceUID'] == a].empty else 'Unknown'\n    \n    folders = glob(\"../input/rsna-intracranial-aneurysm-detection/series/*\")\n    \n    for i in folders:\n        if a in i:\n            break\n    files = sorted(glob(os.path.join(i, \"*\")))\n    i_n = list()\n    for e, f in enumerate(files):\n        ex = pydicom.dcmread(f)\n        i_n.append([ex.InstanceNumber,f])\n    \n    i_n = sorted(i_n)\n    \n    for e, f in enumerate(i_n):\n        if b in f[1]:\n            break\n    \n    c = json.loads(c.replace(\"'\",'\"'))\n    \n    # Load seg if available\n    seg_path = next((p for p in seg_paths if a in p), None)\n    if seg_path:\n        seg = nib.load(seg_path).get_fdata()\n        if seg.shape[2] != len(i_n):\n            zoom_factors = (1, 1, len(i_n) / seg.shape[2])\n            seg = zoom(seg, zoom_factors, order=0)\n    else:\n        seg = None\n\n    for i in range(e-num_around,e+num_around+1):\n        if i < 0 or i >= len(i_n):\n            continue\n        if i == e:\n            print(\"labeled slice\")\n        else:\n            print(f\"nearby slice {i}\")\n        ex = pydicom.dcmread(i_n[i][1])\n        if len(ex.pixel_array.shape) > 2:\n            continue\n        \n        img = ex.pixel_array\n        img = np.flipud(img)  # Flip vertically to match DICOM top-left origin\n        seg_slice = seg[:, :, i] if seg is not None else np.zeros_like(img)\n        seg_slice = np.flipud(seg_slice)  # Flip segmentation to align with image\n        \n        height, width = img.shape\n        \n        fig, axs = plt.subplots(1, 3, figsize=(15, 5))\n        \n        # Panel 1: Image\n        axs[0].imshow(img, cmap='gray', origin='upper', aspect=1)  # Use upper origin for flipped image\n        axs[0].set_title('Image')\n        axs[0].axis('off')\n        \n        # Panel 2: Seg + Label (seg rotated -90 degrees, clockwise)\n        seg_slice_rotated = np.rot90(seg_slice, k=-1)  # Rotate 90 degrees clockwise\n        axs[1].imshow(seg_slice_rotated, cmap='jet', origin='upper', aspect=1)\n        if i == e:\n            # Use flipped coordinates without rotation for rectangle\n            # flipud: (x, y) -> (x, height - 1 - y)\n            x = c[\"x\"]\n            y = height - 1 - c[\"y\"]\n            rect = patches.Rectangle((x-10, y-10), 20, 20, linewidth=1, edgecolor='r', facecolor='none')\n            axs[1].add_patch(rect)\n            # Add label name and modality above rectangle\n            axs[1].text(x, y+35, d, color='yellow', fontsize=10, ha='center', va='bottom')\n            axs[1].text(x, y+55, f'Modality: {modality}', color='red', fontsize=10, ha='center', va='bottom')\n        axs[1].set_title('Seg + Label')\n        axs[1].axis('off')\n        \n        # Panel 3: Overlay (Image + Seg + Label, no rotation for seg)\n        axs[2].imshow(img, cmap='gray', origin='upper', aspect=1)\n        axs[2].imshow(seg_slice_rotated, cmap='jet', alpha=0.5, origin='upper', aspect=1)  # Use non-rotated seg\n        if i == e:\n            # Same coordinates for non-rotated, flipped image\n            x = c[\"x\"]\n            y = height - 1 - c[\"y\"]\n            rect = patches.Rectangle((x-10, y-10), 20, 20, linewidth=1, edgecolor='r', facecolor='none')\n            axs[2].add_patch(rect)\n            # Add label name and modality above rectangle\n            axs[2].text(x, y+35, d, color='yellow', fontsize=10, ha='center', va='bottom')\n            axs[2].text(x, y+55, f'Modality: {modality}', color='red', fontsize=10, ha='center', va='bottom')\n        axs[2].set_title('Overlay')\n        axs[2].axis('off')\n        \n        plt.show()\n\n# Select a random row, prefer one with seg\nall_rows = range(len(localizers_df))\nrows_with_seg = [r for r in all_rows if localizers_df.iloc[r]['SeriesInstanceUID'] in [os.path.basename(os.path.dirname(p)) for p in seg_paths]]\nrow_number = 52\nprint(f\"\\nDisplaying slides for row {row_number}\")\nplot_around_label(row_number, 2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T19:24:17.837794Z","iopub.execute_input":"2025-07-30T19:24:17.838179Z","iopub.status.idle":"2025-07-30T19:24:23.79088Z","shell.execute_reply.started":"2025-07-30T19:24:17.83815Z","shell.execute_reply":"2025-07-30T19:24:23.789784Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}