Python Excel表格数据对比工具

【Excel对比工具】提升工作效率的神奇助手:基于PyQt5和Pandas的文件数据对比应用

相关资源文件已经打包成EXE文件,可双击直接运行程序,且文章末尾已附上相关源码,以供大家学习交流,博主主页还有更多Python相关程序案例,秉着开源精神的想法,望大家喜欢,点个关注不迷路!!!

在日常的数据处理与分析工作中,我们经常需要对比两个 Excel 文件中的数据。这种操作看似简单,但如果手动操作,不仅容易出错,而且非常耗时。今天,我们将深入探讨如何通过 Python 和 PyQt5 结合 pandas 库,快速实现一个高效、自动化的 Excel 数据对比工具。本文将详细介绍该工具的功能和使用方法,并探讨其潜在的扩展性。
在这里插入图片描述

1.概述

数据对比是数据处理中的一项基础工作,特别是在审计、报告、数据验证等任务中,通常需要确保两个数据源的内容一致。在实际的业务操作中,Excel 文件由于其简便易用、兼容性强,成为了大量数据存储的首选。然而,手动对比 Excel 文件往往繁琐且容易出现差错,尤其是在数据量较大的情况下。为了提升工作效率,本文基于 Python 中的 PyQt5 和 pandas 库,开发了一个简洁易用的 Excel 数据对比工具,能够高效地比较两个 Excel 文件中的数据差异。

1.1技术栈

  • PyQt5:用于构建桌面 GUI(图形用户界面),提供友好的用户交互界面。
  • pandas:处理 Excel 文件,进行数据对比。
  • openpyxl:用来处理 Excel 文件的写入和样式设置,导出比较结果。
  • QSS:通过自定义样式表美化界面,提升用户体验。

2.功能使用

2.1. 界面介绍

该工具的界面简单直观,包含以下主要部分:

  • 文件选择:用户可以选择需要比较的两个 Excel 文件。
  • Sheet 选择:加载 Excel 文件后,用户可以选择要比较的工作表。
  • 列选择:用户选择要进行对比的列,可以灵活选择不同列的数据进行比对。
  • 对比结果表格:展示对比结果,包括左侧数据、右侧数据、匹配状态和内容对比。若数据不匹配,相关行会高亮显示。
  • 操作按钮:用户可以点击按钮执行对比操作、导出结果、重置界面和退出程序。

2. 2 文件加载与工作表选择

首先,用户通过点击“打开”按钮选择两个 Excel 文件。文件选择完成后,应用会自动读取并展示文件中的所有工作表名称,用户可以从下拉框中选择对应的工作表。

file_path, _ = QFileDialog.getOpenFileName(self, "选择 Excel 文件", "", "Excel 文件 (*.xlsx *.xls)")
  1. 列选择与数据对比
    用户可以选择要对比的列。点击“开始对比”按钮后,应用会依照选择的列名对比两个工作表中的数据。如果两列数据一致,表格中会显示“✔”标记;若不一致,则显示“✘”标记,并高亮不匹配的行,详细展示差异内容。
left_value = str(df_left[left_col].iloc[i])
right_value = str(df_right[right_col].iloc[i])
match = "✔" if left_value == right_value else "✘"
  1. 导出结果
    用户对比完成后,可以通过“导出结果”按钮将对比结果保存为新的 Excel 文件。保存时,程序会自动高亮不匹配的数据行,并将所有对比信息写入到新文件中,便于后续查看与分析。
ws.append([left_data, right_data, match_status, compare_info])
  1. 重置与退出功能
    程序提供了“重置界面”按钮,用于清空当前选项和对比结果,方便用户重新开始操作。同时,“退出程序”按钮可以关闭应用。

代码实现
上述功能的实现主要依赖于 PyQt5 和 pandas 库。以下是部分关键代码片段:

文件加载与数据读取

def load_file(self, side):
    file_path, _ = QFileDialog.getOpenFileName(self, "选择 Excel 文件", "", "Excel 文件 (*.xlsx *.xls)")
    if not file_path:
        return

    # 选择文件后更新界面
    if side == "left":
        self.left_file = file_path
    else:
        self.right_file = file_path

    # 加载工作表名称
    try:
        sheets = pd.ExcelFile(file_path).sheet_names
        if side == "left":
            self.left_sheet.addItems(sheets)
        else:
            self.right_sheet.addItems(sheets)
    except Exception as e:
        QMessageBox.warning(self, "错误", f"无法读取 Excel 文件: {e}")

数据对比与结果展示

def compare_data(self):
    left_col = self.left_column.currentText()
    right_col = self.right_column.currentText()

    # 读取数据
    df_left = pd.read_excel(self.left_file, sheet_name=self.left_sheet.currentText())
    df_right = pd.read_excel(self.right_file, sheet_name=self.right_sheet.currentText())

    # 对比并更新表格
    for i in range(min(len(df_left), len(df_right))):
        left_value = str(df_left[left_col].iloc[i])
        right_value = str(df_right[right_col].iloc[i])
        match = "✔" if left_value == right_value else "✘"

        row = self.table.rowCount()
        self.table.insertRow(row)

        # 填充表格
        self.table.setItem(row, 0, QTableWidgetItem(left_value))
        self.table.setItem(row, 1, QTableWidgetItem(right_value))
        self.table.setItem(row, 2, QTableWidgetItem(match))

导出结果到 Excel

def export_results(self):
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    file_path, _ = QFileDialog.getSaveFileName(self, "保存 Excel", f"对比结果_{timestamp}.xlsx", "Excel 文件 (*.xlsx)")

    if not file_path:
        return

    # 创建 Excel 文件并保存数据
    wb = openpyxl.Workbook()
    ws = wb.active
    ws.append(["左侧数据", "右侧数据", "匹配状态", "内容对比"])

    for row in range(self.table.rowCount()):
        left_data = self.table.item(row, 0).text()
        right_data = self.table.item(row, 1).text()
        match_status = self.table.item(row, 2).text()
        compare_info = self.table.item(row, 3).text()

        ws.append([left_data, right_data, match_status, compare_info])
        if match_status == "✘":
            for col in range(1, 5):
                ws.cell(row=row+2, column=col).fill = red_fill

    wb.save(file_path)

3.效果展示:

在这里插入图片描述
在这里插入图片描述

4. 相关源码:

import sys
import pandas as pd
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
    QFileDialog, QTableWidget, QTableWidgetItem, QLabel, QComboBox, QMessageBox
)
from PyQt5.QtGui import QBrush, QColor
from datetime import datetime
import openpyxl
from openpyxl.styles import PatternFill

class ExcelComparator(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
#
    def initUI(self):
        self.setWindowTitle("Excel 数据对比工具")
        self.setGeometry(100, 100, 900, 500)
        
        # 应用 QSS 美化界面
        self.setStyleSheet("""
            QWidget { background-color: #f4f4f4; }
            QPushButton { 
                background-color: #0078D7; color: white; 
                border-radius: 5px; padding: 8px; 
                font-size: 14px;
            }
            QPushButton:hover { background-color: #005A9E; }
            QTableWidget { background-color: white; gridline-color: #CCC; }
            QLabel { font-size: 14px;  }
        """)

        layout = QVBoxLayout()

        # 文件选择区
        file_layout = QHBoxLayout()
        self.left_label = QLabel("左侧文件:")
        self.right_label = QLabel("右侧文件:")
        self.left_btn = QPushButton("打开")
        self.right_btn = QPushButton("打开")
        self.left_btn.clicked.connect(lambda: self.load_file("left"))
        self.right_btn.clicked.connect(lambda: self.load_file("right"))
        
        file_layout.addWidget(self.left_label)
        file_layout.addWidget(self.left_btn)
        file_layout.addWidget(self.right_label)
        file_layout.addWidget(self.right_btn)
        
        # Sheet 选择区
        sheet_layout = QHBoxLayout()
        self.left_sheet = QComboBox()
        self.right_sheet = QComboBox()
        self.left_sheet.currentIndexChanged.connect(lambda: self.load_sheet("left"))
        self.right_sheet.currentIndexChanged.connect(lambda: self.load_sheet("right"))
        
        sheet_layout.addWidget(QLabel("左侧 Sheet:"))
        sheet_layout.addWidget(self.left_sheet)
        sheet_layout.addWidget(QLabel("右侧 Sheet:"))
        sheet_layout.addWidget(self.right_sheet)
        
        # 对比列选择
        column_layout = QHBoxLayout()
        self.left_column = QComboBox()
        self.right_column = QComboBox()
        
        column_layout.addWidget(QLabel("左侧对比列:"))
        column_layout.addWidget(self.left_column)
        column_layout.addWidget(QLabel("右侧对比列:"))
        column_layout.addWidget(self.right_column)

        # 结果表格
        self.table = QTableWidget()
        self.table.setColumnCount(4)
        self.table.setHorizontalHeaderLabels(["左侧数据", "右侧数据", "匹配状态", "内容对比"])

        # 操作按钮
        button_layout = QHBoxLayout()
        self.compare_btn = QPushButton("开始对比")
        self.export_btn = QPushButton("导出结果")
        self.reset_btn = QPushButton("重置界面")
        self.exit_btn = QPushButton("退出程序")
        
        self.compare_btn.clicked.connect(self.compare_data)
        self.export_btn.clicked.connect(self.export_results)
        self.reset_btn.clicked.connect(self.reset_ui)
        self.exit_btn.clicked.connect(self.close)
        
        button_layout.addWidget(self.compare_btn)
        button_layout.addWidget(self.export_btn)
        button_layout.addWidget(self.reset_btn)
        button_layout.addWidget(self.exit_btn)

        # 布局组合
        layout.addLayout(file_layout)
        layout.addLayout(sheet_layout)
        layout.addLayout(column_layout)
        layout.addWidget(self.table)
        layout.addLayout(button_layout)
        self.setLayout(layout)

    def load_file(self, side):
        file_path, _ = QFileDialog.getOpenFileName(self, "选择 Excel 文件", "", "Excel 文件 (*.xlsx *.xls)")
        if not file_path:
            return

        if side == "left":
            self.left_label.setText(f"左侧文件: {file_path}")
            self.left_file = file_path
            self.left_sheet.clear()
        else:
            self.right_label.setText(f"右侧文件: {file_path}")
            self.right_file = file_path
            self.right_sheet.clear()

        try:
            sheets = pd.ExcelFile(file_path).sheet_names
            if side == "left":
                self.left_sheet.addItems(sheets)
            else:
                self.right_sheet.addItems(sheets)
        except Exception as e:
            QMessageBox.warning(self, "错误", f"无法读取 Excel 文件: {e}")

    def load_sheet(self, side):
        if side == "left":
            file, sheet_combo, column_combo = self.left_file, self.left_sheet, self.left_column
        else:
            file, sheet_combo, column_combo = self.right_file, self.right_sheet, self.right_column

        if not file or sheet_combo.currentText() == "":
            return

        try:
            df = pd.read_excel(file, sheet_name=sheet_combo.currentText())
            df.columns = df.columns.map(str)  # 确保列名是字符串
            column_combo.clear()
            column_combo.addItems(df.columns)
        except Exception as e:
            QMessageBox.warning(self, "错误", f"无法加载 Sheet 数据: {e}")

    def compare_data(self):
        left_col = self.left_column.currentText()
        right_col = self.right_column.currentText()

        if not left_col or not right_col:
            QMessageBox.warning(self, "错误", "请选择要对比的列!")
            return

        df_left = pd.read_excel(self.left_file, sheet_name=self.left_sheet.currentText())
        df_right = pd.read_excel(self.right_file, sheet_name=self.right_sheet.currentText())

        df_left.columns = df_left.columns.map(str)
        df_right.columns = df_right.columns.map(str)

        self.table.setRowCount(0)

        for i in range(min(len(df_left), len(df_right))):
            left_value = str(df_left[left_col].iloc[i])
            right_value = str(df_right[right_col].iloc[i])
            match = "✔" if left_value == right_value else "✘"

            row = self.table.rowCount()
            self.table.insertRow(row)
            
            # 左侧数据列
            if not self.table.item(row, 0):
                self.table.setItem(row, 0, QTableWidgetItem(left_value))
            else:
                self.table.item(row, 0).setText(left_value)
        
            # 右侧数据列
            if not self.table.item(row, 1):
                self.table.setItem(row, 1, QTableWidgetItem(right_value))
            else:
                self.table.item(row, 1).setText(right_value)
        
            # 匹配状态列
            item_match = QTableWidgetItem(match)
            if not self.table.item(row, 2):
                self.table.setItem(row, 2, item_match)
            else:
                self.table.item(row, 2).setText(match)

            # 高亮不匹配的行
            if match == "✘":
                for col in range(4):  # 增加第四列的信息对比内容列表
                    if not self.table.item(row, col):
                        self.table.setItem(row, col, QTableWidgetItem())
                    self.table.item(row, col).setBackground(QBrush(QColor(255, 150, 150)))

                # 对比内容显示
                differences = f"左侧: {left_value} | 右侧: {right_value}"
                if not self.table.item(row, 3):
                    self.table.setItem(row, 3, QTableWidgetItem(differences))
                else:
                    self.table.item(row, 3).setText(differences)

    def export_results(self):
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        file_path, _ = QFileDialog.getSaveFileName(self, "保存 Excel", f"对比结果_{timestamp}.xlsx", "Excel 文件 (*.xlsx)")
        
        if not file_path:
            return
        
        wb = openpyxl.Workbook()
        ws = wb.active
        ws.append(["左侧数据", "右侧数据", "匹配状态", "内容对比"])
        red_fill = PatternFill(start_color="FF9999", end_color="FF9999", fill_type="solid")

        for row in range(self.table.rowCount()):
            left_data = self.table.item(row, 0).text()
            right_data = self.table.item(row, 1).text()
            match_status = self.table.item(row, 2).text()
            compare_info = self.table.item(row, 3).text()

            ws.append([left_data, right_data, match_status, compare_info])
            if match_status == "✘":
                for col in range(1, 5):
                    ws.cell(row=row+2, column=col).fill = red_fill

        wb.save(file_path)

    def reset_ui(self):
        self.left_sheet.clear()
        self.right_sheet.clear()
        self.left_column.clear()
        self.right_column.clear()
        self.table.setRowCount(0)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = ExcelComparator()
    window.show()
    sys.exit(app.exec_())

5.总结:

通过 PyQt5 和 pandas,我们快速实现了一个 Excel 数据对比工具,能够高效地处理两个 Excel 文件的内容对比,并自动高亮显示差异。此外,用户可以方便地导出对比结果,以便后续的查看和分析。对于需要频繁进行数据比对的工作人员而言,这款工具无疑能够大幅度提高工作效率,减少人工错误。

在未来的版本中,可以考虑以下几点扩展:

  • 多列对比:支持用户选择多个列进行对比。
  • 性能优化:对于大数据量的 Excel 文件,可以优化读取和比较的速度。
  • 自动化脚本:将此工具的功能封装为命令行工具,便于批量处理。

通过不断优化与扩展,我们能够将这款工具打造得更加完善,成为每位数据分析师和审计人员的得力助手。

以上就是本篇文章的完整内容。如果你有任何问题或建议,欢迎留言讨论。希望这篇文章能为你带来启发和帮助!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

创客白泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值