开发者学堂课程【Python 常用数据科学库:大数据处理技巧】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/546/detail/7495
大数据处理技巧
内容介绍
一、查看内存占用量
二、通过转换缩小内存
一、查看内存占用量
1、在使用 pandas 处理大数据的时候,有一些小技巧可以用到。看一下 pandas 能处理多大的数据,首先把稍微大一点的数据导进来,思考怎么能节省空间,在处理数据的时候最大的问题就在于怎么样处理好内存之间的关系,只要内存能够容纳这个数据就没问题。把 csv 数据读进来,这个数据不用具体分析,它就是比赛的数据集,这个比赛数据集有点大。
import pandas as pd
g1= pd.read_csv('game_logs.csv')
g1.head()
g1.shape
先看一下它的样子,这个数据得读一会,打印它的 shape 值看一下数据的大致量,读完之后数据长这个样子,这个数据有161列,shape 值很大,是一个17万的数据,这个数据一共有161列,量上没有那么大,主要是列特别多。
2、如果列少一些,pandas 处理几千万甚至上亿都没问题,但是现在列比较多了,可能数据量也是比较大的,但现在关注的第一个点就是当前这个数据占了多少内存,要打印内存信息,可以这样打印:有一个 memory 参数,把这个参数导进来,并且让这个参数详细的打印一下,可以指定一个 deep 参数来进行控制
g1.info(memory_usage=’deep’)
看一下这个代码,直接出来了,直接显示当前这个数据占了860M的内存,flow 类型77个,int 类型有6个,字符串有78个,总计17万的一个数据,这就是当前数据基本的信息。
3、现在这里有 int 类型,float 类型,还有 object 就是字符串类型,不同的类型所占用的内存肯定不一样,int、float 类型本身就不一样,object 类型就更不一样了。来看一下它们分别占用内存的情况或者平均情况是什么样的,把 float64, int64,object 拿进来,都拿进来之后,写 for 循环,在这个循环当中要看一下 int、float、object 类型平均占了多少内存,但看哪个类型的时候,得单独把满足类型的数据给拿出来,在 gl 中挑选一下它的类型,看一下什么样的类型是满足要求的,select 一下types 值,然后 include 一下,include 里就是当前的一个类型,当前类型就是循环编辑到哪个把它拿到手就好。这里有平均的内存占用,这样算它不是兆,还需要给它转换成以兆为单位的,所以还是转换成 mb,mb 就是上面的这个东西比上1024,还得比上1024的平方。mean_usage_b 就是占用的一个值,这个值等于当前 dtype,用来算内存的量是多大,把 memory 拿过来,然后它的使用情况要写 deep,它是等于一个 True,现在想算 int、float、object 类型平均占了多少内存,所以再 .mean一下,算一下平均的结果,算平均结果之后,中文打印平均内存占用,然后逗号,把dtype 拿过来,再写一个 usage。
for dtype in ['float64','int64','object']:
selected_dtype=g1.select_dtypes(include=[dtype])
mean_usage_b=selected_dtype.memory_usage(deep=True).mean()
mean_usage_mb=mean_usage_b/1024**2
print (平均内存占用’,dtype,mean_usage_mb)
先执行一下看效果,int、float、object 平均内存占用都打印出来了,来看一下结果,再取百分位,比如取小数点后两位。int、float、object 平均内存占用是以兆为单位的,int 类型是1.29兆,float 类型是1.12兆,object 类型是9.51兆,object 类型占用最多平均9.51。对于每一种类型都去选了,选择到所有的类型,然后又计算了它的均值。
二、通过转换缩小内存
(一)int 类型
1、现在要让内存占用量稍微小一些,先来 int 和 float 类型, int、float 现在都是64的,64表示数更多一些。但是现在对于这个任务来说,表达的数不需要那么多。看一下 int32和 int64表示的数的最大范围是多大,numpy 当中也有这个东西,拿 int 类型来举例,传进来一些数:uint8,int8,int16,int32,int64,接下来要看8、16、32、64它们分别能表达的最大的一个数是多少。for int 值,在 int_type 里,然后打印它的iinfo 值。
import numpy as np
int_types ['uint8’, 'int8',‘int16', 'int32', ‘int64']
for it in int_types:
print np.iinfo(it)
执行一下,看一下当前的结果,uint8 能表示的最大数是255,int 是127, int16 最大能表示的数是32767。如果所有的数取值范围都是在-32767~32767之间,那么用int 16就够了,再来看 int32,int 32能表达二十多亿,所以绝大多数情况下,用一个int32来表示就完全足够了。现在默认用int64表示,因为它表示的范围特别大。
2、如果把64位的转换成了32位的,这样就能省一些内存,首先写一个函数,用这个函数计算内存的占用量,里面传进来 pandas 结构, pandas 当中有 data frame,也有 series 结构,需要判断一下它的结构,如果它是 pandas 中的 data frame 类型,就要按照这种类型算它的占用量,然后里面还是把 deep 指定成 True 值,这样算它的总和就可以了,这就是算了一下 data frame 的内存占用量,如果它是 series 结构,它的内存占用量等于这个东西还是调过来,只不过再调的时候,就不需要去执行点 sum 操作,对于 series 结构来说,直接算它的内存占用量就可以了。usage 值还有一个 mb,用兆来表示,等于 usage_b 比上1024**2。之后要 return 它的当前占用量,得写完整,小数点后要保留几位,算的是多少兆,然后点 format 一下,把usage_mb 拿进来。这个函数就写完了,一会算的时候直接用就行了。先把 int 类型的数据都拿到手, select 一下 dtypes 值,值里面指定 include 让它等于 int64。一会要把数据结果进行转换,转换相当于把 int64转换成 int32,对每个数据都执行这样一个操作,这个用 panda 点 apply 操作,然后转换数值就指定成向下进行转换,按照一个 unsigned 的方式进行转换就可以了。
3、这里是有一个网站的,打开看一下这个参数, 可以自己去指定了一下,相当于可以往下进行转换,可以转换成一个 integer 或者是 float 类型,可以按照自己的喜欢方式去转换。先按照 unsigned 方式去转换
转换完之后, print 一下当前的结果吧,把 mem_usage 这个函数拿过来,把int数据读过来,然后新数据还是有一个结果的,这是转换完的。
def mem_usage(pandas_obj):
if isinstance(pandas_obj,pd. DataFrame):
usage_b = pandas_obj.memory_usage(deep=True).sum() else:
usage_b = pandas_obj.memory_usage(deep=True)
usage_mb = usage_b/1024**2
return’(:03.2f) MB'. format(usage_mb)
g1_int = g1.select_dtypes(include=['int64'])
coverted_int = g1_int.apply(pd.to_numeric,downcast='unsigned') print(mem_usage(g1_int))
print (mem_usage(coverted_int)
执行一下,原来的数据占用内存7.84,但是给它进行了一个向下转换,占用内存就比较少了。如果把这个东西向下转换一些并且不耽误其它事情,这个一样能用,只不过占用内存就少了。
(二)float类型
1、对于 int 类型是这么做的,那么对于 float 类型来说也是一样的。直接复制过来,这里是执行了一个 float 操作,64传进来,然后向下转换,转成 float32,一般通常做法是把 float64转换成 float32,这样最少能够节省一半的内存。
g1_float = g1.select_dtypes(include=['float64'])
converted_float = gl_float.apply(pd.to_numeric,downcast='float')
print(mem_usage(g1_float))
print(mem_usage(converted_float))
执行一下,原来 float 类型占用的总内存是100.99M,现在 float 类型占用总内存是50.49M,内存降低了一半,也就是说想把内存降下来,最好的方法就是直接把一个64位的转换成32位,这样内存直接省了一半。
2、对于现在这个任务来说,原始数据占用了860M, 然后再执行这样一个操作,看一下执行完这个操作之后,这个数据能降到多少,相当于把一个整型或者把一个 float 类型都进行一个转换,先把原始 dataframe 拷贝过来,拷过来之后,在新的东西当中去执行就可以了。这里有一个转换完的 int 类型,还有一个转换了的 float 类型,都拿过来,在 int 里得把这些列都给它指定出来,下面也是一样的,这样就得到了转换完的 int 还有 float 类型的数据。原始的 gl 和转换完的 gl 拿过来
optimized_g1= g1.copy()
optimized_g1[coverted_int.columns] = coverted_int optimized_g1[converted_float.columns] = converted_float
print(mem_usage(g1))
print mem_usage(optimized_g1)
接下来打印一下,看一下转换完和转换后的差异,原始的是860 M,现在进行了一个int 类型和 float 类型的转换,转换完是803 M,降了一些但降得不多。
(三)object 类型
1、刚才说过最占内存的是 object 类型,单独关注一下 object,首先还是去找它的dtypes,里面指定一个 include 等于 objcet 类型。把数据 copy 过来,先看一下数据是什么的,describe 一下。
gl_obj=gl.select_dtypes(include=[‘object’]).copy
gl_obj. describe
2、正常情况下无论是在 pandas 还是在 python 中,对于字符串来说就是有一个字符串就有一个索引,一个字符串它有唯一的内存编码,比如有一些字符串 abc,efg, ikn,abc,正常情况下每一个都有一个内存,但是有一些数据,它在一些属性值当中有一些重复的值,有两个 abc,它俩一样,如果让这两个就都指向一个东西,唯一的标识符来当成它的一个映射,这也可以。所以在这里还可以指定这样一个操作,pandas 当中有了额外的一个东西。首先把单独的一个列给拿出来
这个数据里一共有17万个,但是不重复值一共才有七个,说明里面大部分值都是重复的。这种数据非常适合进行转换,看一下里面是什么值。
dow=gl_obj.day_of_week
dow.head()
执行一下,这个数据就是星期几,所以这里只有七个值,每一条数据只可能是七选一当中一个,不用每一个数据都做唯一的内存块,指向星期一的就都是星期一,指向星期二都是星期二,字符串永远不会变。
(四)category 类型
1、可以指定另外一种类型,在 pandas 当中还有一个叫做 category 类型。category类型就是比如两个字符串是一样的,认为它指定的内存就是一样的,所以这可以帮助节省内存占用,指定一个新的 category 类型
dow_cat=dow.asdype(‘category’)
dow_cat.head()
执行看一下结果,还是这个东西没有变,只不过原来是 object 类型,现在是一个category 类型,只有七种不同的数据。
2、还可以点 head 一下看它的前几行数据,还可以看它的一个编码格式
dow_cat.head(10).cat.codes
执行一下,它会把相同的进行标识,都是星期二那就用一个相同的值表示,相对来说就是内存占的更少了,原来是17万个,现在七个为一支。
3、刚才写了一个函数,计算它内存的占用量,把这些指标都传进去。第一个就是当前 object 类型,第二个就是 category 类型
print(mem_usage(dow))
print(mem_usage(dow_cat)
来看结果,对于当前 object 这个列来说,它占了9.84M,但是对于 category 类型它只占了0.16M,这说明如果数据当中有一些列,里面有非常多重复值,它是一个属性,就可以把 object 类型转换成 category 类型,这样内存会急速下降的,非常省空间,原来9.84,现在0.16。
4、对于每一个列都执行这个循环,循环的时候先判断它里面重复的值有多少个或者是不重复值有多少个。如果这里的比例小于50%,就说明里面重复的值比较多,像day_of_week 这个类型,unique 值比较少的,意味着里面重复的多,可以把它变成category 类型,进行一个转换就变成 category 类型了,如果是小于0.5就执行,如果不是小于0.5,就不执行。接下来就是打印一下当前的占用量
converted_obj = pd.DataFrame
for col in g1_obj.columns:
num_unique_values = len(gl_obj[col].unique())
num_total_values = len(gl_obj[col])
if num_unique_values / num_total_values < 0.5:
converted_obj.oc[:,col]=gl_obj[col].astype('category')
else:
converted_obj.loc[:,col]=gl_obj[col]
print(mem_usage(gl_obj)
print(mem_usage(converted_obj))
执行一下,上面是原始的,下面是转换过的,原始的对于一个字符串类型来说,占了七百多兆,现在转这个 category 类型只占了五十多兆。相对来说,省的空间更多了。
5、在 pandas 当中,int 和 float 类型有64转32,object 类型转换成 category 类型,这样是非常省内存空间的,省的空间越多,接下来能传进来的数据也是越多,相对来说,做事情就好做。
(五)时间类型
1、有一个时间类型 date,取前五个数据
date=optimized_gl.date
date[:5]
执行
结果可能是1871年5月4号,看一下它的内存占用量,对于 series 或者 dataframe 来说都可以去进行计算的,把 date 传进去:print(mem_usage(date))得到一个结果0.66M。
2、把 date 这个列换一下,等于正常的 datetime 格式,把 date 给传进 datetime中,然后需要指定它的格式,就按照年月日,这是它的格式
optimized_gl[‘date’]=pd.to_datetime(date,format=’%Y%m%d)
print(mem_usage(optimized_gl[‘date’]))
打印一下,当前的内存占用是1.31M,比原来多了,这就是说对于时间类型来说,写成187154这种形式是比较省内存的,写成一个标准日期格式是比较占空间的。
3、所以处理数据最简单的方法, float int 转换,比如64位转32位能省一半,数据如果都是64位的,那就太占内存了,转成32位的直接省一半,然后对于字符串类型来说,直接转换成 category 类型也能省很多空间。这些就是大数据处理当中最常用、最基本的两个方案。