引子
最近遇到了一个场景,如果一开始我就记起来用Pickle,至少会节省我三四小时的等待时间。这让我感慨一下书到用时方恨少,遂写文记录一下,防止我下次忘记了。如果想直接看用法就不用看我bb了,直接跳去用法那章。
任务大概是这样的,暂存一批数据:十万条左右的题目,和题目转化成的向量对象(np.array
),向量是一维,长度768。
掏出我常用的pandas,新建了一个列来计算向量,存为csv文件,一气呵成。
然而这有一个巨大的问题,文件类型并没有被储存下来,所以本来应该是np.array
类型的那个列,再被读出来之后,只是一个str
类型的数据,如下所示:
import pandas
import numpy as np
data = {
'brand': ['golang', 'Python', 'Java'],
'C': [np.array([1, 2, 3, 1, 1]), np.array([4, 6, 8, 12, 10]), np.array([10, 2, 5, 20, 16])],
}
df = pandas.DataFrame(data)
print(type(df.loc[0, 'C']), df.loc[0, 'C'])
"""
查看储存在df中的数据,是numpy.ndarray类型
<class 'numpy.ndarray'> [1 2 3 1 1]
"""
df.to_csv('test.csv')
df = pandas.read_csv('test.csv') # 储存文件后再读取
print(type(df.loc[0, 'C']), df.loc[0, 'C'])
"""
查看储存在df中的数据,已经变成了str
<class 'str'> [1 2 3 1 1]
"""
这样的话想在算法中复用,只能再重新写一个转换脚本,将字符串列转换成numpy.ndarray
类型,如下:
import pandas
import numpy as np
def _str2ary(s: str):
s = s[1:-1] # 去掉[]号
l = [i for i in s.split(' ')]
while '' in l:
l.remove('')
return np.array([int(i) for i in l])
data = {
'brand': ['golang', 'Python', 'Java'],
'C': [np.array([1, 2, 3, 1, 1]), np.array([4, 6, 8, 12, 10]), np.array([10, 2, 5, 20, 16])],
}
df = pandas.DataFrame(data)
df.to_csv('test.csv')
df = pandas.read_csv('test.csv') # 储存文件后再读取
df['C'] = df['C'].apply(_str2ary)
print(type(df.loc[0, 'C']), df.loc[0, 'C'])
"""
终于转化回来了...
<class 'numpy.ndarray'> [1 2 3 1 1]
"""
折磨,费劲,不优雅…
尤其是十万量级的数据,768的向量长度,每次执行算法,加载9g的csv文件大概5min,然后转化大概要半小时……
此时Pickle的意义就很明显了…
简介
Pickle的官网简介
pickle提供了一个简单的持久化功能。可以将对象以文件的形式存放在磁盘上。
python中几乎所有的数据类型(列表,字典,集合,类等)都可以用pickle来序列化,但是这种数据几乎只能由python来再次使用。
简单来说,就是pickle能把python中的特殊对象以python的方式(二进制数据)直接存在本地,要的时候读出来就好,任意对象都可以序列化,包括跑AI的那些模型参数均可。
用法
常用的几个函数是
-
**pickle.dump(obj, file, protocol=None, *, fix_imports=True)**
-
**pickle.load(file, *, fix_imports=True, encoding='ASCII', errors='strict')**
file
参数就是open
函数打开的一个文件流,其他参数意义不大,这里不详细介绍。
案例
import pickle
import pandas
import numpy as np
data = {
'brand': ['golang', 'Python', 'Java'],
'C': [np.array([1, 2, 3, 1, 1]), np.array([4, 6, 8, 12, 10]), np.array([10, 2, 5, 20, 16])],
}
df = pandas.DataFrame(data)
print(type(df.loc[0, 'C']), df.loc[0, 'C'])
"""
<class 'numpy.ndarray'> [1 2 3 1 1]
"""
# 这里wb不能写成w,w为写入模式,b为二进制写入模式(bit),pickle只支持二进制模式
with open("test.pkl", "wb") as f:
pickle.dump(df, f) # 储存为test.pkl
del df # 删除原来的df对象
with open("test.pkl", "rb") as f:
df = pickle.load(f)
print(type(df.loc[0, 'C']), df.loc[0, 'C'])
"""
还是原来的数据类型
<class 'numpy.ndarray'> [1 2 3 1 1]
"""
test.pkl
会被储存在当前目录,后缀可以随意,如果感兴趣用记事本打开的话,可以看到一堆乱乱码,pickle基本没有可读性,就是给python复用的数据文件。
另外还有两个函数
-
**pickle.dumps(obj, protocol=None, *, fix_imports=True)**
-
**pickle.loads(data, *, fix_imports=True, encoding='ASCII', errors='strict')**
这里的obj
和data
是二进制的数据,因为不常用,所以就提一嘴。
注意事项
尽管pickle非常方便,但在使用它时需要注意一些事项:
-
安全性:反序列化数据时要小心,因为pickle可以执行任意代码。不要从不受信任的来源加载pickle数据,以免遭受安全风险。
-
版本兼容性:在不同版本的Python之间,pickle数据的兼容性可能会有问题。因此,确保在不同版本之间测试并验证pickle数据的兼容性。
-
自定义对象:一些自定义对象的序列化和反序列化可能会受到限制,因此需要额外的配置。你可能需要实现特定的__reduce__方法来控制对象的序列化行为。
这个第三点是,如果你pickle了一个DataFrame
对象到本地,然后copy到第二台电脑,如果第二个电脑环境并没有Pandas库,那么load的时候就会报错。
参考
https://blog.csdn.net/wuShiJingZuo/article/details/133978427
https://blog.csdn.net/m0_46223009/article/details/128086147
一文带你搞懂Python中pickle模块 - 知乎 (zhihu.com)
[文章导入自 http://qzq-go.notion.site/10b314ae54e0428183c36a0d0efa8c07 访问原文获取高清图片]