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

发布于:2025-09-07 ⋅ 阅读:(16) ⋅ 点赞:(0)

思路

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

  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迭代

网站公告

今日签到

点亮在社区的每一天
去签到