Day10:Python实现Excel自动汇总

一、为什么要做这个小工具

假设你是一个团队的负责人,你给手下三个员工(张三、李四、王麻子)发了一个Excel模板,让他们填写各自的销售记录。

周五下班之前,你收到了三个文件:张三-9月销售.xlsx、李四-9月销售.xlsx、王麻子-9月销售.xlsx

你的任务是把这三个表的数据合并到一张总表里,然后计算出总销售额。

以前你可能会这么操作,打开张三 -> 复制 -> 粘贴到总表 -> 打开李四 -> 复制 -> 粘贴到总表...

3个文件你觉得还好,如果是30个呢?头都大了。

有些小公司或者G企就是没有系统来管理这些数据,存靠Excel的。

二、Pandas介绍

要处理Excel,我们不能再用之前open()的土办法了。

我们要请出一个Python数据处理领域里非常好用的工具,Pandas。

你可以暂时把Pandas想象成一个加载在Python里的、超级增强版的Excel。

他专门为了数据分析而生,处理表格数据是他最拿手的。

这是Pandas的官网:https://pandas.pydata.org/

2.1. 安装Pandas

Pandas是一个第三方库,需要我们手动安装。在PyCharm中Alt + F12打开Terminal:

输入:

shell 复制代码
pip install pandas

安装了pandas还不够,Pandas自己不认识.xlsx文件,他需要一个小弟来帮他读写。

我们也需要把这个小弟安装上:

复制代码
pip install openpyxl

安装完成:

注意要在虚拟环境中进行安装模块。

三、准备工作:制造报表

我们在项目路径下面创建一个"销售数据"的文件夹,把准备的销售数据(.xlsx文件)放进去。

两份销售数据的表大概长这个样子:

不一定要按照示例文件的数据来,可以自定义,省去一些不必要的数据。

四、先合并这两个文件

老规矩,我们先用最笨的办法,指定文件名来合并,先让它跑起来。

因为两个Excel的模版是一样的(表头一致),我们就想办法把两个Excel的数据合并到一个里面。

4.1. 敲代码

python 复制代码
import pandas as pd
import os

folder = "销售数据"
file1 = os.path.join(folder, "张三--销售数据.xlsx")
file2 = os.path.join(folder, "李四--销售数据.xlsx")

print("正在读取 张三.xlsx...")
df1 = pd.read_excel(file1)
print("正在读取 李四.xlsx...")
df2 = pd.read_excel(file2)

all_dfs = [df1, df2]

print("正在合并...")
merged_df = pd.concat(all_dfs)

output_file = os.path.join(folder, "合并销售总表.xlsx")
merged_df.to_excel(output_file, index=False)

print(f"合并完成!总表已保存到 {output_file}")

4.2. 运行结果

运行代码后,张三--销售数据.xlsx和李四--销售数据.xlsx就合并成了新的合并销售总表.xlsx。

由于数据较多,我们就对比一下总条数:

4.3. 代码讲解

import pandas as pd这个已经是老熟人了,导入Pandas模块,然后取个小名pd。

pd.read_excel(file)用来读取Excel,这行代码就是替代了手动打开Excel,他会把表格数据读到一个叫DataFrame(数据框) 的东西里。

pd.concat(list_of_dfs)函数的字面意思就是拼接,他会把列表(就是all_dfs)里的所有数据框,像叠罗汉一样,一个接一个竖着叠起来。

merged_df.to_excel(output_file, index=False)就是把合并后的数据框存回到指定的Excel里面。index=False就是告诉Pandas不要把自己那套行号索引存进去。

如果不加上index=False,会把原序号给挪到新表:

五、自动扫描文件夹

上个版本有个问题,如果多了一个其他人的销售数据文件,我们就得改代码。

我们希望的是,不管文件夹里有几个文件,都能自动找到并合并。

这时,我们需要一个新工具glob,他能帮我们搜刮所有符合条件的文件。

本次我们又在销售数据文件夹中新加了王五--销售数据.xlsx、赵六--销售数据.xlsx两个数据文件。

5.1. 敲代码

python 复制代码
import pandas as pd
import os
import glob

folder = "销售数据"
output_file = os.path.join(folder, "合并销售总表.xlsx")

search_path = os.path.join(folder, "*.xlsx")
all_files = glob.glob(search_path)

all_files.remove(output_file) if output_file in all_files else None

all_dfs = []
for file in all_files:
    print(f"正在读取 {file}...")
    df = pd.read_excel(file)
    all_dfs.append(df)

merged_df = pd.concat(all_dfs)

merged_df.to_excel(output_file, index=False)

print(f"合并完成!总表已保存到 {output_file}")

5.2. 运行效果

现在不管加多少个销售数据的文件,都能一次性合并到新的合并总表中了。

5.3. 代码讲解

在这个版本的代码中,我们已经没有固定某几个销售数据文件了,而是指定了一个目录。这个目录下满足条件的文件数据都会进行合并。

我们引入了一个新的模块glob。这也是Python标准库中的一个模块。

详细说明文档:https://docs.python.org/zh-cn/3.14/library/glob.html

这个模块主要用来根据通配符模式来查找匹配的文件路径,非常方便用于批量获取某个文件夹下符合特定规则的文件。

glob.glob(search_path)里面*是一个通配符,*.xlsx的意思就是随便什么名字,只要后缀是.xlsx的,都找出来。

如果我们不用glob这个模块,按照以前传统的打开文件夹、遍历文件、判断文件名这种形式来写也能实现:

python 复制代码
import os
folder = "销售数据"
all_items = os.listdir(folder)
all_files = []
for item in all_items:
    full_path = os.path.join(folder, item)
    if item.endswith(".xlsx") and os.path.isfile(full_path):
        all_files.append(full_path)

不过相比起使用glob模块,这段代码就显得有点冗长了。

all_files.remove(output_file) if output_file in all_files else None这段主要是排除掉我们已经生成的合并总表。

六、自动汇总

数据已经合并了,但是这些只是明细数据,老板可不是想看到这样的数据。

老板想要的是,到底卖了多少,谁卖得多,每个季度有什么变化。

所以合并只是第一步,对数据进行分析汇总展示才是老板最终需要的。

下面我们以"销售数据分析"为例,来看看怎么做数据的汇总分析。

6.1. 敲代码

merge_excel_v3.py

python 复制代码
import pandas as pd

class SalesPerformanceAnalysis:
    def __init__(self, df):
        self.df = df.copy()
        self.df['日期'] = pd.to_datetime(self.df['日期'])
        self.df['是否退货'] = self.df['是否退货'].map({'是': 1, '否': 0})

    def generate_performance_report(self, output_file='销售业绩分析.xlsx'):
        """生成销售业绩分析报告"""

        with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
            performance_df = self._generate_salesperson_performance()
            performance_df.to_excel(writer, sheet_name='销售人员绩效', index=False)

        print(f"销售业绩分析已保存到: {output_file}")

    def _generate_salesperson_performance(self):
        performance = self.df.groupby('销售人员').agg({
            '销售额': ['sum', 'mean', 'max', 'min'],
            '订单ID': 'count',
            '客户评分': 'mean',
            '是否退货': 'sum',
            '销售数量': 'sum'
        }).round(2)

        performance.columns = [
            '总销售额', '平均订单额', '最大订单额', '最小订单额',
            '订单数量', '平均评分', '退货数量', '总销量'
        ]

        performance['退货率%'] = (performance['退货数量'] / performance['订单数量'] * 100).round(2)
        performance['客单价'] = (performance['总销售额'] / performance['总销量']).round(2)

        performance = performance.sort_values('总销售额', ascending=False)
        performance['销售额排名'] = range(1, len(performance) + 1)

        performance = performance.reset_index()

        columns_order = [
            '销售人员', '总销售额', '平均订单额', '最大订单额', '最小订单额',
            '订单数量', '平均评分', '退货数量', '总销量', '退货率%', '客单价', '销售额排名'
        ]

        performance = performance[columns_order]
        return performance

mian.py

python 复制代码
import os
import pandas as pd
from merge_excel_v3 import SalesPerformanceAnalysis

folder = "销售数据"
sales_file = os.path.join(folder, "合并销售总表.xlsx")
df = pd.read_excel(sales_file)
analyzer = SalesPerformanceAnalysis(df)
analyzer.generate_performance_report()

运行的是main.py

6.2. 运行结果

运行代码后,我们会得到这样一个数据文件。

6.3. 代码讲解

我们先看merge_excel_v3.py这个文件。

代码中出现了一个新的关键字class。

6.3.1 类和构造函数

class SalesPerformanceAnalysis:表示我们定义一个名字叫SalesPerformanceAnalysis的类,这个类用来封装销售业绩分析功能。

还不理解什么是类,没关系,暂时把他看成是个工具箱吧,我们在这个工具箱里放很多工具(定义功能函数)。

这个类里面通过def定义了三个方法:init、generate_performance_report、_generate_salesperson_performance。

其中__init__叫类的构造函数,创建对象的时候自动调用的。有两个参数,一个self、一个df。self就是当前这个对象。df是我们要传进去处理的数据框。

在构造函数里面,df.copy()把数据复制一份,免得把原数据弄乱了。然后to_datetime把日期整理成标准格式,map把文字变成数字。

可以看出,构造函数一般都做一些简单的,类似初始化的事情。

6.3.2 写报告文件

generate_performance_report也是个类的方法,第二个参数有个默认值"销售业绩分析.xlsx",如果在调用这个方法的时候,不传值,就会使用这个默认值。

pd.ExcelWriter是pandas提供的一个工具,用来把数据写到Excel文件。而且通过engine='openpyxl'指定使用openpyxl这个第三方库作为引擎来写.xlsx格式的Excel 文件。

with ... as writer应该看到过几次了,就是Python的语法,自动管理我们的文件资源。

简单点说这行代码就是:我要创建/写入一个Excel文件,文件名叫output_file,用openpyxl引擎,然后用writer这个对象来操作他。

self._generate_salesperson_performance()就是调用了当前类的另外一个方法。

to_excel之前我们已经用过了,就是把一个DataFrame(这是调用_generate_salesperson_performance返回的)写到Excel文件。

6.3.3 生成报告数据

_generate_salesperson_performance这个方法前面有个下划线,一般这种就是表示这个方法是内部使用或者私有的,不希望被外部直接调用(但这只是约定,Python不会强制限制访问)。

.groupby('销售人员')就是按照销售人员这一列进行分组,也就是说,下面的聚合操作,是对每个销售人员分别进行的。

.agg({...})是对每个分组,按不同的列,计算不同的统计指标。

.round(2)是保留两位小数的意思。

我列一下看着比较清晰:

列名 聚合操作 含义说明
销售额 'sum' 每个销售人员的总销售额
销售额 'mean' 每个销售人员的平均每单销售额(平均订单额)
销售额 'max' 每个销售人员的最高单笔销售额(最大订单额)
销售额 'min' 每个销售人员的最低单笔销售额(最小订单额)
订单ID 'count' 每个销售人员的订单总数(订单数量)
客户评分 'mean' 每个销售人员的平均客户评分
是否退货 'sum' 每个销售人员的退货订单总数(退货数量)
销售数量 'sum' 每个销售人员的总销售数量

这一步的结果长这个样子,是一个多层列索引的DataFrame。

这一步的多层列索引看着不直观,所以通过直接给performance.columns赋值,把这些多级列名映射成更清晰的单层中文列名。

performance['退货率%']和performance['客单价']是在计算衍生指标。

performance.sort_values('总销售额', ascending=False)是按照总销售额从高到低排序。这样业绩最好的销售员会排在最上面。

performance['销售额排名'] = range(1, len(performance) + 1)是添加了一列,给每个销售人员一个排名,第1名、第2名...。

range(1, len(performance) + 1)生成从1开始的连续整数,个数等于当前销售人员的数量。

performance.reset_index()是在重置索引。这是因为前面用了.groupby('销售人员'),所以销售人员这一列默认变成了索引。

使用reset_index()后,销售人员重新变成普通的一列,索引变成默认的 0,1,2,...。

然后根据columns_order调整了列的显示顺序。

最后推给return这个关键字把生成调整好的数据框返回出去,让调用这个方法的地方接收,最后写到文件里。

6.3.4 运行方法

from merge_excel_v3 import SalesPerformanceAnalysis表示从我们自己写的脚本merge_excel_v3里面导入一个自定义的类SalesPerformanceAnalysis。

analyzer = SalesPerformanceAnalysis(df)这一句是在实例化一个SalesPerformanceAnalysis类的对象,然后把我们刚刚读取的DataFrame (df) 传给了他的构造函数。

最后拿实例对象直接调generate_performance_report方法。

七、小结

回想一下我们最初的痛点:面对三个、甚至三十个Excel文件,只能靠复制粘贴。

而现在,我们只用了几十行Python代码,就实现了从自动扫描、合并数据,到最终生成专业分析报告的全流程自动化。

是不是节省了很多的时间。

今天我们学会了使用 pandas批量读取、合并和写入Excel文件。

还使用了glob模块,让程序能自动帮我们找到所有需要处理的文件。

体验了groupby和agg,把杂乱的明细数据转换成有价值的汇总数据。

初步接触了class(类),学习了怎么把功能封装起来。

当然我们现在看到的,还只是Pandas能力的冰山一角。

相关推荐
TDengine (老段)3 小时前
从“事后抢险”到“事前防控”:江西水投用 TDengine 时序数据库重塑防汛模式
大数据·数据库·物联网·时序数据库·tdengine·涛思数据·1024程序员节
傻童:CPU3 小时前
C语言需要掌握的基础知识点之DFS(深度优先搜索)
c语言·1024程序员节
我是华为OD~HR~栗栗呀3 小时前
华为od-22届考研-C++面经
java·前端·c++·python·华为od·华为·面试
m0_748240253 小时前
华为OD机考:计算正方形数量(Python & C/C++ & JAVA & JS & GO)
c语言·python·华为od
紫薯馍馍3 小时前
ThreeD Tools(PPT插件)v2.1 下载安装教程
1024程序员节
九皇叔叔3 小时前
HTML与CSS基础入门:从语法到页面结构全解析
css·html·1024程序员节
我是华为OD~HR~栗栗呀3 小时前
华为OD, 测试面经
java·c++·python·华为od·华为·面试
Wang's Blog4 小时前
Linux小课堂: 基于 SSH 的安全文件传输与增量同步机制深度解析之从 wget 到 rsync 的全流程实战
linux·ssh·1024程序员节
mit6.8244 小时前
[OP-Agent] `opa run` | 交互模式(REPL) | 服务模式(HTTP API) | runtime包
go·1024程序员节