Python EXCEL 小技巧:总结和比较表格的差异(输出差异和数值相减)

思路

假设第一行是标题行,且两个文件的标题行内容一致(可能顺序不同)。

  1. 读取两个文件,获取DataFrame

    • 假设csv或xlsx
  2. 检查两个DataFrame的形状(行数和列数)是否一致,如果不一致,则直接返回错误

  3. 检查两个DataFrame的列名是否包含相同的列(不考虑顺序)

    • 如果列名集合不同,则返回错误。
    • 如果列名相同但顺序不同,调整第二个DataFrame的列顺序与第一个相同。
  4. 比较两个DataFrame的内容

    • 如果完全相同,则输出相同提示。
    • 如果存在差异,则生成一个差异报告Excel文件,包含两个sheet:
      • 第一个sheet:统计每个列中差异的数量。
      • 第二个sheet:将两个表中存在差异的行整合到一起(显示两个表中对应的行,并标记出差异的单元格)。

V 1.0

import pandas as pd
import numpy as np
from openpyxl import Workbook
from openpyxl.styles import PatternFill

def compare_dataframes(df1, df2):
    """
    比较两个DataFrame,返回差异报告
    
    参数:
    df1: 第一个DataFrame
    df2: 第二个DataFrame
    
    返回:
    如果相同返回True,否则返回差异报告DataFrame和列差异统计
    """
    # 检查形状是否一致
    if df1.shape != df2.shape:
        return False, None, None
    
    # 检查列名是否一致(不考虑顺序)
    if set(df1.columns) != set(df2.columns):
        return False, None, None
    
    # 调整列顺序使其一致
    df2 = df2[df1.columns]
    
    # 比较内容
    if df1.equals(df2):
        return True, None, None
    
    # 找出差异位置
    diff_mask = df1 != df2
    
    # 处理NaN情况 (NaN != NaN 会返回True)
    both_nan = df1.isna() & df2.isna()
    diff_mask = diff_mask & ~both_nan
    
    # 创建差异统计
    diff_stats = pd.DataFrame({
        '列名': df1.columns,
        '差异数量': diff_mask.sum(axis=0).values
    })
    
    # 创建详细差异报告
    diff_rows = []
    for row_idx in range(df1.shape[0]):
        row_diffs = diff_mask.iloc[row_idx]
        if row_diffs.any():  # 如果这一行有差异
            for col_idx, col_name in enumerate(df1.columns):
                if row_diffs.iloc[col_idx]:
                    diff_rows.append({
                        '行号': row_idx + 2,  # +2是因为Excel行号从1开始,且第一行是标题
                        '列名': col_name,
                        '文件1值': df1.iloc[row_idx, col_idx],
                        '文件2值': df2.iloc[row_idx, col_idx]
                    })
    
    diff_details = pd.DataFrame(diff_rows)
    
    return False, diff_stats, diff_details

def create_difference_report(diff_stats, diff_details, output_file):
    """
    创建差异报告Excel文件
    """
    with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
        # 写入统计信息
        diff_stats.to_excel(writer, sheet_name='差异统计', index=False)
        
        # 写入详细信息
        diff_details.to_excel(writer, sheet_name='详细差异', index=False)
        
        # 获取工作簿和工作表对象
        workbook = writer.book
        stats_sheet = writer.sheets['差异统计']
        details_sheet = writer.sheets['详细差异']
        
        # 设置样式
        red_fill = PatternFill(start_color="FFFF0000", end_color="FFFF0000", fill_type="solid")
        
        # 在详细差异表中高亮差异值
        for idx, row in diff_details.iterrows():
            # 文件1值单元格 (C列)
            details_sheet.cell(row=idx+2, column=3).fill = red_fill
            # 文件2值单元格 (D列)
            details_sheet.cell(row=idx+2, column=4).fill = red_fill
        
        # 调整列宽
        for sheet in [stats_sheet, details_sheet]:
            for column in sheet.columns:
                max_length = 0
                column_letter = column[0].column_letter
                for cell in column:
                    try:
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except:
                        pass
                adjusted_width = min(max_length + 2, 50)
                sheet.column_dimensions[column_letter].width = adjusted_width

def compare_files(file1, file2, file1_sheet=None, file2_sheet=None, output_file='difference_report.xlsx'):
    """
    比较两个文件的主函数
    """
    # 读取文件
    if file1.endswith('.csv'):
        df1 = pd.read_csv(file1)
    else:
        df1 = pd.read_excel(file1, sheet_name=file1_sheet)
    
    if file2.endswith('.csv'):
        df2 = pd.read_csv(file2)
    else:
        df2 = pd.read_excel(file2, sheet_name=file2_sheet)
    
    # 比较DataFrame
    is_same, diff_stats, diff_details = compare_dataframes(df1, df2)
    
    if is_same is None:
        print("文件结构不一致,无法比较")
        return False
    elif is_same:
        print("两个表格内容完全相同!")
        return True
    else:
        print("表格内容存在差异,生成差异报告中...")
        create_difference_report(diff_stats, diff_details, output_file)
        print(f"差异报告已保存至: {output_file}")
        return False

# 使用示例
if __name__ == "__main__":
    compare_files('file1.csv', 'file2.csv')

V2.0

主要改进点包括:

  1. 使用向量化操作替代循环提高性能
  2. 优化NaN值处理
  3. 改进列宽调整逻辑
  4. 增强错误处理
  5. 优化内存使用
import pandas as pd
import numpy as np
from openpyxl import Workbook
from openpyxl.styles import PatternFill
from openpyxl.utils import get_column_letter

def compare_dataframes(df1, df2):
    """
    比较两个DataFrame,返回差异报告
    
    参数:
    df1: 第一个DataFrame
    df2: 第二个DataFrame
    
    返回:
    如果相同返回True,否则返回差异报告DataFrame和列差异统计
    """
    # 检查形状是否一致
    if df1.shape != df2.shape:
        return False, None, None
    
    # 检查列名是否一致(不考虑顺序)
    if set(df1.columns) != set(df2.columns):
        return False, None, None
    
    # 调整列顺序使其一致
    df2 = df2[df1.columns]
    
    # 比较内容 - 使用更高效的方法
    if df1.equals(df2):
        return True, None, None
    
    # 找出差异位置 - 使用向量化操作
    # 处理NaN情况: 使用pd.NA安全的比较方法
    diff_mask = ~(df1.fillna('NA').astype(str) == df2.fillna('NA').astype(str))
    
    # 创建差异统计 - 使用向量化操作
    diff_counts = diff_mask.sum()
    diff_stats = pd.DataFrame({
        '列名': df1.columns,
        '差异数量': diff_counts.values
    })
    
    # 如果没有差异,直接返回
    if diff_counts.sum() == 0:
        return True, None, None
    
    # 创建详细差异报告 - 使用更高效的方法
    # 获取差异位置的行列索引
    diff_indices = np.argwhere(diff_mask.values)
    
    # 构建差异详情DataFrame
    diff_rows = []
    for row_idx, col_idx in diff_indices:
        col_name = df1.columns[col_idx]
        diff_rows.append({
            '行号': row_idx + 2,  # +2是因为Excel行号从1开始,且第一行是标题
            '列名': col_name,
            '文件1值': df1.iloc[row_idx, col_idx],
            '文件2值': df2.iloc[row_idx, col_idx]
        })
    
    diff_details = pd.DataFrame(diff_rows)
    
    return False, diff_stats, diff_details

def create_difference_report(diff_stats, diff_details, output_file):
    """
    创建差异报告Excel文件
    """
    with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
        # 写入统计信息
        diff_stats.to_excel(writer, sheet_name='差异统计', index=False)
        
        # 写入详细信息
        diff_details.to_excel(writer, sheet_name='详细差异', index=False)
        
        # 获取工作簿和工作表对象
        workbook = writer.book
        stats_sheet = writer.sheets['差异统计']
        details_sheet = writer.sheets['详细差异']
        
        # 设置样式
        red_fill = PatternFill(start_color="FFFF0000", end_color="FFFF0000", fill_type="solid")
        
        # 在详细差异表中高亮差异值 - 使用更高效的方法
        for row_idx in range(2, len(diff_details) + 2):  # 从第2行开始(标题在第1行)
            details_sheet.cell(row=row_idx, column=3).fill = red_fill
            details_sheet.cell(row=row_idx, column=4).fill = red_fill
        
        # 优化列宽调整 - 使用更高效的方法
        for sheet in [stats_sheet, details_sheet]:
            for col_idx, column in enumerate(sheet.columns, 1):
                max_length = 0
                col_letter = get_column_letter(col_idx)
                
                # 检查标题和内容
                header_length = len(str(column[0].value))
                max_length = max(max_length, header_length)
                
                # 只检查前100行以避免性能问题
                for cell in column[1:101]:
                    try:
                        cell_length = len(str(cell.value))
                        if cell_length > max_length:
                            max_length = cell_length
                    except:
                        pass
                
                adjusted_width = min(max_length + 2, 50)
                sheet.column_dimensions[col_letter].width = adjusted_width

def compare_files(file1, file2, file1_sheet=None, file2_sheet=None, output_file='difference_report.xlsx'):
    """
    比较两个文件的主函数
    """
    try:
        # 读取文件 - 添加更多错误处理
        if file1.endswith('.csv'):
            df1 = pd.read_csv(file1)
        else:
            df1 = pd.read_excel(file1, sheet_name=file1_sheet or 0, engine='openpyxl')
        
        if file2.endswith('.csv'):
            df2 = pd.read_csv(file2)
        else:
            df2 = pd.read_excel(file2, sheet_name=file2_sheet or 0, engine='openpyxl')
        
        # 比较DataFrame
        is_same, diff_stats, diff_details = compare_dataframes(df1, df2)
        
        if is_same is None:
            print("文件结构不一致,无法比较")
            return False
        elif is_same:
            print("两个表格内容完全相同!")
            return True
        else:
            print("表格内容存在差异,生成差异报告中...")
            create_difference_report(diff_stats, diff_details, output_file)
            print(f"差异报告已保存至: {output_file}")
            return False
    except Exception as e:
        print(f"比较过程中发生错误: {str(e)}")
        return False

# 使用示例
if __name__ == "__main__":
    compare_files('file1.csv', 'file2.csv')

详细优化点:

  1. NaN值处理优化:
  • 使用 fillna(‘NA’) 和 astype(str) 安全地比较包含NaN的值

  • 避免了双重NaN检查逻辑

  1. 性能优化:
  • 使用向量化操作替代循环(特别是 diff_mask 计算)

  • 使用 np.argwhere 高效获取差异位置

  • 限制列宽计算只检查前100行以避免性能问题

  1. 内存优化:
  • 避免创建不必要的中间DataFrame

  • 使用更高效的数据结构存储差异位置

  1. 错误处理增强:
  • 添加了全局异常处理

  • 明确了Excel读取引擎

  • 处理了sheet_name为None的情况

  1. 列宽调整优化:
  • 使用 get_column_letter 简化列索引转换

  • 同时考虑标题长度

  1. 高亮逻辑优化:
  • 直接根据行索引高亮,避免DataFrame迭代

V2.1

导入必要的库:

import pandas as pd
import numpy as np
from pathlib import Path
from openpyxl import Workbook
from openpyxl.styles import PatternFill
import warnings
warnings.filterwarnings('ignore')

1. 文件读取函数

def read_files(file1_path, file2_path, sheet1_name=None, sheet2_name=None):
    """
    读取两个文件并返回DataFrame
    
    参数:
    file1_path: 第一个文件路径
    file2_path: 第二个文件路径
    sheet1_name: 第一个Excel文件的sheet名(如果是Excel文件)
    sheet2_name: 第二个Excel文件的sheet名(如果是Excel文件)
    
    返回:
    两个DataFrame
    """
    # 处理第一个文件
    file1_ext = Path(file1_path).suffix.lower()
    if file1_ext == '.csv':
        df1 = pd.read_csv(file1_path, header=0)
    elif file1_ext in ['.xlsx', '.xls']:
        if sheet1_name:
            df1 = pd.read_excel(file1_path, sheet_name=sheet1_name, header=0)
        else:
            df1 = pd.read_excel(file1_path, header=0)
    else:
        raise ValueError("不支持的文件格式")
    
    # 处理第二个文件
    file2_ext = Path(file2_path).suffix.lower()
    if file2_ext == '.csv':
        df2 = pd.read_csv(file2_path, header=0)
    elif file2_ext in ['.xlsx', '.xls']:
        if sheet2_name:
            df2 = pd.read_excel(file2_path, sheet_name=sheet2_name, header=0)
        else:
            df2 = pd.read_excel(file2_path, header=0)
    else:
        raise ValueError("不支持的文件格式")
    
    return df1, df2

2. DataFrame比较函数

def compare_dataframes(df1, df2, output_file):
    """
    比较两个DataFrame并生成差异报告
    
    参数:
    df1: 第一个DataFrame
    df2: 第二个DataFrame
    output_file: 输出文件路径
    """
    # 创建原始数据的副本,避免修改原始数据
    df1_clean = df1.copy()
    df2_clean = df2.copy()
    
    # 第一步:删除全为空值的行和列
    df1_clean = df1_clean.dropna(how='all').dropna(axis=1, how='all')
    df2_clean = df2_clean.dropna(how='all').dropna(axis=1, how='all')
    
    # 检查行列数是否一致
    if df1_clean.shape != df2_clean.shape:
        print(f"清理后数据形状不一致: df1 {df1_clean.shape}, df2 {df2_clean.shape}")
        return False
    
    # 第二步:检查列名是否一致
    if not df1_clean.columns.equals(df2_clean.columns):
        print("列名不一致,尝试按df1的顺序重排df2的列")
        # 检查是否包含相同的列
        if set(df1_clean.columns) != set(df2_clean.columns):
            print("列名不完全相同,无法比较")
            missing_in_df2 = set(df1_clean.columns) - set(df2_clean.columns)
            missing_in_df1 = set(df2_clean.columns) - set(df1_clean.columns)
            print(f"df2中缺少的列: {missing_in_df2}")
            print(f"df1中缺少的列: {missing_in_df1}")
            return False
        
        # 按df1的顺序重排df2的列
        df2_clean = df2_clean[df1_clean.columns]
        print("已按df1的顺序重排df2的列")
    
    # 第三步:比较数据内容
    # 创建比较矩阵
    comparison = df1_clean.values == df2_clean.values
    
    # 检查是否所有数据都相同
    if comparison.all():
        print("两个表格内容完全相同")
        return True
    
    # 如果有差异,创建差异报告
    print("发现差异,正在生成报告...")
    
    # 创建Excel写入器
    with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
        # Sheet1: 差异统计
        diff_counts = []
        for col in df1_clean.columns:
            col_idx = df1_clean.columns.get_loc(col)
            diff_count = np.sum(~comparison[:, col_idx])
            diff_counts.append(diff_count)
        
        summary_df = pd.DataFrame({
            '列名': df1_clean.columns,
            '差异数量': diff_counts
        })
        summary_df = summary_df[summary_df['差异数量'] > 0]  # 只显示有差异的列
        summary_df.to_excel(writer, sheet_name='差异统计', index=False)
        
        # Sheet2: df1的差异行
        # 找出有差异的行
        diff_rows = np.any(~comparison, axis=1)
        df1_diff = df1_clean[diff_rows].copy()
        df1_diff.to_excel(writer, sheet_name='文件1差异', index=False)
        
        # Sheet3: df2的差异行
        df2_diff = df2_clean[diff_rows].copy()
        df2_diff.to_excel(writer, sheet_name='文件2差异', index=False)
    
    # 高亮显示差异单元格
    highlight_differences(output_file, df1_clean, df2_clean, comparison, diff_rows)
    
    print(f"差异报告已生成: {output_file}")
    return False

def highlight_differences(output_file, df1, df2, comparison, diff_rows):
    """
    高亮显示差异单元格
    
    参数:
    output_file: 输出文件路径
    df1: 第一个DataFrame
    df2: 第二个DataFrame
    comparison: 比较矩阵
    diff_rows: 有差异的行索引
    """
    # 加载工作簿
    wb = Workbook()
    wb = pd.ExcelWriter(output_file, engine='openpyxl').book
    
    # 黄色填充
    yellow_fill = PatternFill(start_color='FFFF00', end_color='FFFF00', fill_type='solid')
    
    # 处理文件1差异sheet
    ws1 = wb['文件1差异']
    for row_idx in range(2, len(diff_rows) + 2):  # Excel行从1开始,且第一行是标题
        if diff_rows[row_idx - 2]:  # 调整索引
            for col_idx in range(1, len(df1.columns) + 1):
                if not comparison[row_idx - 2, col_idx - 1]:
                    ws1.cell(row=row_idx, column=col_idx).fill = yellow_fill
    
    # 处理文件2差异sheet
    ws2 = wb['文件2差异']
    for row_idx in range(2, len(diff_rows) + 2):
        if diff_rows[row_idx - 2]:
            for col_idx in range(1, len(df2.columns) + 1):
                if not comparison[row_idx - 2, col_idx - 1]:
                    ws2.cell(row=row_idx, column=col_idx).fill = yellow_fill
    
    # 保存工作簿
    wb.save(output_file)

3. 主函数

def compare_tables(file1_path, file2_path, sheet1_name=None, sheet2_name=None, output_file=None):
    """
    主函数:比较两个表格文件
    
    参数:
    file1_path: 第一个文件路径
    file2_path: 第二个文件路径
    sheet1_name: 第一个Excel文件的sheet名(可选)
    sheet2_name: 第二个Excel文件的sheet名(可选)
    output_file: 输出文件路径(可选)
    """
    # 设置默认输出文件名
    if output_file is None:
        output_file = "比较结果.xlsx"
    
    # 读取文件
    df1, df2 = read_files(file1_path, file2_path, sheet1_name, sheet2_name)
    
    # 比较DataFrame
    result = compare_dataframes(df1, df2, output_file)
    
    return result

4. 使用示例

# 示例1: 比较两个CSV文件
compare_tables('file1.csv', 'file2.csv', output_file='comparison_result.xlsx')

# 示例2: 比较Excel文件中的不同sheet
compare_tables('data.xlsx', 'data.xlsx', sheet1_name='Sheet1', sheet2_name='Sheet2', output_file='sheet_comparison.xlsx')

# 示例3: 比较CSV和Excel文件
compare_tables('data.csv', 'data.xlsx', sheet2_name='Sheet1', output_file='cross_format_comparison.xlsx')

代码说明

  1. 文件读取函数:支持CSV和Excel格式,可以指定sheet名称
  2. 数据清理:删除全为空值的行和列
  3. 列名检查:确保两个DataFrame的列名一致,如果不一致但包含相同的列,会按第一个文件的列顺序重排第二个文件
  4. 差异检测:使用NumPy进行高效比较,找出所有差异单元格
  5. 报告生成
    • 第一个sheet显示每列的差异数量
    • 第二个和第三个sheet分别显示两个文件中存在差异的行,并高亮显示差异单元格

V2.2

思路检查:

  1. 文件读取:支持CSV/Excel(含多Sheet),使用pathlib处理路径更健壮
  2. 预处理逻辑:增加全空行列删除功能,避免空白数据干扰比较
  3. 列名检查:严格检查列名集合一致性,自动标准化列顺序(按字母排序)
  4. 差异检测:使用向量化操作高效比较,正确处理NaN值(视为相等)
  5. 报告生成
    • 总结页:列名+差异数量统计
    • 差异行页:高亮显示具体差异单元格(黄色背景)
    • 支持无差异行时空Sheet处理
  6. 异常处理:关键步骤添加异常捕获,提供明确错误提示
import pandas as pd
from pathlib import Path
from typing import Optional, Tuple
import openpyxl
from openpyxl.styles import PatternFill
from openpyxl.utils.dataframe import dataframe_to_rows

def convert_to_dataframe(file_path: Path, sheet_name: Optional[str] = None) -> pd.DataFrame:
    """
    将CSV/Excel文件转换为DataFrame(首行为列名)
    :param file_path: 文件路径(Path对象)
    :param sheet_name: Excel工作表名(仅Excel需要)
    :return: DataFrame对象
    """
    try:
        if file_path.suffix.lower() == '.csv':
            return pd.read_csv(file_path, header=0)
        elif file_path.suffix.lower() in ('.xlsx', '.xls'):
            return pd.read_excel(file_path, sheet_name=sheet_name, header=0)
        else:
            raise ValueError(f"不支持的文件格式: {file_path.suffix}")
    except Exception as e:
        raise RuntimeError(f"文件读取失败: {str(e)}") from e

def preprocess_dataframe(df: pd.DataFrame) -> pd.DataFrame:
    """
    预处理DataFrame:删除全空行和全空列
    :param df: 原始DataFrame
    :return: 清洗后DataFrame
    """
    # 删除所有值为空的行(how='all'表示整行全空才删除)
    cleaned = df.dropna(axis=0, how='all')
    # 删除所有值为空的列(how='all'表示整列全空才删除)
    cleaned = cleaned.dropna(axis=1, how='all')
    return cleaned

def validate_dataframe_shapes(df1: pd.DataFrame, df2: pd.DataFrame) -> None:
    """
    验证两个DataFrame清洗后的形状是否一致
    :param df1: 清洗后DataFrame1
    :param df2: 清洗后DataFrame2
    :raises ValueError: 形状不一致时抛出异常
    """
    if df1.shape != df2.shape:
        raise ValueError(
            f"数据维度不一致(清洗后):
"
            f"文件1: {df1.shape[0]}行 x {df1.shape[1]}列
"
            f"文件2: {df2.shape[0]}行 x {df2.shape[1]}列"
        )

def validate_and_align_columns(df1: pd.DataFrame, df2: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """
    验证列名一致性并对齐列顺序
    :param df1: 清洗后DataFrame1
    :param df2: 清洗后DataFrame2
    :return: 列顺序对齐后的两个DataFrame
    :raises ValueError: 列名集合不一致时抛出异常
    """
    cols1 = set(df1.columns)
    cols2 = set(df2.columns)
    
    if cols1 != cols2:
        raise ValueError(
            f"列名不一致:
"
            f"文件1特有列: {cols1 - cols2}
"
            f"文件2特有列: {cols2 - cols1}"
        )
    
    # 按字母顺序对齐列(保证比较顺序一致)
    sorted_cols = sorted(cols1)
    return df1[sorted_cols], df2[sorted_cols]

def detect_differences(df1: pd.DataFrame, df2: pd.DataFrame) -> Tuple[bool, pd.DataFrame, pd.Series]:
    """
    检测两个DataFrame的差异
    :param df1: 对齐后的DataFrame1
    :param df2: 对齐后的DataFrame2
    :return: (是否完全相同, 差异掩码, 列差异数量统计)
    """
    # 快速检查是否完全相同(优化性能)
    if df1.equals(df2):
        return True, None, None
    
    # 生成差异掩码(True表示对应位置不同)
    # 注意:pandas中NaN != NaN,这里需要特殊处理(视为相同)
    diff_mask = df1.fillna('__NULL__').ne(df2.fillna('__NULL__'))
    
    # 统计每列的差异数量
    col_diff_counts = diff_mask.sum()
    
    return False, diff_mask, col_diff_counts

def apply_highlight_style(diff_mask: pd.DataFrame) -> callable:
    """
    生成差异高亮样式函数
    :param diff_mask: 差异掩码DataFrame
    :return: 样式应用函数
    """
    def style_func(row):
        styles = []
        for idx, value in enumerate(row):
            # 检查当前位置是否是差异点
            if diff_mask.iloc[row.name, idx]:
                styles.append('background-color: #FFFF00')  # 黄色背景
            else:
                styles.append('')
        return styles
    return style_func

def generate_comparison_report(
    df1: pd.DataFrame,
    df2: pd.DataFrame,
    diff_mask: pd.DataFrame,
    col_diff_counts: pd.Series,
    output_path: Path
) -> None:
    """
    生成完整的比较报告Excel文件
    :param df1: 对齐后的DataFrame1(用于展示差异行)
    :param df2: 对齐后的DataFrame2(用于展示差异行)
    :param diff_mask: 差异掩码DataFrame
    :param col_diff_counts: 列差异数量统计Series
    :param output_path: 报告输出路径(Path对象)
    """
    with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
        # ------------------------- 总结说明页 -------------------------
        summary_data = col_diff_counts.reset_index()
        summary_data.columns = ['列名称', '差异数量']
        summary_data.to_excel(writer, sheet_name='总结说明', index=False)
        
        # 设置总结页列宽(可选优化)
        worksheet = writer.sheets['总结说明']
        worksheet.column_dimensions['A'].width = 20
        worksheet.column_dimensions['B'].width = 15
        
        # ------------------------- 文件1差异行页 -------------------------
        diff_rows = diff_mask.any(axis=1)  # 找到有差异的行
        df1_diff = df1[diff_rows].reset_index(drop=True)
        
        if not df1_diff.empty:
            # 应用高亮样式
            styled_df1 = df1_diff.style.apply(
                apply_highlight_style(diff_mask.loc[diff_rows]),
                axis=1
            )
            # 写入Excel(保留标题)
            styled_df1.to_excel(
                writer,
                sheet_name='文件1差异行',
                index=False,
                header=True,
                startrow=0
            )
        else:
            # 无差异时创建空页
            pd.DataFrame(columns=df1.columns).to_excel(
                writer,
                sheet_name='文件1差异行',
                index=False
            )
        
        # ------------------------- 文件2差异行页 -------------------------
        df2_diff = df2[diff_rows].reset_index(drop=True)
        
        if not df2_diff.empty:
            styled_df2 = df2_diff.style.apply(
                apply_highlight_style(diff_mask.loc[diff_rows]),
                axis=1
            )
            styled_df2.to_excel(
                writer,
                sheet_name='文件2差异行',
                index=False,
                header=True,
                startrow=0
            )
        else:
            pd.DataFrame(columns=df2.columns).to_excel(
                writer,
                sheet_name='文件2差异行',
                index=False
            )
        
        # 自动调整列宽(可选优化)
        for sheet_name in writer.sheets:
            worksheet = writer.sheets[sheet_name]
            for idx, col in enumerate(worksheet.columns):
                max_length = max(len(str(cell.value)) for cell in col if cell.value)
                adjusted_width = (max_length + 2) * 1.2
                worksheet.column_dimensions[chr(65 + idx)].width = min(adjusted_width, 50)

def compare_files(
    file1_path: Path,
    file2_path: Path,
    sheet1_name: Optional[str] = None,
    sheet2_name: Optional[str] = None,
    output_path: Path = Path('表格比较结果.xlsx')
) -> None:
    """
    主函数:执行完整的文件比较流程
    :param file1_path: 文件1路径(CSV/Excel)
    :param file2_path: 文件2路径(CSV/Excel)
    :param sheet1_name: 文件1的Sheet名(仅Excel需要)
    :param sheet2_name: 文件2的Sheet名(仅Excel需要)
    :param output_path: 报告输出路径(默认当前目录)
    """
    try:
        # -------------------- 步骤1:读取文件 --------------------
        print("正在读取文件...")
        df1 = convert_to_dataframe(file1_path, sheet_name=sheet1_name)
        df2 = convert_to_dataframe(file2_path, sheet_name=sheet2_name)
        print("文件读取完成")

        # -------------------- 步骤2:数据清洗 --------------------
        print("正在清洗数据(删除全空行列)...")
        df1_clean = preprocess_dataframe(df1)
        df2_clean = preprocess_dataframe(df2)
        print("数据清洗完成")

        # -------------------- 步骤3:维度验证 --------------------
        print("正在验证数据维度...")
        validate_dataframe_shapes(df1_clean, df2_clean)
        print("数据维度验证通过")

        # -------------------- 步骤4:列名对齐 --------------------
        print("正在对齐列名...")
        df1_aligned, df2_aligned = validate_and_align_columns(df1_clean, df2_clean)
        print("列名对齐完成")

        # -------------------- 步骤5:差异检测 --------------------
        print("正在检测数据差异...")
        is_identical, diff_mask, col_diff_counts = detect_differences(df1_aligned, df2_aligned)
        
        if is_identical:
            print("结论:两个表格内容完全相同")
            return

        # -------------------- 步骤6:生成报告 --------------------
        print("正在生成差异报告...")
        generate_comparison_report(
            df1_aligned,
            df2_aligned,
            diff_mask,
            col_diff_counts,
            output_path
        )
        print(f"结论:发现数据差异,报告已生成至:{output_path.absolute()}")

    except Exception as e:
        print(f"比较过程中发生错误: {str(e)}")
        # 可选:添加调试断点(取消注释以下行)
        # import pdb; pdb.set_trace()

# ------------------------- 使用示例 -------------------------
if __name__ == "__main__":
    # 示例1:比较两个CSV文件
    # compare_files(
    #     file1_path=Path("文件1.csv"),
    #     file2_path=Path("文件2.csv"),
    #     output_path=Path("CSV比较结果.xlsx")
    # )

    # 示例2:比较Excel的两个Sheet
    compare_files(
        file1_path=Path("文件1.xlsx"),
        file2_path=Path("文件2.xlsx"),
        sheet1_name="销售数据",  # 文件1的Sheet名
        sheet2_name="2025年销售",  # 文件2的Sheet名
        output_path=Path("ExcelSheet比较结果.xlsx")
    )
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值