Eu tenho um arquivo de texto salvo no S3 que é uma tabela delimitada por tabulação. Quero carregá-lo no pandas, mas não consigo salvá-lo primeiro porque estou executando em um servidor heroku. Aqui está o que tenho até agora.
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)
o erro é
OSError: Expected file path name or file-like object, got <class 'bytes'> type
Como faço para converter o corpo da resposta em um formato que os pandas aceitarão?
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
ATUALIZAÇÃO - usando o seguinte funcionou
file = response["Body"].read()
e
pd.read_csv(io.BytesIO(file), header=14, delimiter="\t", low_memory=False)
io.BytesIO(file)
ou emio.StringIO(file)
vez defile
naread_csv()
chamadaio.StringIO
como nesta resposta .Respostas:
pandas
usaboto
pararead_csv
, então você deve ser capaz de:import boto data = pd.read_csv('s3://bucket....csv')
Se você precisa
boto3
porque está ligadopython3.4+
, você podeimport boto3 import io s3 = boto3.client('s3') obj = s3.get_object(Bucket='bucket', Key='key') df = pd.read_csv(io.BytesIO(obj['Body'].read()))
Desde que usa a versão 0.20.1 , veja a resposta abaixo.
pandas
s3fs
fonte
boto3
documentos mostram como configurar a autenticação para que você também possa acessar arquivos privados: boto3.readthedocs.io/en/latest/guide/quickstart.htmldf = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
Agora os pandas podem lidar com URLs S3 . Você poderia simplesmente fazer:
import pandas as pd import s3fs df = pd.read_csv('s3://bucket-name/file.csv')
Você precisa instalar
s3fs
se ainda não tiver.pip install s3fs
Autenticação
Se o seu intervalo S3 for privado e exigir autenticação, você terá duas opções:
1- Adicione credenciais de acesso ao seu
~/.aws/credentials
arquivo de configuraçãoOu
2- Defina as seguintes variáveis de ambiente com seus valores adequados:
aws_access_key_id
aws_secret_access_key
aws_session_token
fonte
Isso agora é compatível com os pandas mais recentes. Vejo
http://pandas.pydata.org/pandas-docs/stable/io.html#reading-remote-files
por exemplo.,
df = pd.read_csv('s3://pandas-test/tips.csv')
fonte
Com s3fs, isso pode ser feito da seguinte maneira:
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)
fonte
df = pd.read_csv('s3://mybucket/path/to/object/foo.pkl')
Como os arquivos podem ser muito grandes, não é aconselhável carregá-los no dataframe juntos. Portanto, leia linha por linha e salve-o no dataframe. Sim, também podemos fornecer o tamanho do bloco no read_csv, mas então temos que manter o número de linhas lidas.
Portanto, eu vim com esta engenharia:
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=",")
Eu também excluo o df quando o trabalho é concluído.
del df
fonte
Para arquivos de texto, você pode usar o código abaixo com arquivo delimitado por barra vertical, por exemplo: -
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)
fonte
Uma opção é converter o csv em json via
df.to_dict()
e armazená-lo como uma string. Observe que isso só é relevante se o CSV não for um requisito, mas você apenas deseja colocar rapidamente o dataframe em um depósito S3 e recuperá-lo novamente.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()))
Isso irá converter o df em uma string de dicionário e salvá-la como json no S3. Você pode lê-lo mais tarde no mesmo formato json:
As outras soluções também são boas, mas isso é um pouco mais simples. Yaml pode não ser necessariamente obrigatório, mas você precisa de algo para analisar a string json. Se o arquivo S3 não precisa necessariamente ser um CSV, isso pode ser uma solução rápida.
fonte
Para python 3.6+, a Amazon agora tem uma biblioteca muito boa para usar o Pandas com seus serviços, chamada 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=['--'])
Para instalar o awswrangler:
pip install awswrangler
fonte
import s3fs import pandas as pd s3 = s3fs.S3FileSystem(profile='<profile_name>') pd.read_csv(s3.open(<s3_path>))
fonte