为何pandas.read_sas()性能远逊于pandas.read_csv()?求解析与优化
我来帮你拆解一下为什么两者性能差这么多,以及几个可以直接落地的优化方案:
一、性能差异的核心原因
SAS7BDAT格式的解析复杂度
SAS7BDAT是二进制格式,包含大量额外的元数据(变量类型、格式、标签、存储压缩信息等),pandas默认的read_sas底层依赖的纯Python实现sas7bdat库,解析这些元数据的开销远大于CSV的纯文本解析。而pd.read_csv默认使用C实现的引擎,经过多年优化,解析效率极高。分块读取的额外开销叠加
你的分块大小是50000,意味着要循环处理约1420次(7100万/5万)。每次read_sas分块都要重新解析部分元数据,而read_csv的分块读取在元数据处理上要轻量得多,循环次数多了之后,这个开销会被显著放大。日期列处理的叠加影响
虽然单看日期转换的开销不大,但在read_sas本身解析慢的基础上,每一次分块都要执行日期转换操作,相当于在慢的基础上又加了一层循环开销,进一步拉大了和read_csv的差距。Windows系统的IO特性
Windows的文件系统在处理二进制文件的随机读取时,效率不如类Unix系统,而read_sas的分块读取涉及更多随机IO操作,read_csv的顺序读取则更适配Windows的IO优化。
二、具体优化技巧
1. 切换到更高效的SAS读取引擎:pyreadstat
pyreadstat是用C语言实现的SAS文件读取库,性能比pandas默认的sas7bdat引擎快数倍,而且能自动解析SAS的日期格式(包括你这种从1960-1-1开始的日期间隔)。
代码示例:
首先安装pyreadstat:
pip install pyreadstat
然后用pandas调用它作为引擎:
import pandas as pd want = [] # pyreadstat会自动识别SAS日期格式,无需手动转换 for chunk in pd.read_sas('my_sas_table.sas7bdat', chunksize=1000000, encoding='cp1252', engine='pyreadstat'): want.append(chunk) df = pd.concat(want, ignore_index=True)
或者直接用pyreadstat的分块读取:
import pyreadstat reader = pyreadstat.read_sas7bdat('my_sas_table.sas7bdat', chunksize=1000000, encoding='cp1252') want = [] for df_chunk, meta in reader: # meta包含元数据,不需要可以忽略 want.append(df_chunk) df = pd.concat(want, ignore_index=True)
这个方法应该能把读取时间降到和CSV接近的水平。
2. 优化日期列的处理(如果需要手动处理)
如果因为某些原因不能用pyreadstat的自动转换,把日期转换改为更高效的numpy实现:
import numpy as np for chunk in pd.read_sas('my_sas_table.sas7bdat', chunksize=1000000, encoding='cp1252'): # 用numpy的timedelta计算,比pandas的to_timedelta快 chunk['DT_COL'] = pd.Timestamp('1960-1-1') + np.timedelta64(1, 'D') * chunk['DT_COL'].values want.append(chunk)
3. 增大分块大小
把chunksize从50000增大到100万甚至更大(根据你的内存情况调整),减少循环次数,降低每次分块的元数据解析开销。比如你的内存如果有16G以上,试试chunksize=2000000,循环次数从1420次降到36次,能大幅减少总开销。
4. 只读取需要的列
如果39列里不是所有列都需要,用usecols参数指定需要的列,减少读取的数据量:
# 假设你只需要DT_COL和另外5列 cols_to_read = ['DT_COL', 'COL1', 'COL2', 'COL3', 'COL4', 'COL5'] for chunk in pd.read_sas('my_sas_table.sas7bdat', chunksize=1000000, encoding='cp1252', engine='pyreadstat', usecols=cols_to_read): want.append(chunk)
5. 升级Python版本
你当前用的Python3.6已经停止维护,Python3.8+在IO、循环和内存管理上有很多性能优化,同时pandas在新版本上对read_sas和pyreadstat的集成也更好。升级到Python3.8或更高版本,能带来一定的性能提升。
6. 优化文件IO环境
- 把SAS文件放在SSD上,SSD的随机读取速度远快于HDD,能显著减少
read_sas的IO等待时间。 - 临时关闭Windows的实时保护或文件索引服务,避免系统后台进程干扰文件读取。
三、额外的进阶方案:用Dask并行处理
如果你的机器有多核CPU,可以用Dask来并行处理分块读取和转换,进一步提升速度:
import dask.dataframe as dd # 用Dask读取SAS文件,自动并行处理 ddf = dd.read_sas('my_sas_table.sas7bdat', chunksize=1000000, encoding='cp1252', engine='pyreadstat') # 如果需要手动处理日期 ddf['DT_COL'] = pd.Timestamp('1960-1-1') + np.timedelta64(1, 'D') * ddf['DT_COL'].values # 转换为pandas DataFrame df = ddf.compute()
内容的提问来源于stack exchange,提问作者Pype

