大家好,高效的数据处理是使用Pandas的基石,特别是在处理大型数据集时。本文将重点介绍如何优化数据加载过程,这其中涵盖关键策略,如优化数据类型和使用分块加载,并深入探讨其他方法,如选择性列加载、指定日期列、使用转换器、跳过行、内存映射和选择高效的文件格式。每种方法都附有实用的代码示例,使大家能够轻松将这些技巧融入到工作流程中。
选择高效的数据类型是减少内存使用和加快数据加载的关键方法。下表比较了常见的数据类型及其占用内存较少的替代方案:
代价较大的数据类型 | 高效的替代方案 |
---|---|
int64 | int32或int16 |
float64 | float32 |
object | category(适用于有限唯一值) |
虽然这些转换可以显著优化性能,但仍需谨慎。更改数据类型有时可能会导致意想不到的后果。
范围限制:
如果值超过int16
的范围,将int64
转换为int16
可能会导致溢出。
import?pandas?as?pd
import?numpy?as?np
import?logging
#?使用int64的原始DataFrame
df?=?pd.DataFrame({'int_column':?[np.iinfo(np.int64).max,?100,?200]})
logging.warning(f'original?dataframe?is?\n{df}')
#?尝试转换为int16
df['int_column']?=?df['int_column'].astype('int16')
logging.warning(f'converted?dataframe?is?\n{df}')
结果如下:
WARNING:root:original?dataframe?is?
????????????int_column
0??9223372036854775807
1??????????????????100
2??????????????????200
WARNING:root:converted?dataframe?is?
???int_column
0??????????-1
1?????????100
2?????????200
可以看到int_column
的第一个条目由一个非常大的数变为-1
,这是由于类型转换时数值溢出。
浮点数精度:
将float64
的精度降低为float32
可能会导致舍入误差。虽然舍入误差可能看起来很小,但在需要高数值精度的情况下(如科学计算或金融计算),这一点非常重要。
#?使用float64的原始DataFrame
df?=?pd.DataFrame({'float_column':?[1.23456789012345,?2.34567890123456]})
#?转换为float32
df['float_column']?=?df['float_column'].astype('float32')
print(df)
分类转换:
将对象转换为分类类型可能会导致出现意外的唯一值问题。
df?=?pd.DataFrame({'object_column':?['apple',?'banana',?'apple',?'orange']})
#?转换为分类类型
df['object_column']?=?df['object_column'].astype('category')
#?添加新的一行,其中包含一个意外的类别
df?=?df.append({'object_column':?'kiwi'},?ignore_index=True)
print(df)
在附加了新值'kiwi'
之后,这个值不是原始类别集的一部分,'object_column'
的数据类型已经更改为?dtype('O')
,表示为对象数据类型。这意味着该列不再属于分类类型。
那么在开始时将原始对象类型转换为分类类型的意义是什么?选择使用分类类型而不是对象类型通常是出于减少内存使用和提高性能的需求,正如前面所述。如果列中包含大量重复值(例如性别、国家名称、产品类别),将其转换为分类类型可以显著减少内存占用,并加快计算速度。
处理空值:
整数列默认不支持np.nan
,需要使用可为空的整数类型来解决这些问题。为此,Pandas引入了可空整数类型("Int8"
、"Int16"
、"Int32"
、"Int64"
等)。这些类型专门设计用于处理缺失数据,同时保留整数数据类型。
df?=?pd.DataFrame({'int_column':?[1,?np.nan,?3]})
#?尝试转换为普通int类型会导致错误
#?使用可空整数类型代替
df['int_column']?=?df['int_column'].astype('Int32')
print(df)
没有任何错误,输出结果为:
???int_column
0???????????1
1????????<NA>
2???????????3
但当Pandas数据帧需要与其他使用int64
、int32
等的库进行交互时,需要谨慎操作。因为大写的"Int16"
、"Int32"
等是Pandas特定的数据类型,可能与其他库不兼容。
以上每个示例都说明了在更改数据类型时可能出现的问题。了解这些潜在问题后,可以在优化数据时做出更明智的决策。
对于非常大的数据集,将整个数据集加载到内存中可能并不可行。Pandas允许将数据分成较小的块加载,每次处理一个块。
chunk_size?=?10000??#?每个数据块的行数
for?chunk?in?pd.read_csv('huge_dataset.csv',?chunksize=chunk_size):
????process(chunk)
在基于数据块的加载示例中,process(chunk)
代表一个占位符,用于在每个数据块上执行任何数据处理操作。性能上的主要区别在于减少了内存使用量,并且可以在加载整个数据集之前就开始处理数据。当处理的数据集太大,无法一次性放入内存时,这种方法尤其有益。
可以通过将'huge_dataset.csv'
替换为自己的数据集来测试上述代码,并将其与Pandas的常规加载进行比较。可以观察到基于块的加载所带来的效率提升,特别是在数据集的大小增加时。
在加载数据时,可以指定要加载的列。如果数据集有许多列,但只需要其中的一部分进行分析。
#?只加载特定列
df?=?pd.read_csv('data.csv',?usecols=['Column1',?'Column2'])
如果数据集包含日期字段,在加载时指定它们可以节省时间,因为Pandas会立即将它们解析为日期,而不需要后续进行单独的转换步骤。
#?加载列并解析日期
df?=?pd.read_csv('data.csv',?parse_dates=['DateColumn'])
转换器允许在加载过程中对数据应用函数。在使用Pandas加载数据时使用转换器函数确实可以帮助进行内存优化,但其主要目的通常是数据清理或转换,而不是节省内存。然而,在某些情况下,它可以间接地提高内存效率,这样就可以避免在加载后创建额外的中间数据结构进行后处理。
#?定义转换器函数
def?converter_function(value):
????return?value.strip()
#?在加载过程中使用转换器
df?=?pd.read_csv('data.csv',?converters={'Column':?converter_function})
如果数据集包含标题、页脚或其他非必要行,可以在加载时跳过它们,以节省时间和内存。
#?跳过文件的前10行
df?=?pd.read_csv('data.csv',?skiprows=10)
对于非常大的数据集,内存映射是一种有效的方式,可以在不完全加载文件到内存中的情况下访问大型文件。
#?对大型文件使用内存映射
df?=?pd.read_csv('large_data.csv',?memory_map=True)
这听起来与分块处理类似,但实际上两者有不同的目的。
内存映射更适用于需要随机访问文件不同部分的场景,而不需要复杂的转换。它适用于大文件上的读取操作。
相反,分块处理更适合需要执行复杂的数据处理或转换的场景,由于受到内存限制,这些处理无法在一次加载中完成。它非常适用于可以接受或需要对数据进行顺序处理的场景。
不同的文件格式会对加载时间有重大影响,Parquet或HDF5等格式通常比传统的CSV或Excel文件更高效。
#?加载Parquet文件
df?=?pd.read_parquet('data.parquet')
Parquet和HDF5等格式支持压缩,从而减少了文件大小和需要加载到内存中的数据量。
与选择性列加载类似,Parquet使用列式存储格式,对于某些类型的查询和操作更高效,特别是涉及到子集列的情况。这可以减少加载特定列时的内存占用。
另外,整数和日期可以以更高效的方式存储,而不是在CSV文件中表示为文本。
因此,如果能够从一开始就将数据存储在更高效的格式(如Parquet和HDF5)中,那么上述许多方法就不再需要了。
要创建Parquet或HDF5格式的文件,可以使用Pandas和其他附加库:
Parquet:需要pyarrow
或fastparquet
库。
df.to_parquet('filename.parquet',?engine='pyarrow')
HDF5:使用h5py
或PyTables
库。
df.to_hdf('filename.h5',?key='data',?format='table')
多种优化技巧:探讨了几种在Pandas中优化数据加载的方法,包括数据类型优化、分块处理、选择性列加载、日期解析等。
了解权衡:每种方法都有其利弊权衡。了解这些可以帮助你根据特定的数据和需求做出明智的决策。
从源头解决问题:将数据存储在高效的格式(如Parquet和HDF5)中,可以从一开始就解决问题。