Python高效数据处理——从基础方法到性能优化
wptr33 2025-07-15 01:27 24 浏览
数据处理是数据分析的核心环节,高效的数据处理方法能显著提升代码性能。本文将深入介绍Pandas中的各种数据处理技术,并分析它们的性能特点。
使用apply方法应用自定义函数
apply是Pandas中最灵活的数据处理方法之一,可以对Series或DataFrame的行/列应用自定义函数。
Series的apply方法
import pandas as pd
import numpy as np
# 创建示例Series
s = pd.Series([1, 2, 3, 4, 5])
# 使用apply应用平方函数
squared = s.apply(lambda x: x**2)
print("平方后的Series:")
print(squared)
# 使用命名函数
def add_prefix(x):
return f"值_{x}"
prefixed = s.apply(add_prefix)
print("\n添加前缀后的Series:")
print(prefixed)
DataFrame的apply方法
# 创建示例DataFrame
df = pd.DataFrame({
'A': np.random.randint(1, 10, 5),
'B': np.random.randn(5),
'C': np.random.choice(['X', 'Y', 'Z'], 5)
})
# 对列应用函数
col_sum = df.apply(np.sum, axis=0) # axis=0对列操作
print("\n列求和结果:")
print(col_sum)
# 对行应用函数
row_max = df.apply(np.max, axis=1) # axis=1对行操作
print("\n行最大值结果:")
print(row_max)
# 复杂行处理
def process_row(row):
return f"{row['C']}_{round(row['A'] * row['B'], 2)}"
processed = df.apply(process_row, axis=1)
print("\n复杂行处理结果:")
print(processed)
性能考虑
# 创建大数据集测试性能
big_df = pd.DataFrame(np.random.randn(10000, 4), columns=['A', 'B', 'C', 'D'])
# 测试apply性能
%timeit big_df.apply(np.sum, axis=0) # 列操作通常较快
%timeit big_df.apply(np.sum, axis=1) # 行操作较慢
使用map和applymap方法
map方法(Series专用)
# 创建示例Series
s = pd.Series(['cat', 'dog', 'cat', 'bird'])
# 简单映射
simple_map = s.map({'cat': '猫', 'dog': '狗', 'bird': '鸟'})
print("\n简单映射结果:")
print(simple_map)
# 函数映射
length_map = s.map(lambda x: len(x))
print("\n字符串长度映射:")
print(length_map)
# 使用str方法替代简单映射
print("\nstr方法实现:")
print(s.str.len())
applymap方法(DataFrame专用)
# 创建示例DataFrame
df = pd.DataFrame(np.random.randn(3, 3), columns=['A', 'B', 'C'])
# 对所有元素应用函数
rounded = df.applymap(lambda x: round(x, 2))
print("\n四舍五入后的DataFrame:")
print(rounded)
# 条件应用
def format_cell(x):
if x > 0:
return f"+{x:.2f}"
return f"{x:.2f}"
formatted = df.applymap(format_cell)
print("\n格式化后的DataFrame:")
print(formatted)
性能对比
# 比较applymap与向量化操作
%timeit df.applymap(lambda x: x * 2)
%timeit df * 2 # 向量化操作快得多
使用iterrows和itertuples进行迭代
虽然Pandas通常推荐向量化操作,但有时迭代是必要的。
iterrows方法
# 创建示例DataFrame
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'age': [25, 30, 35],
'score': [85, 92, 78]
})
# 使用iterrows迭代
print("\n使用iterrows迭代:")
for index, row in df.iterrows():
print(f"索引: {index}, 姓名: {row['name']}, 年龄+分数: {row['age'] + row['score']}")
itertuples方法(更快)
# 使用itertuples迭代
print("\n使用itertuples迭代:")
for row in df.itertuples():
print(f"索引: {row.Index}, 姓名: {row.name}, 年龄+分数: {row.age + row.score}")
性能对比
# 创建大数据集
big_df = pd.DataFrame(np.random.randn(10000, 4), columns=['A', 'B', 'C', 'D'])
# 比较各种迭代方法
%timeit for index, row in big_df.iterrows(): pass # 最慢
%timeit for row in big_df.itertuples(): pass # 较快
%timeit for col in big_df.items(): pass # 列迭代
使用vectorize加速数值计算
NumPy的vectorize函数可以将Python函数转换为向量化函数,提高性能。
基础vectorize使用
import numpy as np
# 定义普通Python函数
def my_func(x):
if x > 0:
return x ** 2
else:
return x / 2
# 创建数组
arr = np.array([1, -2, 3, -4, 5])
# 普通Python循环
result = [my_func(x) for x in arr]
print("普通循环结果:", result)
# 使用vectorize
vec_func = np.vectorize(my_func)
vec_result = vec_func(arr)
print("\nvectorize结果:", vec_result)
性能对比
# 创建大型数组
big_arr = np.random.randn(1000000)
# 性能测试
%timeit [my_func(x) for x in big_arr] # Python循环
%timeit np.vectorize(my_func)(big_arr) # vectorize
%timeit np.where(big_arr > 0, big_arr**2, big_arr/2) # 纯NumPy向量化
结合Pandas使用
# 在Pandas中使用vectorize
df = pd.DataFrame(np.random.randn(1000, 3), columns=['A', 'B', 'C'])
# 定义复杂函数
def complex_calc(a, b, c):
return a**2 + b*3 + np.sqrt(abs(c))
# 普通apply方法
%timeit df.apply(lambda row: complex_calc(row['A'], row['B'], row['C']), axis=1)
# 使用vectorize
vec_complex = np.vectorize(complex_calc)
%timeit vec_complex(df['A'], df['B'], df['C'])
实战案例:电商数据分析
# 创建模拟电商数据集
np.random.seed(42)
n = 100000
data = {
'order_id': range(1000, 1000 + n),
'user_id': np.random.randint(100, 200, n),
'product_id': np.random.choice(['A100', 'B200', 'C300', 'D400'], n),
'price': np.random.uniform(10, 500, n).round(2),
'quantity': np.random.randint(1, 10, n),
'is_member': np.random.choice([True, False], n)
}
ecom_df = pd.DataFrame(data)
# 计算总金额
# 方法1: 简单列运算 (最快)
ecom_df['total'] = ecom_df['price'] * ecom_df['quantity']
# 方法2: 使用apply (较慢但灵活)
def calc_total(row):
discount = 0.9 if row['is_member'] else 1.0
return row['price'] * row['quantity'] * discount
ecom_df['total_with_discount'] = ecom_df.apply(calc_total, axis=1)
# 方法3: 使用vectorize (折中方案)
@np.vectorize
def vec_calc_total(price, quantity, is_member):
discount = 0.9 if is_member else 1.0
return price * quantity * discount
ecom_df['total_vec'] = vec_calc_total(
ecom_df['price'],
ecom_df['quantity'],
ecom_df['is_member']
)
# 性能比较
print("\n性能比较:")
%timeit ecom_df['price'] * ecom_df['quantity']
%timeit ecom_df.apply(calc_total, axis=1)
%timeit vec_calc_total(ecom_df['price'], ecom_df['quantity'], ecom_df['is_member'])
最佳实践与性能优化
方法选择指南
场景 | 推荐方法 | 备注 |
简单列运算 | 直接向量化运算 | df['A'] + df['B'] |
复杂行处理 | apply(axis=1) | 灵活性高但性能较低 |
元素级转换 | applymap或map | DataFrame或Series专用 |
高性能迭代 | itertuples | 比iterrows快得多 |
数值计算加速 | np.vectorize | 介于纯Python和纯NumPy之间 |
性能优化技巧
# 1. 避免链式操作
# 不推荐
result = df[df['A'] > 0]['B']
# 推荐
result = df.loc[df['A'] > 0, 'B']
# 2. 使用eval实现高效列运算
expr = "(A + B) / (C - D)"
result = df.eval(expr)
# 3. 使用category类型减少内存
df['category_col'] = df['category_col'].astype('category')
常见陷阱与解决方案
# 陷阱1: apply中的意外副作用
def bad_func(row):
print(row) # 副作用:打印
return row['A'] + row['B']
# 解决方案:确保函数是纯函数
# 陷阱2: 修改迭代中的行无效
for index, row in df.iterrows():
row['A'] = 0 # 不会修改原DataFrame
# 解决方案:使用.loc直接修改
for index, _ in df.iterrows():
df.loc[index, 'A'] = 0
总结与进阶学习
核心要点总结
- apply:灵活但性能不高,适合复杂行/列操作
- map/applymap:元素级转换的专用方法
- 迭代方法:itertuples > iterrows,但都应作为最后选择
- vectorize:加速Python函数的好方法,但仍不如纯NumPy操作
进阶学习方向
# 1. 使用Numba加速Python函数
# from numba import vectorize
# @vectorize
# def numba_func(x): ...
# 2. 使用Dask处理超大数据集
# import dask.dataframe as dd
# ddf = dd.from_pandas(df, npartitions=4)
# 3. 使用Cython编写高性能扩展
# %load_ext Cython
# %%cython
# def cython_func(): ...
掌握这些高效数据处理技巧后,我们就可以处理各种规模的数据分析任务了。记住,在Pandas中,向量化操作通常是最佳选择,只有在必要时才使用迭代方法。
相关推荐
- MySQL进阶五之自动读写分离mysql-proxy
-
自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...
- 3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?
-
引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...
- 一文由浅入深带你完全掌握MySQL的锁机制原理与应用
-
本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...
- 验证Mysql中联合索引的最左匹配原则
-
后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...
- MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)
-
目录1.索引基础...
- 你会看 MySQL 的执行计划(EXPLAIN)吗?
-
SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...
- MySQL 从入门到精通(四)之索引结构
-
索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...
- mysql总结——面试中最常问到的知识点
-
mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...
- mysql总结——面试中最常问到的知识点(2)
-
首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...
- MySQL基础全知全解!超详细无废话!轻松上手~
-
本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...
- 深入剖析 MySQL 中的锁机制原理_mysql 锁详解
-
在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...
- Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析
-
引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...
- MySQL基础篇:DQL数据查询操作_mysql 查
-
一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...
- MySql:索引的基本使用_mysql索引的使用和原理
-
一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
Java面试必考问题:什么是乐观锁与悲观锁
-
如何将AI助手接入微信(打开ai手机助手)
-
redission YYDS spring boot redission 使用
-
SparkSQL——DataFrame的创建与使用
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)