使用Python實現(xiàn)Exce格式化批處理工具
1. 概述
在數(shù)據(jù)分析、報表整理、數(shù)據(jù)庫管理等工作中,數(shù)據(jù)清洗是不可或缺的一步。原始Excel數(shù)據(jù)常常存在格式不統(tǒng)一、空值、重復(fù)數(shù)據(jù)等問題,影響數(shù)據(jù)的準(zhǔn)確性和可用性。本篇文章將詳細(xì)介紹一款高效的Excel數(shù)據(jù)清洗工具,幫助您輕松處理雜亂數(shù)據(jù),提高數(shù)據(jù)質(zhì)量。
2. 功能使用

基本操作流程
使用本工具進(jìn)行數(shù)據(jù)清洗的操作流程如下:
- 打開文件:點(diǎn)擊"瀏覽"按鈕,或使用菜單欄"文件 > 打開"選擇需要處理的Excel文件。
- 數(shù)據(jù)清洗:在左側(cè)工具面板選擇需要的清洗操作,例如刪除重復(fù)行、格式化日期等。
- 預(yù)覽結(jié)果:右側(cè)區(qū)域?qū)崟r顯示數(shù)據(jù)變化,確保清洗效果符合預(yù)期。
- 保存文件:點(diǎn)擊"保存"按鈕,或使用菜單欄"文件 > 保存",將處理后的文件存儲。
3. 主要功能說明
1. 刪除重復(fù)行
作用:刪除數(shù)據(jù)表中完全相同的行,確保數(shù)據(jù)唯一性。
2. 刪除空行
作用:清除所有值均為空的行,避免無效數(shù)據(jù)干擾分析。
3. 去除空格
作用:移除文本字段中的首尾空格,防止隱藏字符影響計算。
4. 統(tǒng)一大小寫
作用:可選擇轉(zhuǎn)換為小寫、大寫或首字母大寫,以確保數(shù)據(jù)格式一致。
5. 數(shù)值格式化
作用:統(tǒng)一數(shù)值的小數(shù)位數(shù)(默認(rèn)保留2位),保證數(shù)據(jù)規(guī)范。
6. 日期格式化
作用:提供多種日期格式選項,避免因格式混亂導(dǎo)致的數(shù)據(jù)處理錯誤。
7. 刪除特殊字符
作用:去除文本中的標(biāo)點(diǎn)符號、特殊字符,適用于純文本數(shù)據(jù)處理。
8. 填充空值
作用:支持多種空值填充方式(平均值、中位數(shù)、眾數(shù)等),提高數(shù)據(jù)完整性。
4. 適用場景
4.1 典型使用場景
數(shù)據(jù)預(yù)處理:在進(jìn)行數(shù)據(jù)分析前,先對原始數(shù)據(jù)進(jìn)行標(biāo)準(zhǔn)化處理。
報表整理:整合不同來源的數(shù)據(jù),保證格式統(tǒng)一。
數(shù)據(jù)庫導(dǎo)入準(zhǔn)備:清理Excel數(shù)據(jù),使其符合數(shù)據(jù)庫字段要求。
數(shù)據(jù)遷移:在不同系統(tǒng)之間轉(zhuǎn)移數(shù)據(jù)時,保證格式一致。
日常辦公:快速整理雜亂的Excel表格,提高工作效率。
4.2 適用人群
數(shù)據(jù)分析師
財務(wù)/行政人員
市場研究人員
數(shù)據(jù)庫管理員
任何需要處理Excel數(shù)據(jù)的辦公人員
5. 注意事項
5.1 使用前注意事項
備份原始數(shù)據(jù):建議在處理前保存一份原始文件,以免數(shù)據(jù)丟失。
數(shù)據(jù)量限制:預(yù)覽功能僅顯示前100行,但清洗操作會應(yīng)用于所有數(shù)據(jù)。
文件格式:支持.xlsx和.xls格式,建議使用.xlsx以獲得更好的兼容性。
5.2 操作注意事項
撤銷功能:當(dāng)前版本不支持撤銷操作,請謹(jǐn)慎執(zhí)行。
特殊字符處理:刪除特殊字符可能影響某些編碼數(shù)據(jù),請?zhí)崆皺z查。
日期識別:自動識別日期列可能不夠準(zhǔn)確,建議手動確認(rèn)。
數(shù)值處理:非數(shù)值字段嘗試數(shù)值格式化可能導(dǎo)致錯誤。
6.系統(tǒng)要求
本工具依賴Python環(huán)境,使用以下庫來處理數(shù)據(jù):
- Python 3.6+
- pandas(數(shù)據(jù)處理核心庫)
- numpy(數(shù)值運(yùn)算支持)
- openpyxl(用于Excel文件操作)
- tkinter(用于GUI界面,Python自帶)
- matplotlib(可視化功能支持)
7.高級技巧
大型文件處理:對于超過10MB的文件,處理可能較慢,建議分批處理。
數(shù)據(jù)可視化:工具提供基本的可視化功能,適用于數(shù)值型數(shù)據(jù)分析。
快速數(shù)據(jù)分析:可查看基本統(tǒng)計信息,如均值、中位數(shù)、方差等,幫助快速了解數(shù)據(jù)分布。
8.相關(guān)源碼
import pandas as pd
import numpy as np
from tkinter import *
from tkinter import ttk, filedialog, messagebox
import os
from tkinter.scrolledtext import ScrolledText
import threading
from queue import Queue
import logging
from datetime import datetime
# 配置日志
logging.basicConfig(
filename='excel_cleaner.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# 模擬 DataHandler, ColumnSelector, ParameterDialog 類
class DataHandler:
def __init__(self):
self.df = None
self.operation_history = []
self.redo_history = []
def load_excel(self, file_path):
self.df = pd.read_excel(file_path)
return self.df
def save_excel(self, file_path):
self.df.to_excel(file_path, index=False)
def get_statistics(self):
return {
'row_count': len(self.df),
'column_count': len(self.df.columns)
}
def get_column_types(self):
return self.df.dtypes
def remove_spaces(self, columns):
for col in columns:
if self.df[col].dtype == object:
self.df[col] = self.df[col].str.strip()
return self.df
def normalize_case(self, case_type, columns):
for col in columns:
if self.df[col].dtype == object:
if case_type == 'lower':
self.df[col] = self.df[col].str.lower()
elif case_type == 'upper':
self.df[col] = self.df[col].str.upper()
elif case_type == 'title':
self.df[col] = self.df[col].str.title()
return self.df
def format_numbers(self, decimal_places, columns):
for col in columns:
if pd.api.types.is_numeric_dtype(self.df[col]):
self.df[col] = self.df[col].round(decimal_places)
return self.df
def format_dates(self, date_format, columns):
for col in columns:
if pd.api.types.is_datetime64_any_dtype(self.df[col]):
self.df[col] = self.df[col].dt.strftime(date_format)
return self.df
def remove_special_chars(self, pattern, columns):
for col in columns:
if self.df[col].dtype == object:
self.df[col] = self.df[col].str.replace(pattern, '', regex=True)
return self.df
def fill_empty_values(self, method, value=None, columns=None):
if columns is None:
columns = self.df.columns
for col in columns:
if method == 'value':
self.df[col].fillna(value, inplace=True)
elif method == 'mean':
self.df[col].fillna(self.df[col].mean(), inplace=True)
elif method == 'median':
self.df[col].fillna(self.df[col].median(), inplace=True)
elif method == 'mode':
self.df[col].fillna(self.df[col].mode()[0], inplace=True)
elif method == 'ffill':
self.df[col].fillna(method='ffill', inplace=True)
elif method == 'bfill':
self.df[col].fillna(method='bfill', inplace=True)
return self.df
class ColumnSelector:
def __init__(self, parent, columns, column_types, title, callback):
self.callback = callback
self.selected_columns = []
self.window = Toplevel(parent)
self.window.title(title)
ttk.Label(self.window, text="選擇列:").pack(pady=10)
self.listbox = Listbox(self.window, selectmode=MULTIPLE)
for col in columns:
self.listbox.insert(END, col)
self.listbox.pack(fill=BOTH, expand=True, padx=10, pady=10)
button_frame = ttk.Frame(self.window)
button_frame.pack(fill=X, padx=10, pady=10)
ttk.Button(button_frame, text="確定", command=self.on_confirm).pack(side=LEFT, padx=10)
ttk.Button(button_frame, text="取消", command=self.window.destroy).pack(side=LEFT)
def on_confirm(self):
self.selected_columns = [self.listbox.get(i) for i in self.listbox.curselection()]
self.callback(self.selected_columns)
self.window.destroy()
class ParameterDialog:
def __init__(self, parent, params, title, callback):
self.callback = callback
self.params = params
self.values = {}
self.window = Toplevel(parent)
self.window.title(title)
for param_name, param_info in params.items():
ttk.Label(self.window, text=param_info['label']).pack(pady=5)
if param_info['type'] == 'choice':
var = StringVar()
var.set(param_info['default'])
ttk.Combobox(self.window, textvariable=var, values=param_info['choices']).pack(fill=X, padx=10)
self.values[param_name] = var
elif param_info['type'] == 'int':
var = IntVar()
var.set(param_info['default'])
ttk.Spinbox(self.window, from_=param_info['min'], to=param_info['max'], textvariable=var).pack(fill=X, padx=10)
self.values[param_name] = var
elif param_info['type'] == 'str':
var = StringVar()
var.set(param_info['default'])
ttk.Entry(self.window, textvariable=var).pack(fill=X, padx=10)
self.values[param_name] = var
button_frame = ttk.Frame(self.window)
button_frame.pack(fill=X, padx=10, pady=10)
ttk.Button(button_frame, text="確定", command=self.on_confirm).pack(side=LEFT, padx=10)
ttk.Button(button_frame, text="取消", command=self.window.destroy).pack(side=LEFT)
def on_confirm(self):
result = {param_name: var.get() for param_name, var in self.values.items()}
self.callback(result)
self.window.destroy()
class ExcelCleaner:
def __init__(self):
self.window = Tk()
self.window.title("Excel數(shù)據(jù)格式化批處理工具")
self.window.geometry("1000x800")
self.window.configure(bg='#f0f0f0')
# 初始化數(shù)據(jù)處理器
self.data_handler = DataHandler()
self.processing_queue = Queue()
# 設(shè)置樣式
self.setup_styles()
# 創(chuàng)建菜單欄
self.create_menu()
# 創(chuàng)建主框架
main_frame = ttk.Frame(self.window)
main_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)
# 左側(cè)工具面板
left_panel = ttk.LabelFrame(main_frame, text="工具面板", padding=10)
left_panel.pack(side=LEFT, fill=Y, padx=5, pady=5)
# 文件操作區(qū)域
self.create_file_frame(left_panel)
# 清洗操作區(qū)域
self.create_clean_frame(left_panel)
# 右側(cè)主要內(nèi)容區(qū)域
right_panel = ttk.Frame(main_frame)
right_panel.pack(side=LEFT, fill=BOTH, expand=True, padx=5)
# 預(yù)覽區(qū)域
self.create_preview_frame(right_panel)
# 狀態(tài)欄
self.create_status_bar()
# 進(jìn)度條
self.create_progress_bar()
# 綁定快捷鍵
self.bind_shortcuts()
def setup_styles(self):
style = ttk.Style()
style.theme_use('clam')
# 配置按鈕樣式
style.configure(
"Tool.TButton",
padding=5,
font=('微軟雅黑', 10),
background='#e1e1e1',
foreground='#333333'
)
# 配置標(biāo)簽樣式
style.configure(
"Title.TLabel",
font=('微軟雅黑', 12, 'bold'),
background='#f0f0f0',
foreground='#333333'
)
# 配置框架樣式
style.configure(
"Card.TLabelframe",
background='#ffffff',
padding=10
)
# 配置樹形視圖樣式
style.configure(
"Preview.Treeview",
font=('微軟雅黑', 10),
rowheight=25
)
# 配置進(jìn)度條樣式
style.configure(
"Progress.Horizontal.TProgressbar",
troughcolor='#f0f0f0',
background='#4CAF50',
thickness=10
)
def create_progress_bar(self):
self.progress_var = DoubleVar()
self.progress_bar = ttk.Progressbar(
self.window,
style="Progress.Horizontal.TProgressbar",
variable=self.progress_var,
maximum=100
)
self.progress_bar.pack(fill=X, padx=5, pady=2)
def bind_shortcuts(self):
self.window.bind('<Control-o>', lambda e: self.select_file())
self.window.bind('<Control-s>', lambda e: self.save_file())
self.window.bind('<Control-z>', lambda e: self.undo())
self.window.bind('<Control-y>', lambda e: self.redo())
self.window.bind('<F1>', lambda e: self.show_help())
def process_in_background(self, func, *args, **kwargs):
"""在后臺線程中處理耗時操作"""
def wrapper():
try:
self.progress_var.set(0)
self.status_var.set("正在處理...")
self.window.update()
# 執(zhí)行操作
result = func(*args, **kwargs)
# 更新UI
self.window.after(0, self.update_ui_after_processing, result)
except Exception as e:
logging.error(f"處理錯誤: {str(e)}")
self.window.after(0, self.show_error, str(e))
finally:
self.window.after(0, self.progress_var.set, 100)
self.window.after(0, self.status_var.set, "處理完成")
# 啟動后臺線程
thread = threading.Thread(target=wrapper)
thread.daemon = True
thread.start()
def update_ui_after_processing(self, result):
"""處理完成后更新UI"""
if isinstance(result, tuple):
self.data_handler.df = result[0]
if len(result) > 1:
removed_rows = result[1]
self.status_var.set(f"已刪除 {removed_rows} 行數(shù)據(jù)")
elif isinstance(result, pd.DataFrame):
self.data_handler.df = result
if result is not None:
self.update_preview()
def show_error(self, error_msg):
"""顯示錯誤消息"""
messagebox.showerror("錯誤", f"處理過程中出現(xiàn)錯誤:{error_msg}")
self.status_var.set("處理失敗")
def select_file(self):
file_path = filedialog.askopenfilename(
filetypes=[("Excel files", "*.xlsx *.xls")]
)
if file_path:
self.process_in_background(self.data_handler.load_excel, file_path)
def save_file(self):
if self.data_handler.df is not None:
file_path = filedialog.asksaveasfilename(
defaultextension=".xlsx",
filetypes=[("Excel files", "*.xlsx")]
)
if file_path:
self.process_in_background(self.data_handler.save_excel, file_path)
def undo(self):
"""撤銷上一步操作"""
if self.data_handler.operation_history:
last_operation = self.data_handler.operation_history.pop()
self.data_handler.df = last_operation['previous_state'].copy()
self.update_preview()
self.status_var.set("已撤銷上一步操作")
def redo(self):
"""重做上一步操作"""
if hasattr(self.data_handler, 'redo_history') and self.data_handler.redo_history:
last_operation = self.data_handler.redo_history.pop()
self.data_handler.df = last_operation['next_state'].copy()
self.data_handler.operation_history.append(last_operation)
self.update_preview()
self.status_var.set("已重做上一步操作")
def add_operation_to_history(self, operation_name, previous_state, next_state):
"""添加操作到歷史記錄"""
self.data_handler.operation_history.append({
'name': operation_name,
'previous_state': previous_state.copy(),
'next_state': next_state.copy()
})
# 清空重做歷史
if hasattr(self.data_handler, 'redo_history'):
self.data_handler.redo_history.clear()
def remove_duplicates(self):
if self.data_handler.df is not None:
previous_state = self.data_handler.df.copy()
self.data_handler.df = self.data_handler.df.drop_duplicates()
removed_rows = len(previous_state) - len(self.data_handler.df)
self.add_operation_to_history("刪除重復(fù)行", previous_state, self.data_handler.df.copy())
self.update_preview()
self.status_var.set(f"已刪除 {removed_rows} 行重復(fù)數(shù)據(jù)")
def remove_empty_rows(self):
if self.data_handler.df is not None:
previous_state = self.data_handler.df.copy()
self.data_handler.df = self.data_handler.df.dropna(how='all')
removed_rows = len(previous_state) - len(self.data_handler.df)
self.add_operation_to_history("刪除空行", previous_state, self.data_handler.df.copy())
self.update_preview()
self.status_var.set(f"已刪除 {removed_rows} 行空數(shù)據(jù)")
def remove_spaces(self):
if self.data_handler.df is not None:
def on_columns_selected(columns):
self.process_in_background(
self.data_handler.remove_spaces,
columns=columns
)
ColumnSelector(
self.window,
list(self.data_handler.df.columns),
self.data_handler.get_column_types(),
title="選擇要去除空格的列",
callback=on_columns_selected
)
def normalize_case(self):
if self.data_handler.df is not None:
def on_params_set(params):
def on_columns_selected(columns):
self.process_in_background(
self.data_handler.normalize_case,
case_type=params["case_type"],
columns=columns
)
ColumnSelector(
self.window,
list(self.data_handler.df.columns),
self.data_handler.get_column_types(),
title="選擇要統(tǒng)一大小寫的列",
callback=on_columns_selected
)
params = {
"case_type": {
"type": "choice",
"label": "大小寫格式",
"default": "lower",
"choices": ["lower", "upper", "title"]
}
}
ParameterDialog(
self.window,
params,
title="選擇大小寫格式",
callback=on_params_set
)
def format_numbers(self):
if self.data_handler.df is not None:
def on_params_set(params):
def on_columns_selected(columns):
self.process_in_background(
self.data_handler.format_numbers,
decimal_places=params["decimal_places"],
columns=columns
)
ColumnSelector(
self.window,
list(self.data_handler.df.columns),
self.data_handler.get_column_types(),
title="選擇要格式化的數(shù)值列",
callback=on_columns_selected
)
params = {
"decimal_places": {
"type": "int",
"label": "小數(shù)位數(shù)",
"default": 2,
"min": 0,
"max": 10
}
}
ParameterDialog(
self.window,
params,
title="設(shè)置數(shù)值格式",
callback=on_params_set
)
def format_dates(self):
if self.data_handler.df is not None:
def on_params_set(params):
def on_columns_selected(columns):
self.process_in_background(
self.data_handler.format_dates,
date_format=params["date_format"],
columns=columns
)
ColumnSelector(
self.window,
list(self.data_handler.df.columns),
self.data_handler.get_column_types(),
title="選擇要格式化的日期列",
callback=on_columns_selected
)
params = {
"date_format": {
"type": "choice",
"label": "日期格式",
"default": "%Y-%m-%d",
"choices": [
"%Y-%m-%d",
"%Y/%m/%d",
"%d-%m-%Y",
"%m/%d/%Y"
]
}
}
ParameterDialog(
self.window,
params,
title="選擇日期格式",
callback=on_params_set
)
def remove_special_chars(self):
if self.data_handler.df is not None:
def on_params_set(params):
def on_columns_selected(columns):
self.process_in_background(
self.data_handler.remove_special_chars,
pattern=params["pattern"],
columns=columns
)
ColumnSelector(
self.window,
list(self.data_handler.df.columns),
self.data_handler.get_column_types(),
title="選擇要處理的列",
callback=on_columns_selected
)
params = {
"pattern": {
"type": "str",
"label": "正則表達(dá)式",
"default": r'[^\w\s]'
}
}
ParameterDialog(
self.window,
params,
title="設(shè)置正則表達(dá)式",
callback=on_params_set
)
def fill_empty_values(self):
if self.data_handler.df is not None:
def on_params_set(params):
def on_columns_selected(columns):
value = params.get("value")
if params["method"] == "value" and value:
try:
# 嘗試轉(zhuǎn)換為數(shù)值
value = float(value) if '.' in value else int(value)
except ValueError:
pass
self.process_in_background(
self.data_handler.fill_empty_values,
method=params["method"],
value=value,
columns=columns
)
ColumnSelector(
self.window,
list(self.data_handler.df.columns),
self.data_handler.get_column_types(),
title="選擇要填充的列",
callback=on_columns_selected
)
params = {
"method": {
"type": "choice",
"label": "填充方式",
"default": "mean",
"choices": ["mean", "median", "mode", "ffill", "bfill", "value"]
},
"value": {
"type": "str",
"label": "填充值",
"default": ""
}
}
ParameterDialog(
self.window,
params,
title="選擇填充方式",
callback=on_params_set
)
def analyze_data(self):
if self.data_handler.df is not None:
analysis_window = Toplevel(self.window)
analysis_window.title("數(shù)據(jù)分析")
analysis_window.geometry("600x400")
stats_text = ScrolledText(analysis_window, wrap=WORD, width=70, height=20)
stats_text.pack(padx=10, pady=10, fill=BOTH, expand=True)
stats = []
stats.append("數(shù)據(jù)基本信息:")
stats.append("-" * 50)
stats.append(f"總行數(shù):{len(self.data_handler.df)}")
stats.append(f"總列數(shù):{len(self.data_handler.df.columns)}")
stats.append("\n數(shù)值列統(tǒng)計:")
stats.append("-" * 50)
numeric_stats = self.data_handler.df.describe()
stats.append(str(numeric_stats))
stats.append("\n空值統(tǒng)計:")
stats.append("-" * 50)
null_counts = self.data_handler.df.isnull().sum()
stats.append(str(null_counts))
stats_text.insert(END, "\n".join(stats))
stats_text.configure(state='disabled')
def visualize_data(self):
if self.data_handler.df is not None:
try:
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
# 設(shè)置中文字體
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標(biāo)簽
plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負(fù)號
viz_window = Toplevel(self.window)
viz_window.title("數(shù)據(jù)可視化")
viz_window.geometry("800x600")
options_frame = ttk.Frame(viz_window)
options_frame.pack(fill=X, padx=10, pady=5)
ttk.Label(options_frame, text="圖表類型:").pack(side=LEFT)
chart_type = StringVar(value="bar")
ttk.Radiobutton(options_frame, text="柱狀圖", variable=chart_type, value="bar").pack(side=LEFT)
ttk.Radiobutton(options_frame, text="折線圖", variable=chart_type, value="line").pack(side=LEFT)
ttk.Radiobutton(options_frame, text="散點(diǎn)圖", variable=chart_type, value="scatter").pack(side=LEFT)
# 添加列選擇
ttk.Label(options_frame, text=" 選擇列:").pack(side=LEFT)
column_var = StringVar()
numeric_columns = list(self.data_handler.df.select_dtypes(include=[np.number]).columns)
if not numeric_columns:
messagebox.showwarning("警告", "沒有可用的數(shù)值列進(jìn)行可視化")
return
column_combo = ttk.Combobox(options_frame, textvariable=column_var, values=numeric_columns)
column_combo.pack(side=LEFT)
column_combo.set(numeric_columns[0])
fig, ax = plt.subplots(figsize=(10, 6))
canvas = FigureCanvasTkAgg(fig, master=viz_window)
canvas.get_tk_widget().pack(fill=BOTH, expand=True, padx=10, pady=5)
def update_chart():
try:
ax.clear()
chart_style = chart_type.get()
selected_column = column_var.get()
if not selected_column:
messagebox.showwarning("警告", "請選擇要可視化的列")
return
if chart_style == "bar":
self.data_handler.df[selected_column].plot(kind='bar', ax=ax)
ax.set_title(f"{selected_column} 柱狀圖")
elif chart_style == "line":
self.data_handler.df[selected_column].plot(kind='line', ax=ax)
ax.set_title(f"{selected_column} 折線圖")
else: # scatter
if len(numeric_columns) >= 2:
x_col = selected_column
y_col = next(col for col in numeric_columns if col != x_col)
self.data_handler.df.plot(kind='scatter', x=x_col, y=y_col, ax=ax)
ax.set_title(f"{x_col} vs {y_col} 散點(diǎn)圖")
else:
messagebox.showwarning("警告", "需要至少兩個數(shù)值列才能創(chuàng)建散點(diǎn)圖")
return
plt.tight_layout()
canvas.draw()
except Exception as e:
messagebox.showerror("錯誤", f"繪圖時發(fā)生錯誤:{str(e)}")
ttk.Button(options_frame, text="更新圖表", command=update_chart).pack(side=LEFT, padx=10)
update_chart()
except ImportError:
messagebox.showwarning("警告", "請安裝matplotlib庫以使用可視化功能")
def run(self):
self.window.mainloop()
def create_menu(self):
menubar = Menu(self.window)
self.window.config(menu=menubar)
# 文件菜單
file_menu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="文件", menu=file_menu)
file_menu.add_command(label="打開 (Ctrl+O)", command=self.select_file)
file_menu.add_command(label="保存 (Ctrl+S)", command=self.save_file)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.window.quit)
# 編輯菜單
edit_menu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="編輯", menu=edit_menu)
edit_menu.add_command(label="撤銷 (Ctrl+Z)", command=self.undo)
edit_menu.add_command(label="重做 (Ctrl+Y)", command=self.redo)
# 視圖菜單
view_menu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="視圖", menu=view_menu)
view_menu.add_checkbutton(label="顯示狀態(tài)欄", command=self.toggle_status_bar)
# 幫助菜單
help_menu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="幫助", menu=help_menu)
help_menu.add_command(label="使用說明 (F1)", command=self.show_help)
help_menu.add_command(label="關(guān)于", command=self.show_about)
def create_file_frame(self, parent):
file_frame = ttk.LabelFrame(parent, text="文件操作", padding=10, style="Card.TLabelframe")
file_frame.pack(fill=X, pady=(0, 10))
# 文件選擇
self.file_path = StringVar()
ttk.Label(file_frame, text="Excel文件:", style="Title.TLabel").pack(anchor=W)
ttk.Entry(file_frame, textvariable=self.file_path, width=30).pack(fill=X, pady=5)
button_frame = ttk.Frame(file_frame)
button_frame.pack(fill=X)
ttk.Button(button_frame, text="瀏覽", command=self.select_file, style="Tool.TButton").pack(side=LEFT, padx=2)
ttk.Button(button_frame, text="保存", command=self.save_file, style="Tool.TButton").pack(side=LEFT, padx=2)
def create_clean_frame(self, parent):
clean_frame = ttk.LabelFrame(parent, text="數(shù)據(jù)清洗", padding=10, style="Card.TLabelframe")
clean_frame.pack(fill=BOTH, expand=True)
operations = [
("刪除重復(fù)行", self.remove_duplicates),
("刪除空行", self.remove_empty_rows),
("去除空格", self.remove_spaces),
("統(tǒng)一大小寫", self.normalize_case),
("數(shù)值格式化", self.format_numbers),
("日期格式化", self.format_dates),
("刪除特殊字符", self.remove_special_chars),
("填充空值", self.fill_empty_values),
("數(shù)據(jù)分析", self.analyze_data),
("數(shù)據(jù)可視化", self.visualize_data)
]
for text, command in operations:
btn = ttk.Button(clean_frame, text=text, command=command, style="Tool.TButton")
btn.pack(fill=X, pady=2)
def create_preview_frame(self, parent):
preview_frame = ttk.LabelFrame(parent, text="數(shù)據(jù)預(yù)覽", padding=10, style="Card.TLabelframe")
preview_frame.pack(fill=BOTH, expand=True)
# 創(chuàng)建帶滾動條的樹形視圖
tree_frame = ttk.Frame(preview_frame)
tree_frame.pack(fill=BOTH, expand=True)
# 創(chuàng)建水平滾動條
h_scrollbar = ttk.Scrollbar(tree_frame, orient=HORIZONTAL)
h_scrollbar.pack(side=BOTTOM, fill=X)
# 創(chuàng)建垂直滾動條
v_scrollbar = ttk.Scrollbar(tree_frame)
v_scrollbar.pack(side=RIGHT, fill=Y)
# 創(chuàng)建樹形視圖
self.tree = ttk.Treeview(
tree_frame,
style="Preview.Treeview",
xscrollcommand=h_scrollbar.set,
yscrollcommand=v_scrollbar.set
)
self.tree.pack(fill=BOTH, expand=True)
# 配置滾動條
h_scrollbar.config(command=self.tree.xview)
v_scrollbar.config(command=self.tree.yview)
# 創(chuàng)建統(tǒng)計信息面板
stats_frame = ttk.Frame(preview_frame)
stats_frame.pack(fill=X, pady=(10, 0))
self.stats_label = ttk.Label(stats_frame, text="", style="Title.TLabel")
self.stats_label.pack(side=LEFT)
def create_status_bar(self):
self.status_var = StringVar()
self.status_bar = ttk.Label(
self.window,
textvariable=self.status_var,
relief=SUNKEN,
padding=(5, 2)
)
self.status_bar.pack(fill=X, padx=5, pady=2)
def toggle_status_bar(self):
# 切換狀態(tài)欄顯示/隱藏
if self.status_bar.winfo_viewable():
self.status_bar.pack_forget()
else:
self.status_bar.pack(fill=X, padx=5, pady=2)
def update_preview(self):
# 清空現(xiàn)有數(shù)據(jù)
for item in self.tree.get_children():
self.tree.delete(item)
if self.data_handler.df is not None:
df = self.data_handler.df
# 設(shè)置列
self.tree["columns"] = list(df.columns)
self.tree["show"] = "headings"
for column in df.columns:
self.tree.heading(column, text=column)
self.tree.column(column, width=100, anchor='center')
# 添加數(shù)據(jù)(僅顯示前100行)
for i, row in df.head(100).iterrows():
self.tree.insert("", END, values=list(row))
# 更新統(tǒng)計標(biāo)簽
stats = self.data_handler.get_statistics()
self.stats_label.config(
text=f"行數(shù): {stats['row_count']} | 列數(shù): {stats['column_count']}"
)
# 更新狀態(tài)欄
self.status_var.set(
f"當(dāng)前加載文件: {os.path.basename(self.file_path.get())} | "
f"行數(shù): {stats['row_count']} | 列數(shù): {stats['column_count']}"
)
else:
self.status_var.set("請先加載文件")
def show_help(self):
help_text = """
Excel數(shù)據(jù)格式化批處理工具使用說明:
1. 文件操作:
- 點(diǎn)擊"瀏覽"選擇Excel文件
- 點(diǎn)擊"保存"保存處理后的文件
2. 數(shù)據(jù)清洗功能:
- 刪除重復(fù)行:刪除完全重復(fù)的數(shù)據(jù)行
- 刪除空行:刪除全為空值的行
- 去除空格:刪除文本中的首尾空格
- 統(tǒng)一大小寫:統(tǒng)一文本的大小寫格式
- 數(shù)值格式化:統(tǒng)一數(shù)值的小數(shù)位數(shù)
- 日期格式化:統(tǒng)一日期的顯示格式
- 刪除特殊字符:清除文本中的特殊字符
- 填充空值:使用多種方式填充缺失值
3. 數(shù)據(jù)分析:
- 查看基本統(tǒng)計信息
- 空值分析
- 數(shù)據(jù)分布可視化
4. 快捷鍵:
- Ctrl+O:打開文件
- Ctrl+S:保存文件
- Ctrl+Z:撤銷
- Ctrl+Y:重做
- F1:顯示幫助
"""
help_window = Toplevel(self.window)
help_window.title("使用說明")
help_window.geometry("600x400")
help_text_widget = ScrolledText(help_window, wrap=WORD, width=70, height=20)
help_text_widget.pack(padx=10, pady=10, fill=BOTH, expand=True)
help_text_widget.insert(END, help_text)
help_text_widget.configure(state='disabled')
def show_about(self):
about_text = """
Excel數(shù)據(jù)格式化批處理工具
功能特點(diǎn):
- 支持多種數(shù)據(jù)清洗操作
- 實時預(yù)覽數(shù)據(jù)變化
- 數(shù)據(jù)分析和可視化
- 后臺處理,避免卡頓
- 撤銷/重做功能
- 友好的圖形界面
"""
messagebox.showinfo("關(guān)于", about_text)
if __name__ == "__main__":
try:
app = ExcelCleaner()
app.run()
except Exception as e:
logging.error(f"程序運(yùn)行錯誤: {str(e)}")
messagebox.showerror("錯誤", f"程序運(yùn)行出錯:{str(e)}")
# 優(yōu)化的代碼,運(yùn)行即出現(xiàn)GUI界面
9.總結(jié)
Excel格式化批處理工具是數(shù)據(jù)分析和日常辦公中不可或缺的步驟。本工具提供了一系列高效的功能,幫助用戶快速整理數(shù)據(jù),提升數(shù)據(jù)質(zhì)量。無論是數(shù)據(jù)分析師還是日常辦公人員,都可以借助該工具提高工作效率,減少數(shù)據(jù)整理的繁瑣工作。希望本篇指南能幫助大家更好地利用工具,提高數(shù)據(jù)處理能力!
以上就是使用Python實現(xiàn)Exce格式化批處理工具的詳細(xì)內(nèi)容,更多關(guān)于Python Exce格式化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python 根據(jù)日志級別打印不同顏色的日志的方法示例
這篇文章主要介紹了Python 根據(jù)日志級別打印不同顏色的日志的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Django import export實現(xiàn)數(shù)據(jù)庫導(dǎo)入導(dǎo)出方式
這篇文章主要介紹了Django import export實現(xiàn)數(shù)據(jù)庫導(dǎo)入導(dǎo)出方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04
深入理解Python虛擬機(jī)中列表(list)的實現(xiàn)原理及源碼剖析
在本篇文章當(dāng)中主要給大家介紹?cpython?虛擬機(jī)當(dāng)中針對列表的實現(xiàn),在?Python?中,List?是一種非常常用的數(shù)據(jù)類型,可以存儲任何類型的數(shù)據(jù),并且支持各種操作,如添加、刪除、查找、切片等,在本篇文章當(dāng)中將深入去分析這一點(diǎn)是如何實現(xiàn)的2023-03-03
使用python切片實現(xiàn)二維數(shù)組復(fù)制示例
今天小編就為大家分享一篇使用python切片實現(xiàn)二維數(shù)組復(fù)制示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11

