Pandas数据处理实战:从3小时报表到3分钟搞定

刚接手公司销售数据那会儿,我天真地以为用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的参数顺序,我第一次写的时候死活调不对,indexcolumns搞反了好几次。官方文档那段文档不够清晰,建议直接跑上面这段代码,改参数看看效果。

(总结前:生成的透视表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倍。

总结一下,你可以立刻用的三个点

  • 读数据必加info():花3秒看数据结构,省后面半小时排查错误
  • pivot_table替代Excel手动透视:一行代码生成报表,参数不会?把我上面那段复制过去改
  • 向量化操作替代循环applynp.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='合并数据')

    这样一整个工作簿,老板看了直呼内行。

    滚动至顶部