def train_model(self):
    """Train the Random Forest model with SMOTETomek resampling.
    
    This method:
    1. Prepares the training data by handling missing values
    2. Creates a pipeline with:
       - IterativeImputer (if missing values exist)
       - StandardScaler for feature scaling
       - SMOTETomek for class balancing
       - RandomForestClassifier with optimized parameters
    3. Fits the pipeline on the training data
    4. Calculates both permutation and SHAP importance
    5. Saves the trained model
    
    The RandomForestClassifier is configured with:
    - 10,000 trees for robust ensemble learning
    - No maximum depth to allow full tree growth
    - Minimum samples split/leaf of 2/1 for detailed splits
    - Square root of features for each split
    - Bootstrap sampling for diversity
    - Parallel processing enabled
    
    Returns:
        None
    
    Raises:
        Exception: If any step in the training process fails
    """
    try:
        self.logger.info("\n=== Training Model ===")
        
        # Get training data
        X_train = self.feature_splits['train'].copy()
        y_train = self.target_splits['train']
        
        # Drop Participant_ID
        X_train = X_train.drop('Participant_ID', axis=1)
        
        # Check for NaN values
        nan_columns = self._check_nan_columns(X_train)
        
        # Handle missing values
        X_train = self._handle_missing_values(X_train)
        
        # Create pipeline steps
        steps = []
        
        # Add imputation if needed
        if len(nan_columns) > 0:
            steps.append(('imputer', IterativeImputer(
                random_state=self.random_state,
                max_iter=10,
                n_nearest_features=min(10, len(nan_columns))
            )))
        
        # Add remaining steps
        steps.extend([
            ('scaler', StandardScaler()),
            ('smote', SMOTETomek(random_state=self.random_state)),
            ('classifier', RandomForestClassifier(
                n_estimators=10000,
                max_depth=None,
                min_samples_split=2,
                min_samples_leaf=1,
                max_features='sqrt',
                bootstrap=True,
                random_state=self.random_state,
                n_jobs=-1,
                verbose=0
            ))
        ])
        
        # Create pipeline
        self.pipeline = Pipeline(steps)
        
        # Fit pipeline
        self.logger.info("Fitting pipeline...")
        self.pipeline.fit(X_train, y_train)
        
        # Extract model components
        self.model = self.pipeline.named_steps['classifier']
        self.scaler = self.pipeline.named_steps['scaler']
        
        # Calculate both importance metrics
        self.calculate_permutation_importance()
        self.calculate_shap_importance()
        
        # Save model
        self._save_model()
        
        self.logger.info("Model training completed successfully")
        
    except Exception as e:
        self.logger.error(f"Error in train_model: {str(e)}")
        raise

def calculate_permutation_importance(self):
    """Calculate and plot permutation importance for feature ranking.
    
    This method:
    1. Uses scikit-learn's permutation_importance to assess feature importance
    2. Performs 20 repeats for robust importance estimation
    3. Generates three visualization types:
       a. Original importance scores with error bars
       b. Z-score normalized importance (importance/standard_error)
       c. Color-coded importance based on standard error
    4. Saves detailed results including:
       - All permutation repeats
       - Mean importance scores
       - Standard errors
       - Z-scores
    
    The method uses validation data to ensure unbiased importance estimates
    and transforms the data through the pipeline steps before SMOTE to
    maintain consistency with the training process.
    
    Returns:
        pd.DataFrame: DataFrame containing feature importance metrics
            Columns:
            - feature: Feature name
            - importance_mean: Mean importance score
            - importance_std: Standard error of importance
            - importance_zscore: Z-score normalized importance
    
    Raises:
        Exception: If importance calculation or plotting fails
    """
    try:
        self.logger.info("\n=== Calculating Permutation Importance ===")
        
        # Get validation data
        X_val = self.feature_splits['validation'].copy()
        y_val = self.target_splits['validation']
        
        # Drop Participant_ID and handle missing values
        X_val = X_val.drop('Participant_ID', axis=1)
        X_val = self._handle_missing_values(X_val)
        
        # Transform the validation data using pipeline steps before SMOTE
        X_val_transformed = X_val.copy()
        for name, transform in self.pipeline.steps[:-2]:  # All steps except SMOTE and classifier
            X_val_transformed = transform.transform(X_val_transformed)
        
        # Get the classifier from the pipeline
        classifier = self.pipeline.named_steps['classifier']
        
        # Calculate permutation importance
        r = permutation_importance(
            classifier,  # Use the classifier instead of the full pipeline
            X_val_transformed, y_val,
            n_repeats=20,
            random_state=self.random_state,
            n_jobs=-1,
            scoring='balanced_accuracy'
        )
        
        # Save detailed permutation importance results as CSV
        importances_all_repeats = pd.DataFrame(
            r.importances,
            columns=[f'repeat_{i}' for i in range(r.importances.shape[1])],
            index=X_val.columns
        )
        importances_all_repeats.to_csv(
            self.output_dir / f'{self.timestamp}_permutation_importance_all_repeats.csv'
        )
        self.logger.info("Saved detailed permutation importance results")
        
        # Create importance DataFrame
        importance_df = pd.DataFrame({
            'feature': X_val.columns,
            'importance_mean': r.importances_mean,
            'importance_std': r.importances_std
        })
        
        # Add z-score importance
        importance_df['importance_zscore'] = importance_df['importance_mean'] / importance_df['importance_std']
        
        # Sort and filter features
        importance_df = importance_df.sort_values('importance_mean', ascending=False)
        significant_features = importance_df[importance_df['importance_mean'] > 0].head(20)
        
        # Plot 1: Original with error bars
        plt.figure(figsize=(12, 10))
        bars = plt.barh(
            range(len(significant_features)), 
            significant_features['importance_mean'],
            xerr=significant_features['importance_std'],
            align='center',
            alpha=0.8,
            color='skyblue',
            capsize=5
        )
        
        plt.yticks(range(len(significant_features)), significant_features['feature'])
        plt.xlabel('Permutation Importance Score')
        plt.title(f'Transcriptomics {self.target_type.upper()} - Top Feature Importance\nWith Standard Error Bars')
        
        # Add value labels
        for bar in bars:
            width = bar.get_width()
            plt.text(width, 
                    bar.get_y() + bar.get_height()/2,
                    f'{width:.3f}',
                    ha='left', 
                    va='center',
                    fontweight='bold')
        
        plt.tight_layout()
        plt.savefig(self.output_dir / f'{self.timestamp}_permutation_importance_validation.pdf', bbox_inches='tight')
        plt.close()
        
        # Plot 2: Z-score importance
        plt.figure(figsize=(12, 10))
        significant_features_sorted = significant_features.sort_values('importance_zscore', ascending=True)
        bars = plt.barh(
            range(len(significant_features_sorted)), 
            significant_features_sorted['importance_zscore'],
            align='center',
            alpha=0.8,
            color='skyblue'
        )
        
        plt.yticks(range(len(significant_features_sorted)), significant_features_sorted['feature'])
        plt.xlabel('Z-Score (Importance / Standard Error)')
        plt.title(f'Transcriptomics {self.target_type.upper()} - Normalized Feature Importance\nScaled by Standard Error')
        
        # Add value labels
        for bar in bars:
            width = bar.get_width()
            plt.text(width, 
                    bar.get_y() + bar.get_height()/2,
                    f'{width:.3f}',
                    ha='left', 
                    va='center',
                    fontweight='bold')
        
        plt.tight_layout()
        plt.savefig(self.output_dir / f'{self.timestamp}_permutation_importance_zscore.pdf', bbox_inches='tight')
        plt.close()
        
        # Plot 3: Color-coded by standard error
        fig, ax = plt.subplots(figsize=(12, 10))
        
        # Create color map based on standard errors
        std_normalized = (significant_features['importance_std'] - significant_features['importance_std'].min()) / \
                       (significant_features['importance_std'].max() - significant_features['importance_std'].min())
        colors = plt.cm.Blues(1 - std_normalized)  # Darker = more certain
        
        bars = ax.barh(
            range(len(significant_features)), 
            significant_features['importance_mean'],
            align='center',
            alpha=0.8,
            color=colors
        )
        
        ax.set_yticks(range(len(significant_features)))
        ax.set_yticklabels(significant_features['feature'])
        ax.set_xlabel('Permutation Importance Score')
        ax.set_title(f'Transcriptomics {self.target_type.upper()} - Feature Importance\nColor Intensity Indicates Certainty (Darker = More Certain)')
        
        # Add value labels
        for bar in bars:
            width = bar.get_width()
            ax.text(width, 
                   bar.get_y() + bar.get_height()/2,
                   f'{width:.3f}',
                   ha='left', 
                   va='center',
                   fontweight='bold')
        
        # Add colorbar
        norm = plt.Normalize(vmin=significant_features['importance_std'].max(), 
                           vmax=significant_features['importance_std'].min())
        sm = plt.cm.ScalarMappable(cmap=plt.cm.Blues, norm=norm)
        sm.set_array([])
        cbar = plt.colorbar(sm, ax=ax)
        cbar.set_label('Standard Error (Reversed Scale)')
        
        plt.tight_layout()
        plt.savefig(self.output_dir / f'{self.timestamp}_permutation_importance_color_coded.pdf', bbox_inches='tight')
        plt.close()
        
        # Save importance scores
        importance_df.to_csv(
            self.output_dir / f'{self.timestamp}_feature_importance.csv',
            index=False
        )
        
        self.logger.info("Feature importance calculation complete")
        self.logger.info(f"\nTop 5 most important features:")
        for _, row in importance_df.head().iterrows():
            self.logger.info(f"{row['feature']}: {row['importance_mean']:.4f} ± {row['importance_std']:.4f}")
        
        return importance_df
        
    except Exception as e:
        self.logger.error(f"Error in calculate_permutation_importance: {str(e)}")
        raise 