Как импортировать текстовый файл на AWS S3 в панды без записи на диск

У меня есть текстовый файл, сохраненный на S3, который является таблицей с разделителями табуляции. Я хочу загрузить его в панды, но не могу сохранить его сначала, потому что я бегу на сервере heroku. Вот что я до сих пор.

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)

ошибка

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

Как преобразовать тело ответа в формат, который будет принимать 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

ОБНОВЛЕНИЕ - использование следующих отработанных

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

а также

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

Ответы

Ответ 1

pandas использует boto для read_csv, так что вы должны быть в состоянии:

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

Если вам нужен boto3 потому что вы находитесь на python3.4+, вы можете

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

Ответ 2

Теперь панды могут обрабатывать S3 URL. Вы можете просто сделать:

import pandas as pd
import s3fs

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

Вам нужно установить s3fs если у вас его нет. pip install s3fs

Аутентификация

Если ваша корзина S3 является частной и требует аутентификации, у вас есть два варианта:

1- Добавьте учетные данные для доступа к ~/.aws/credentials конфигурации ~/.aws/credentials

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

Или же

2- Установите следующие переменные среды с их правильными значениями:

  • aws_access_key_id
  • aws_secret_access_key
  • aws_session_token

Ответ 4

С s3fs это можно сделать следующим образом:

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)

Ответ 5

Вы можете настроить функцию, которая выглядит так, чтобы обернуть pd.read_csv

import pandas as pd
import io
import boto3

s3_resource = boto3.resource('s3')
s3_client = boto3.client('s3')

def pd_read_csv_s3(path, *args, **kwargs):
    path = path.replace("s3://", "")
    bucket, key = path.split('/', 1)
    obj = s3_client.get_object(Bucket=bucket, Key=key)
    return pd.read_csv(io.BytesIO(obj['Body'].read()), *args, **kwargs)

# Example usage
pd_read_csv_s3("s3://my_bucket/my_folder/file.csv", skiprows=2)

Обратите внимание, что аргументы, такие как skiprows или что-то еще, что вы хотите, прошли правильно

Ответ 6

Поскольку файлы могут быть слишком большими, нецелесообразно загружать их в кадр данных вообще. Следовательно, читайте построчно и сохраняйте его в кадре данных. Да, мы можем также указать размер фрагмента в read_csv, но тогда мы должны сохранить количество прочитанных строк.

Следовательно, я придумал эту разработку:

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=",")

Я также удаляю df, как только работа сделана. del df

Ответ 7

Один из вариантов - преобразовать csv в json с помощью df.to_dict() а затем сохранить его в виде строки. Обратите внимание, что это уместно только в том случае, если CSV не является обязательным требованием, но вы просто хотите быстро поместить фрейм данных в корзину S3 и получить его снова.

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

Это преобразует df в строку dict, а затем сохраняет ее как json в S3. Позже вы можете прочитать его в том же формате JSON:

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

Другие решения тоже хороши, но это немного проще. Yaml не обязательно требуется, но вам нужно что-то для анализа строки json. Если файл S3 не обязательно должен быть CSV, это может быть быстрым решением.