import pandas as pd
import numpy as np


# 数据清洗类
class DataCleaner:
    # 初始化
    def __init__(self, in_path, out_path):
        self.in_path = in_path
        self.out_path = out_path
        self.or_df = None
        self.clean_df = None

    # 加载数据
    def load_data(self):
        print(f"读取数据：{self.in_path}")
        self.or_df = pd.read_csv(self.in_path, encoding="utf-8")
        self.clean_df = self.or_df.copy()

        print(f"读取完成：数据info:{self.or_df.info()}=数据纬度:{self.or_df.shape}")
        self.or_df.dropna(subset=['order_date'])
        return self

    # 清洗数据-处理缺失值
    def del_miss_value(self):
        missing_before = self.clean_df.isnull().sum()
        missing_before = missing_before[missing_before > 0]
        print(missing_before)
        for col, count in missing_before.items():
            ratio = count / len(self.clean_df) * 100
            print(f"{col} 空占比{ratio:.2f}%")
        # 关键字段 -> 删除行
        self.clean_df = self.clean_df.dropna(subset=['order_date'])
        self.clean_df = self.clean_df.dropna(subset=['order_id'])
        # 数值字段 -> 中位数填充
        self.clean_df["quantity"] = self.clean_df["quantity"].fillna(self.or_df['quantity'].median())
        self.clean_df["unit_price"] = self.clean_df["unit_price"].fillna(self.or_df['unit_price'].median())
        self.clean_df["cost"] = self.clean_df["cost"].fillna(self.or_df['cost'].median())
        # 分类字段 -> 众数填充
        category_columns = ['customer_id', 'product_id', 'category', 'region', 'channel']
        for field in category_columns:
            mode_val = self.or_df[field].mode()
            self.clean_df[field] = self.clean_df[field].fillna(mode_val[0])
        print(f"清洗完->{self.clean_df.isnull().sum()}")
        # 重新计算计算字段
        mask = self.clean_df["sales_amount"].isnull()
        print(f"计算位置：{mask}")
        self.clean_df.loc[mask, "sales_amount"] = self.clean_df.loc[mask, "quantity"] \
                                                  * self.clean_df.loc[mask, "unit_price"]

        print(f"清洗完->{self.clean_df.isnull().sum()}")
        mask = self.clean_df['profit'].isnull()
        self.clean_df.loc[mask, 'profit'] = self.clean_df.loc[mask, "sales_amount"] \
                                            - self.clean_df.loc[mask, "quantity"] \
                                            * self.clean_df.loc[mask, "cost"]
        print(f"清洗完->{self.clean_df.isnull().sum()}")

        # 清洗数据-处理异常值
        # 处理负数
        dealer_fields = ['quantity', 'unit_price', 'sales_amount', 'cost']
        for field in dealer_fields:
            mask = self.clean_df[field] < 0
            if mask.sum() > 0:
                self.clean_df.loc[mask, field] = self.clean_df[self.clean_df[field] >= 0][field].median()

        # 处理极端值 使用IQR方法
        dealer_fields = ['quantity', 'unit_price', 'sales_amount']
        for field in dealer_fields:
            Q1 = self.clean_df[field].quantile(0.25)
            Q3 = self.clean_df[field].quantile(0.75)
            IQR = Q3 - Q1
            lower_bound = Q1 - IQR * 1.5
            upper_bound = Q3 + IQR * 1.5
            mask = (self.clean_df[field] < lower_bound) | (self.clean_df[field] > lower_bound)
            if mask.sum() > 0:
                self.clean_df.loc[self.clean_df[field] < lower_bound, field] = lower_bound
                self.clean_df.loc[self.clean_df[field] > upper_bound, field] = upper_bound

        # 处理逻辑不一致的值
        mask = np.abs(self.clean_df['quantity'] * self.clean_df['unit_price'] - self.clean_df['sales_amount']) > 0.01
        print(f"处理逻辑不一致的值   {mask.sum()}")

        if mask.sum() > 0:
            self.clean_df.loc[mask, 'sales_amount'] = self.clean_df['quantity'] * self.clean_df['unit_price']

        mask = np.abs(self.clean_df['sales_amount'] - self.clean_df['quantity'] * self.clean_df['cost'] - self.clean_df[
            'profit']) > 0.01
        if mask.sum() > 0:
            self.clean_df.loc[mask, 'profit'] = self.clean_df['sales_amount'] - self.clean_df['quantity'] * \
                                                self.clean_df['cost'] - self.clean_df['profit']
        print(f"处理逻辑不一致的值   {mask.sum()}")

        # 处理日期格式
        date_mask = pd.to_datetime(self.clean_df['order_date'], errors='coerce').isnull()
        if date_mask.sum() > 0:
            self.clean_df = self.clean_df[~date_mask]
        self.clean_df["order_date"] = pd.to_datetime(self.clean_df['order_date'])
        date_mask = self.clean_df["order_date"].dt.year != 2023
        if date_mask.sum() > 0:
            self.clean_df = self.clean_df[~date_mask]

        # 处理分类范围
        category = ['电子产品', '家居用品', '服装鞋帽', '食品饮料', '图书文具', '运动户外']
        mask = ~self.clean_df['category'].isin(category)
        self.clean_df.loc[mask, 'category'] = self.clean_df[self.clean_df['category'].isin(category)]['category'].mode(
            0)
        return self

    # 清洗数据-处理重复值
    def del_chongfu(self):
        self.clean_df.drop_duplicates(subset="order_id", keep='first')
        return self

    # 清洗数据-处理类型转换
    def del_type_change(self):
        # 日期类型转换
        # if self.clean_df['order_date'].dtype != 'datatime64[ns]':
        self.clean_df['order_date'] = pd.to_datetime(self.clean_df['order_date'])
        # 整数类型转换
        self.clean_df['quantity'] = self.clean_df['quantity'].astype(int)
        # 浮点保留2位小数
        for field in ['unit_price', 'sales_amount', 'cost', 'profit']:
            self.clean_df[field] = self.clean_df[field].round(2).astype(float)
        # 统一渠道格式
        cannel_mapping = {
            '线上': '线上',
            'online': '线上',
            '在线': '线上',
            '线下': '线下',
            'offline': '线下',
        }
        self.clean_df['channel'] = self.clean_df['channel'].map(cannel_mapping)
        # 分类类型转换
        for field in ['category', 'region', 'channel']:
            self.clean_df[field] = self.clean_df[field].astype('category')
        print(f"类型转换后 {self.clean_df.info()}")

    # 保存清洗后的数据
    def del_save_clean_data(self):
        self.clean_df.to_csv(self.out_path)


def main():
    in_path = "../data/raw_sales_data.csv"
    out_path = "../output/clean_sale_data.csv"
    cleaner = DataCleaner(in_path, out_path)
    cleaner.load_data()
    cleaner.del_miss_value()
    cleaner.del_chongfu()
    cleaner.del_type_change()
    cleaner.del_save_clean_data()
    print(f"清洗后数据类型{cleaner.clean_df.head(10)}")
    print(f"清洗后数据类型{cleaner.clean_df.dtypes}")
    print(f"清洗后数据描述{cleaner.clean_df.describe()}")


if __name__ == '__main__':
    main()
