朋友们!!!今天咱们聊聊 Pandas —— 这个数据分析领域的瑞士军刀,也是无数人深夜 debug 的罪魁祸首(别问我怎么知道的)。用了这么多年 Pandas,我踩过的坑连起来能绕地球…… 好吧夸张了,但填满一个小水塘绰绰有余!今天就掏心窝子分享三个让我痛彻心扉后又爽到飞起的核心技巧,保你数据处理效率翻倍,头发少掉几根!
新手最爱干的事就是 pd.read_csv("我的宇宙无敌大文件.csv")
然后…… 眼睁睁看着内存占用飙升到 99%,风扇开始狂啸!(别笑,我干过!)
血泪教训实录: 那次处理一个 20GB 的日志文件,直接 read_csv
,等了 10 分钟,Jupyter 内核它… 它挂了!!(心态崩了啊!!!)
救星参数大揭秘:
dtype
(超级重要!!!)
int64
,但明明用 int32
甚至 category
就够了!df = pd.read_csv('big_file.csv', nrows=1000)
读个小样本,然后 print(df.dtypes)
看看数据类型。接着:dtype_dict = {
'user_id': 'int32', # 省一半内存!
'product_id': 'category', # 分类数据的神器,内存杀手克星!
'is_purchased': 'bool', # 比 object/string 省多了
'timestamp': 'str' # 先当字符串读,后续再转datetime
}
df = pd.read_csv('big_file.csv', dtype=dtype_dict)
usecols
(精兵简政!)
usecols=['user_id', 'purchase_time', 'amount']
,瞬间砍掉 70% 内存负担!parse_dates
和 infer_datetime_format
(时间杀手组合)
pd.to_datetime()
慢到怀疑人生?df = pd.read_csv(
'data.csv',
parse_dates=['order_time', 'delivery_time'], # 指定哪些列是日期
infer_datetime_format=True # 让它智能猜日期格式,加速转换!
)
效果对比: 上次那个 20GB 的文件,用了这三板斧,内存占用从 32GB+ (崩了) 降到了 8GB 左右,顺利跑完!(喜极而泣.jpg)
fillna(0)
了!fillna(0)
是快,但简直是数据界的粗暴拆迁!不分青红皂白全填 0,你知道会埋下多少雷吗?(真实案例预警⚠️)
翻车现场回放: 分析用户购买行为,看到 avg_purchase_amount
列有 NaN,想都没想 fillna(0)
。结果… 新用户(暂无购买记录)和老用户(真的买过 0 元商品,比如优惠券)混为一谈!导致新用户群体的分析完全失真!(被老板质问时脚趾抠出三室一厅…)
正确姿势工具箱:
df.isna().sum()
(先摸清敌情!)
分情况讨论 (灵魂所在!)
fillna(df['income'].median())
:用中位数填充,比均值更抗极端值干扰。fillna(method='ffill'/'bfill')
:用前一个/后一个有效值填充(适合有序数据,如时间序列)。fillna('Missing')
:直接标记为 “Missing”,保留“空值”本身的信息量!比乱填一个类别好得多。fillna(df['city'].mode()[0])
:用众数(出现最频繁的值)填充。fillna(method='ffill')
:尤其常用,用上一个时间点的值填充。终极武器:pd.NA
(Pandas 1.0+)
pd.NA
,比老旧的 np.nan
在类型一致性上表现更好(尤其在整数列和布尔列),减少意外错误。善用它!核心思想: 空值本身也是信息! 无脑填 0 是最懒(也最危险)的做法。想想这个空值为什么会出现?它代表了什么业务含义?再决定处理策略。
groupby
+ agg
玩出花,告别低效循环!多少人还在写 for group in df.groupby('city')
?然后循环里做各种操作?(举手,我以前也是!)效率低不说,代码还冗长!Pandas 的向量化操作才是王道!
性能痛点: 一个地级市销售数据分组统计,循环写法跑了 30 秒,用 groupby
+ agg
0.5 秒搞定!这就是差距!
超高效组合拳:
# 目标:按城市分组,计算每组的:
# - 销售额总和
# - 订单量
# - 平均订单金额
# - 最大单笔订单金额
# - 客户数(去重)
# - 销售额最高的产品ID (假设有product_id列)
result = df.groupby('city').agg(
total_sales=('sales_amount', 'sum'),
order_count=('order_id', 'count'), # 计数所有行
avg_order_value=('sales_amount', 'mean'),
max_order=('sales_amount', 'max'),
unique_customers=('customer_id', 'nunique'), # 去重计数!
top_product=('product_id', lambda x: x.value_counts().index[0]) # 匿名函数找频率最高
).reset_index() # 把city从索引变回列,方便后续操作
# 看!一行groupby+agg搞定所有统计!
print(result.head())
解释一下魔法:
groupby('city')
: 按城市分组。.agg()
: 聚合操作,传入一个 字典 或 元组列表。
total_sales
, order_count
等):你自定义的结果列名!(清晰明了)(要操作的列名, 聚合函数/字符串)
。
'sum'
, 'mean'
, 'count'
, 'nunique'
, 'max'
, 'min'
, 'std'
等是内置快捷方式。top_product
示例) 实现复杂逻辑!超灵活!.reset_index()
: 把分组依据 city
从索引变回普通的列。不加这句的话,city
会作为结果的索引,有时候不方便。优势爆炸:
这三个技巧,看着简单吧?(现在看是简单!)但都是我用无数个加班的夜晚和差点报废的内存条换来的经验啊!Pandas 文档博大精深,很多坑官方其实都写了(但谁会逐字逐句看文档呢?),更多时候就是靠实践(和踩坑)出真知。
记住这三点核心思想:
groupby
+agg
/apply
就别写循环!把这“三招鲜”吃透,绝对能让你在数据处理的道路上少走 80% 的弯路!快去试试吧,保证你直呼“真香”!(要是还踩坑了… 欢迎来吐槽,咱们一起填坑!)