将Dataframe保存到csv直接保存到s3 Python


124

我有一个要上传到新CSV文件的pandas DataFrame。问题是在将文件传输到s3之前,我不想在本地保存文件。是否有像to_csv这样的方法可以将数据帧直接写入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

3
df.to_csv('s3://mybucket/dfs/somedf.csv')。有关更多信息,请访问stackoverflow.com/a/56275519/908886
彼得·伯格

Answers:


156

您可以使用:

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())

9
如果这是一个大文件,这对内存有什么作用?
citynorman '18

2
如果文件更大,则可用的RAM将会失败,并且会发生异常(不知道是哪个异常)。这应该作为答案接受
Eran Moshe

5
TypeError: unicode argument expected, got 'str'在使用错误StringIO。我用过BytesIO,效果很好。注:这是在Python 2.7
阿布舍克乌帕德亚雅

1
什么是bucket对象?您是如何创建的?
周星驰

1
bucket是您在S3上存储对象的位置。该代码假定您已经创建了存储此目标的目的地(think:目录)。请参阅S3文档
Stefan

65

您可以直接使用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文件处理

熊猫现在使用s3fs处理S3连接。这不应破坏任何代码。但是,由于s3fs不是必需的依赖项,因此您将需要单独安装它,例如以前版本的panda中的boto。GH11915


7
这绝对是最简单的答案,现在,它使用幕后s3fs所以你需要的是添加到您的requirements.txt
JD d

1
我喜欢这很容易,但是由于我不断遇到以下错误,因此似乎没有用NoCredentialsError: Unable to locate credentials。有什么建议?
CathyQian

1
我可以确认这不适用于<= 0.23.4的熊猫,因此请确保升级到熊猫0.24
Guido,

1
这是我在尝试使用to_csv命令TypeError时看到的错误:write()参数1必须是unicode,而不是str
Raj

13
我正在使用pandas 0.24.2,我得到的是NotImplementedError: Text mode not supported, use mode='wb' and manage bytes。有什么建议?
Binyamin甚至

57

我喜欢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模块获取文件网址?
M.Zaman

我一直在寻找从哪里可以下载书面文件的URL,无论如何我都是通过S3FileSystem获得的。谢谢
M.Zaman

这就是我用的;谢谢。我很好奇,为什么pd.read_csv(<s3path>)按预期工作,但写我们要围绕使用本作品..除非我是直接写入S3存储我jupyter的情况是英寸
蕾妮

@ michcio1234如何在追加模式下执行相同操作?我需要在现有的CSV数据追加对S3
J'

@j' s3fs似乎不支持追加模式。
michcio1234 '19

43

这是最新的答案:

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数据框及其字符串副本保存在内存中似乎效率很低。

如果您在ec2 Instant中工作,则可以为其赋予IAM角色以使其能够写入s3,因此您无需直接传递凭据。但是,您也可以通过将凭据传递给S3FileSystem()功能来连接到存储桶。请参阅文档:https : //s3fs.readthedocs.io/en/latest/


由于某种原因,当我这样做时,输出CSV中的每一行都被跳过了
kjmerf

嗯。不知道为什么会这样。也许尝试与另一个熊猫df一起看看是否仍然存在问题?如果您的熊猫版本支持,请尝试@ amit-kushwaha的答案,在此您将s3 url直接传递到 to_csv()。似乎是一个更清洁的实现。
erncyp

@erncyp我似乎到达那里错误:botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied ...我什至使存储桶成为PUBLIC READ,并且在我的特定帐户IAM用户下,在存储桶策略中添加了以下操作:"Action": [ "s3:PutObject", "s3:PutObjectAcl", "s3:GetObject", "s3:GetObjectAcl", "s3:DeleteObject" ]
ajoros

好像您缺少权限?确保将S3读写权限附加到您正在使用的IAM角色
erncyp,

@erncyp我在IAM用户上附加了AdministratorAccess策略,因此从理论上讲,我应该可以读/写就可以了...奇怪的是,当我使用我创建的以下函数时,可以使用另一个StackOverflow用户的身份来写得很好。建议(fyi分号是行尾的,因为我不知道如何在评论部分中设置格式):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

13

如果None将第一个参数传递to_csv()给数据,则将以字符串形式返回。从那里开始,只需一步即可将其上传到S3。

也可以将一个StringIO对象传递给to_csv(),但是使用字符串会更容易。


用哪种方式会更容易?正确的方法是什么?
伊兰·莫西

@EranMoshe:两种方法都可以正常工作,但是显然,传递和使用返回的字符串比创建对象然后读取数据None要容易to_csv()得多StringIO
mhawke

作为一个懒惰的程序员,这就是我所做的。对于编写更少代码的程序员来说,这意味着更轻松:>
Eran Moshe

2

您还可以使用AWS Data Wrangler

import awswrangler

session = awswrangler.Session()
session.pandas.to_csv(
    dataframe=df,
    path="s3://...",
)

请注意,由于它是并行上传的,因此它将分为几部分。


2

我发现也可以使用client,而不仅仅是resource

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')

0

由于您正在使用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')

-1

我找到了一个似乎很有效的简单解决方案:

s3 = boto3.client("s3")

s3.put_object(
    Body=open("filename.csv").read(),
    Bucket="your-bucket",
    Key="your-key"
)

希望能有所帮助!


-5

我从存储桶s3中读取了两列的csv,并将文件csv的内容放入了pandas数据框。

例:

config.json

{
  "credential": {
    "access_key":"xxxxxx",
    "secret_key":"xxxxxx"
}
,
"s3":{
       "bucket":"mybucket",
       "key":"csv/user.csv"
   }
}

cls_config.json

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import json

class cls_config(object):

    def __init__(self,filename):

        self.filename = filename


    def getConfig(self):

        fileName = os.path.join(os.path.dirname(__file__), self.filename)
        with open(fileName) as f:
        config = json.load(f)
        return config

cls_pandas.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pandas as pd
import io

class cls_pandas(object):

    def __init__(self):
        pass

    def read(self,stream):

        df = pd.read_csv(io.StringIO(stream), sep = ",")
        return df

cls_s3.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import boto3
import json

class cls_s3(object):

    def  __init__(self,access_key,secret_key):

        self.s3 = boto3.client('s3', aws_access_key_id=access_key, aws_secret_access_key=secret_key)

    def getObject(self,bucket,key):

        read_file = self.s3.get_object(Bucket=bucket, Key=key)
        body = read_file['Body'].read().decode('utf-8')
        return body

test.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from cls_config import *
from cls_s3 import *
from cls_pandas import *

class test(object):

    def __init__(self):
        self.conf = cls_config('config.json')

    def process(self):

        conf = self.conf.getConfig()

        bucket = conf['s3']['bucket']
        key = conf['s3']['key']

        access_key = conf['credential']['access_key']
        secret_key = conf['credential']['secret_key']

        s3 = cls_s3(access_key,secret_key)
        ob = s3.getObject(bucket,key)

        pa = cls_pandas()
        df = pa.read(ob)

        print df

if __name__ == '__main__':
    test = test()
    test.process()

4
请不要仅仅发布解决方案,也要添加解释。
sjaustirni

制作如此复杂的解决方案(对于Python中的新手)是否有任何优势?
哈维尔·洛佩兹·托马斯(JavierLópezTomás)

1
这从s3读取文件,问题是如何向s3写入df。
Damian Satterthwaite-Phillips
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.