Python数据分析进阶课程 从数据清洗到可视化实战 用真实销售数据教你自动处理企业报表 解决每天重复劳动的痛点
说到做报表这件事,我真的太懂了。每个月末,销售部的王姐总是第一个加班到深夜,对着Excel表格一条条核对数据、筛选月份、计算汇总,有时候还因为公式写错导致数字对不上。那种感觉就像每天都在重复走一条看不见尽头的路。
但你知道吗?用Python写一段脚本,把这些操作自动化,她每天节省的时间至少有两个小时。今天我就来手把手带你走完这个完整流程,从原始数据到一份精美的可视化报表,整个过程只需要你运行一次代码。
先看看我们要处理的是什么数据
假设我们是一家电商公司的数据分析师,手上有一份销售数据,格式大概长这样:
订单号,日期,销售员,产品类别,产品名称,数量,单价,地区,客户类型
ORD001,2024-01-05,张三,电子产品,iPhone 15,1,7999,华东,会员
ORD002,2024-01-05,李四,服装,夏季T恤,3,129,华南,普通
ORD003,2024-01-06,王五,食品,进口零食礼盒,2,299,华北,会员
ORD004,2024-01-06,张三,电子产品,华为Mate 60,1,5999,华东,会员
ORD005,2024-01-07,赵六,家居,空气净化器,1,1299,西南,普通
看起来整齐?实际上原始数据远没有这么干净。真实的企业数据经常出现这些问题:日期格式不统一,有的写”2024/1/5”,有的写”01-05”,还有的直接是”一月五日”;销售员名字有错别字,张珊、张三、张山混在一起;金额字段有时候带”元”字,有时候是空值;甚至还有一行数据整行缺失。
别担心,这些我们在Python里都能处理。
第一步:把数据搬到Python里
我们先用pandas把数据读进来。pandas是Python里最强大的数据分析库,专门为处理表格数据而生。
import pandas as pd
import numpy as np
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')
# 读取数据,假设数据保存在sales_raw.csv
df = pd.read_csv('sales_raw.csv', encoding='utf-8-sig')
# 看看数据的基本面貌
print(df.head(10))
print('\n数据形状:', df.shape)
print('\n数据类型:', df.dtypes)
print('\n缺失值统计:\n', df.isnull().sum())
print('\n基本信息:\n', df.info())
运行之后,你会看到数据的第一眼印象。df.shape告诉你有多少行多少列,df.isnull().sum()告诉你每列缺了多少数据,df.dtypes告诉你每列是什么类型。这些信息决定了我们接下来该怎么处理。
第二步:处理日期——这是最容易踩坑的地方
原始数据里的日期格式千奇百怪,我们用pd.to_datetime来统一解析。pandas的日期解析功能非常智能,能自动识别多种格式。
# 尝试解析日期列,errors='coerce'会把无法解析的变成NaT(空日期)
df['日期'] = pd.to_datetime(df['日期'], format='mixed', errors='coerce')
# 看看解析失败了多少条
failed_dates = df[df['日期'].isna()]
print(f'日期解析失败: {len(failed_dates)}条')
# 如果解析失败较多,尝试更宽松的方式
if len(failed_dates) > 0:
# 手动处理一些特殊格式
def smart_date_parser(date_val):
if pd.isna(date_val):
return pd.NaT
date_str = str(date_val).strip()
# 处理中文日期格式
chinese_month = {'一':1,'二':2,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9,'十':10,'十一':11,'十二':12}
for cn, num in chinese_month.items():
date_str = date_str.replace(cn, str(num))
date_str = date_str.replace('月', '-').replace('日', '')
try:
return pd.to_datetime(date_str)
except:
return pd.NaT
df['日期'] = df['日期'].apply(smart_date_parser)
日期处理完之后,我们可以提取出年月日,方便后续按时间维度分析:
# 提取时间维度特征
df['年'] = df['日期'].dt.year
df['月'] = df['日期'].dt.month
df['季度'] = df['日期'].dt.quarter
df['周'] = df['日期'].dt.isocalendar().week
df['星期'] = df['日期'].dt.day_name()
df['是否是月末'] = df['日期'].dt.is_month_end
这样,我们就能回答很多问题了:比如每个月的销售趋势、每周几的销售最高峰、季度之间的对比等等。
第三步:处理缺失值和异常值
缺失值不是简单删除就完事的,要看具体情况。如果是关键业务字段缺失,比如”订单号”为空,那这条数据几乎没用,直接删除。但如果是”备注”这种可选字段缺失,保留原样就行。
# 删除关键业务字段缺失的行
df = df.dropna(subset=['订单号', '日期', '产品名称', '数量', '单价'])
# 处理数量异常值:数量为0或负数的数据通常是录入错误
print(f'数量异常数据: {((df["数量"] <= 0) | (df["数量"] > 100)).sum()}条')
df = df[(df['数量'] > 0) & (df['数量'] <= 100)]
# 处理单价异常值:单价为0或负数
print(f'单价异常数据: {((df["单价"] <= 0) | (df["单价"] > 100000)).sum()}条')
df = df[(df['单价'] > 0) & (df['单价'] <= 100000)]
# 计算销售额,并处理可能的异常
df['销售额'] = df['数量'] * df['单价']
# 销售额超过10万的订单可能需要人工复核
high_value_orders = df[df['销售额'] > 100000]
print(f'高额订单(>10万): {len(high_value_orders)}条')
print(high_value_orders[['订单号', '产品名称', '数量', '单价', '销售额']])
这里用到了百分位数来判断异常值,比固定阈值更科学:
# 用IQR方法检测异常值
def detect_outliers_iqr(series, factor=1.5):
Q1 = series.quantile(0.25)
Q3 = series.quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - factor * IQR
upper = Q3 + factor * IQR
return (series < lower) | (series > upper)
# 对销售额做异常检测,但只是标记,不删除
df['销售额异常'] = detect_outliers_iqr(df['销售额'])
outlier_count = df['销售额异常'].sum()
print(f'销售额异常订单数: {outlier_count}条(已标记,未删除)')
第四步:清洗销售员姓名——模糊匹配解决错别字
这是企业数据中最常见的问题之一。销售部的人手很多,录入错误不可避免。我们用模糊匹配来纠正:
from difflib import get_close_matches
# 先看看有哪些不同的名字写法
unique_sellers = df['销售员'].dropna().unique()
print(f'销售员列表: {unique_sellers}')
# 建立正确的名字映射表
seller_corrections = {
'张珊': '张三',
'张山': '张三',
'李斯': '李四',
'李思': '李四',
'王五 ': '王五', # 去除多余空格
' 王五': '王五', # 去除前导空格
'赵六 ': '赵六',
}
# 先做简单的字符串清理
df['销售员'] = df['销售员'].astype(str).str.strip()
# 应用修正映射
df['销售员'] = df['销售员'].replace(seller_corrections)
# 对于无法匹配的,用模糊匹配自动查找
def fix_name(name, reference_list, threshold=0.7):
if name in reference_list:
return name
matches = get_close_matches(name, reference_list, n=1, cutoff=threshold)
return matches[0] if matches else name
# 获取所有销售员的"正确"名单
reference_sellers = df['销售员'].drop_duplicates().tolist()
df['销售员_修正'] = df['销售员'].apply(lambda x: fix_name(x, reference_sellers))
print(f'修正前不同姓名: {df["销售员"].nunique()}个')
print(f'修正后不同姓名: {df["销售员_修正"].nunique()}个')
# 把修正后的名字覆盖回去
df['销售员'] = df['销售员_修正']
df.drop(columns=['销售员_修正'], inplace=True)
经过这一步,原本分散的”张珊”、”张三”、”张山”都合并成了正确的”张三”,数据统计就准确了。
第五步:处理产品类别和名称的标准化
产品数据同样存在命名不统一的问题:
# 查看产品类别有哪些写法
print('产品类别分布:')
print(df['产品类别'].value_counts())
# 统一产品类别名称
category_mapping = {
'电子产品': '电子产品',
'电子': '电子产品',
'电产品': '电子产品',
'服装': '服装',
'服饰': '服装',
'食品': '食品',
'零食': '食品',
'家居': '家居用品',
'居家': '家居用品',
'家居用品': '家居用品',
}
df['产品类别'] = df['产品类别'].map(category_mapping).fillna(df['产品类别'])
# 产品名称标准化:提取关键信息
def standardize_product_name(name):
if pd.isna(name):
return '未知产品'
name = str(name).strip()
# 统一空格和特殊字符
name = name.replace(' ', '').replace('-', '-').replace('—', '-')
return name
df['产品名称_标准'] = df['产品名称'].apply(standardize_product_name)
第六步:创建完整的数据集市表
数据清洗完之后,我们把所有需要的字段整理好,形成一份干净的分析用数据表:
# 创建最终分析表
df_analysis = df.copy()
# 重命名和整理字段
df_analysis = df_analysis[['订单号', '日期', '年', '月', '季度', '星期', '是否是月末',
'销售员', '产品类别', '产品名称', '产品名称_标准',
'数量', '单价', '销售额', '地区', '客户类型', '销售额异常']]
# 按日期排序
df_analysis = df_analysis.sort_values('日期').reset_index(drop=True)
# 保存清洗后的数据
df_analysis.to_csv('sales_cleaned.csv', index=False, encoding='utf-8-sig')
print(f'数据清洗完成!共处理 {len(df_analysis)} 条记录')
print(f'保存至: sales_cleaned.csv')
# 展示清洗后的数据概览
print('\n清洗后数据预览:')
print(df_analysis.head(10).to_string())
print('\n数据概览:')
print(df_analysis.describe())
print('\n各月销售额汇总:')
monthly_sales = df_analysis.groupby('月')['销售额'].agg(['sum', 'mean', 'count'])
monthly_sales.columns = ['总销售额', '平均订单额', '订单数']
print(monthly_sales)
第七步:让报表自动更新
到这里,你已经拥有了清洗好的数据。但真正的自动化,是每次有新数据进来,脚本能自动跑一遍。我们写一个完整的处理函数:
def process_sales_report(input_file, output_file='sales_cleaned.csv'):
"""
销售数据自动化处理函数
输入原始CSV文件,输出清洗后的数据和分析报告
"""
# 读取数据
df = pd.read_csv(input_file, encoding='utf-8-sig')
# 清理销售员姓名
df['销售员'] = df['销售员'].astype(str).str.strip()
df['销售员'] = df['销售员'].replace(seller_corrections)
df['销售员'] = df['销售员'].apply(lambda x: fix_name(x, list(seller_corrections.values())))
# 统一产品类别
df['产品类别'] = df['产品类别'].map(category_mapping).fillna(df['产品类别'])
# 处理日期
df['日期'] = pd.to_datetime(df['日期'], format='mixed', errors='coerce')
df['日期'] = df['日期'].apply(smart_date_parser)
# 提取时间维度
df['年'] = df['日期'].dt.year
df['月'] = df['日期'].dt.month
df['季度'] = df['日期'].dt.quarter
df['星期'] = df['日期'].dt.day_name()
df['是否是月末'] = df['日期'].dt.is_month_end
# 标准化产品名称
df['产品名称_标准'] = df['产品名称'].apply(standardize_product_name)
# 计算销售额
df['销售额'] = df['数量'] * df['单价']
df['销售额异常'] = detect_outliers_iqr(df['销售额'])
# 筛选有效数据
df = df.dropna(subset=['订单号', '日期', '产品名称', '数量', '单价'])
df = df[(df['数量'] > 0) & (df['数量'] <= 100)]
df = df[(df['单价'] > 0) & (df['单价'] <= 100000)]
# 整理字段
df_analysis = df[['订单号', '日期', '年', '月', '季度', '星期', '是否是月末',
'销售员', '产品类别', '产品名称', '产品名称_标准',
'数量', '单价', '销售额', '地区', '客户类型', '销售额异常']]
df_analysis = df_analysis.sort_values('日期').reset_index(drop=True)
# 保存结果
df_analysis.to_csv(output_file, index=False, encoding='utf-8-sig')
# 返回报告数据
report = {
'总订单数': len(df_analysis),
'总销售额': df_analysis['销售额'].sum(),
'平均订单额': df_analysis['销售额'].mean(),
'中位数订单额': df_analysis['销售额'].median(),
'异常订单数': df_analysis['销售额异常'].sum(),
'销售员人数': df_analysis['销售员'].nunique(),
'产品类别数': df_analysis['产品类别'].nunique(),
'时间范围': f"{df_analysis['日期'].min().strftime('%Y-%m-%d')} ~ {df_analysis['日期'].max().strftime('%Y-%m-%d')}",
'月度汇总': df_analysis.groupby('月')['销售额'].agg(['sum','mean','count']).round(2).to_dict(),
'分类汇总': df_analysis.groupby('产品类别')['销售额'].agg(['sum','mean','count']).round(2).to_dict(),
}
return df_analysis, report
# 使用示例
cleaned_df, report_summary = process_sales_report('sales_raw.csv')
print('📊 报表生成报告')
print(f'总订单数: {report_summary["总订单数"]}')
print(f'总销售额: ¥{report_summary["总销售额"]:,.2f}')
print(f'平均订单额: ¥{report_summary["平均订单额"]:,.2f}')
print(f'异常订单数: {report_summary["异常订单数"]}')
print(f'时间范围: {report_summary["时间范围"]}')
现在,每天早上王姐只需要把昨天的新数据追加到原始CSV里,运行这个函数,一份干净的报表就出来了。
第八步:让数据会说话——可视化
数据清洗只是第一步,真正的价值在于让数据呈现出来。我们用matplotlib和seaborn来做可视化,这些是Python最经典的图表库。
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体,避免乱码
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 设置图表风格
sns.set_style("whitegrid")
plt.style.use('seaborn-v0_8-whitegrid')
# 创建画布
fig = plt.figure(figsize=(16, 12))
# 1. 月度销售趋势图
ax1 = plt.subplot(2, 3, 1)
monthly_sales = cleaned_df.groupby('月')['销售额'].sum()
ax1.plot(monthly_sales.index, monthly_sales.values, marker='o', linewidth=2.5, markersize=8, color='#2E86AB')
ax1.fill_between(monthly_sales.index, monthly_sales.values, alpha=0.3, color='#2E86AB')
ax1.set_title('月度销售趋势', fontsize=14, fontweight='bold')
ax1.set_xlabel('月份')
ax1.set_ylabel('销售额 (元)')
for i, v in enumerate(monthly_sales.values):
ax1.annotate(f'¥{v:,.0f}', (i, v), textcoords="offset points", xytext=(0,10), ha='center', fontsize=9)
# 2. 产品销售类别占比
ax2 = plt.subplot(2, 3, 2)
category_sales = cleaned_df.groupby('产品类别')['销售额'].sum().sort_values(ascending=False)
colors = ['#E74C3C', '#F39C12', '#27AE60', '#9B59B6', '#3498DB']
wedges, texts, autotexts = ax2.pie(category_sales.values, labels=category_sales.index, autopct='%1.1f%%',
colors=colors, startangle=90, textprops={'fontsize': 11})
for autotext in autotexts:
autotext.set_fontweight('bold')
ax2.set_title('产品类别销售占比', fontsize=14, fontweight='bold')
# 3. 各地区销售额
ax3 = plt.subplot(2, 3, 3)
region_sales = cleaned_df.groupby('地区')['销售额'].sum().sort_values(ascending=True)
bars = ax3.barh(region_sales.index, region_sales.values, color='#E91E63', height=0.6)
ax3.set_title('各地区销售额', fontsize=14, fontweight='bold')
ax3.set_xlabel('销售额 (元)')
for bar, val in zip(bars, region_sales.values):
ax3.text(val + max(region_sales.values)*0.02, bar.get_y() + bar.get_height()/2,
f'¥{val:,.0f}', va='center', fontsize=10, fontweight='bold')
# 4. 销售员业绩排名
ax4 = plt.subplot(2, 3, 4)
seller_sales = cleaned_df.groupby('销售员')['销售额'].sum().sort_values(ascending=False).head(10)
bars4 = ax4.bar(range(len(seller_sales)), seller_sales.values, color='#9C27B0', edgecolor='white', linewidth=1.5)
ax4.set_xticks(range(len(seller_sales)))
ax4.set_xticklabels(seller_sales.index, rotation=45, ha='right')
ax4.set_title('TOP10 销售员业绩', fontsize=14, fontweight='bold')
ax4.set_ylabel('销售额 (元)')
for bar, val in zip(bars4, seller_sales.values):
ax4.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(seller_sales.values)*0.02,
f'¥{val:,.0f}', ha='center', fontsize=9, fontweight='bold')
# 5. 客户类型对比
ax5 = plt.subplot(2, 3, 5)
customer_sales = cleaned_df.groupby('客户类型')['销售额'].agg(['sum', 'mean', 'count'])
customer_sales.columns = ['总销售额', '平均订单额', '订单数']
x = np.arange(len(customer_sales))
width = 0.35
bars1 = ax5.bar(x - width/2, customer_sales['总销售额'], width, label='总销售额', color='#FF9800')
bars2 = ax5.bar(x + width/2, customer_sales['平均订单额'], width, label='平均订单额', color='#4CAF50')
ax5.set_xticks(x)
ax5.set_xticklabels(customer_sales.index)
ax5.set_title('客户类型对比', fontsize=14, fontweight='bold')
ax5.legend()
# 6. 星期销售热力图
ax6 = plt.subplot(2, 3, 6)
weekday_sales = cleaned_df.groupby('星期')['销售额'].sum()
weekday_order = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
weekday_cn = ['周一','周二','周三','周四','周五','周六','周日']
weekday_data = [weekday_sales.get(d, 0) for d in weekday_order]
bars6 = ax6.bar(weekday_cn, weekday_data, color='#00BCD4', edgecolor='white', linewidth=1.5)
ax6.set_title('星期销售分布', fontsize=14, fontweight='bold')
ax6.set_ylabel('销售额 (元)')
for bar, val in zip(bars6, weekday_data):
ax6.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(weekday_data)*0.02,
f'¥{val:,.0f}', ha='center', fontsize=9, fontweight='bold')
plt.tight_layout()
plt.savefig('sales_dashboard.png', dpi=150, bbox_inches='tight', facecolor='white')
plt.show()
图表生成后,你会得到一张包含六个子图的完整销售仪表盘。这张图可以直接放到报告里,也可以用更高级的交互图表库进一步美化。
第九步:生成自动化的Excel报表
除了图表,我们还需要一份结构化的Excel报表,方便销售部的同事查看和二次分析:
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils.dataframe import dataframe_to_rows
def generate_excel_report(df, output_path='销售报表.xlsx'):
"""生成格式化的Excel报表"""
wb = openpyxl.Workbook()
# 定义样式
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
header_font = Font(bold=True, color='FFFFFF', size=11)
total_fill = PatternFill(start_color='FFD700', end_color='FFD700', fill_type='solid')
border = Border(
left=Side(style='thin'), right=Side(style='thin'),
top=Side(style='thin'), bottom=Side(style='thin')
)
# ===== Sheet 1: 原始数据明细 =====
ws1 = wb.active
ws1.title = '数据明细'
for r_idx, row in enumerate(dataframe_to_rows(df, index=False, header=True), 1):
for c_idx, value in enumerate(row, 1):
cell = ws1.cell(row=r_idx, column=c_idx, value=value)
cell.border = border
if r_idx == 1:
cell.fill = header_fill
cell.font = header_font
cell.alignment = Alignment(horizontal='center', vertical='center')
else:
if c_idx == df.columns.get_loc('日期') + 1:
cell.number_format = 'YYYY-MM-DD'
elif '额' in df.columns[c_idx-1] or '价' in df.columns[c_idx-1]:
cell.number_format = '¥#,##0.00'
cell.alignment = Alignment(horizontal='center', vertical='center')
# 自动调整列宽
for col in ws1.columns:
max_length = max(len(str(cell.value or '')) for cell in col)
ws1.column_dimensions[col[0].column_letter].width = max_length + 4
# ===== Sheet 2: 月度汇总 =====
ws2 = wb.create_sheet('月度汇总')
monthly = df.groupby('月').agg({
'销售额': ['sum', 'mean', 'count', 'median'],
'订单号': 'count'
}).round(2)
monthly.columns = ['总销售额', '平均订单额', '订单数', '中位数', '订单数_校验']
monthly = monthly[['总销售额', '平均订单额', '订单数', '中位数']]
monthly['销售额环比'] = monthly['总销售额'].pct_change() * 100
monthly['销售额环比'] = monthly['销售额环比'].round(2)
for r_idx, row in enumerate(dataframe_to_rows(monthly, index=False, header=True), 1):
for c_idx, value in enumerate(row, 1):
cell = ws2.cell(row=r_idx, column=c_idx, value=value)
cell.border = border
if r_idx == 1:
cell.fill = header_fill
cell.font = header_font
if c_idx <= 4:
cell.number_format = '¥#,##0.00'
elif c_idx == 5:
cell.number_format = '0.00%'
cell.alignment = Alignment(horizontal='center', vertical='center')
# 添加总计行
total_row = len(monthly) + 2
ws2.cell(row=total_row, column=1, value='总计').font = Font(bold=True)
ws2.cell(row=total_row, column=2, value=monthly['总销售额'].sum()).number_format = '¥#,##0.00'
ws2.cell(row=total_row, column=3, value=monthly['平均订单额'].mean()).number_format = '¥#,##0.00'
ws2.cell(row=total_row, column=4, value=monthly['订单数'].sum()).number_format = '#,##0'
for col in range(1, 5):
ws2.cell(row=total_row, column=col).fill = total_fill
ws2.cell(row=total_row, column=col).font = Font(bold=True)
# ===== Sheet 3: 分类汇总 =====
ws3 = wb.create_sheet('分类汇总')
category_summary = df.groupby('产品类别').agg({
'销售额': ['sum', 'mean', 'count'],
'数量': 'sum'
}).round(2)
category_summary.columns = ['总销售额', '平均订单额', '订单数', '总数量']
for r_idx, row in enumerate(dataframe_to_rows(category_summary, index=False, header=True), 1):
for c_idx, value in enumerate(row, 1):
cell = ws3.cell(row=r_idx, column=c_idx, value=value)
cell.border = border
if r_idx == 1:
cell.fill = header_fill
cell.font = header_font
if c_idx in [1, 2]:
cell.number_format = '¥#,##0.00'
cell.alignment = Alignment(horizontal='center', vertical='center')
# ===== Sheet 4: 销售员业绩 =====
ws4 = wb.create_sheet('销售员业绩')
seller_summary = df.groupby('销售员').agg({
'销售额': ['sum', 'mean', 'count'],
'数量': 'sum'
}).round(2)
seller_summary.columns = ['总销售额', '平均订单额', '订单数', '总数量']
seller_summary = seller_summary.sort_values('总销售额', ascending=False)
for r_idx, row in enumerate(dataframe_to_rows(seller_summary, index=False, header=True), 1):
for c_idx, value in enumerate(row, 1):
cell = ws4.cell(row=r_idx, column=c_idx, value=value)
cell.border = border
if r_idx == 1:
cell.fill = header_fill
cell.font = header_font
if c_idx <= 2:
cell.number_format = '¥#,##0.00'
cell.alignment = Alignment(horizontal='center', vertical='center')
# ===== Sheet 5: 地区分析 =====
ws5 = wb.create_sheet('地区分析')
region_summary = df.groupby('地区').agg({
'销售额': ['sum', 'mean', 'count'],
'数量': 'sum'
}).round(2)
region_summary.columns = ['总销售额', '平均订单额', '订单数', '总数量']
region_summary = region_summary.sort_values('总销售额', ascending=False)
for r_idx, row in enumerate(dataframe_to_rows(region_summary, index=False, header=True), 1):
for c_idx, value in enumerate(row, 1):
cell = ws5.cell(row=r_idx, column=c_idx, value=value)
cell.border = border
if r_idx == 1:
cell.fill = header_fill
cell.font = header_font
if c_idx <= 2:
cell.number_format = '¥#,##0.00'
cell.alignment = Alignment(horizontal='center', vertical='center')
# 保存文件
wb.save(output_path)
print(f'报表已生成: {output_path}')
# 生成报表
generate_excel_report(cleaned_df)
第十步:一键生成完整日报——把这些串起来
现在我们把所有步骤整合成一个完整的自动化脚本:
"""
销售数据自动处理脚本
每天运行一次,自动完成数据清洗、分析和报表生成
用法: python sales_auto_processor.py --input 原始数据.csv --output 报表
"""
import argparse
import os
from datetime import datetime
def main():
parser = argparse.ArgumentParser(description='销售数据自动处理工具')
parser.add_argument('--input', default='sales_raw.csv', help='输入文件路径')
parser.add_argument('--output', default='sales_report', help='输出文件前缀')
args = parser.parse_args()
print(f'🚀 开始处理销售数据...')
print(f'📂 输入文件: {args.input}')
print(f'⏰ 处理时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
print('=' * 50)
# 步骤1: 处理数据
cleaned_df, report = process_sales_report(args.input)
# 步骤2: 生成可视化图表
generate_dashboard(cleaned_df, f'{args.output}_dashboard.png')
# 步骤3: 生成Excel报表
generate_excel_report(cleaned_df, f'{args.output}.xlsx')
# 步骤4: 输出统计摘要
print('\n' + '=' * 50)
print('📊 处理结果摘要')
print('=' * 50)
print(f'✅ 总订单数: {report["总订单数"]:,} 条')
print(f'✅ 总销售额: ¥{report["总销售额"]:,.2f}')
print(f'✅ 平均订单额: ¥{report["平均订单额"]:,.2f}')
print(f'✅ 中位数订单额: ¥{report["中位数订单额"]:,.2f}')
print(f'✅ 异常订单数: {report["异常订单数"]} 条(已标记)')
print(f'✅ 活跃销售员: {report["销售员人数"]} 人')
print(f'✅ 产品类别: {report["产品类别数"]} 类')
print(f'✅ 时间跨度: {report["时间范围"]}')
print('\n📁 输出文件:')
print(f' - 清洗数据: {args.output}_cleaned.csv')
print(f' - 可视化图表: {args.output}_dashboard.png')
print(f' - Excel报表: {args.output}.xlsx')
print('=' * 50)
print('✨ 处理完成!每天自动运行此脚本,告别重复劳动。')
if __name__ == '__main__':
main()
有了这个脚本,每天早上王姐只需要把新的销售数据放到指定文件夹,双击运行一次,十几秒钟,一份完整的报表就出来了。以前需要两三个小时的工作,现在只需要点几下鼠标。
把这个脚本变成真正的自动化
更高级的做法是把它定时运行。在Windows上可以用任务计划程序,在Linux/Mac上可以用crontab:
# Linux/Mac crontab 示例:每天早上8点自动运行
0 8 * * * cd /path/to/script && python sales_auto_processor.py --input daily_sales.csv --output daily_report
# Windows 任务计划程序示例(用at命令或schtasks)
schtasks /create /tn "SalesDailyReport" /tr "python C:\scripts\sales_auto_processor.py" /sc daily /st 08:00
或者更简单的方式——让Excel直接调用Python脚本。用openpyxl读取Excel、用pandas处理数据、再用openpyxl把结果写回Excel,整个过程不需要离开Excel界面:
# 嵌入到Excel的VBA宏中调用
def run_python_from_excel():
"""在Excel中通过VBA调用Python脚本"""
import subprocess
result = subprocess.run(
['python', 'sales_auto_processor.py',
'--input', 'sales_raw.csv',
'--output', 'sales_report'],
capture_output=True, text=True
)
print(result.stdout)
为什么这个方案能真正帮到你
说实话,一开始学Python的时候我也觉得门槛很高。但数据处理这件事,一旦你跨过那个坎,就会发现整个世界都变了。以前做报表是体力活,现在变成了”点一下按钮”的事。
更重要的是,Python处理数据的速度是Excel无法比拟的。几万行的数据,Excel可能需要几分钟甚至更久,pandas几秒钟就搞定。当数据量增长到几十万、上百万行时,这种差距会变得非常明显。
还有一点很重要——Python的重复执行不会出错。Excel公式写错一个符号,整个表格可能就乱了。Python脚本只要逻辑正确,运行一百次结果都一样,不会出现”今天和昨天的报表对不上”这种尴尬情况。
如果你刚开始接触Python,不要怕。从上面的代码开始,一行一行理解,先跑通,再慢慢调整。数据处理这件事,上手之后你会发现特别有意思——看着一堆杂乱无章的数据,经过你的代码整理,变成清晰的洞察和漂亮的图表,那种成就感无可替代。
每天节省下来的两个小时,你可以用来做更有价值的事:分析销售趋势、找出增长机会、优化产品组合。这些才是数据分析真正的价值所在,而不是困在Excel里重复劳动。
