{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":99552,"databundleVersionId":13190393,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install ultralytics\n!pip uninstall albumentations -y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T05:40:05.046479Z","iopub.execute_input":"2025-07-30T05:40:05.046647Z","iopub.status.idle":"2025-07-30T05:41:35.614056Z","shell.execute_reply.started":"2025-07-30T05:40:05.04663Z","shell.execute_reply":"2025-07-30T05:41:35.613219Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 1: Imports and Setup\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom pathlib import Path\nimport pydicom\nimport cv2\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import roc_auc_score\nimport json\nimport os\nfrom typing import List, Tuple, Dict\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# YOLO imports\nfrom ultralytics import YOLO\nimport yaml\n\n# Configure plotting\nplt.style.use('default')\nsns.set_palette(\"husl\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-30T05:41:35.616341Z","iopub.execute_input":"2025-07-30T05:41:35.616591Z","iopub.status.idle":"2025-07-30T05:41:41.244031Z","shell.execute_reply.started":"2025-07-30T05:41:35.616567Z","shell.execute_reply":"2025-07-30T05:41:41.243336Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class AneurysmDataExplorer:\n    \"\"\"Class to handle data exploration for the brain aneurysm detection challenge\"\"\"\n    \n    def __init__(self, data_path: str = \"/kaggle/input/rsna-intracranial-aneurysm-detection\"):\n        self.data_path = Path(data_path)\n        self.train_df = None\n        self.localizer_df = None\n        self._load_data()\n        \n    def _load_data(self):\n        \"\"\"Load training and localizer datasets\"\"\"\n        self.train_df = pd.read_csv(self.data_path / \"train.csv\")\n        self.localizer_df = pd.read_csv(self.data_path / \"train_localizers.csv\")\n        \n    def get_basic_stats(self):\n        \"\"\"Return basic dataset statistics\"\"\"\n        stats = {\n            'total_series': len(self.train_df),\n            'total_localizations': len(self.localizer_df),\n            'aneurysm_positive': self.train_df['Aneurysm Present'].sum(),\n            'modalities': self.train_df['Modality'].value_counts().to_dict(),\n            'age_stats': {\n                'mean': self.train_df['PatientAge'].mean(),\n                'std': self.train_df['PatientAge'].std()\n            }\n        }\n        return stats\n        \n    def get_location_analysis(self):\n        \"\"\"Analyze aneurysm locations and return sorted counts\"\"\"\n        location_cols = [\n            'Left Infraclinoid Internal Carotid Artery',\n            'Right Infraclinoid Internal Carotid Artery', \n            'Left Supraclinoid Internal Carotid Artery',\n            'Right Supraclinoid Internal Carotid Artery',\n            'Left Middle Cerebral Artery',\n            'Right Middle Cerebral Artery',\n            'Anterior Communicating Artery',\n            'Left Anterior Cerebral Artery',\n            'Right Anterior Cerebral Artery', \n            'Left Posterior Communicating Artery',\n            'Right Posterior Communicating Artery',\n            'Basilar Tip',\n            'Other Posterior Circulation'\n        ]\n        \n        location_counts = {col: self.train_df[col].sum() for col in location_cols}\n        return location_counts, location_cols\n    \n    def plot_data_overview(self):\n        \"\"\"Create comprehensive data visualization\"\"\"\n        location_counts, location_cols = self.get_location_analysis()\n        \n        fig, axes = plt.subplots(2, 2, figsize=(15, 12))\n        \n        # Modality distribution\n        modality_counts = self.train_df['Modality'].value_counts()\n        axes[0,0].pie(modality_counts.values, labels=modality_counts.index, autopct='%1.1f%%')\n        axes[0,0].set_title('Imaging Modality Distribution')\n        \n        # Age distribution\n        axes[0,1].hist(self.train_df['PatientAge'].dropna(), bins=30, alpha=0.7, color='skyblue')\n        axes[0,1].set_xlabel('Age (years)')\n        axes[0,1].set_ylabel('Frequency')\n        axes[0,1].set_title('Patient Age Distribution')\n        \n        # Aneurysm presence\n        aneurysm_counts = self.train_df['Aneurysm Present'].value_counts()\n        axes[1,0].bar(['No Aneurysm', 'Aneurysm Present'], aneurysm_counts.values, \n                     color=['lightcoral', 'lightgreen'])\n        axes[1,0].set_ylabel('Count')\n        axes[1,0].set_title('Aneurysm Presence Distribution')\n        \n        # Top aneurysm locations\n        sorted_locations = sorted(location_counts.items(), key=lambda x: x[1], reverse=True)[:8]\n        locations, counts = zip(*sorted_locations)\n        \n        axes[1,1].barh(range(len(locations)), counts)\n        axes[1,1].set_yticks(range(len(locations)))\n        axes[1,1].set_yticklabels([loc.replace(' Artery', '') for loc in locations], fontsize=8)\n        axes[1,1].set_xlabel('Count')\n        axes[1,1].set_title('Most Common Aneurysm Locations')\n        \n        plt.tight_layout()\n        plt.show()\n        \n        return location_counts, location_cols\n\nexplorer = AneurysmDataExplorer()\nstats = explorer.get_basic_stats()\nlocation_counts, location_cols = explorer.plot_data_overview()\n\nprint(f\"Dataset: {stats['total_series']} series, {stats['aneurysm_positive']} positive cases\")\nprint(f\"Positive rate: {stats['aneurysm_positive']/stats['total_series']*100:.1f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T05:41:41.244858Z","iopub.execute_input":"2025-07-30T05:41:41.245384Z","iopub.status.idle":"2025-07-30T05:41:42.170863Z","shell.execute_reply.started":"2025-07-30T05:41:41.245358Z","shell.execute_reply":"2025-07-30T05:41:42.170028Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_path = Path(\"/kaggle/input/rsna-intracranial-aneurysm-detection\")\nprint(\"=== DATA STRUCTURE DIAGNOSTIC ===\")\nprint(f\"Data path exists: {data_path.exists()}\")\n\nif data_path.exists():\n    print(f\"Contents: {list(data_path.iterdir())}\")\n    \n    # Check for series folder\n    series_path = data_path / \"series\"\n    print(f\"Series path exists: {series_path.exists()}\")\n    \n    if series_path.exists():\n        series_folders = list(series_path.iterdir())\n        print(f\"Number of series folders: {len(series_folders)}\")\n        print(f\"First few series: {[f.name for f in series_folders[:3]]}\")\n        \n        # Check a specific series folder\n        if series_folders:\n            sample_series = series_folders[0]\n            dcm_files = list(sample_series.glob(\"*.dcm\"))\n            print(f\"Sample series {sample_series.name}: {len(dcm_files)} DICOM files\")\n\n# Check if our selected series exist\nprint(f\"\\n=== SERIES VERIFICATION ===\")\nif len(explorer.train_df) > 0:\n    # Get series IDs\n    positive_cases = explorer.train_df[explorer.train_df['Aneurysm Present'] == 1]\n    negative_cases = explorer.train_df[explorer.train_df['Aneurysm Present'] == 0]\n    \n    print(f\"Positive cases: {len(positive_cases)}\")\n    print(f\"Negative cases: {len(negative_cases)}\")\n    \n    if len(positive_cases) > 0 and len(negative_cases) > 0:\n        pos_series_id = positive_cases['SeriesInstanceUID'].iloc[0]\n        neg_series_id = negative_cases['SeriesInstanceUID'].iloc[0]\n        \n        print(f\"Selected positive series: {pos_series_id}\")\n        print(f\"Selected negative series: {neg_series_id}\")\n        \n        # Check if these series folders exist\n        pos_series_path = data_path / \"series\" / pos_series_id\n        neg_series_path = data_path / \"series\" / neg_series_id\n        \n        print(f\"Positive series folder exists: {pos_series_path.exists()}\")\n        print(f\"Negative series folder exists: {neg_series_path.exists()}\")\n        \n        # If folders exist, check DICOM files\n        if pos_series_path.exists():\n            pos_dcm_files = list(pos_series_path.glob(\"*.dcm\"))\n            print(f\"Positive series DICOM files: {len(pos_dcm_files)}\")\n        \n        if neg_series_path.exists():\n            neg_dcm_files = list(neg_series_path.glob(\"*.dcm\"))\n            print(f\"Negative series DICOM files: {len(neg_dcm_files)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T05:41:42.171804Z","iopub.execute_input":"2025-07-30T05:41:42.172106Z","iopub.status.idle":"2025-07-30T05:41:42.541921Z","shell.execute_reply.started":"2025-07-30T05:41:42.172077Z","shell.execute_reply":"2025-07-30T05:41:42.541028Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 3F: Updated DICOM Processing with Correct Path\nclass WorkingDICOMProcessor:\n    \"\"\"DICOM processor with the correct data path\"\"\"\n    \n    def __init__(self, data_path: str = \"/kaggle/input/rsna-intracranial-aneurysm-detection\"):\n        self.data_path = Path(data_path)\n        self.series_path = self.data_path / \"series\"\n        \n    def load_dicom_series(self, series_id: str, max_slices: int = None) -> Tuple[List[np.ndarray], List[Dict]]:\n        \"\"\"Load DICOM files for a series\"\"\"\n        series_folder = self.series_path / series_id\n        \n        if not series_folder.exists():\n            return [], []\n            \n        dicom_files = list(series_folder.glob(\"*.dcm\"))\n        \n        # Sort files by instance number if possible\n        dicom_files_with_info = []\n        for dcm_file in dicom_files:\n            try:\n                ds = pydicom.dcmread(dcm_file)\n                instance_num = getattr(ds, 'InstanceNumber', 0)\n                dicom_files_with_info.append((instance_num, dcm_file))\n            except:\n                dicom_files_with_info.append((0, dcm_file))\n        \n        # Sort by instance number\n        dicom_files_with_info.sort(key=lambda x: x[0])\n        dicom_files = [x[1] for x in dicom_files_with_info]\n        \n        if max_slices:\n            # Take evenly spaced slices\n            total_files = len(dicom_files)\n            if total_files > max_slices:\n                indices = np.linspace(0, total_files-1, max_slices, dtype=int)\n                dicom_files = [dicom_files[i] for i in indices]\n            \n        images, metadata = [], []\n        \n        for dcm_file in dicom_files:\n            try:\n                ds = pydicom.dcmread(dcm_file)\n                \n                # Extract pixel array\n                img = ds.pixel_array.astype(np.float32)\n                \n                # Apply rescaling if available\n                if hasattr(ds, 'RescaleSlope') and hasattr(ds, 'RescaleIntercept'):\n                    img = img * ds.RescaleSlope + ds.RescaleIntercept\n                \n                images.append(img)\n                \n                # Store metadata\n                meta = {\n                    'SOPInstanceUID': ds.SOPInstanceUID,\n                    'InstanceNumber': getattr(ds, 'InstanceNumber', len(images)),\n                    'SliceThickness': getattr(ds, 'SliceThickness', None),\n                    'PixelSpacing': getattr(ds, 'PixelSpacing', None),\n                    'Modality': getattr(ds, 'Modality', 'Unknown')\n                }\n                metadata.append(meta)\n                \n            except Exception as e:\n                continue\n                \n        return images, metadata\n    \n    def normalize_image(self, img: np.ndarray, window_center: float = None, window_width: float = None) -> np.ndarray:\n        \"\"\"Normalize image with optional windowing\"\"\"\n        if img.size == 0:\n            return img\n            \n        if window_center is not None and window_width is not None:\n            # Apply medical imaging windowing\n            img_windowed = np.clip(img, \n                                 window_center - window_width/2, \n                                 window_center + window_width/2)\n        else:\n            # Use percentile-based normalization\n            p1, p99 = np.percentile(img, [1, 99])\n            img_windowed = np.clip(img, p1, p99)\n        \n        # Normalize to 0-1\n        img_range = img_windowed.max() - img_windowed.min()\n        if img_range > 0:\n            img_norm = (img_windowed - img_windowed.min()) / img_range\n        else:\n            img_norm = np.zeros_like(img_windowed)\n        \n        return img_norm\n    \n    def visualize_series_with_aneurysms(self, series_id: str, localizer_df: pd.DataFrame, max_slices: int = 12):\n        \"\"\"Visualize series and highlight aneurysm locations if available\"\"\"\n        \n        images, metadata = self.load_dicom_series(series_id, max_slices)\n        \n        if not images:\n            return None, None\n        \n        # Get aneurysm localizations for this series\n        series_localizations = localizer_df[localizer_df['SeriesInstanceUID'] == series_id]\n        \n        # Create mapping of SOPInstanceUID to localization\n        localization_map = {}\n        for _, loc in series_localizations.iterrows():\n            sop_uid = loc['SOPInstanceUID']\n            coords = loc['coordinates']\n            location = loc['location']\n            \n            if sop_uid not in localization_map:\n                localization_map[sop_uid] = []\n            localization_map[sop_uid].append({\n                'coords': coords,\n                'location': location\n            })\n        \n        # Display images\n        n_images = len(images)\n        cols = 4\n        rows = (n_images + cols - 1) // cols\n        \n        fig, axes = plt.subplots(rows, cols, figsize=(16, 4*rows))\n        if rows == 1:\n            axes = axes.reshape(1, -1)\n        elif n_images == 1:\n            axes = np.array([[axes]])\n        \n        for i, (img, meta) in enumerate(zip(images, metadata)):\n            row, col = i // cols, i % cols\n            \n            # Normalize image\n            img_norm = self.normalize_image(img)\n            \n            # Display image\n            axes[row, col].imshow(img_norm, cmap='gray')\n            \n            # Check if this image has aneurysm annotations\n            sop_uid = meta['SOPInstanceUID']\n            title = f\"Slice {meta.get('InstanceNumber', i+1)}\"\n            \n            if sop_uid in localization_map:\n                title += f\" ⚠️ ({len(localization_map[sop_uid])} aneurysms)\"\n                \n                # Plot aneurysm locations\n                for loc_info in localization_map[sop_uid]:\n                    coords_str = loc_info['coords']\n                    try:\n                        # Parse coordinates\n                        coords = coords_str.strip('()[]').split(',')\n                        x, y = float(coords[0]), float(coords[1])\n                        \n                        # Plot marker\n                        axes[row, col].plot(x, y, 'r+', markersize=10, markeredgewidth=2)\n                        axes[row, col].plot(x, y, 'ro', markersize=15, alpha=0.3)\n                        \n                    except:\n                        pass\n            \n            axes[row, col].set_title(title, fontsize=9)\n            axes[row, col].axis('off')\n        \n        # Hide unused subplots\n        for i in range(n_images, rows * cols):\n            row, col = i // cols, i % cols\n            axes[row, col].axis('off')\n        \n        plt.tight_layout()\n        plt.show()\n        \n        return images, metadata\n\n# Initialize the working processor\nprocessor = WorkingDICOMProcessor()\n\n# Load the data correctly\nexplorer = AneurysmDataExplorer(\"/kaggle/input/rsna-intracranial-aneurysm-detection\")\nstats = explorer.get_basic_stats()\nlocation_counts, location_cols = explorer.get_location_analysis()\n\nprint(f\"✅ Successfully loaded competition data:\")\nprint(f\"   Total series: {stats['total_series']}\")\nprint(f\"   Positive cases: {stats['aneurysm_positive']} ({stats['aneurysm_positive']/stats['total_series']*100:.1f}%)\")\nprint(f\"   Localizations: {len(explorer.localizer_df)}\")\n\n# Now visualize actual cases with aneurysms\nprint(f\"\\n🎯 Visualizing actual positive case with aneurysms:\")\npositive_series = explorer.train_df[explorer.train_df['Aneurysm Present'] == 1]['SeriesInstanceUID'].iloc[0]\nseries_info = explorer.train_df[explorer.train_df['SeriesInstanceUID'] == positive_series].iloc[0]\n\nprint(f\"Series: {positive_series}\")\nprint(f\"Modality: {series_info['Modality']}\")\nprint(f\"Patient Age: {series_info['PatientAge']}\")\n\n# Visualize with aneurysm markers\npos_images, pos_meta = processor.visualize_series_with_aneurysms(\n    positive_series, explorer.localizer_df, max_slices=12\n)\n\nprint(f\"\\n🔍 Visualizing negative case (no aneurysms):\")\nnegative_series = explorer.train_df[explorer.train_df['Aneurysm Present'] == 0]['SeriesInstanceUID'].iloc[0]\nneg_series_info = explorer.train_df[explorer.train_df['SeriesInstanceUID'] == negative_series].iloc[0]\n\nprint(f\"Series: {negative_series}\")\nprint(f\"Modality: {neg_series_info['Modality']}\")\nprint(f\"Patient Age: {neg_series_info['PatientAge']}\")\n\n# Visualize negative case\nneg_images, neg_meta = processor.visualize_series_with_aneurysms(\n    negative_series, explorer.localizer_df, max_slices=12\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T05:41:42.5429Z","iopub.execute_input":"2025-07-30T05:41:42.54321Z","iopub.status.idle":"2025-07-30T05:41:50.271859Z","shell.execute_reply.started":"2025-07-30T05:41:42.543183Z","shell.execute_reply":"2025-07-30T05:41:50.270882Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 3G: Enhanced Data Analysis with Real Data\ndef analyze_real_competition_data():\n    \"\"\"Comprehensive analysis of the actual competition dataset\"\"\"\n    \n    # Basic statistics\n    print(\"=== COMPREHENSIVE DATA ANALYSIS ===\")\n    train_df = explorer.train_df\n    localizer_df = explorer.localizer_df\n    \n    # Dataset overview\n    print(f\"📊 Dataset Overview:\")\n    print(f\"   Total series: {len(train_df):,}\")\n    print(f\"   Positive cases: {train_df['Aneurysm Present'].sum():,}\")\n    print(f\"   Negative cases: {(train_df['Aneurysm Present'] == 0).sum():,}\")\n    print(f\"   Positive rate: {train_df['Aneurysm Present'].mean()*100:.1f}%\")\n    \n    # Modality analysis\n    print(f\"\\n🔬 Modality Distribution:\")\n    modality_stats = train_df['Modality'].value_counts()\n    for modality, count in modality_stats.items():\n        pos_rate = train_df[train_df['Modality'] == modality]['Aneurysm Present'].mean() * 100\n        print(f\"   {modality}: {count:,} series ({count/len(train_df)*100:.1f}%) - {pos_rate:.1f}% positive\")\n    \n    # Age analysis\n    print(f\"\\n👥 Demographics:\")\n    print(f\"   Age: {train_df['PatientAge'].mean():.1f} ± {train_df['PatientAge'].std():.1f} years\")\n    print(f\"   Age range: {train_df['PatientAge'].min():.0f} - {train_df['PatientAge'].max():.0f} years\")\n    \n    # Gender analysis\n    gender_stats = train_df['PatientSex'].value_counts()\n    for gender, count in gender_stats.items():\n        pos_rate = train_df[train_df['PatientSex'] == gender]['Aneurysm Present'].mean() * 100\n        print(f\"   {gender}: {count:,} ({count/len(train_df)*100:.1f}%) - {pos_rate:.1f}% positive\")\n    \n    # Aneurysm location analysis\n    print(f\"\\n🧭 Aneurysm Location Analysis:\")\n    print(f\"   Total localizations: {len(localizer_df):,}\")\n    \n    # Most common locations\n    location_text_stats = localizer_df['location'].value_counts().head(10)\n    print(f\"   Most common locations:\")\n    for location, count in location_text_stats.items():\n        print(f\"     {location}: {count} ({count/len(localizer_df)*100:.1f}%)\")\n    \n    # Series with multiple aneurysms\n    series_aneurysm_counts = localizer_df['SeriesInstanceUID'].value_counts()\n    multiple_aneurysms = (series_aneurysm_counts > 1).sum()\n    max_aneurysms = series_aneurysm_counts.max()\n    \n    print(f\"\\n📈 Aneurysm Distribution:\")\n    print(f\"   Series with multiple aneurysms: {multiple_aneurysms}\")\n    print(f\"   Maximum aneurysms in one series: {max_aneurysms}\")\n    print(f\"   Average aneurysms per positive series: {len(localizer_df)/train_df['Aneurysm Present'].sum():.2f}\")\n    \n    # Sample series analysis\n    print(f\"\\n🔍 Sample Series Analysis:\")\n    sample_series = train_df['SeriesInstanceUID'].head(10).tolist()\n    \n    series_dicom_counts = []\n    for series_id in sample_series:\n        series_path = Path(\"/kaggle/input/rsna-intracranial-aneurysm-detection/series\") / series_id\n        if series_path.exists():\n            dcm_count = len(list(series_path.glob(\"*.dcm\")))\n            series_dicom_counts.append(dcm_count)\n    \n    if series_dicom_counts:\n        print(f\"   DICOM files per series (sample): {np.mean(series_dicom_counts):.1f} ± {np.std(series_dicom_counts):.1f}\")\n        print(f\"   Range: {min(series_dicom_counts)} - {max(series_dicom_counts)} files\")\n\n# Run comprehensive analysis\nanalyze_real_competition_data()\n\n# Create enhanced visualizations\nfig, axes = plt.subplots(2, 3, figsize=(18, 12))\n\n# 1. Modality distribution\nmodality_counts = explorer.train_df['Modality'].value_counts()\naxes[0,0].pie(modality_counts.values, labels=modality_counts.index, autopct='%1.1f%%')\naxes[0,0].set_title('Imaging Modality Distribution')\n\n# 2. Age distribution by aneurysm presence\npositive_ages = explorer.train_df[explorer.train_df['Aneurysm Present'] == 1]['PatientAge'].dropna()\nnegative_ages = explorer.train_df[explorer.train_df['Aneurysm Present'] == 0]['PatientAge'].dropna()\n\naxes[0,1].hist(negative_ages, bins=30, alpha=0.7, label='No Aneurysm', color='lightblue')\naxes[0,1].hist(positive_ages, bins=30, alpha=0.7, label='Aneurysm Present', color='lightcoral')\naxes[0,1].set_xlabel('Age (years)')\naxes[0,1].set_ylabel('Frequency')\naxes[0,1].set_title('Age Distribution by Aneurysm Presence')\naxes[0,1].legend()\n\n# 3. Aneurysm presence by modality\nmodality_aneurysm = explorer.train_df.groupby('Modality')['Aneurysm Present'].agg(['count', 'sum']).reset_index()\nmodality_aneurysm['rate'] = modality_aneurysm['sum'] / modality_aneurysm['count'] * 100\n\naxes[0,2].bar(modality_aneurysm['Modality'], modality_aneurysm['rate'], color='lightgreen')\naxes[0,2].set_ylabel('Positive Rate (%)')\naxes[0,2].set_title('Aneurysm Rate by Modality')\naxes[0,2].tick_params(axis='x', rotation=45)\n\n# 4. Top aneurysm locations (from localization data)\nlocation_text_counts = explorer.localizer_df['location'].value_counts().head(8)\naxes[1,0].barh(range(len(location_text_counts)), location_text_counts.values)\naxes[1,0].set_yticks(range(len(location_text_counts)))\naxes[1,0].set_yticklabels(location_text_counts.index, fontsize=8)\naxes[1,0].set_xlabel('Count')\naxes[1,0].set_title('Most Common Aneurysm Locations')\n\n# 5. Aneurysms per series distribution\nseries_aneurysm_counts = explorer.localizer_df['SeriesInstanceUID'].value_counts()\naxes[1,1].hist(series_aneurysm_counts.values, bins=range(1, series_aneurysm_counts.max()+2), \n               alpha=0.7, color='gold', edgecolor='black')\naxes[1,1].set_xlabel('Number of Aneurysms per Series')\naxes[1,1].set_ylabel('Frequency')\naxes[1,1].set_title('Distribution of Aneurysms per Series')\n\n# 6. Gender distribution with aneurysm rates\ngender_stats = explorer.train_df.groupby('PatientSex')['Aneurysm Present'].agg(['count', 'sum']).reset_index()\ngender_stats['rate'] = gender_stats['sum'] / gender_stats['count'] * 100\n\nx_pos = range(len(gender_stats))\naxes[1,2].bar([p - 0.2 for p in x_pos], gender_stats['count'], 0.4, label='Total', alpha=0.7)\nax2 = axes[1,2].twinx()\nax2.bar([p + 0.2 for p in x_pos], gender_stats['rate'], 0.4, label='Positive Rate (%)', \n        color='orange', alpha=0.7)\n\naxes[1,2].set_xlabel('Gender')\naxes[1,2].set_ylabel('Count')\nax2.set_ylabel('Positive Rate (%)')\naxes[1,2].set_title('Gender Distribution and Aneurysm Rates')\naxes[1,2].set_xticks(x_pos)\naxes[1,2].set_xticklabels(gender_stats['PatientSex'])\naxes[1,2].legend(loc='upper left')\nax2.legend(loc='upper right')\n\nplt.tight_layout()\nplt.show()\n\nprint(\"\\n✅ Enhanced data analysis and visualization complete!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T05:41:50.27284Z","iopub.execute_input":"2025-07-30T05:41:50.273075Z","iopub.status.idle":"2025-07-30T05:41:52.268161Z","shell.execute_reply.started":"2025-07-30T05:41:50.273055Z","shell.execute_reply":"2025-07-30T05:41:52.267252Z"}},"outputs":[],"execution_count":null}]}