Lưu Dataframe vào csv trực tiếp vào s3 Python


124

Tôi có DataFrame gấu trúc mà tôi muốn tải lên tệp CSV mới. Vấn đề là tôi không muốn lưu tệp cục bộ trước khi chuyển nó sang s3. Có phương pháp nào như to_csv để ghi trực tiếp dataframe vào s3 không? Tôi đang sử dụng boto3.
Đây là những gì tôi có cho đến nay:

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 để biết thêm thông tin.
Peter Berg

Câu trả lời:


156

Bạn có thể dùng:

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
Nếu đây là một tệp lớn, thì điều này sẽ làm gì đối với bộ nhớ ...?
citynorman

2
Nếu tệp lớn hơn thì RAM bạn có sẵn, hành động sẽ thất bại và sẽ ngoại trừ Ngoại lệ (không biết cái nào). Điều này nên được chấp nhận như câu trả lời
Eran Moshe

5
Tôi đã TypeError: unicode argument expected, got 'str'gặp lỗi trong khi sử dụng StringIO. Tôi đã sử dụng BytesIOvà nó hoạt động hoàn toàn tốt. Lưu ý: đây là trong Python 2.7
Abhishek Upadhyaya

1
bucketđối tượng là gì? Làm thế nào bạn tạo ra điều đó?
Charles Chow

1
bucketlà nơi bạn lưu trữ các đối tượng trên S3. Mã giả định rằng bạn đã tạo đích (thư mục: think) nơi lưu trữ này. Xem tài liệu S3
Stefan

64

Bạn có thể trực tiếp sử dụng đường dẫn S3. Tôi đang sử dụng 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

Lưu ý phát hành:

Xử lý tệp S3

gấu trúc hiện sử dụng s3fs để xử lý các kết nối S3. Điều này không nên phá vỡ bất kỳ mã. Tuy nhiên, vì s3fs không phải là một phụ thuộc bắt buộc, bạn sẽ cần phải cài đặt riêng, như boto trong các phiên bản trước của gấu trúc. GH11915 .


7
Đây chắc chắn là câu trả lời dễ nhất bây giờ, nó sử dụng s3fs đằng sau hậu trường, do đó bạn cần thêm nó vào yêu cầu của bạn
JD D

1
Tôi thích nó rất dễ, nhưng có vẻ như nó không thực sự hoạt động vì tôi liên tục gặp phải lỗi sau NoCredentialsError: Unable to locate credentials. Bất kỳ đề xuất?
CathyQian

1
Tôi có thể xác nhận điều này không hoạt động với gấu trúc <= 0.23.4, vì vậy hãy chắc chắn nâng cấp lên gấu trúc 0,24
Guido

1
Đây là lỗi tôi thấy khi tôi cố gắng sử dụng lệnh to_csv TypeError: write () đối số 1 phải là unicode, không phải str
Raj

13
Tôi đang sử dụng gấu trúc 0.24.2 và những gì tôi nhận được là NotImplementedError: Text mode not supported, use mode='wb' and manage bytes. bất kỳ đề xuất?
Binyamin Ngay cả

57

Tôi thích s3fs cho phép bạn sử dụng s3 (gần như) như một hệ thống tập tin cục bộ.

Bạn có thể làm được việc này:

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)

s3fschỉ hỗ trợ rbwbcác chế độ mở tệp, đó là lý do tại sao tôi làm bytes_to_writecông cụ này .


Tuyệt quá! Làm cách nào tôi có thể nhận được url tệp bằng cách sử dụng cùng một mô-đun s3fs?
M.Zaman

Tôi đã tìm kiếm URL từ nơi tôi có thể tải xuống tệp bằng văn bản, dù sao tôi cũng nhận được thông qua S3FileSystem. Cảm ơn
M.Zaman

đây là những gì tôi sử dụng; cảm ơn. Tôi tò mò tại sao pd.read_csv (<s3path>) hoạt động như mong đợi nhưng để viết chúng tôi phải sử dụng công việc này xung quanh .. ngoại trừ trong trường hợp tôi đang viết trực tiếp vào s3 xô jupyter của tôi.
Renée

@ michcio1234 làm thế nào tôi có thể làm tương tự trong chế độ chắp thêm? Tôi cần nối thêm dữ liệu trong csv hiện có trên s3
j '

@j ' s3fsdường như không hỗ trợ chế độ chắp thêm.
michcio1234

43

Đây là một câu trả lời cập nhật hơn:

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)

Vấn đề với StringIO là nó sẽ ăn mòn bộ nhớ của bạn. Với phương pháp này, bạn đang truyền tệp đến s3, thay vì chuyển đổi nó thành chuỗi, sau đó viết nó thành s3. Giữ dataframe gấu trúc và sao chép chuỗi của nó trong bộ nhớ có vẻ rất không hiệu quả.

Nếu bạn đang làm việc ngay lập tức ec2, bạn có thể đóng vai trò IAM để cho phép ghi nó vào s3, do đó bạn không cần phải chuyển trực tiếp thông tin đăng nhập. Tuy nhiên, bạn cũng có thể kết nối với một nhóm bằng cách chuyển thông tin đăng nhập cho S3FileSystem()chức năng. Xem tài liệu: https://s3fs.readthedocs.io/en/latest/


Vì một số lý do khi tôi làm điều này, mọi dòng đều bị bỏ qua trong CSV đầu ra
kjmerf

hmm không chắc tại sao điều đó sẽ xảy ra. có lẽ thử với một con gấu trúc khác để xem bạn còn gặp vấn đề gì không? Nếu phiên bản gấu trúc của bạn hỗ trợ nó, hãy thử câu trả lời của @ amit-kushwaha, nơi bạn chuyển trực tiếp url s3 tới to_csv(). có vẻ như một thực hiện sạch hơn.
erncyp

@erncyp tôi dường như nhận được có lỗi: botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied ... Tôi thậm chí còn đưa ra xô CÔNG READ và tôi đã thêm Actions sau, dưới tài khoản người dùng IAM cụ thể của tôi, trong Hợp đồng bảo hiểm Bucket:"Action": [ "s3:PutObject", "s3:PutObjectAcl", "s3:GetObject", "s3:GetObjectAcl", "s3:DeleteObject" ]
ajoros

Có vẻ như bạn đang thiếu quyền? Đảm bảo đính kèm quyền đọc ghi S3 vào vai trò IAM bạn đang sử dụng
erncyp

@erncyp Tôi có chính sách AdministratorAccess được đính kèm với người dùng IAM của tôi, vì vậy về lý thuyết tôi có thể đọc / ghi tốt ... Thật kỳ lạ, tôi có thể viết tốt khi tôi sử dụng chức năng sau đây do tôi tạo, sử dụng một người dùng StackOverflow khác lời khuyên (fyi semi-colons là dòng cuối vì tôi không biết cách định dạng trong phần bình luận):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

Nếu bạn chuyển Nonelàm đối số đầu tiên choto_csv() dữ liệu sẽ được trả về dưới dạng chuỗi. Từ đó, một bước dễ dàng để tải nó lên S3 trong một lần.

Cũng có thể truyền một StringIOđối tượng đến to_csv(), nhưng sử dụng một chuỗi sẽ dễ dàng hơn.


Sẽ dễ dàng hơn theo cách nào? Cách chính xác để làm điều đó là gì?
Eran Moshe

@EranMoshe: một trong hai cách sẽ làm việc một cách chính xác, nhưng rõ ràng nó dễ dàng hơn để vượt qua Noneđể to_csv()và sử dụng chuỗi trả lại hơn là để tạo ra một StringIOđối tượng và sau đó đọc dữ liệu sao lưu ra.
mhawke

Là một lập trình viên lười biếng, đó là những gì tôi đã làm. Và bạn có nghĩa là dễ dàng hơn cho các lập trình viên viết ít mã hơn:>
Eran Moshe

2

Bạn cũng có thể sử dụng Trình sắp xếp dữ liệu AWS :

import awswrangler

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

Lưu ý rằng nó sẽ chia thành nhiều phần kể từ khi tải lên song song.


2

Tôi thấy điều này có thể được thực hiện bằng cách sử dụng clientcũng và không chỉ 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

vì bạn đang sử dụng boto3.client(), hãy thử:

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

Tôi tìm thấy một giải pháp rất đơn giản mà dường như đang hoạt động:

s3 = boto3.client("s3")

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

Mong rằng sẽ giúp!


-5

Tôi đọc một csv với hai cột từ xô s3 và nội dung của tệp csv tôi đặt trong khung dữ liệu pandas.

Thí dụ:

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

kiểm tra

#!/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
xin vui lòng không chỉ đăng giải pháp, thêm một lời giải thích về nó.
sjaustirni

Có bất kỳ lợi thế nào khi tạo ra một giải pháp phức tạp như vậy (đối với người mới sử dụng Python) không?
Javier López Tomás

1
Điều này đọc một tập tin từ s3, câu hỏi là làm thế nào để viết một df vào s3.
Damian Satterthwaite-Phillips
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.