刚接手公司销售数据那会儿,我天真地以为用Excel处理几千行数据不是事儿。结果连续三个月,每月底都要加两天班:数据合并、去重、算汇总、做报表…直到我认真学了一周Pandas,现在同样的工作,喝杯咖啡的功夫就搞定了。
先聊聊最让我头疼的数据清洗。真实业务数据有多脏?同一个客户名,这个月叫”腾讯科技”,下个月变成”深圳市腾讯计算机系统”,还有”腾讯(深圳)”。用Excel的VLOOKUP,那真是欲哭无泪。
(开篇点题:一张处理前脏乱差的数据截图 vs 用Pandas清洗后的整洁数据)
第一步:读数据,别掉坑里
“python
import pandas as pd
最基础的读取方式,但90%的新手会踩坑
df = pd.read_excel('销售数据_2024.xlsx')
你以为读完了?看看数据长啥样
print(df.head())
print(df.info()) # 这个info()能救你命
p>e>`
为什么要这么写?因为很多Excel文件里藏着你看不到的合并单元格、空行、隐藏列。df.info()能告诉你:
- 每列有多少非空值(空值多的列赶紧处理)
- 数据类型对不对(日期变字符串?数值变对象?)
- 内存占用多少(数据量大到卡死?)
我第一次处理200MB的CSV文件,直接用pd.read_csv(),结果内存爆炸。后来加了个参数:
`python`
df = pd.read_csv('big_data.csv', chunksize=10000) # 分块读取,内存友好
数据清洗三步走
1. 列名标准化
真实数据的列名经常让人抓狂:''(空格)、'金额(元)'、'金额/元'、'Amount'... 不统一啊!
`python
统一列名为英文小写,方便后续操作
df.columns = df.columns.str.strip().str.lower().str.replace('[()/]', '_', regex=True)
输出:'金额_元_', '金额_元', 'amount' 变成 '金额_元_', '金额元', 'amount'
如果还有问题,直接硬映射
column_map = {
'金额_元_': 'amount',
'金额元': 'amount',
'金额(元)': 'amount'
>}
df.rename(columns=column_map, inplace=True)
`
2. 处理重复值和空值
另一个坑:你以为drop_duplicates()就完了?天真。
`python
先看看有多少重复
print(f'原始行数: {len(df)}')
print(f'去重后行数: {len(df.drop_duplicates())}')
按关键列去重(比如订单号、客户名+日期)
df = df.drop_duplicates(subset=['order_id']) # 保留第一条
或者保留最后一条
df = df.drop_duplicates(subset=['customer', 'date'], keep='last')
空值处理:别直接dropna
先看看空值分布
print(df.isnull().sum())
数值列用均值填充,分类列用众数
df['amount'].fillna(df['amount'].median(), inplace=True)
df['region'].fillna(df['region'].mode()[0], inplace=True)
`
(核心过程:一张数据清洗前后的对比图,展示脏数据如何变干净)
数据合并:别再Ctrl+C/V了
我见过同事用Excel手动合并12个月的数据表,花了一整天。用Pandas,5行代码搞定:
`python
import glob
import os
读取所有Excel文件
all_files = glob.glob('data/*.xlsx') # 匹配data文件夹下所有xlsx
df_list = []
for file in all_files:
temp_df = pd.read_excel(file)
# 加一列记录来源文件名,方便溯源
temp_df['source_file'] = os.path.basename(file)
df_list.append(temp_df)
纵向合并(类似SQL的UNION ALL)
combined_df = pd.concat(df_list, ignore_index=True)
print(f'合并后行数: {len(combined_df)}')
`
注意:pd.concat()是纵向堆叠,pd.merge()才是类似SQL的JOIN。如果你要按客户ID匹配两个表:
`python
左连接:保留左侧表所有记录
merged_df = pd.merge(df_sales, df_customer_info,
on='customer_id',
how='left',
indicator=True) # 加个指示列,看看哪些没匹配上
检查匹配情况
print(merged_df['_merge'].value_counts())
输出:left_only表示没匹配上,那是数据质量问题,得查
`
透视表:从数据到洞见
Excel的透视表很强大,但Pandas的pivot_table更灵活。比如我要看每个销售员每月的业绩汇总:
`python
先确保日期列是datetime类型
df['date'] = pd.to_datetime(df['date'])
df['month'] = df['date'].dt.to_period('M') # 提取月份
透视表:行是销售员,列是月份,值是销售额总和
pivot = pd.pivot_table(
df,
values='amount', # 要汇总的列
index='salesperson', # 行索引
columns='month', # 列索引
aggfunc='sum', # 聚合函数
fill_value=0, # 空值填0
margins=True, # 加总计行/列
margins_name='总计'
)
排序:按总计列降序
pivot = pivot.sort_values('总计', ascending=False)
输出到Excel,加上条件格式(手动的话)
pivot.to_excel('销售业绩透视表.xlsx')
`
这个设计真的反人类:pivot_table的参数顺序,我第一次写的时候死活调不对,index和columns搞反了好几次。官方文档那段文档不够清晰,建议直接跑上面这段代码,改参数看看效果。
(总结前:生成的透视表Excel截图,带总计行和条件格式效果)
性能优化:从3分钟到3秒
处理10万行以上的数据,Pandas默认性能可能不够。几个实测有效的技巧:
`python
1. 指定数据类型,别让Pandas猜
dtype_dict = {
'customer_id': 'int32', # 比默认int64省一半内存
'amount': 'float32',
'region': 'category' # 分类数据用category
}
df = pd.read_csv('data.csv', dtype=dtype_dict)
2. 只读需要的列
usecols = ['customer_id', 'amount', 'region', 'date']
df = pd.read_csv('data.csv', usecols=usecols)
3. 向量化操作,别用循环
错误写法(慢到哭):
for i in range(len(df)):
if df.iloc[i]['amount'] > 1000:
df.iloc[i]['level'] = 'VIP'
正确写法(快100倍):
df['level'] = df['amount'].apply(lambda x: 'VIP' if x > 1000 else '普通')
或者用np.where更快
import numpy as np
df['level'] = np.where(df['amount'] > 1000, 'VIP', '普通')
`
实测数据:未优化前处理50万行数据需要3.2秒,优化后降到0.8秒,提升4倍。
总结一下,你可以立刻用的三个点
:花3秒看数据结构,省后面半小时排查错误替代Excel手动透视:一行代码生成报表,参数不会?把我上面那段复制过去改或np.where比for循环快几十倍,数据量大时是质变最后给个锦上添花的技巧:用to_excel输出时,可以加多个sheet:
`python“
with pd.ExcelWriter('最终报表.xlsx') as writer:
pivot.to_excel(writer, sheet_name='透视表')
df_cleaned.to_excel(writer, sheet_name='清洗后数据', index=False)
df_merged.to_excel(writer, sheet_name='合并数据')
这样一整个工作簿,老板看了直呼内行。