内存优化——chunksize、内存释放、制作json、矩阵压缩、datatype转换、去除冗余

内存优化——chunksize、内存释放、矩阵压缩、datatype转换、去除冗余

前言

之前工作很少遇到内存方面的困扰。前段时间朋友找我帮忙做一家某小型上市银行一年期的交易数据JE,百亿量级。吭哧吭哧写完代码,结果放在她的本地运行直接内存爆掉。于是稍微尝试了一些内存优化的方法,记录一下心德。

选择正确的方法

选择正确的操作方式尤为重要。其实百亿还好,不太用到hive。之前她们领导也是说了推荐SQL server去做。anyway,最后和她同学一起帮她写了python,而且没有服务器,还要在本地运行,她公司电脑装不上任何IDE,还要用CMD指令。好的,由于我们远程没法帮她调试,但是强烈建议,无论何任务都要选择最恰当的解决办法(好像是废话)。
在这里插入图片描述

ChunkSize

chunk——块。假设我们现在有一个200Mb数据矩阵,我们对这个矩阵进行计算,而内存上限为210Mb, 一不小心就会超出内存,导致后面的计算通通停掉。这个方法通俗讲就是,一块大蛋糕我一口气吃不下,那就切成n块小蛋糕一点一点吃。因此,我们可以看出,这样的方法节省时间,但是会很耗时。时间复杂度和空间复杂度,鱼和熊掌不可兼得。(第一次朋友貌似跑了一晚上呵呵0.0~~)
在这里插入图片描述

with open(name, 'r', encoding="utf-8") as fp:
    line = fp.readline()
    if "Field_1" in line:
        skip_head = True

if skip_head:
    table = pd.read_csv(name, sep='|', chunksize=20000, header=0,
                        names=['Field_1', 'Field_2', 'Field_3', 'Field_4', 'Field_5', 'Field_6', 'Field_7',
                               'Field_8', 'Field_9', 'Field_10', 'Field_11', 'Field_12', 'Field_13', 'Field_14',
                               'Field_15', 'Field_16', 'Field_17', 'Field_18', 'Field_19'])
else:
    table = pd.read_csv(name, sep='|', chunksize=20000, header=None)
    table.columns = ['Field_1', 'Field_2', 'Field_3', 'Field_4', 'Field_5', 'Field_6', 'Field_7', 'Field_8',
                     'Field_9', 'Field_10', 'Field_11', 'Field_12', 'Field_13', 'Field_14', 'Field_15',
                     'Field_16', 'Field_17', 'Field_18', 'Field_19']

由于是个超大的del文件,因此sep=’|’, chunksieze设定就是20000。
之后的计算,每一个chunk去执行打包好的算法(如float64_lower, Criteria_A…),最后再append,或者concat在一起,大框架就是如此。

 for chunk in table:
     print("cal {}".format(ddc * 20000))
     chunk.columns = [........]
     df1 = self.Float64_lower(chunk)
     df_A = self.Criteria_A(df1)
     df_B = self.Criteria_B(df1)
     .........

内存释放

Python对于占用较大的内存不会立刻释放,即便你del X,或者赋一个空值,所占用的空间都无法释放。

https://blog.csdn.net/qq_36357814/article/details/90237599

mydata=pd.DataFrame(DF)
mydata.info(memory_usage='deep')

此处该mydata占用32Mb.释放其缓存方法,配合python垃圾回收机制gc包:

import gc
del mydata
gc.collect()

此后,这个变量引用被删掉,并释放内存。一般在进行大量计算时候,要酌情一边计算一边清空释放内存,避免溢出。

制作json(或字典)

当需要去从另外一个dataframe,数据源查找匹配的时候,一般操作都是merge、join、caoncat,这很像SQL的思路。但是当两个数据库都极其庞大的时候,我们为了节省空间,其实可以将需要查找、匹配的第二第三数据源制作成一个json、字典dict、甚至是一个array组。这样可以避免dataframe其他的数据占用过多空间。
1.手工输入,或者传入参数

mydict={'A':0.3, 'B':0.5,'C':0.2}
df_2['Result']=df_1['Catogory'].apply(lambda x: mydict[x]*2)

2.去除多余数据将dataframe制作为map
在这里插入图片描述

myarray = np.array([['A', 'B','C'],[0.3, 0.5, 0.2],['boy','cow','cat'],['AB','CD','EF']])
df = pd.DataFrame(myarray).T
df.columns=['0','1','2','3']
df_map = df.groupby('0')['1'].sum()

得到的字典如下:
在这里插入图片描述
接下来可以进行上面一样的操作了。这种“伪字典”占用空间有点大,但是很方便,现成的dataframe可以直接用。
3.直接用dict去定义

dic={}
with open('Level.csv', newline='', encoding='UTF-8') as woww:
    readerw=csv.DictReader(woww)
    for row in readerw:
        dic[row['\ufeffCIK']]=row['URLF']
for k,v in dic.items(): 
    with urlopen(v) as text:
        soup = BeautifulSoup(text, 'html.parser')

其实可以直接写成建立空dic{},并指定key, value。用于后续运算。

矩阵压缩

说道矩阵压缩,首先介绍下稀疏矩阵(sparse matrix)和稠密矩阵(Dense Matrix)。在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵;与之相反,若非0元素数目占大多数时,则称该矩阵为稠密矩阵。定义非零元素的总数比上矩阵所有元素的总数为矩阵的稠密度。

矩阵压缩。则是把这些元素分别用行列index和value来表示。
在这里插入图片描述
(1,1,1):数据元素为 1,在矩阵中的位置为 (1,1);
(3,3,1):数据元素为 3,在矩阵中的位置为 (3,1);
(5,2,3):数据元素为 5,在矩阵中的位置为 (2,3);
除此之外,还要存储矩阵的行数 3 和列数 3;

我们利用scipy包中的sparse可以轻松压缩和还原稀松矩阵。
但是更建议用sparse来存储数据,以很低的内存耗用量。使用的时候释放还原。避免在计算峰值的时候溢出。

七种sparse:

  1. csc_matrix: Compressed Sparse Column format
  2. csr_matrix: Compressed Sparse Row format
  3. bsr_matrix: Block Sparse Row format
  4. lil_matrix: List of Lists format
  5. dok_matrix: Dictionary of Keys format
  6. coo_matrix: COOrdinate format (aka IJV, triplet format)
  7. dia_matrix: DIAgonal format
from scipy import sparse
myrow = np.array([0, 0, 1, 2, 2, 2])
mycol = np.array([0, 2, 2, 0, 1, 2])
mydata = np.array([1, 2, 3, 4, 5, 6])
matrix = sparse.csr_matrix((data, (row, col)), shape=(3, 3))

matrix就是这样的一种存储, 当然可以从dataframe里直接拎出array来,毕竟也是numpy底层。
在这里插入图片描述

##当需要还原的时候
matrix.toarray()

datatype转换

我们知道数据类型有很多种,参与计算最常见的就是int(), float()。
int一般比float的计算要慢一点(待认证),所以我每次都习惯pd.to_numeric把整数转成float。然后,int/float 都会有64、32、16、8位的格式。如:float64,int32…
下图我们可以看到不同数据类型,占用占用内存不一样。因此一般如果不需要那么高精度的时候,可以通过转换数据类型来减缓内存占用量。
在这里插入图片描述
1. 首先,很多数据类型,即便你看到的数字,py默认都是object,尽量把它都转成datetime或者numeric形式,内存会小很多。
在这里插入图片描述
在这里插入图片描述
如图对比,我们只转换了三列object位float64.内存从19.7M降低到14.4M。
2. Float Int 类型转换降级。

DF = df_Final.select_dtypes(include=['float64'])
##转换前数据占用量
DF.info(memory_usage='deep')
##转换成最低
DF_2 = DF.apply(pd.to_numeric,downcast='unsigned')
DF_2.info(memory_usage='deep')

在这里插入图片描述
在这里插入图片描述
我们可以看到内存从421kb—>81kb。巨幅下降。
3. Object转换为category。
这个转换简直是令人惊奇!!

##转换前
DF = df_Final.select_dtypes(include=['object'])
DF.info(memory_usage='deep')
##转换后
DF2 = DF.astype('category')
DF2.info(memory_usage='deep')

在这里插入图片描述
after
记住!!Object转Category神技!直接从13.7M降低到299.3Kb!

去除冗余

其实这个很好理解,很多时候我们的数据存在很多冗余,重复。可以用drop把它去掉。所以预先的数据清洗很重要。
另外,千万不要以为groupy会让数据内存表小,看上去表小但是实际上内存占用更大!!

欢迎一起讨论,共同进步,如有错误帮忙指正~!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值