Come importare un file di testo su AWS S3 in panda senza scrivere su disco


98

Ho un file di testo salvato su S3 che è una tabella delimitata da tabulazioni. Voglio caricarlo in panda ma non posso salvarlo prima perché sto funzionando su un server heroku. Ecco cosa ho finora.

import io
import boto3
import os
import pandas as pd

os.environ["AWS_ACCESS_KEY_ID"] = "xxxxxxxx"
os.environ["AWS_SECRET_ACCESS_KEY"] = "xxxxxxxx"

s3_client = boto3.client('s3')
response = s3_client.get_object(Bucket="my_bucket",Key="filename.txt")
file = response["Body"]


pd.read_csv(file, header=14, delimiter="\t", low_memory=False)

l'errore è

OSError: Expected file path name or file-like object, got <class 'bytes'> type

Come faccio a convertire il corpo della risposta in un formato accettato dai panda?

pd.read_csv(io.StringIO(file), header=14, delimiter="\t", low_memory=False)

returns

TypeError: initial_value must be str or None, not StreamingBody

pd.read_csv(io.BytesIO(file), header=14, delimiter="\t", low_memory=False)

returns

TypeError: 'StreamingBody' does not support the buffer interface

AGGIORNAMENTO - Utilizzando quanto segue ha funzionato

file = response["Body"].read()

e

pd.read_csv(io.BytesIO(file), header=14, delimiter="\t", low_memory=False)

provalo in questo modo: io.BytesIO(file)o io.StringIO(file)invece che filenella read_csv()chiamata
MaxU

Potresti usare io.StringIOcome in questa risposta .
IanS

Nessuno di questi suggerimenti ha funzionato. Puoi vedere gli errori nella mia modifica del post.
alpalalpal

1
La parte UPDATE ha funzionato per me. Grazie.
Wim Berchmans

Risposte:


116

pandasutilizza botoper read_csv, quindi dovresti essere in grado di:

import boto
data = pd.read_csv('s3://bucket....csv')

Se hai bisogno boto3perché sei attivo python3.4+, puoi farlo

import boto3
import io
s3 = boto3.client('s3')
obj = s3.get_object(Bucket='bucket', Key='key')
df = pd.read_csv(io.BytesIO(obj['Body'].read()))

Poiché la versione 0.20.1 pandas utilizza s3fs, vedere la risposta di seguito.


C'è un modo per utilizzare un URL senza renderlo pubblico a tutti? Il file deve rimanere privato.
alpalalpal

I boto3documenti mostrano come configurare l'autenticazione in modo da poter accedere anche ai file privati: boto3.readthedocs.io/en/latest/guide/quickstart.html
Stefan

1
Sta lanciando NoCredentialsError. Come si impostano le credenziali s3? Sono nuovo di Python e Boto
Sunil Rao

15
Ho scoperto che dovevo fare quanto segue sull'ultimo esempio con boto3: df = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
user394430

Questa risposta non è aggiornata . Si prega di vedere la risposta di Wesams .
gerrit

85

Ora i panda possono gestire gli URL S3 . Potresti semplicemente fare:

import pandas as pd
import s3fs

df = pd.read_csv('s3://bucket-name/file.csv')

Devi installare s3fs se non lo si dispone. pip install s3fs

Autenticazione

Se il tuo bucket S3 è privato e richiede l'autenticazione, hai due opzioni:

1- Aggiungi le credenziali di accesso al tuo ~/.aws/credentialsfile di configurazione

[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

O

2- Impostare le seguenti variabili d'ambiente con i loro valori corretti:

  • aws_access_key_id
  • aws_secret_access_key
  • aws_session_token

Bellissimo. Funziona in python3.
Kyler Brown,

che ne dici di autenticazione ..?
James Wierzba

1
@ JamesWierzba, ho aggiunto ulteriori dettagli sull'autenticazione alla mia risposta sopra.
Wesam

3
Quando si ha a che fare con più profili aws, come si può selezionare quale profilo deve essere utilizzato? s3fs ha l'opzione profile_name, ma non sono sicuro di come funzioni con i panda.
Ivo Merchiers

1
@IanS Non proprio, attualmente, apro prima l'oggetto file con s3fs (usando il profilo specificato) e poi lo leggo con i panda, come fanno qui github.com/pandas-dev/pandas/issues/16692
Ivo Merchiers

16

Questo è ora supportato negli ultimi panda. Vedere

http://pandas.pydata.org/pandas-docs/stable/io.html#reading-remote-files

per esempio.,

df = pd.read_csv('s3://pandas-test/tips.csv')

4
Ricorda: "Anche gli URL S3 vengono gestiti ma richiedono l'installazione della libreria S3F"
Julio Villane,

che dire dell'autenticazione
James Wierzba

url con autenticazione può essere difficile a meno che l'URL non sia esposto come pubblico, non sono sicuro che l'autenticazione http semplice / di base funzionerà,
Raveen Beemsingh

10

Con s3fs si può fare come segue:

import s3fs
import pandas as pd
fs = s3fs.S3FileSystem(anon=False)

# CSV
with fs.open('mybucket/path/to/object/foo.pkl') as f:
    df = pd.read_csv(f)

# Pickle
with fs.open('mybucket/path/to/object/foo.pkl') as f:
    df = pd.read_pickle(f)

2
Penso che con s3fs puoi persino scriveredf = pd.read_csv('s3://mybucket/path/to/object/foo.pkl')
louis_guitton

1
@louis_guitton questo sembra funzionare con pd-read_csv ma ​​non con read_pickle
Sip

2

Poiché i file possono essere troppo grandi, non è saggio caricarli del tutto nel dataframe. Quindi, leggi riga per riga e salvalo nel dataframe. Sì, possiamo anche fornire la dimensione del blocco in read_csv ma ​​poi dobbiamo mantenere il numero di righe lette.

Quindi, ho ideato questa ingegneria:

def create_file_object_for_streaming(self):
        print("creating file object for streaming")
        self.file_object = self.bucket.Object(key=self.package_s3_key)
        print("File object is: " + str(self.file_object))
        print("Object file created.")
        return self.file_object

for row in codecs.getreader(self.encoding)(self.response[u'Body']).readlines():
            row_string = StringIO(row)
            df = pd.read_csv(row_string, sep=",")

Elimino anche il df una volta terminato il lavoro. del df


1

Per i file di testo, puoi utilizzare il codice seguente con un file delimitato da barre verticali, ad esempio: -

import pandas as pd
import io
import boto3
s3_client = boto3.client('s3', use_ssl=False)
bucket = #
prefix = #
obj = s3_client.get_object(Bucket=bucket, Key=prefix+ filename)
df = pd.read_fwf((io.BytesIO(obj['Body'].read())) , encoding= 'unicode_escape', delimiter='|', error_bad_lines=False,header=None, dtype=str)

0

Un'opzione è convertire il csv in json tramite df.to_dict()e quindi memorizzarlo come stringa. Nota che questo è rilevante solo se il CSV non è un requisito, ma vuoi solo inserire rapidamente il dataframe in un bucket S3 e recuperarlo di nuovo.

from boto.s3.connection import S3Connection
import pandas as pd
import yaml

conn = S3Connection()
mybucket = conn.get_bucket('mybucketName')
myKey = mybucket.get_key("myKeyName")

myKey.set_contents_from_string(str(df.to_dict()))

Questo convertirà il df in una stringa dict, quindi lo salverà come json in S3. Puoi leggerlo successivamente nello stesso formato json:

df = pd.DataFrame(yaml.load(myKey.get_contents_as_string()))

Anche le altre soluzioni sono buone, ma questo è un po 'più semplice. Yaml potrebbe non essere necessariamente richiesto, ma hai bisogno di qualcosa per analizzare la stringa json. Se il file S3 non ha necessariamente bisogno di essere un CSV questa può essere una soluzione rapida.


0

Per python 3.6+ Amazon ora ha una libreria davvero carina per usare Pandas con i loro servizi, chiamata awswrangler .

import awswrangler as wr
import boto3


# Boto3 session
session = boto3.session.Session(aws_access_key_id='XXXX', 
                                aws_secret_access_key='XXXX')

# Awswrangler pass forward all pd.read_csv() function args
df = wr.s3.read_csv(path='s3://bucket/path/',
                    boto3_session=session,
                    skiprows=2,
                    sep=';',
                    decimal=',',
                    na_values=['--'])

Per installare awswrangler: pip install awswrangler


-1
import s3fs
import pandas as pd
s3 = s3fs.S3FileSystem(profile='<profile_name>')
pd.read_csv(s3.open(<s3_path>))

1
Per favore aggiungi qualche spiegazione al tuo codice.
andrey.shedko
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.