Exce格式化批处理工具详解:高效处理,让数据更干净!
Exce格式化批处理工具详解:高效处理,让数据更干净!
1. 概述
在数据分析、报表整理、数据库管理等工作中,数据清洗是不可或缺的一步。原始Excel数据常常存在格式不统一、空值、重复数据等问题,影响数据的准确性和可用性。本篇文章将详细介绍一款高效的Excel数据清洗工具,帮助您轻松处理杂乱数据,提高数据质量。
2. 功能使用
基本操作流程
使用本工具进行数据清洗的操作流程如下:
- 打开文件:点击"浏览"按钮,或使用菜单栏"文件 > 打开"选择需要处理的Excel文件。
- 数据清洗:在左侧工具面板选择需要的清洗操作,例如删除重复行、格式化日期等。
- 预览结果:右侧区域实时显示数据变化,确保清洗效果符合预期。
- 保存文件:点击"保存"按钮,或使用菜单栏"文件 > 保存",将处理后的文件存储。
3. 主要功能说明
1. 删除重复行
作用:删除数据表中完全相同的行,确保数据唯一性。
2. 删除空行
作用:清除所有值均为空的行,避免无效数据干扰分析。
3. 去除空格
作用:移除文本字段中的首尾空格,防止隐藏字符影响计算。
4. 统一大小写
作用:可选择转换为小写、大写或首字母大写,以确保数据格式一致。
5. 数值格式化
作用:统一数值的小数位数(默认保留2位),保证数据规范。
6. 日期格式化
作用:提供多种日期格式选项,避免因格式混乱导致的数据处理错误。
7. 删除特殊字符
作用:去除文本中的标点符号、特殊字符,适用于纯文本数据处理。
8. 填充空值
作用:支持多种空值填充方式(平均值、中位数、众数等),提高数据完整性。
4. 适用场景
4.1 典型使用场景
- 数据预处理:在进行数据分析前,先对原始数据进行标准化处理。
- 报表整理:整合不同来源的数据,保证格式统一。
- 数据库导入准备:清理Excel数据,使其符合数据库字段要求。
- 数据迁移:在不同系统之间转移数据时,保证格式一致。
- 日常办公:快速整理杂乱的Excel表格,提高工作效率。
4.2 适用人群
- 数据分析师
- 财务/行政人员
- 市场研究人员
- 数据库管理员
- 任何需要处理Excel数据的办公人员
5. 注意事项
5. 1 使用前注意事项
- 备份原始数据:建议在处理前保存一份原始文件,以免数据丢失。
- 数据量限制:预览功能仅显示前100行,但清洗操作会应用于所有数据。
- 文件格式:支持.xlsx和.xls格式,建议使用.xlsx以获得更好的兼容性。
5.2 操作注意事项
- 撤销功能:当前版本不支持撤销操作,请谨慎执行。
- 特殊字符处理:删除特殊字符可能影响某些编码数据,请提前检查。
- 日期识别:自动识别日期列可能不够准确,建议手动确认。
- 数值处理:非数值字段尝试数值格式化可能导致错误。
6.系统要求
本工具依赖Python环境,使用以下库来处理数据:
- Python 3.6+
- pandas(数据处理核心库)
- numpy(数值运算支持)
- openpyxl(用于Excel文件操作)
- tkinter(用于GUI界面,Python自带)
- matplotlib(可视化功能支持)
7.高级技巧
- 大型文件处理:对于超过10MB的文件,处理可能较慢,建议分批处理。
- 数据可视化:工具提供基本的可视化功能,适用于数值型数据分析。
- 快速数据分析:可查看基本统计信息,如均值、中位数、方差等,帮助快速了解数据分布。
8.总结
Excel格式化批处理工具是数据分析和日常办公中不可或缺的步骤。本工具提供了一系列高效的功能,帮助用户快速整理数据,提升数据质量。无论是数据分析师还是日常办公人员,都可以借助该工具提高工作效率,减少数据整理的繁琐工作。希望本篇指南能帮助大家更好地利用工具,提高数据处理能力!
9.相关源码:
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 = Noneself.operation_history = []self.redo_history = []def load_excel(self, file_path):self.df = pd.read_excel(file_path)return self.dfdef 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.dtypesdef remove_spaces(self, columns):for col in columns:if self.df[col].dtype == object:self.df[col] = self.df[col].str.strip()return self.dfdef 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.dfdef 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.dfdef 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.dfdef 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.dfdef fill_empty_values(self, method, value=None, columns=None):if columns is None:columns = self.df.columnsfor 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.dfclass ColumnSelector:def __init__(self, parent, columns, column_types, title, callback):self.callback = callbackself.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 = callbackself.params = paramsself.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] = varelif 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] = varelif 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] = varbutton_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数据格式化批处理工具")self.window.geometry("1000x800")self.window.configure(bg='#f0f0f0')# 初始化数据处理器self.data_handler = DataHandler()self.processing_queue = Queue()# 设置样式self.setup_styles()# 创建菜单栏self.create_menu()# 创建主框架main_frame = ttk.Frame(self.window)main_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)# 左侧工具面板left_panel = ttk.LabelFrame(main_frame, text="工具面板", padding=10)left_panel.pack(side=LEFT, fill=Y, padx=5, pady=5)# 文件操作区域self.create_file_frame(left_panel)# 清洗操作区域self.create_clean_frame(left_panel)# 右侧主要内容区域right_panel = ttk.Frame(main_frame)right_panel.pack(side=LEFT, fill=BOTH, expand=True, padx=5)# 预览区域self.create_preview_frame(right_panel)# 状态栏self.create_status_bar()# 进度条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')# 配置标签样式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)# 配置进度条样式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()# 执行操作result = func(*args, **kwargs)# 更新UIself.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 = Truethread.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} 行数据")elif isinstance(result, pd.DataFrame):self.data_handler.df = resultif result is not None:self.update_preview()def show_error(self, error_msg):"""显示错误消息"""messagebox.showerror("错误", f"处理过程中出现错误:{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("删除重复行", previous_state, self.data_handler.df.copy())self.update_preview()self.status_var.set(f"已删除 {removed_rows} 行重复数据")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} 行空数据")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="选择要统一大小写的列",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="选择要格式化的数值列",callback=on_columns_selected)params = {"decimal_places": {"type": "int","label": "小数位数","default": 2,"min": 0,"max": 10}}ParameterDialog(self.window,params,title="设置数值格式",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": "正则表达式","default": r'[^\w\s]'}}ParameterDialog(self.window,params,title="设置正则表达式",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:# 尝试转换为数值value = float(value) if '.' in value else int(value)except ValueError:passself.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("数据分析")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("数据基本信息:")stats.append("-" * 50)stats.append(f"总行数:{len(self.data_handler.df)}")stats.append(f"总列数:{len(self.data_handler.df.columns)}")stats.append("\n数值列统计:")stats.append("-" * 50)numeric_stats = self.data_handler.df.describe()stats.append(str(numeric_stats))stats.append("\n空值统计:")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 pltfrom matplotlib.backends.backend_tkagg import FigureCanvasTkAgg# 设置中文字体plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号viz_window = Toplevel(self.window)viz_window.title("数据可视化")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="散点图", 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("警告", "没有可用的数值列进行可视化")returncolumn_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("警告", "请选择要可视化的列")returnif 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: # scatterif len(numeric_columns) >= 2:x_col = selected_columny_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} 散点图")else:messagebox.showwarning("警告", "需要至少两个数值列才能创建散点图")returnplt.tight_layout()canvas.draw()except Exception as e:messagebox.showerror("错误", 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="显示状态栏", 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="关于", 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="数据清洗", padding=10, style="Card.TLabelframe")clean_frame.pack(fill=BOTH, expand=True)operations = [("删除重复行", self.remove_duplicates),("删除空行", self.remove_empty_rows),("去除空格", self.remove_spaces),("统一大小写", self.normalize_case),("数值格式化", self.format_numbers),("日期格式化", self.format_dates),("删除特殊字符", self.remove_special_chars),("填充空值", self.fill_empty_values),("数据分析", self.analyze_data),("数据可视化", 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="数据预览", padding=10, style="Card.TLabelframe")preview_frame.pack(fill=BOTH, expand=True)# 创建带滚动条的树形视图tree_frame = ttk.Frame(preview_frame)tree_frame.pack(fill=BOTH, expand=True)# 创建水平滚动条h_scrollbar = ttk.Scrollbar(tree_frame, orient=HORIZONTAL)h_scrollbar.pack(side=BOTTOM, fill=X)# 创建垂直滚动条v_scrollbar = ttk.Scrollbar(tree_frame)v_scrollbar.pack(side=RIGHT, fill=Y)# 创建树形视图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)# 创建统计信息面板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):# 切换状态栏显示/隐藏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):# 清空现有数据for item in self.tree.get_children():self.tree.delete(item)if self.data_handler.df is not None:df = self.data_handler.df# 设置列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')# 添加数据(仅显示前100行)for i, row in df.head(100).iterrows():self.tree.insert("", END, values=list(row))# 更新统计标签stats = self.data_handler.get_statistics()self.stats_label.config(text=f"行数: {stats['row_count']} | 列数: {stats['column_count']}")# 更新状态栏self.status_var.set(f"当前加载文件: {os.path.basename(self.file_path.get())} | "f"行数: {stats['row_count']} | 列数: {stats['column_count']}")else:self.status_var.set("请先加载文件")def show_help(self):help_text = """
Excel数据格式化批处理工具使用说明:1. 文件操作:- 点击"浏览"选择Excel文件- 点击"保存"保存处理后的文件2. 数据清洗功能:- 删除重复行:删除完全重复的数据行- 删除空行:删除全为空值的行- 去除空格:删除文本中的首尾空格- 统一大小写:统一文本的大小写格式- 数值格式化:统一数值的小数位数- 日期格式化:统一日期的显示格式- 删除特殊字符:清除文本中的特殊字符- 填充空值:使用多种方式填充缺失值3. 数据分析:- 查看基本统计信息- 空值分析- 数据分布可视化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数据格式化批处理工具功能特点:
- 支持多种数据清洗操作
- 实时预览数据变化
- 数据分析和可视化
- 后台处理,避免卡顿
- 撤销/重做功能
- 友好的图形界面"""messagebox.showinfo("关于", about_text)if __name__ == "__main__":try:app = ExcelCleaner()app.run()except Exception as e:logging.error(f"程序运行错误: {str(e)}")messagebox.showerror("错误", f"程序运行出错:{str(e)}")# 优化的代码,运行即出现GUI界面
相关文章:
Exce格式化批处理工具详解:高效处理,让数据更干净!
Exce格式化批处理工具详解:高效处理,让数据更干净! 1. 概述 在数据分析、报表整理、数据库管理等工作中,数据清洗是不可或缺的一步。原始Excel数据常常存在格式不统一、空值、重复数据等问题,影响数据的准确性和可用…...
CExercise_06_1指针和数组_1查找数组的最大值和最小值
题目: 查找数组的最大值和最小值,但要将最大值作为返回值返回,最小值则依靠传入的指针完成赋值。 要求不能使用"[]"运算符。 函数的声明如下: int max_min(int *arr, int len, int *pmin); 关键点 1) * 运算符用于解引用…...
redis中的hash
Redis中的hash是什么 Hash: 哈希,也叫散列,是一种通过哈希函数将键映射到表中位置的数据结构,哈希函数是关键,它把键转换成索引。 Redis Hash(散列表)是一种 field-value pairs(键值对&#x…...
【学习笔记】李沐斯坦福21秋季:实用机器学习中文版
这里写自定义目录标题 数据处理数据获取数据标注数据清洗特征工程 数据处理 数据获取 爬虫 实际工作中大部分都是从数据库里取数 数据标注 只有一小部分有标签 大部分无标签的话 半监督学习:没标注数据和有标注数据共同使用 做法1:半监督学习 基于有标签的小部分…...
UE5学习笔记 FPS游戏制作43 UI材质
文章目录 实现目标制作UI材质使用UI材质 实现目标 把图片变为灰色 制作UI材质 右键新建一个材质 左侧细节栏,材质域改为用户界面,混合模式改为半透明 此时输出节点应该有两个属性 在内容浏览器里找到要用的图片,然后向上拖动到材质标题…...
QT控件 修改QtTreePropertyBrowser自定义属性编辑器源码,添加第一列标题勾选,按钮,右键菜单事件等功能
头阵子遇到一个需要修改QtTreePropertyBrowser控件的需求,QT开发做这么久了,这个控件倒是第一次用,费了点时间研究,在这里做个简单的总结。 QtTreePropertyBrowser控件 是 Qt 解决方案 (Qt Solutions) 中的一个组件,用…...
MFC工具栏CToolBar从专家到小白
CToolBar m_wndTool; //创建控件 m_wndTool.CreateEx(this, TBSTYLE_FLAT|TBSTYLE_NOPREFIX, WS_CHILD | WS_VISIBLE | CBRS_FLYBY | CBRS_TOP | CBRS_SIZE_DYNAMIC); //加载工具栏资源 m_wndTool.LoadToolBar(IDR_TOOL_LOAD) //在.rc中定义:IDR_TOOL_LOAD BITMAP …...
Golang 项目平滑重启
引言 平滑重启(Graceful Restart)技术作为一种常用的解决方案,通过允许新进程接管而不中断现有的请求,确保了系统的稳定运行和业务连续性。同时目前公司的服务重启绝大部分也都适用的 go 的平滑重启技术。 本部分将对平滑重启的…...
Vue2 插槽 Slot
提示:插槽的目的是让我买原来的设备具备更多的扩展性。 文章目录 前言在组件中定义插槽(子组件视角)1. 默认插槽2. 具名插槽(带名称的插槽)3. 作用域插槽(带数据的插槽) 使用插槽(父…...
关于sqlsugar实体多层List映射的问题
如上图所示,当一个主表(crm_fina_pay_req)的子表list<文件附件关系表>( List<crm_fina_payreq_evidofpay_relation> )中,还包含有sysfile(SysFile SysFiles)类型的文件信…...
使用stm32cubeide stm32f407 lan8720a freertos lwip 实现udp client网络数据转串口数据过程详解
1前言 项目需要使用MCU实现网络功能,后续确定方案stm32f407 外接lan8720a实现硬件平台搭建,针对lan8720a也是用的比较多的phy,网上比较多的开发板,硬件上都是选用了这个phy,项目周期比较短,选用了这个常用…...
LangChain4j(4):预设角色(系统消息SystemMessage)
1 预设角色(系统消息SystemMessage) 基础大模型是没有目的性的, 你聊什么给什么,但是如果我们开发的事一个智能票务助手, 我需要他以一个票务助手的角色跟我对话, 并且在我跟他说”退票”的时候, 让大模型一定要告诉我…...
自然语言处理利器NLTK:从入门到核心功能解析
文章目录 一、NLP领域的基石工具包二、NLTK核心模块全景解析1 数据获取与预处理2 语言特征发现3 语义与推理 三、设计哲学与架构优势1 四维设计原则2 性能优化策略 四、典型应用场景1 学术研究2 工业实践 五、生态系统与未来演进 一、NLP领域的基石工具包 自然语言工具包&…...
常见接口协议介绍
1. I2C(Inter-Integrated Circuit) 定义:两线制串行总线(SDA数据线 SCL时钟线),支持主从模式多设备通信。特点: 地址机制:每个设备有唯一地址,主设备通过地址选择从设备…...
宝塔面板使用CDN 部署后获取真实客户端 IP教程
在宝塔面板环境中配置 CDN 加速服务后,服务器日志默认记录的是 CDN 节点 IP,这给网站流量分析带来不便。本文将为您提供多种解决方案,帮助您在 CDN 生效的同时获取真实访客 IP。 一、Nginx 配置调整方案 日志格式优化 在宝塔面板中打开 Ngi…...
生鲜果蔬便利店实体零售门店商城小程序
——线上线下融合赋能社区零售新生态 随着新零售模式的深化和消费者需求的升级,生鲜果蔬便利店亟需通过数字化工具实现经营效率与用户体验的双重提升。结合线下实体门店与线上商城的一体化小程序,成为行业转型的核心工具。以下从功能模块、运营策略及行…...
C++(初阶)(十)——vector模拟实现
vector vector构造尾插(删)和扩容inert(插入)迭代器失效erase(删除)resize(调整空间)深浅拷贝迭代器拷贝和赋值(v2(v1)和v1 v3)多个数据插入迭代器区间初始化…...
利用解析差异SSRF + sqlite注入 + waf逻辑漏洞 -- xyctf 2025 fate WP
本文章附带TP(Thinking Process)! #!/usr/bin/env python3 # 导入所需的库 import flask # Flask web框架 import sqlite3 # SQLite数据库操作 import requests # HTTP请求库 import string # 字符串处理 import json # JSON处理app flask.Flask(__name__) # 创建Flask应…...
VScode无法激活conda虚拟环境,不显示虚拟环境名称
在VScode中终端中激活环境时出现下面的情况 PS F:\Model\stMMR-main> conda activate env_mamba usage: conda-script.py [-h] [--no-plugins] [-V] COMMAND ... conda-script.py: error: argument COMMAND: invalid choice: activate (choose from clean, compare, config…...
vscode Colipot 编程助手
1、登录到colipot,以github账号,关联登录 点击【continue】按钮,继续。 点击【打开Visual Studio Code】,回到vscode中。 2、问一下11? 可以看出,很聪明,一下子就算出来了。 3、帮我们写一个文件…...
vscode中REST Client插件
最近发现vscode中REST Client插件也可以测试接口 简介 在 VS Code 中,REST Client 是一个非常受欢迎的插件,用于测试和调试 RESTful API。以下是关于该插件的安装、使用和功能的详细介绍: 安装 REST Client 插件 打开 VS Code。点击左侧的扩…...
路由器工作在OSI模型的哪一层?
路由器主要工作在OSI模型的第三层,即网络层。网络层的主要功能是将数据包从源地址路由到目标地址,路由器通过检查数据包中的目标IP地址,并根据路由表确定最佳路径来实现这一功能。 路由器的主要功能: a、路由决策:路…...
(PROFINET 转 EtherCAT)EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关
型号 协议转换通信网关 PROFINET 转 EtherCAT MS-GW31 概述 MS-GW31 是 PROFINET 和 EtherCAT 协议转换网关,为用户提供两种不同通讯协议的 PLC 进行数据交互的解决方案,可以轻松容易将 EtherCAT 网络接入 PROFINET 网络中,方便扩展&…...
【教程】MacBook 安装 VSCode 并连接远程服务器
目录 需求步骤问题处理 需求 在 Mac 上安装 VSCode,并连接跳板机和服务器。 步骤 Step1:从VSCode官网(https://code.visualstudio.com/download)下载安装包: Step2:下载完成之后,直接双击就能…...
Solidity基础入门—web3
Remix介绍 官网地址 Remix 是一个基于浏览器的 Solidity 开发环境,主要用于编写、测试、调试和部署以太坊智能合约。 Solidity基本数据类型 类型说明示例uint / int无符号 / 有符号整数uint256, int8, int256bool布尔类型(true / false)bo…...
微信小程序 request 流式数据处理
什么是流式数据处理? 流式数据处理(Streaming Data)指逐步接收并处理数据片段的技术,无需等待全部数据加载完成。适用于大文件下载、实时日志、AI生成报告等场景,可显著降低内存占用并提升用户体验。 微信小程序中的…...
Kotlin与HttpClient编写视频爬虫
想用Apache HttpClient库和Kotlin语言写一个视频爬虫。首先,我需要确定用户的具体需求。视频爬虫通常涉及发送HTTP请求,解析网页内容,提取视频链接,然后下载视频。可能需要处理不同的网站结构,甚至可能需要处理动态加载…...
数据结构:通俗解释AOE 网中事件的最早发生时间和最迟发生时间
1. 事件的最早发生时间 在 AOE 网(Activity On Edge Network,边表示活动的网络)中,事件的最早发生时间指从源点(起点)到该事件结点的最长路径长度(即所需时间)。它决定了所有以该事…...
爬虫中遇到的问题
网页假请求导致的阻塞 可以在requests请求当中添加timeout参数,来让网站重新请求 在爬虫请求中,timeout参数的主要作用是控制请求的最大等待时间,避免因服务器响应缓慢或网络问题导致程序长时间阻塞,从而提升爬虫的效率和稳定性…...
聊一聊没有接口文档时如何开展测试
目录 一、前期准备与信息收集 二、使用抓包工具分析接口 三、逆向工程构造测试用例 四、安全测试 五、 模糊测试(Fuzz Testing) 六、记录并维护发现的接口信息 七、 推动团队规范流程 其它注意事项 在我们进行接口测试时,总会遇到各种…...
第一部分:MCP协议与多智能体系统基础-第1课:MCP服务协议核心架构解析
以下是为《MCP服务协议核心架构解析》设计的课件内容,采用“概念解析→代码实践→运行验证”三段式教学结构,结合可视化图表与可运行代码示例,增强学生对MCP协议核心组件的理解与实操能力: 一、课程导入:MCP协议定位与…...
WEB安全--内网渗透--捕获NET-NTLMv2 Hash
一、前言 在LM&NTLM基础篇中我们了解到了NTLM协议的流程与加密的方式,以及具体的在type3的response中Net-ntlm hash v2的生成方式。 思考: 如果我们入侵的服务器中有域管理员的登录后的密码缓存,那就能用工具(mimikatz&…...
使用 J-Flash 读取芯片 Flash 数据的方法
基本读取步骤 硬件连接 确保 J-Link 调试器正确连接到目标板 给目标板供电(可通过 J-Link 供电或外部电源) 创建/打开项目 启动 J-Flash 软件 选择 "File" > "New Project" 创建新项目 选择正确的目标芯片型号(或…...
Spring MVC 返回 JSON 视图的方式及对比(6种)
Spring MVC 返回 JSON 视图的方式及对比(新增 MappingJackson2JsonView) 1. 方式一:ResponseBody 注解 作用:直接返回对象,由消息转换器(如 Jackson)序列化为 JSON。 适用场景:简单…...
SpringMVC的数据响应
1)页面跳转 直接返回字符串 通过ModelAndView对象返回 //方式三(model和view拆开)RequestMapping("/quick4")public String save4(Model model){model.addAttribute("username","lisi3");return "success";}//方式二RequestMapping(&…...
GraphRAG与知识图谱
一、GraphRAG介绍 1.1 什么是 Graph RAG? Graph RAG(Retrieval-Augmented Generation),是一种基于知识图谱的检索增强技术, 通过构建图模型的知识表达,将实体和关系之间的联系用图的形式进行展示ÿ…...
hive通过元数据库删除分区操作步骤
删除分区失败: alter table proj_60_finance.dwd_fm_ma_kpi_di_mm drop partition(year2025,month0-3,typeADJ); 1、查询分区的DB_ID、TBL_ID – 获取数据库ID-26110 SELECT DB_ID FROM DBS WHERE NAME ‘proj_60_finance’; – 获取表ID-307194 SELECT TBL_ID FR…...
LINUX 5 cat du head tail wc 计算机拓扑结构 计算机网络 服务器 计算机硬件
计算机网络 计算机拓扑结构 计算机按性能指标分:巨型机、大型机、小型机、微型机。大型机、小型机安全稳定,小型机用于邮件服务器 Unix系统。按用途分:专用机、通用机 计算机网络:局域网‘、广域网 通信协议’ 计算机终端、客户端…...
flink 增量快照同步文件引用关系和恢复分析
文章目录 文件引用分析相关代码分析从state 恢复,以rocksdb为例不修改并行度修改并行度keyGroupRange过程问题 文件引用分析 每次生成的checkpoint 里都会有所有文件的引用信息 问题,引用分析里如何把f1,f2去掉了,可以参考下面的代码&#…...
属性修改器 (AttributeModifier)
主页面设置组件 import { MyButtonModifier } from ../datastore/MyButtonModifier;Entry ComponentV2 struct MainPage {// 支持用状态装饰器修饰,行为和普通的对象一致Local modifier: MyButtonModifier new MyButtonModifier();build() {Column() {Button(&quo…...
汽车BMS技术分享及其HIL测试方案
一、BMS技术简介 在全球碳中和目标的战略驱动下,新能源汽车产业正以指数级速度重塑交通出行格局。动力电池作为电动汽车的"心脏",其性能与安全性不仅直接决定了车辆的续航里程、使用寿命等关键指标,更深刻影响着消费者对电动汽车的…...
电网电能质量分析:原理、算法及实际应用
一、引言 在现代社会,电力供应的稳定性和可靠性对工业生产、社会生活的各个方面都至关重要。电能质量作为衡量电力系统供电能力的关键指标,其优劣直接影响到电力设备的运行效率、使用寿命以及生产过程的稳定性。随着电力系统规模的不断扩大,新…...
PyCharm Community社区版链接WSL虚拟环境
#记录工作 在过去,PyCharm Community Edition(社区版)不具备链接 WSL 虚拟环境的功能,该功能仅在 PyCharm Professional(专业版)和企业版中提供。如今,从 PyCharm Community Edition 2024.3.5 …...
2026考研数学张宇武忠祥复习视频课,高数基础班+讲义PDF
2026考研数学武忠祥老师课(网盘):点击下方链接 2026考研数学武忠祥网课(最新网盘) 一、基础阶段(3-5个月) 目标:搭建知识框架掌握基础题型 教材使用: 高数:…...
Spring Boot嵌入前端静态资源:从原理到实战的完整指南
在Java Spring Boot项目中集成前端静态资源是构建现代Web应用的必备技能。本文将深入解析Spring Boot的静态资源处理机制,通过实战案例演示完整的集成流程,并分享性能优化与安全加固的最佳实践。 一、Spring Boot静态资源处理原理 1.1 默认资源路径 S…...
DeepSeek对比ChatGPT有何改进,可以用更低成本计算
下面是基于DeepSeek公开论文和代码,与ChatGPT对比后总结的改进点,以及其为何能用更少算力训练大模型的解析。 https://arxiv.org/pdf/2412.19437 1. 改进点对比 1.1 架构稀疏化与混合专家(MoE)设计 DeepSeek采用稀疏激活与混合…...
JavaScript双问号操作符(??)详解,解决使用 || 时因类型转换带来的问题
目录 JavaScript双问号操作符(??)详解,解决使用||时因类型转换带来的问题 一、双问号操作符??的基础用法 1、传统方式的痛点 2、双问号操作符??的精确判断 3、双问号操作符??与逻辑或操作符||的对比 二、复杂场景下的空值处理 …...
Go语言从零构建SQL数据库(5)-Pratt解析算法:SQL表达式解析的核心引擎
Pratt解析算法:SQL表达式解析的核心引擎 1. 算法概述与工作原理 Pratt解析算法(自顶向下运算符优先级解析)是一种优雅的表达式解析方法,特别适合处理具有不同优先级运算符的复杂表达式。在我们的SQL解析器中,它负责解…...
数字政府与电子政务综合分析报告
数字政府与电子政务综合分析报告 一、引言 随着信息技术的飞速发展,数字政府和电子政务成为全球公共管理领域的重要趋势。数字政府和电子政务的建设不仅是提升政府治理能力的必然选择,也是推动国家治理现代化的重要途径。本文将对数字政府和电子政务进…...
服务器虚拟化技术深度解析:医药流通行业IT架构优化指南
一、服务器虚拟化的定义与原理 (一)技术定义:从物理到虚拟的资源重构 服务器虚拟化是通过软件层(Hypervisor)将物理服务器的CPU、内存、存储、网络等硬件资源抽象为逻辑资源池,分割成多个相互隔离的虚拟机…...