降低数据存储消耗并提高处理效率,本文教你如何通过优化数值类型、object
类型和
datetime
类型三个方面来减少内存使用量。其中,使用
Categoricals
优化
object
类型数据是效果最显著的一种优化方式。另外,还介绍了如何在读取数据的时候就完成优化步骤,避免加载完数据再做优化的麻烦。
简单的概念
1 | # coding: utf-8 |
pandas中每一个数据类型都有一个专门的类来处理。
ObjectBlock
: 字符串列的块FloatBlock
: 浮点数列的块Numpy ndarray
:整型和浮点数值的块(非常快,用C数组构建的)
1 | for dtype in ['float', 'int', 'object']: |
这里我们可以发现 object
类占用内存最大
子类型(subtype)
memory usage | float | int | uint | datetime | bool | object |
---|---|---|---|---|---|---|
1 bytes | int8 | uint8 | bool | |||
2 bytes | float16 | int16 | uint16 | |||
4 bytes | float32 | int32 | uint32 | |||
8 bytes | float64 | int64 | uint64 | datetime64 | ||
variable | object |
一个
int8
类型值使用1个字节的存储空间,可以表示256(2^8)个二进制数,即-128到127的所有整数值。根据表可以得出,对于每个列,应该尽可能"抠"一点,
物尽其用。这里的
uint
表示无符号整型,可以更有效地处理正整数的列。
优化
数值型数据
这里可以使用 to_numeric()
对数值类型进行
downcast
(向下转型)的操作。
1 | # 选中整数型的列 |
这里能够有效的利用内存,但是缺点很明显的就是只能用于数值型数据,而且优化的空间有限。
object
型数据
object
类型实际上使用的是Python字符串对应的值,由于Python解释性语言的特性,字符串存储方式很碎片化(消耗更多内存,访问速度更慢)。object
列中的每个元素都是一个指针。object
类型的数据内存使用情况是可变的。如果每个字符串是单独存储的,那么实际上字符串占用内存是很大。
1 | In [1]: from sys import getsizeof |
这里通过观察看到,在这里pandas使用了int64来存储,实际占用大小与字符串本身是一样的。这里可以使用
Categoricals
来优化
object
类型。Categoricals
的工作原理我理解为,某个
object
类有有限的分类情况(比如只有
red
、yellow
、blue
、black
等颜色相关),那么
Categoricals
将这些分类 object
对象转换为
int
子类型(对应上面的0, 1, 2, 3)
1 | # 选中object类型 |
如果
unique
(不同值)的数量很少,那么就可以使用这种优化方案。
1 | # 假设某一列符合优化条件,为df_obj_less,使类型转换为category |
这里提升的空间远远超过第一步优化的空间,具体要结合数据来检验(最好貌似可以减少98%的使用量)。这里有个很大的缺点就是无法进行数值计算,即没有办法使用
pd.Series.min()
、pd.Series.max()
等与数值相关操作。
还有一个问题就是在有多少个
unique
(不同值)的情况下才使用这种方法。首先,毫无疑问的是如果需要计算操作的列是不能使用的。第二是如果
unique
的比例小于50%(个人觉得比例应该更小)就可以使用这种情况,如果过多的话,转换之后消耗的内存会更多(不仅需要存储string还有int)。
附上筛选 unique
比例小于50%的代码
1 | # 提取unique少于50%的object列 |
datetime
类型
这里其实不能叫做优化,因为这里是将数值型数据转换为
datetime
类型,虽然提高了内存的使用,但是转换为
datetime
类型的数据更容易进行分析(时间序列分析)。当然如果有
datetime
类型不在分析的范围内,自然可以无视。
1 | # 假设df_num_col为需要转换的列, 这里format格式看情况更改 |
总结
写了这么多,如果每次等加载完数据再做优化操作,感觉有些鸡肋。但是其实可以使用
read_csv
等读取函数的几个参数来帮助我们在读取的时候就完成优化步骤。
1 | # 首先,需要整理出每一列最终的数据类型,组成一个dict |