这篇文章主要介绍了python使用pandas处理大数据节省内存技巧,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
一般来说,用pandas处理小于100兆的数据,性能不是问题。当用pandas来处理100兆至几个G的数据时,将会比较耗时,同时会导致程序因内存不足而运行失败。
当然,像Spark这类的工具能够胜任处理100G至几个T的大数据集,但要想充分发挥这些工具的优势,通常需要比较贵的硬件设备。而且,这些工具不像pandas那样具有丰富的进行高质量数据清洗、探索和分析的特性。对于中等规模的数据,我们的愿望是尽量让pandas继续发挥其优势,而不是换用其他工具。
本文我们讨论pandas的内存使用,展示怎样简单地为数据列选择合适的数据类型,就能够减少dataframe近90%的内存占用。
处理棒球比赛记录数据
我们将处理130年的棒球甲级联赛的数据,数据源于
Retrosheet(http://www.retrosheet.org/gamelogs/index.html)
原始数据放在127个csv文件中,我们已经用csvkit
(https://csvkit.readthedocs.io/en/1.0.2/)
(https://data.world/dataquest/mlb-game-logs)
我们从导入数据,并输出前5行开始:
我们将一些重要的字段列在下面:
date- 比赛日期
v_name- 客队名
v_league- 客队联赛
h_name- 主队名
h_league- 主队联赛
v_score- 客队得分
h_score- 主队得分
v_line_score- 客队线得分, 如010000(10)00.
h_line_score- 主队线得分, 如010000(10)0X.
park_id- 主办场地的ID
attendance- 比赛出席人数
我们可以用Dataframe.info()方法来获得我们dataframe的一些高level信息,譬如数据量、数据类型和内存使用量。
这个方法默认情况下返回一个近似的内存使用量,现在我们设置参数memory_usage为‘deep’来获得准确的内存使用量:
我们可以看到它有171907行和161列。pandas已经为我们自动检测了数据类型,其中包括83列数值型数据和78列对象型数据。对象型数据列用于字符串或包含混合数据类型的列。
由此我们可以进一步了解我们应该如何减少内存占用,下面我们来看一看pandas如何在内存中存储数据。
Dataframe对象的内部表示
在底层,pandas会按照数据类型将列分组形成数据块(blocks)。下图所示为pandas如何存储我们数据表的前十二列:
可以注意到,这些数据块没有保持对列名的引用,这是由于为了存储dataframe中的真实数据,这些数据块都经过了优化。有个BlockManager类
会用于保持行列索引与真实数据块的映射关系。他扮演一个API,提供对底层数据的访问。每当我们查询、编辑或删除数据时,dataframe类会利用BlockManager类接口将我们的请求转换为函数和方法的调用。
每种数据类型在pandas.core.internals模块中都有一个特定的类。pandas使用ObjectBlock类来表示包含字符串列的数据块,用FloatBlock类来表示包含浮点型列的数据块。对于包含数值型数据(比如整型和浮点型)的数据块,pandas会合并这些列,并把它们存储为一个Numpy数组(ndarray)。Numpy数组是在C数组的基础上创建的,其值在内存中是连续存储的。基于这种存储机制,对其切片的访问是相当快的。
由于不同类型的数据是分开存放的,我们将检查不同数据类型的内存使用情况,我们先看看各数据类型的平均内存使用量:
由于不同类型的数据是分开存放的,我们将检查不同数据类型的内存使用情况,我们先看看各数据类型的平均内存使用量:
我们可以看到内存使用最多的是78个o来源gaodai$ma#com搞$代*码*网bject列,我们待会再来看它们,我们先来看看我们能否提高数值型列的内存使用效率。
选理解子类(Subtypes)
刚才我们提到,pandas在底层将数值型数据表示成Numpy数组,并在内存中连续存储。这种存储方式消耗较少的空间,并允许我们较快速地访问数据。由于pandas使用相同数量的字节来表示同一类型的每一个值,并且numpy数组存储了这些值的数量,所以pandas能够快速准确地返回数值型列所消耗的字节量。
pandas中的许多数据类型具有多个子类型,它们可以使用较少的字节去表示不同数据,比如,float型就有float16、float32和float64这些子类型。这些类型名称的数字部分表明了这种类型使用了多少比特来表示数据,比如刚才列出的子类型分别使用了2、4、8个字节。下面这张表列出了pandas中常用类型的子类型:
一个int8类型的数据使用1个字节(8位比特)存储一个值,可以表示256(2^8)个二进制数值。这意味着我们可以用这种子类型去表示从-128到127(包括0)的数值。
我们可以用numpy.iinfo类来确认每一个整型子类型的最小和最大值,如下:
这里我们还可以看到uint(无符号整型)和int(有符号整型)的区别。两者都占用相同的内存存储量,但无符号整型由于只存正数,所以可以更高效的存储只含正数的列。
用子类型优化数值型列
我们可以用函数pd.to_numeric()来对数值型进行向下类型转换。我们用DataFrame.select_dtypes来只选择整型列,然后我们优化这种类型,并比较内存使用量。
我们看到内存用量从7.9兆下降到1.5兆,降幅达80%。这对我们原始dataframe的影响有限,这是由于它只包含很少的整型列。
同理,我们再对浮点型列进行相应处理:
我们可以看到所有的浮点型列都从float64转换为float32,内存用量减少50%。
我们再创建一个原始dataframe的副本,将其数值列赋值为优化后的类型,再看看内存用量的整体优化效果。
可以看到通过我们显著缩减数值型列的内存用量,我们的dataframe的整体内存用量减少了7%。余下的大部分优化将针对object类型进行。
在这之前,我们先来研究下与数值型相比,pandas如何存储字符串。
选对比数值与字符的储存
object类型用来表示用到了Python字符串对象的值,有一部分原因是Numpy缺少对缺失字符串值的支持。因为Python是一种高层、解析型语言,它没有提供很好的对内存中数据如何存储的细粒度控制。
这一限制导致了字符串以一种碎片化方式进行存储,消耗更多前端的相关知识将我们的pandas dataframe的内存用量降低了近90%,仅仅只用了一点简单的技巧:
将数值型列降级到更高效的类型
将字符串列转换为类别类型
通过对列的优化,我们是pandas的内存用量从861.6兆降到104.28兆,有效降低88%。
以上就是python使用pandas处理大数据节省内存技巧(推荐)的详细内容,更多请关注gaodaima搞代码网其它相关文章!