Tengo un archivo de texto guardado en S3 que es una tabla delimitada por tabulaciones. Quiero cargarlo en pandas pero no puedo guardarlo primero porque lo estoy ejecutando en un servidor heroku. Esto es lo que tengo hasta ahora.
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)
el error es
OSError: Expected file path name or file-like object, got <class 'bytes'> type
¿Cómo convierto el cuerpo de la respuesta a un formato que aceptarán los pandas?
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
ACTUALIZACIÓN: el uso de lo siguiente funcionó
file = response["Body"].read()
y
pd.read_csv(io.BytesIO(file), header=14, delimiter="\t", low_memory=False)
io.BytesIO(file)
io.StringIO(file)
file
read_csv()
io.StringIO
como en esta respuesta .Respuestas:
pandas
utilizaboto
pararead_csv
, por lo que debería poder:import boto data = pd.read_csv('s3://bucket....csv')
Si lo necesita
boto3
porque está encendidopython3.4+
, puedeimport boto3 import io s3 = boto3.client('s3') obj = s3.get_object(Bucket='bucket', Key='key') df = pd.read_csv(io.BytesIO(obj['Body'].read()))
Dado que la versión 0.20.1
pandas
usas3fs
, vea la respuesta a continuación.fuente
boto3
documentos muestran cómo configurar la autenticación para que también pueda acceder a archivos privados: boto3.readthedocs.io/en/latest/guide/quickstart.htmldf = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
Ahora los pandas pueden manejar las URL de S3 . Simplemente podrías hacer:
import pandas as pd import s3fs df = pd.read_csv('s3://bucket-name/file.csv')
Debe instalarlo
s3fs
si no lo tiene.pip install s3fs
Autenticación
Si su bucket de S3 es privado y requiere autenticación, tiene dos opciones:
1- Agregue credenciales de acceso a su
~/.aws/credentials
archivo de configuraciónO
2- Establezca las siguientes variables de entorno con sus valores adecuados:
aws_access_key_id
aws_secret_access_key
aws_session_token
fuente
Esto ahora es compatible con los últimos pandas. Ver
http://pandas.pydata.org/pandas-docs/stable/io.html#reading-remote-files
p.ej.,
df = pd.read_csv('s3://pandas-test/tips.csv')
fuente
Con s3fs se puede hacer de la siguiente manera:
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)
fuente
df = pd.read_csv('s3://mybucket/path/to/object/foo.pkl')
Dado que los archivos pueden ser demasiado grandes, no es aconsejable cargarlos en el marco de datos por completo. Por lo tanto, lea línea por línea y guárdelo en el marco de datos. Sí, también podemos proporcionar el tamaño del fragmento en read_csv, pero luego tenemos que mantener el número de filas leídas.
Por lo tanto, se me ocurrió esta ingeniería:
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=",")
También elimino el df una vez que se realiza el trabajo.
del df
fuente
Para archivos de texto, puede usar el siguiente código con un archivo delimitado por tuberías, por ejemplo: -
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)
fuente
Una opción es convertir el csv a json mediante
df.to_dict()
y luego almacenarlo como una cadena. Tenga en cuenta que esto solo es relevante si el CSV no es un requisito, pero solo desea colocar rápidamente el marco de datos en un depósito S3 y recuperarlo nuevamente.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()))
Esto convertirá el df en una cadena dict y luego lo guardará como json en S3. Más tarde puede leerlo en el mismo formato json:
Las otras soluciones también son buenas, pero esto es un poco más simple. Es posible que Yaml no sea necesariamente necesario, pero necesita algo para analizar la cadena json. Si el archivo S3 no tiene por qué ser un CSV, esta puede ser una solución rápida.
fuente