首页 > 解决方案 > 将 Pandas DataFrame 作为 Pickle 写入 S3

问题描述

这是我的要求。

我创建了以下简单的函数,将 Pandas 数据帧作为 csv 上传到 s3:

def df_to_s3_csv(df, filename, sep=','):
    s3 = boto3.resource('s3')
    buffer = io.StringIO()
    df.to_csv(buffer, sep=sep, index=False)
    s3.Object(s3bucket, f'{s3_upload_path}/{filename}').put(Body=buffer.getvalue())

此功能工作正常,并完成了它应该做的事情。对于泡菜文件,我以类似的方式创建了以下函数:

def df_to_s3_pckl(df, filename):
    s3 = boto3.resource('s3')
    buffer = io.BytesIO()
    df.to_pickle(buffer)
    buffer.seek(0)
    obj = s3.Object(s3bucket, f'{s3_upload_path}/{filename}')
    obj.put(Body=buffer.getvalue())

我在有和没有部分的情况下尝试了这个函数,seek无论哪种方式它都会引发以下错误:ValueError: I/O operation on closed file.

进一步研究这个问题,我发现一旦被调用就buffer被认为是。这可以通过发出以下命令来重现:closeddf.to_pickle

buffer = io.BytesIO()
df.to_pickle(buffer)
print(buffer.closed)

以上打印True。似乎BytesIO缓冲区已关闭to_pickle,因此无法引用其数据。如何解决此问题,或者是否有满足我要求的替代方案?我在 SO 上发现了几个关于如何使用 boto3 上传到 S3 的问题,但没有关于如何使用 BytesIO 缓冲区上传 Pandas 创建的泡菜文件。

这是潜在问题的最小可重现示例:

import pandas as pd
import numpy as np
import io
df = pd.DataFrame(np.random.randint(0,100,size=(4,4)))                                                                                   
buffer = io.BytesIO()
df.to_pickle(buffer)
print(buffer.closed)

标签: pythonpandasdataframeamazon-s3pickle

解决方案


看来问题可以追溯到熊猫源代码。这最终可能是 pandas 中的一个错误,该错误是由方法中意外使用BytesIO对象所揭示的to_pickle。我设法使用以下代码在最小可重现示例中规避了该问题,该代码使用模块中的dump方法pickle

import pandas as pd
import numpy as np
import io
from pickle import dump
df = pd.DataFrame(np.random.randint(0,100,size=(4,4)))
buffer = io.BytesIO()
dump(df, buffer)
buffer.seek(0)
print(buffer.closed)

现在打印语句打印False并且BytesIO可以访问流数据。


推荐阅读