Як імпортувати текстовий файл на AWS S3 до панд без запису на диск


91

У мене є текстовий файл, збережений на 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

Як перетворити тіло відповіді у формат, який прийматимуть панди?

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)

спробуйте так: io.BytesIO(file)або io.StringIO(file)замість того, щоб fileу read_csv()дзвінку
MaxU

Ви можете використати, io.StringIOяк у цій відповіді .
IanS

Жодна з цих пропозицій не спрацювала. Ви можете побачити помилки в моєму редагуванні допису.
alpalalpal

1
Частина UPDATE спрацювала для мене. Дякую.
Вім Берхманс

Відповіді:


110

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

Оскільки використовується версія 0.20.1 , див. Відповідь нижче.pandass3fs


Чи є спосіб використовувати URL-адресу, не роблячи її загальнодоступною для всіх? Файл повинен залишатися приватним.
alpalalpal

У boto3документах показано, як налаштувати автентифікацію, щоб ви також мали
Стефан

1
Це кидає NoCredentialsError. Як встановити облікові дані s3? Я новачок у пітоні та бото
Суніл Рао,

15
Я виявив, що в останньому прикладі з boto3 мені довелося зробити наступне: df = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
user394430

Ця відповідь застаріла . Будь ласка, дивіться відповідь Весама .
gerrit

80

Тепер панди можуть обробляти URL-адреси S3 . Ви можете просто зробити:

import pandas as pd
import s3fs

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

Вам потрібно встановити,s3fs якщо у вас його немає. pip install s3fs

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

Якщо ваш сегмент S3 є приватним і вимагає автентифікації, у вас є два варіанти:

1- Додайте облікові дані доступу до вашого ~/.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

Гарний. Працює в python3.
Кайлер Браун

як щодо автентифікації ..?
James Wierzba

1
@JamesWierzba, я додав більше деталей щодо автентифікації до своєї відповіді вище.
Wesam

3
Якщо ви маєте справу з декількома профілями aws, як ви можете вибрати, який профіль слід використовувати? s3fs має опцію profile_name, але я не впевнений, як це працює з пандами.
Ivo Merchiers,

1
@IanS Не справді, наразі я спочатку відкриваю об'єкт файлу за допомогою s3fs (використовуючи вказаний профіль), а потім читаю його за допомогою панд, як це роблять тут github.com/pandas-dev/pandas/issues/16692
Іво Мерчієр

15

Зараз це підтримується в останніх версіях. Подивитися

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

наприклад,

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

4
Пам'ятайте, що "URL-адреси S3 також обробляються, але вимагають встановлення бібліотеки S3F"
Хуліо Віллане,

як щодо автентифікації
Джеймс

url з auth може бути важким, якщо URL-адреса не виставлена ​​як загальнодоступна, не впевнений, чи буде працювати простий / базовий http auth,
Raveen Beemsingh

9

За допомогою 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)

2
Думаю, за допомогою s3fs ви навіть можете писатиdf = pd.read_csv('s3://mybucket/path/to/object/foo.pkl')
louis_guitton

1
@louis_guitton, здається, це працює з pd-read_csv, але не з read_pickle
Сіп

1

Оскільки файли можуть бути занадто великими, нерозумно завантажувати їх у фрейм даних взагалі. Отже, читайте рядок за рядком і зберігайте його у фреймі даних. Так, ми також можемо вказати розмір фрагменту в 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


1

Для текстових файлів ви можете використовувати наведений нижче код із файлом з роздільником, наприклад: -

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

Варіант - перетворити 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, це може бути швидким виправленням.


0

Робота з декількома профілями AWS БУДЕ підтримуватися, запланована на v1.2.0.

Реалізація повинна бути такою:

pd.read_csv(
  's3://bucket/path', 
  storage_options={
    'aws_access_key_id': 'xxxxxxxxxx',
    'aws_secret_access_key': 'yyyyyyyyyy'})

https://github.com/pandas-dev/pandas/pull/35381 https://github.com/pandas-dev/pandas/blob/df32e83f36bf485be803be2b87d23135be30540a/pandas/io/parsers.py#L358

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.