
import pandas as pd
import numpy as np

class FeatureAnalyzer:
    def __init__(self):
        self.summary = None
        self.drop_cols = []
        
    def detect_and_convert_types(self, df, numeric_threshold=0.8):
        """
        Intelligently detect and convert feature types.
        If a column is object type but contains mostly numbers (ratio > numeric_threshold),
        convert it to numeric.
        """
        df_new = df.copy()
        converted_cols = []
        
        for col in df_new.columns:
            # Skip if already numeric
            if pd.api.types.is_numeric_dtype(df_new[col]):
                continue
                
            # Try converting to numeric
            converted = pd.to_numeric(df_new[col], errors='coerce')
            valid_ratio = converted.notna().mean()
            
            if valid_ratio >= numeric_threshold:
                df_new[col] = converted
                converted_cols.append(col)
                # print(f"Converted '{col}' to numeric (valid ratio: {valid_ratio:.2f})")
                
        return df_new, converted_cols

    def clean_data(self, df, target_col=None, row_missing_threshold=0.9):
        """
        Clean data by removing invalid samples.
        1. Remove rows with missing target or invalid target (not 0/1)
        2. Remove rows with high missing rate
        """
        df_clean = df.copy()
        initial_len = len(df_clean)
        
        # 1. Target cleaning
        if target_col and target_col in df_clean.columns:
            # Remove missing target
            df_clean = df_clean.dropna(subset=[target_col])
            
            # Remove invalid target values (only keep 0 and 1)
            # Assuming binary classification
            df_clean = df_clean[df_clean[target_col].isin([0, 1])]
            
        # 2. High missing rate row removal
        # Calculate missing rate per row
        row_missing_rate = df_clean.isnull().mean(axis=1)
        df_clean = df_clean[row_missing_rate <= row_missing_threshold]
        
        cleaned_len = len(df_clean)
        if cleaned_len < initial_len:
            print(f"Data Cleaning: Removed {initial_len - cleaned_len} samples ({(initial_len - cleaned_len)/initial_len:.2%}).")
            
        return df_clean

    def analyze(self, df, target=None):
        """
        Analyze dataset and return summary statistics.
        """
        stats = []
        for col in df.columns:
            series = df[col]
            dtype = series.dtype
            
            # Count unique values
            nunique = series.nunique()
            
            # Missing rate
            missing = series.isnull().sum()
            missing_rate = missing / len(df)
            
            # Single value rate (单值率 - 最大单值的占比)
            if nunique == 1:
                single_value_rate = 1.0
            else:
                single_value_rate = series.value_counts().max() / len(series)
            
            # Mode (众数)
            mode_val = series.mode()
            if len(mode_val) > 0:
                mode_val = mode_val[0]
            else:
                mode_val = np.nan
            
            # Median (中位数)
            if pd.api.types.is_numeric_dtype(series):
                median_val = series.median()
            else:
                median_val = np.nan
            
            # Correlation with target (与target的相关系数)
            corr_with_target = np.nan
            if target is not None:
                try:
                    if pd.api.types.is_numeric_dtype(series) and pd.api.types.is_numeric_dtype(target):
                        corr_with_target = series.corr(target)
                except:
                    corr_with_target = np.nan
            
            # Basic stats
            if np.issubdtype(dtype, np.number):
                min_val = series.min()
                max_val = series.max()
                mean_val = series.mean()
                std_val = series.std()
                zeros = (series == 0).sum()
                zeros_rate = zeros / len(df)
            else:
                min_val = np.nan
                max_val = np.nan
                mean_val = np.nan
                std_val = np.nan
                zeros = 0
                zeros_rate = 0
                
            stats.append({
                'variable': col,
                'dtype': str(dtype),
                'nunique': nunique,
                'missing_rate': missing_rate,
                'single_value_rate': single_value_rate,
                'mode': mode_val,
                'median': median_val,
                'corr_with_target': corr_with_target,
                'min': min_val,
                'max': max_val,
                'mean': mean_val,
                'std': std_val,
                'zeros_rate': zeros_rate
            })
            
        self.summary = pd.DataFrame(stats)
        return self.summary
        
    def get_drop_columns(self, threshold_missing=0.95, threshold_unique=1):
        """
        Identify columns to drop based on missing rate or single value.
        """
        if self.summary is None:
            raise ValueError("Run analyze() first.")
            
        drop_missing = self.summary[self.summary['missing_rate'] > threshold_missing]['variable'].tolist()
        drop_unique = self.summary[self.summary['nunique'] <= threshold_unique]['variable'].tolist()
        
        self.drop_cols = list(set(drop_missing + drop_unique))
        return self.drop_cols
    
    def calculate_advanced_stats(self, df, target=None):
        """
        Calculate advanced statistics including mode, median, single value rate.
        """
        stats = []
        for col in df.columns:
            series = df[col]
            
            # Single value rate (单值率)
            single_value_rate = 1.0 if series.nunique() == 1 else 0.0
            
            # Mode (众数)
            mode_val = series.mode()
            if len(mode_val) > 0:
                mode_val = mode_val[0]
            else:
                mode_val = np.nan
            
            # Median (中位数)
            if pd.api.types.is_numeric_dtype(series):
                median_val = series.median()
            else:
                median_val = np.nan
            
            # Correlation with target (与target的相关系数)
            corr_with_target = np.nan
            if target is not None and target in df.columns:
                try:
                    if pd.api.types.is_numeric_dtype(series) and pd.api.types.is_numeric_dtype(df[target]):
                        corr_with_target = series.corr(df[target])
                except:
                    corr_with_target = np.nan
            
            stats.append({
                'variable': col,
                'single_value_rate': single_value_rate,
                'mode': mode_val,
                'median': median_val,
                'corr_with_target': corr_with_target
            })
        
        return pd.DataFrame(stats)
