将Dataframe直接保存为csv文件到s3 Python

207

我有一个pandas DataFrame,想将其上传到新的CSV文件中。问题是我不想在传输到s3之前将文件保存在本地。是否有类似于to_csv的方法可以直接将DataFrame写入s3?我正在使用boto3。
以下是我目前的代码:

import boto3
s3 = boto3.client('s3', aws_access_key_id='key', aws_secret_access_key='secret_key')
read_file = s3.get_object(Bucket, Key)
df = pd.read_csv(read_file['Body'])

# Make alterations to DataFrame

# Then export DataFrame to CSV through direct transfer to s3

14
df.to_csv('s3://mybucket/dfs/somedf.csv')的意思是将DataFrame对象保存为CSV格式,并上传到名为"mybucket"的Amazon S3存储桶中的"dfs"文件夹下,文件名为"somedf.csv"。请参考https://dev59.com/zloT5IYBdhLWcg3w0B-_#56275519了解更多信息。 - Peter Berg
我喜欢这个解决方案。非常紧凑、简洁和优雅!谢谢分享!! - ASH
@PeterBerg,它给了我访问被拒绝的错误 :( - Raksha
15个回答

268

您可以使用:

from io import StringIO # python3; python2: BytesIO 
import boto3

bucket = 'my_bucket_name' # already created on S3
csv_buffer = StringIO()
df.to_csv(csv_buffer)
s3_resource = boto3.resource('s3')
s3_resource.Object(bucket, 'df.csv').put(Body=csv_buffer.getvalue())

22
如果这是一个大文件,那么它会对内存造成什么影响? - citynorman
4
如果文件的大小超过了你可用的内存,那么该操作将失败并抛出一个异常(不知道是哪个异常)。这应该被视为答案。 - Eran Moshe
8
在使用StringIO时,我遇到了TypeError: unicode argument expected, got 'str'的错误。我改用BytesIO后,一切都正常了。请注意:这是在Python 2.7中发生的。 - Abhishek Upadhyaya
3
"Bucket"对象是云存储服务中的一种数据存储容器,通常用于存储大量的非结构化数据(例如图像、视频等)。您可以通过云存储服务提供商的控制台或API创建Bucket对象。 - Charles Chow
2
"bucket" 是在 S3 上存储对象的地方。代码假定您已经创建了目标(类似于目录),用于存储这些对象。请参阅S3文档 - Stefan
显示剩余8条评论

159

您可以直接使用S3路径。我正在使用Pandas 0.24.1

In [1]: import pandas as pd

In [2]: df = pd.DataFrame( [ [1, 1, 1], [2, 2, 2] ], columns=['a', 'b', 'c'])

In [3]: df
Out[3]:
   a  b  c
0  1  1  1
1  2  2  2

In [4]: df.to_csv('s3://experimental/playground/temp_csv/dummy.csv', index=False)

In [5]: pd.__version__
Out[5]: '0.24.1'

In [6]: new_df = pd.read_csv('s3://experimental/playground/temp_csv/dummy.csv')

In [7]: new_df
Out[7]:
   a  b  c
0  1  1  1
1  2  2  2

发布说明:

S3文件处理

pandas现在使用s3fs来处理S3连接。这不应该破坏任何代码。但是,由于s3fs不是必需的依赖项,您需要单独安装它,就像在以前版本的pandas中安装boto一样。GH11915


16
这绝对是目前最简单的答案,它在后台使用s3fs,因此您需要将其添加到您的requirements.txt文件中。 - JD D
4
我喜欢它很简单,但似乎并没有真正起作用,因为我不停地收到以下错误NoCredentialsError: Unable to locate credentials。有什么建议吗? - CathyQian
2
我可以确认,这不适用于pandas <= 0.23.4版本,所以请确保升级到pandas 0.24。 - Guido
15
我正在使用 pandas 0.24.2 版本,但是遇到了 NotImplementedError: Text mode not supported, use mode='wb' and manage bytes 的错误。你有什么建议吗?需要将模式更改为 'wb' 并管理字节。 - Binyamin Even
1
这个链接清晰地展示了如何传递凭据给使用s3fs的pandas API:https://towardsdatascience.com/reading-and-writing-files-from-to-amazon-s3-with-pandas-ccaf90bfe86c - jason
显示剩余7条评论

71

我喜欢s3fs,它让你可以像使用本地文件系统一样使用S3(几乎)。

你可以这样做:

import s3fs

bytes_to_write = df.to_csv(None).encode()
fs = s3fs.S3FileSystem(key=key, secret=secret)
with fs.open('s3://bucket/path/to/file.csv', 'wb') as f:
    f.write(bytes_to_write)

s3fs 只支持以 rbwb 模式打开文件,这就是我使用 bytes_to_write 的原因。


太好了!我该如何使用相同的s3fs模块获取文件url? - M.Zaman
我正在寻找可以下载写入文件的URL,无论如何,我通过S3FileSystem得到了它。谢谢。 - M.Zaman
这是我使用的;谢谢。我很好奇为什么 pd.read_csv(<s3path>) 可以正常工作,但是对于写入,我们必须使用这个解决方法...除非我直接写入到我的 jupyter 所在的 s3 存储桶中。 - Renée
1
@michcio1234,我该如何以追加模式完成相同的操作?我需要将数据附加到现有的S3上的CSV文件中。 - j '
2
@j的s3fs似乎不支持追加模式。 - michcio1234

57

这是一个更加最新的答案:

import s3fs

s3 = s3fs.S3FileSystem(anon=False)

# Use 'w' for py3, 'wb' for py2
with s3.open('<bucket-name>/<filename>.csv','w') as f:
    df.to_csv(f)

StringIO的问题在于它会耗尽你的内存。使用这种方法,您将文件流式传输到S3,而不是将其转换为字符串,然后将其写入S3。在内存中保存pandas dataframe及其字符串副本似乎非常低效。

如果您正在使用EC2实例,则可以为其提供IAM角色以启用将其写入S3,因此无需直接传递凭据。 但是,您还可以通过向 S3FileSystem()函数传递凭据来连接到Bucket。请参阅文档:https://s3fs.readthedocs.io/en/latest/


1
由于某种原因,当我执行此操作时,输出的 CSV 文件中每一行都被跳过了。 - kjmerf
不确定为什么会发生这种情况。也许尝试使用另一个pandas df来查看是否仍然存在问题?如果您的pandas版本支持,可以尝试@amit-kushwaha的答案,直接将s3 url传递给to_csv()。似乎是更清晰的实现方式。 - erncyp
似乎您缺少权限?请确保将S3读写权限附加到您正在使用的IAM角色。 - erncyp
@erncyp 我的IAM用户已附加AdministratorAccess策略,理论上我应该能够正常读写...奇怪的是,当我使用以下函数时,我可以正常写入,这是根据另一个StackOverflow用户的建议制作的(请注意,分号表示行末,因为我不知道如何在注释部分进行格式化): def send_to_bucket(df, fn_out, bucketname): csv_buffer = StringIO(); df.to_csv(csv_buffer); s3_resource = boto3.resource('s3'); s3_resource.Object(bucketname, fn_out).put(Body=csv_buffer.getvalue()); - ajoros
我收到了“botocore.exceptions.NoCredentialsError: Unable to locate credentials”错误。 - Raksha
显示剩余4条评论

17

您还可以使用 AWS数据整理器

import awswrangler as wr
    
wr.s3.to_csv(
    df=df,
    path="s3://...",
)

请注意它将处理多部分上传,以使上传更快。


17
如果在to_csv()的第一个参数中传递None,则数据将作为字符串返回。然后,只需一步就可以将其上传到S3。
也可以将StringIO对象传递给to_csv(),但是使用字符串会更容易。

1
哪种方式会更容易?正确的做法是什么? - Eran Moshe
@EranMoshe:无论哪种方式都可以正常工作,但显然将None传递给to_csv()并使用返回的字符串要比创建一个StringIO对象,然后再读取数据更容易。 - mhawke
作为一个懒惰的程序员,这就是我所做的。而且你的意思是让写更少代码的程序员更容易些 :> - Eran Moshe

12

我发现这也可以使用客户端而不仅仅是资源来完成。

from io import StringIO
import boto3
s3 = boto3.client("s3",\
                  region_name=region_name,\
                  aws_access_key_id=aws_access_key_id,\
                  aws_secret_access_key=aws_secret_access_key)
csv_buf = StringIO()
df.to_csv(csv_buf, header=True, index=False)
csv_buf.seek(0)
s3.put_object(Bucket=bucket, Body=csv_buf.getvalue(), Key='path/test.csv')

5

我使用AWS Data Wrangler。例如:

import awswrangler as wr
import pandas as pd

# read a local dataframe
df = pd.read_parquet('my_local_file.gz')

# upload to S3 bucket
wr.s3.to_parquet(df=df, path='s3://mys3bucket/file_name.gz')

同样适用于csv文件。使用正确的文件扩展名,而不是使用read_parquetto_parquet,请改用read_csvto_csv

4

您可以使用以下工具:

  • pandas
  • boto3
  • s3fs(版本≤0.4)

我使用 to_csv 将数据写入路径中包含 s3:// 的文件,并添加相应的storage_options

key = "folder/file.csv"

df.to_csv(
    f"s3://{YOUR_S3_BUCKET}/{key}",
    index=False,
    storage_options={
        "key": AWS_ACCESS_KEY_ID,
        "secret": AWS_SECRET_ACCESS_KEY,
        "token": AWS_SESSION_TOKEN,
    },

2

由于您正在使用 boto3.client(),请尝试:

import boto3
from io import StringIO #python3 
s3 = boto3.client('s3', aws_access_key_id='key', aws_secret_access_key='secret_key')
def copy_to_s3(client, df, bucket, filepath):
    csv_buf = StringIO()
    df.to_csv(csv_buf, header=True, index=False)
    csv_buf.seek(0)
    client.put_object(Bucket=bucket, Body=csv_buf.getvalue(), Key=filepath)
    print(f'Copy {df.shape[0]} rows to S3 Bucket {bucket} at {filepath}, Done!')

copy_to_s3(client=s3, df=df_to_upload, bucket='abc', filepath='def/test.csv')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接