pandas遇到大数据,内存不够怎么办?

在处理小数据集时,比如1GB以下,pandas和SAS的性能,对我来说没有太大的区别。但如果数据大到好几个GB,pandas对于内存的消耗就显而易见了,经常由于内存不足导致运行失败。

假如有这样一个需求,从数据库中抽取某一个月的交易数据,然后保存在本地电脑。如果使用SAS的data步进行处理,加上where条件,run一下就会看到逻辑库对应的文件夹下,所需要的数据正在生成。只要硬盘够大,理论上多大的数据都可以通过data步写入硬盘。

为了减小数据集占用硬盘的大小,在data步中可以加入informat,format对抽取字段的长度进行限定。然而,使用pandas来处理时,所面临的首要问题就是,如何像SAS那样把这些数据读取下来,并且保存在硬盘中(python对象的持久化)。

一个思路就是分块处理,然后合并结果,不保留中间过程数据集,只要最终的处理结果;另一个思路就是利用一些方法进行优化,尝试最大限度减小pandas数据框对内存的消耗,然后再进行处理,如果需要保留中间结果,就把中间结果对象导出。

我们先来构造一个“大数据”集(只是为了举例和说明,这个所谓的大数据集并不大)。

数据集df有1000万行,float64,int64,object类型的字段各2个,总计占用内存450+MB,利用select_dtypes()按照字段类型筛选列,然后通过memory_usage()查看不同类型的字段所占用内存大小。在df这个数据集中,三种不同类型的字段各自占用的内存大小均为152MB。保存为hdf格式,压缩后整个文件的大小为142MB。

对于不同类型的字段,我们分别进行downcast,需要说明的是,字符类型的字段object,在转换为category之后,在底层实际上是用整数值来映射原始值,尤其是当某个object字段的唯一值仅有几个,明显小于字段长度时,这种转换能够较大程度地节省内存。通过对df数据集的downcast处理,内存消耗从457.8MB减小到了143MB,内存占用降低了约70%。以压缩的方式保存为hdf或是pkl格式的文件,downcast之前是142MB,downcast之后是70MB,对硬盘的占用空间也减小了一半。

如果原始数据再大一些,不能一次性读入内存呢?这就回到了文章前面提到的问题。这时候可以采用分块处理的方式,设置无论是读取csv,hdf5,还是从数据库读取数据,都可以设置chunksize参数分块来处理。我们将df数据集已经写入了mysql保存在test1表,假想这是一个很大的表,现在我们通过分块的方式来完成downcast,耗时大约5分钟,保存在硬盘上的my_test_data数据集大小约为98MB。下面的处理过程中,实际上在导出数据之前,对所有downcast之后的chunk进行了合并,如果这个数据仍然过大,同样很有可能出现memory error(又或者,如果只是需要一个简单的最终的汇总结果,可以将chunksize设置的再小一些,然后压根不需要downcast,不过chunksize过小消耗的时间一般来说也就越多)。

再“花哨”一点,可以给上面的函数增加一个处理进度条。如果是读取的table格式的hdf5数据集,可以通过计算循环i/chunk总个数的比例来显示百分比进度,如果是从数据库读取,显然不能一开始就得到所要读取的数据的行数,但可以通过时间来显示相对进度,就像下面这样:

总结:

为了减小数据集对内存和硬盘的空间占用,可以:

1、数值类型的字段可以downcast;

2、字符类型的字段可以转换为category;

3、设置chunksize参数,进行分块处理;

4、导出数据集时选择压缩;

最后感叹一下,就数据处理这一块,上面的所有问题对于SAS来说,似乎都不叫事!

参考资料:

https://www.cnblogs.com/xxr1/p/7397767.html

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181207B09XAP00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券