Dataframe'i csv'ye doğrudan s3 Python'a kaydedin


126

Yeni bir CSV dosyasına yüklemek istediğim bir panda DataFrame'im var. Sorun şu ki, dosyayı s3'e aktarmadan önce yerel olarak kaydetmek istemiyorum. Dataframe'i doğrudan s3'e yazmak için to_csv gibi bir yöntem var mı? Boto3 kullanıyorum.
Şimdiye kadar sahip olduğum şeyler:

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'). Daha fazla bilgi için stackoverflow.com/a/56275519/908886 .
Peter Berg

Yanıtlar:


160

Kullanabilirsiniz:

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

10
Bu büyük bir dosyaysa, bu belleğe ne yapar ...?
citynorman

2
Dosya daha büyükse, sahip olduğunuz RAM, eylem başarısız olur ve bir İstisna dışında kalır (hangisi olduğunu bilmiyorum). Bu cevap olarak kabul edilmelidir
Eran Moshe

5
TypeError: unicode argument expected, got 'str'Kullanırken hata aldım StringIO. Kullandım BytesIOve mükemmel çalıştı. Not: Bu, Python 2.7
Abhishek Upadhyaya

1
nedir bucketnesne? bunu nasıl yarattın?
Charles Chow

1
bucketnesneleri S3'te depoladığınız yerdir. Kod, bunu saklayacağınız hedefi (düşünün: dizin) zaten oluşturduğunuzu varsayar. S3 belgelerine
Stefan

67

Doğrudan S3 yolunu kullanabilirsiniz. Pandalar 0.24.1 kullanıyorum

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

Sürüm notu:

S3 Dosya İşleme

Pandalar artık S3 bağlantılarını işlemek için s3fs kullanıyor. Bu, herhangi bir kodu bozmamalıdır. Bununla birlikte, s3fs gerekli bir bağımlılık olmadığı için, pandaların önceki sürümlerindeki boto gibi, ayrı olarak kurmanız gerekecektir. GH11915 .


7
bu kesinlikle şu anda en kolay cevap, perde arkasında s3fs kullanıyor, bu yüzden bunu gereksinimlerinize eklemeniz gerekiyor
JD D

1
Kolay olmasını seviyorum, ancak aşağıdaki hatayı almaya devam ettiğim için gerçekten çalışmıyor gibi görünüyor NoCredentialsError: Unable to locate credentials. Baska öneri?
CathyQian

1
Bunun panda <= 0.23.4 ile çalışmadığını teyit edebilirim, bu nedenle pandalar 0.24
Guido

1
Bu, to_csv komutunu kullanmaya çalıştığımda gördüğüm hatadır TypeError: write () argüman 1, str değil, unicode olmalıdır
Raj

13
Pandalar 0.24.2 kullanıyorum ve elde ettiğim şey NotImplementedError: Text mode not supported, use mode='wb' and manage bytes. baska öneri?
Binyamin

57

Sevdiğim s3fs Bir yerel dosya sistemi gibi s3 (neredeyse) kullanmanızı sağlar.

Bunu yapabilirsiniz:

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 sadece destekler rb ve wbbunu neden yaptığını bulunuyor dosyayı açma modları bytes_to_writeşeyler.


Harika! Aynı s3fs modülünü kullanarak dosya url'sini nasıl alabilirim?
M.Zaman

Yazılı dosyayı indirebileceğim URL'yi arıyordum, yine de S3FileSystem aracılığıyla alıyorum. Teşekkürler
M.Zaman

bu benim kullandığım şey; Teşekkürler. Pd.read_csv'nin (<s3path>) neden beklendiği gibi çalıştığını merak ediyorum, ancak yazmak için bu işi kullanmalıyız .. jupyter'imin içinde bulunduğu s3 kovasına doğrudan yazdığım durumlar dışında.
Renée

@ michcio1234 aynı şeyi ekleme modunda nasıl yapabilirim? S3
j '

@j ' s3fs, ekleme modunu desteklemiyor gibi görünüyor.
michcio1234

43

Bu daha güncel bir cevap:

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 ile ilgili sorun, hafızanızı tüketecek olmasıdır. Bu yöntemle, dosyayı dizeye dönüştürmek yerine s3'e aktarır, ardından s3'e yazarsınız. Pandalar veri çerçevesini ve onun dize kopyasını bellekte tutmak çok verimsiz görünüyor.

Bir ec2 anında çalışıyorsanız, s3'e yazmayı etkinleştirmek için ona bir IAM rolü verebilirsiniz, böylece kimlik bilgilerini doğrudan aktarmanıza gerek kalmaz. Bununla birlikte, kimlik bilgilerini S3FileSystem()işleve ileterek bir pakete de bağlanabilirsiniz . Belgelere bakın: https://s3fs.readthedocs.io/en/latest/


Bazı nedenlerden dolayı, bunu yaptığımda CSV çıktısında her satır atlandı
kjmerf

hı. bunun neden olacağından emin değilim. Hala sorunu yaşayıp yaşamadığınızı görmek için başka bir pandas df ile deneyebilirsiniz? Panda sürümünüz destekliyorsa, @ amit-kushwaha'nın yanıtını deneyin, burada s3 url'sini doğrudan iletin to_csv(). daha temiz bir uygulama gibi görünüyor.
erncyp

@erncyp Oraya hata alıyorum gibi görünüyor: botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied ... Kova'yı KAMU OKUMA bile yaptım ve Kova Politikasına özel hesap IAM kullanıcım altında aşağıdaki İşlemleri ekledim:"Action": [ "s3:PutObject", "s3:PutObjectAcl", "s3:GetObject", "s3:GetObjectAcl", "s3:DeleteObject" ]
ajoros

izinlere sahip değilsiniz gibi görünüyor? Kullandığınız IAM rolüne S3 okuma yazma izinlerini eklediğinizden emin olun
erncyp

@erncyp IAM kullanıcıma ekli AdministratorAccess politikam var, bu yüzden teoride gayet iyi okuyabilir / yazabilirim ... İşin garibi, başka bir StackOverflow kullanıcısını kullanarak yaptığım aşağıdaki işlevi kullandığımda çok iyi yazabiliyorum tavsiye (yorum bölümünde nasıl biçimlendireceğimi bilmediğim için noktalı virgüller satır sonu):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

Verilere Noneilk bağımsız değişken olarak geçerseniz to_csv(), bir dizge olarak döndürülür. Oradan bunu tek seferde S3'e yüklemek kolay bir adım.

Aynı zamanda bir StringIOnesneyi de iletmek mümkün olmalı to_csv(), ancak bir dizge kullanmak daha kolay olacaktır.


Hangi yönden daha kolay olacak? Bunu yapmanın doğru yolu nedir?
Eran Moshe

@EranMoshe: her iki şekilde düzgün çalışacaktır ancak belli ki geçmek kolay Noneiçin to_csv()ve bir yaratmaktır daha döndürülen dize kullanmak StringIOnesne ve daha sonra veri vazgeçme okuyun.
mhawke

Tembel bir programcı olarak yaptığım buydu. Ve daha az kod yazan programcı için daha kolay demek
istediniz

3

Bunun clientsadece kullanılarak da yapılabileceğini buldum 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')

2

AWS Data Wrangler'ı da kullanabilirsiniz :

import awswrangler

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

Paralel olarak yüklediği için birkaç bölüme ayrılacağını unutmayın.


0

kullandığınız için şunu boto3.client()deneyin:

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

Çalışıyor gibi görünen çok basit bir çözüm buldum:

s3 = boto3.client("s3")

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

Umarım yardımcı olur !


-5

S3 kovasından iki sütun içeren bir csv ve pandas veri çerçevesine koyduğum csv dosyasının içeriğini okudum.

Misal:

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
lütfen çözümü sadece göndermeyin, bunun bir açıklamasını da ekleyin.
sjaustirni

Bu kadar karmaşık (Python'da yeni başlayanlar için) bir çözüm yapmanın herhangi bir avantajı var mı?
Javier López Tomás

1
Bu s3'ten bir dosya okuyor, soru s3'e df'nin nasıl yazılacağıydı.
Damian Satterthwaite-Phillips
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.