Cara mengimpor file teks di AWS S3 ke panda tanpa menulis ke disk


91

Saya memiliki file teks yang disimpan di S3 yang merupakan tabel tab delimited. Saya ingin memuatnya ke dalam panda tetapi tidak dapat menyimpannya terlebih dahulu karena saya menjalankannya di server heroku. Inilah yang saya miliki sejauh ini.

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)

kesalahannya adalah

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

Bagaimana cara mengubah isi respons menjadi format yang akan diterima 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

UPDATE - Menggunakan yang berikut ini berhasil

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

dan

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

coba cara ini: io.BytesIO(file)atau io.StringIO(file)bukan filedalam read_csv()panggilan
MaxU

Anda bisa menggunakan io.StringIOseperti dalam jawaban ini .
IanS

Tak satu pun dari saran ini berhasil. Anda dapat melihat kesalahan dalam edit posting saya.
alpalalpal

1
Bagian UPDATE berhasil untuk saya. Terima kasih.
Wim Berchmans

Jawaban:


110

pandasdigunakan botountuk read_csv, jadi Anda harus bisa:

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

Jika Anda membutuhkan boto3karena Anda aktif python3.4+, Anda bisa

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

Sejak versi 0.20.1 pandas menggunakan s3fs, lihat jawaban di bawah.


Apakah ada cara untuk menggunakan URL tanpa membuatnya menjadi publik untuk semua orang? File tersebut harus tetap pribadi.
alpalalpal

The boto3docs menunjukkan bagaimana otentikasi configure sehingga Anda dapat mengakses file pribadi juga: boto3.readthedocs.io/en/latest/guide/quickstart.html
Stefan

1
Itu melempar NoCredentialsError. Bagaimana cara mengatur kredensial s3 tl itu? Saya baru mengenal python dan boto
Sunil Rao

15
Saya menemukan bahwa saya harus melakukan hal berikut pada contoh terakhir dengan boto3: df = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
user394430

Jawaban ini sudah ketinggalan zaman . Silakan lihat jawaban Wesam .
gerrit

80

Sekarang panda dapat menangani URL S3 . Anda cukup melakukan:

import pandas as pd
import s3fs

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

Anda perlu menginstals3fs jika Anda tidak memilikinya. pip install s3fs

Autentikasi

Jika bucket S3 Anda bersifat pribadi dan memerlukan autentikasi, Anda memiliki dua opsi:

1- Tambahkan kredensial akses ke file konfigurasi Anda~/.aws/credentials

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

Atau

2- Atur variabel lingkungan berikut dengan nilai yang tepat:

  • aws_access_key_id
  • aws_secret_access_key
  • aws_session_token

Cantik. Bekerja di python3.
Kyler Brown

bagaimana dengan otentikasi ..?
James Wierzba

1
@JamesWierzba, saya menambahkan lebih banyak detail tentang otentikasi ke jawaban saya di atas.
Wesam

3
Saat menangani beberapa profil aws, bagaimana Anda dapat memilih profil mana yang harus digunakan? s3fs memiliki opsi profile_name, tapi saya tidak yakin bagaimana cara kerjanya dengan panda.
Ivo Merchiers

1
@IanS Tidak juga, saat ini, saya pertama kali membuka objek file dengan s3fs (menggunakan profil yang ditentukan) dan kemudian saya membacanya dengan panda, seperti yang mereka lakukan di sini github.com/pandas-dev/pandas/issues/16692
Ivo Merchiers


9

Dengan s3fs dapat dilakukan sebagai berikut:

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
Saya pikir dengan s3fs Anda bahkan dapat menulisdf = pd.read_csv('s3://mybucket/path/to/object/foo.pkl')
louis_guitton

1
@louis_guitton ini sepertinya bekerja dengan pd-read_csv tetapi tidak dengan read_pickle
Sip

1

Karena file bisa terlalu besar, tidak bijaksana untuk memuatnya ke dalam dataframe sama sekali. Karenanya, baca baris demi baris dan simpan di dataframe. Ya, kami juga dapat memberikan ukuran potongan di read_csv tetapi kemudian kami harus mempertahankan jumlah baris yang dibaca.

Oleh karena itu, saya menemukan teknik ini:

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

Saya juga menghapus df setelah pekerjaan selesai. del df


1

Untuk file teks, Anda dapat menggunakan kode di bawah ini dengan file yang dipisahkan tanda pipa misalnya: -

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

Pilihannya adalah mengonversi csv ke json melalui df.to_dict()dan kemudian menyimpannya sebagai string. Perhatikan bahwa ini hanya relevan jika CSV bukan persyaratan, tetapi Anda hanya ingin meletakkan kerangka data dengan cepat di bucket S3 dan mengambilnya kembali.

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

Ini akan mengubah df menjadi string dict, dan kemudian menyimpannya sebagai json di S3. Anda nanti dapat membacanya dalam format json yang sama:

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

Solusi lain juga bagus, tetapi ini sedikit lebih sederhana. Yaml mungkin tidak selalu diperlukan, tetapi Anda memerlukan sesuatu untuk mengurai string json. Jika file S3 tidak harus berupa CSV, ini bisa menjadi perbaikan cepat.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.