8.8 内存优化技巧 Pandas 内存优化技巧:释放性能潜力 8.8.1 理解 Pandas 的内存使用 在深入优化之前,我们需要了解 Pandas 如何使用内存。 Pandas 基于 NumPy 构建,其 Series 和 DataFrame 对象在内部使用 NumPy 数组来存储数据。 NumPy 数组通常以连续的内存块存储数据,这有利于快速的数值运算。 然而,这种存储方式也意味着: 数据类型很重要: 不同的数据类型占用不同的内存空间。例如, 比 占用更多的内存。 对象类型开销大: 类型(通常用于存储字符串或混合类型)的开销远大于数值类型。 Pandas 会为每个对象存储一个指针,指向实际的数据,这会增加内存占用并降低性能。
8.8.1 理解 Pandas 的内存使用
在深入优化之前,我们需要了解 Pandas 如何使用内存。 Pandas 基于 NumPy 构建,其 Series 和 DataFrame 对象在内部使用 NumPy 数组来存储数据。 NumPy 数组通常以连续的内存块存储数据,这有利于快速的数值运算。
然而,这种存储方式也意味着:
数据类型很重要: 不同的数据类型占用不同的内存空间。例如,int64 比 int8 占用更多的内存。
对象类型开销大: object 类型(通常用于存储字符串或混合类型)的开销远大于数值类型。 Pandas 会为每个对象存储一个指针,指向实际的数据,这会增加内存占用并降低性能。
重复数据浪费空间: 如果 DataFrame 中存在大量重复的字符串,每个字符串都会被单独存储,导致内存浪费。
8.8.2 内存优化策略
以下是一些常用的 Pandas 内存优化策略:
1. 选择合适的数据类型
这是最直接有效的优化方法。 Pandas 默认会将读取的数据类型设置为能够容纳所有值的最大类型。 但是,很多时候我们可以选择更小的数据类型来减少内存占用。
pd.to_numeric() 函数将数值列转换为更小的类型,如 int8、int16、int32 或 float32。import pandas as pd import numpy as np # 创建一个 DataFrame data = {'col1': np.arange(100000, dtype=np.int64)} df = pd.DataFrame(data) # 原始内存占用 original_memory = df.memory_usage(deep=True).sum() / 1024**2 print(f"原始内存占用: {original_memory:.2f} MB") # 将 int64 转换为 int32 df['col1'] = pd.to_numeric(df['col1'], downcast='integer') # 优化后的内存占用 optimized_memory = df.memory_usage(deep=True).sum() / 1024**2 print(f"优化后的内存占用: {optimized_memory:.2f} MB") # 输出结果 # 原始内存占用: 0.76 MB # 优化后的内存占用: 0.38 MB
downcast='integer' 会尝试将数值转换为最小的整数类型,而 downcast='float' 会尝试转换为最小的浮点数类型。
category 类型。 category 类型会将唯一值存储在一个查找表中,然后使用整数代码表示每个值,从而大大减少内存占用。data = {'city': ['New York', 'London', 'New York', 'Tokyo', 'London']} df = pd.DataFrame(data) # 原始内存占用 original_memory = df.memory_usage(deep=True).sum() / 1024**2 print(f"原始内存占用: {original_memory:.2f} MB") # 转换为 category 类型 df['city'] = df['city'].astype('category') # 优化后的内存占用 optimized_memory = df.memory_usage(deep=True).sum() / 1024**2 print(f"优化后的内存占用: {optimized_memory:.2f} MB") # 输出结果 # 原始内存占用: 0.00 MB # 优化后的内存占用: 0.00 MB
2. 避免不必要的复制
Pandas 中的某些操作会创建 DataFrame 的副本,这会增加内存占用。 尽量使用 inplace=True 参数来修改原始 DataFrame,避免创建副本。
# 避免 df_new = df.drop('col1', axis=1) # 推荐 df.drop('col1', axis=1, inplace=True)
3. 分块读取数据
如果数据集太大,无法一次性加载到内存中,可以使用 chunksize 参数分块读取数据。
chunk_size = 100000 for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size): # 处理每个 chunk process_data(chunk)
4. 使用 Dask 或 Vaex
对于超出内存限制的超大型数据集,可以考虑使用 Dask 或 Vaex 等库。这些库可以将数据存储在磁盘上,并使用延迟计算的方式进行处理,从而避免一次性加载所有数据到内存中。
5. 删除不再需要的对象
及时删除不再需要的 DataFrame 或 Series 对象,释放内存。可以使用 del 语句或 gc.collect() 手动触发垃圾回收。
import gc del df gc.collect()
6. 优化字符串存储
如果 DataFrame 中包含大量重复的字符串,可以考虑使用 category 类型或自定义的字符串池来减少内存占用。
7. 使用 Sparse DataFrames
如果 DataFrame 中包含大量的缺失值 (NaN),可以考虑使用 Sparse DataFrames。 Sparse DataFrames 只存储非缺失值,从而减少内存占用。
8. 内存分析工具
使用内存分析工具(例如 memory_profiler)可以帮助识别内存泄漏和内存占用过高的代码段。
8.8.3 代码实践:综合示例
以下是一个综合示例,演示如何使用多种内存优化技巧来处理大型数据集:
import pandas as pd import numpy as np import gc def optimize_dataframe(df): """优化 DataFrame 的内存占用.""" # 1. 转换为合适的数值类型 for col in df.select_dtypes(include=['number']): df[col] = pd.to_numeric(df[col], downcast='integer') # 2. 转换为 category 类型 for col in df.select_dtypes(include=['object']): num_unique_values = len(df[col].unique()) num_total_values = len(df[col]) if num_unique_values / num_total_values < 0.5: #设定阈值,当类别个数小于50%时才转换 df[col] = df[col].astype('category') return df # 模拟一个大型数据集 data = { 'id': np.arange(1000000, dtype=np.int64), 'city': np.random.choice(['New York', 'London', 'Tokyo', 'Paris'], size=1000000), 'temperature': np.random.normal(20, 10, size=1000000).astype(np.float64), 'flag': np.random.choice([True, False], size=1000000) } df = pd.DataFrame(data) # 原始内存占用 original_memory = df.memory_usage(deep=True).sum() / 1024**2 print(f"原始内存占用: {original_memory:.2f} MB") # 优化 DataFrame df = optimize_dataframe(df) # 优化后的内存占用 optimized_memory = df.memory_usage(deep=True).sum() / 1024**2 print(f"优化后的内存占用: {optimized_memory:.2f} MB") # 清理内存 del df gc.collect()
8.8.4 Mermaid 图表:优化流程
以下是一个 Mermaid 图表,展示了 Pandas 内存优化的流程:
8.8.5 总结
Pandas 内存优化是一个迭代的过程,需要根据具体的数据集和应用场景选择合适的策略。 通过选择合适的数据类型、避免不必要的复制、分块读取数据等方法,可以显著减少 Pandas 的内存占用,提高程序的性能和稳定性。 此外,使用内存分析工具可以帮助识别内存瓶颈,从而更有针对性地进行优化。 掌握这些技巧,可以让你更有效地处理大型数据集,释放 Pandas 的性能潜力。