Pandas 高级技巧与最佳实践
Pandas 高级技巧与最佳实践
🎯目标:掌握 Pandas 的进阶用法和性能优化技巧,成为 Pandas 高手。
11.1 性能优化
11.1.1 向量化 vs 循环
importpandasaspdimportnumpyasnpimporttime# 创建大数据集n=1_000_000df=pd.DataFrame({'A':np.random.randn(n),'B':np.random.randn(n)})# ❌ 慢:用循环defloop_add(df):result=[]foriinrange(len(df)):result.append(df.iloc[i]['A']+df.iloc[i]['B'])returnresult start=time.time()loop_result=loop_add(df)print(f"循环耗时:{time.time()-start:.2f}秒")# ✅ 快:用向量化start=time.time()vector_result=df['A']+df['B']print(f"向量化耗时:{time.time()-start:.4f}秒")💡黄金法则:永远不要对 DataFrame 用循环!
11.1.2 使用合适的数据类型
# 创建示例数据df=pd.DataFrame({'int64_col':np.random.randint(0,100,1000000),'float64_col':np.random.randn(1000000),'category_col':np.random.choice(['A','B','C','D'],1000000),'object_col':np.random.choice(['北京','上海','广州','深圳'],1000000)})print("原始内存使用:")print(df.memory_usage(deep=True).sum()/1024**2,"MB")# 🌟 优化数据类型df_optimized=df.copy()df_optimized['int64_col']=df_optimized['int64_col'].astype('int32')# int64 → int32df_optimized['float64_col']=df_optimized['float64_col'].astype('float32')# float64 → float32df_optimized['category_col']=df_optimized['category_col'].astype('category')df_optimized['object_col']=df_optimized['object_col'].astype('category')print("\n优化后内存使用:")print(df_optimized.memory_usage(deep=True).sum()/1024**2,"MB")11.1.3 使用 eval 和 query
# 创建大数据df=pd.DataFrame(np.random.randn(100000,4),columns=['A','B','C','D'])# 🌟 eval:高效计算# 普通方式df['E']=df['A']+df['B']*df['C']-df['D']# eval 方式(更快)df['E']=df.eval('A + B * C - D')# 🌟 query:高效筛选# 普通方式result=df[(df['A']>0)&(df['B']<0)]# query 方式(更快)result=df.query('A > 0 and B < 0')11.2 内存优化
11.2.1 分块读取大文件
# 🌟 分块读取 CSVchunk_size=100000chunks=[]forchunkinpd.read_csv('large_file.csv',chunksize=chunk_size):# 处理每个 chunkprocessed=chunk[chunk['value']>0]# 示例处理chunks.append(processed)# 合并结果df=pd.concat(chunks,ignore_index=True)11.2.2 使用迭代器
# 🌟 迭代器方式读取forchunkinpd.read_csv('large_file.csv',chunksize=10000):forrowinchunk.itertuples():# 处理每一行pass11.2.3 删除中间变量
# ❌ 内存占用大df1=pd.read_csv('data.csv')df2=df1[df1['A']>0]df3=df2.groupby('B').sum()df4=df3.reset_index()# ✅ 及时释放内存df=pd.read_csv('data.csv')df=df[df['A']>0]df=df.groupby('B').sum()df=df.reset_index()11.3 链式操作
11.3.1 方法链式调用
# ❌ 传统方式(多个变量)df=pd.read_csv('data.csv')df=df[df['A']>0]df=df.dropna()df=df.assign(C=lambdax:x['A']+x['B'])df=df.groupby('D').sum()# ✅ 链式操作(一个表达式)result=(pd.read_csv('data.csv').query('A > 0').dropna().assign(C=lambdax:x['A']+x['B']).groupby('D').sum().reset_index())11.3.2 使用 pipe
# 🌟 自定义函数deffilter_positive(df,col):returndf[df[col]>0]defadd_ratio(df,num_col,den_col):returndf.assign(ratio=df[num_col]/df[den_col])# 🌟 使用 pipe 链式调用result=(pd.read_csv('data.csv').pipe(filter_positive,'sales').pipe(add_ratio,'profit','sales').groupby('category').agg({'ratio':'mean'}))11.4 实用技巧合集
11.4.1 快速查看数据
df=pd.DataFrame(np.random.randn(1000,10),columns=[f'col_{i}'foriinrange(10)])# 🌟 快速查看print(df.head())# 前5行print(df.tail())# 后5行print(df.sample(5))# 随机5行print(df.describe())# 统计摘要print(df.info())# 数据信息print(df.dtypes)# 数据类型print(df.shape)# 形状print(df.columns.tolist())# 列名列表print(df.index)# 索引11.4.2 快速选择数据
# 🌟 快速选择# 选择数值列numeric_cols=df.select_dtypes(include=[np.number]).columns# 选择字符串列string_cols=df.select_dtypes(include=['object']).columns# 选择列名包含特定字符串的列df.filter(like='col_1')# 选择列名匹配正则表达式的列df.filter(regex='col_[0-5]')11.4.3 快速处理缺失值
df=pd.DataFrame({'A':[1,2,np.nan,4],'B':[5,np.nan,np.nan,8],'C':[9,10,11,12]})# 🌟 快速查看缺失值print(df.isnull().sum())# 每列缺失值数量print(df.isnull().sum().sum())# 总缺失值数量print(df.isnull().mean()*100)# 缺失值百分比# 🌟 快速删除print(df.dropna())# 删除包含缺失值的行print(df.dropna(axis=1))# 删除包含缺失值的列print(df.dropna(thresh=2))# 保留至少2个非缺失值的行# 🌟 快速填充print(df.fillna(0))# 用0填充print(df.fillna(df.mean()))# 用均值填充print(df.fillna(method='ffill'))# 前向填充print(df.fillna(method='bfill'))# 后向填充11.4.4 快速数据转换
df=pd.DataFrame({'A':[1,2,3,4],'B':['a','b','c','d'],'C':[1.5,2.5,3.5,4.5]})# 🌟 类型转换print(df.astype({'A':'float64','C':'int32'}))# 🌟 字符串操作df['B_upper']=df['B'].str.upper()df['B_len']=df['B'].str.len()# 🌟 数值操作df['A_squared']=df['A'].pow(2)df['A_log']=np.log(df['A'])# 🌟 条件赋值df['A_category']=np.where(df['A']>2,'high','low')11.5 调试技巧
11.5.1 查看中间结果
# 🌟 使用 pipe 查看中间结果defdebug_print(df,message=""):print(f"\n{message}")print(f"Shape:{df.shape}")print(df.head())returndf result=(pd.read_csv('data.csv').pipe(debug_print,"After loading").query('A > 0').pipe(debug_print,"After filtering").groupby('B').sum().pipe(debug_print,"After grouping"))11.5.2 使用选项设置
# 🌟 显示设置pd.set_option('display.max_rows',100)# 最大显示行数pd.set_option('display.max_columns',50)# 最大显示列数pd.set_option('display.width',200)# 显示宽度pd.set_option('display.float_format','{:.2f}'.format)# 浮点数格式# 🌟 重置设置pd.reset_option('all')11.6 常见陷阱与解决方案
11.6.1 SettingWithCopyWarning
df=pd.DataFrame({'A':[1,2,3],'B':[4,5,6]})# ❌ 警告:SettingWithCopyWarningdf[df['A']>1]['B']=100# 可能不生效# ✅ 正确方式1:使用 locdf.loc[df['A']>1,'B']=100# ✅ 正确方式2:使用 copysubset=df[df['A']>1].copy()subset['B']=10011.6.2 索引问题
# ❌ 问题:重置索引后丢失原索引df=df.reset_index()# ✅ 保留原索引df=df.reset_index(drop=False)# 保留为列df=df.reset_index(drop=True)# 完全删除11.6.3 类型推断问题
# ❌ 问题:整数列有缺失值会变成 floatdf=pd.DataFrame({'A':[1,2,None,4]})print(df.dtypes)# float64# ✅ 使用 nullable 整数类型df=pd.DataFrame({'A':pd.array([1,2,None,4],dtype=pd.Int64Dtype())})print(df.dtypes)# Int6411.7 与其他库的集成
11.7.1 NumPy
# 🌟 DataFrame 和 NumPy 数组互转df=pd.DataFrame(np.random.randn(100,4),columns=['A','B','C','D'])arr=df.values# 转 NumPy 数组df2=pd.DataFrame(arr,columns=['A','B','C','D'])# 转 DataFrame# 🌟 使用 NumPy 函数print(np.sqrt(df))print(np.log(df))print(np.mean(df,axis=0))11.7.2 Matplotlib
importmatplotlib.pyplotasplt df=pd.DataFrame({'A':np.random.randn(100).cumsum(),'B':np.random.randn(100).cumsum()},index=pd.date_range('2024-01-01',periods=100))# 🌟 快速绘图df.plot(figsize=(10,6))plt.title('Time Series Plot')plt.show()# 🌟 子图df.plot(subplots=True,figsize=(10,8))plt.show()11.7.3 Scikit-learn
fromsklearn.preprocessingimportStandardScalerfromsklearn.model_selectionimporttrain_test_split df=pd.DataFrame(np.random.randn(100,4),columns=['A','B','C','D'])# 🌟 数据预处理scaler=StandardScaler()df_scaled=pd.DataFrame(scaler.fit_transform(df),columns=df.columns,index=df.index)# 🌟 划分训练集和测试集train,test=train_test_split(df,test_size=0.2)11.8 本章小结
核心要点
✅性能优化:
- 使用向量化操作,避免循环
- 选择合适的数据类型
- 使用 eval 和 query
✅内存优化:
- 分块读取大文件
- 及时删除中间变量
- 使用 category 类型
✅链式操作:
- 使用方法链提高可读性
- 使用 pipe 自定义函数
✅实用技巧:
- 快速查看数据
- 快速选择和处理数据
- 调试技巧
✅避免陷阱:
- SettingWithCopyWarning
- 索引问题
- 类型推断问题
最佳实践清单
- 使用向量化操作
- 选择合适的数据类型
- 使用链式操作提高可读性
- 及时释放内存
- 使用 loc 进行赋值
- 处理大文件时使用分块读取
- 使用 pipe 进行调试
