วิธีนำเข้าไฟล์ข้อความบน AWS S3 เข้าสู่แพนด้าโดยไม่ต้องเขียนลงดิสก์


96

ฉันมีไฟล์ข้อความที่บันทึกไว้ใน 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

UPDATE - การใช้งานต่อไปนี้ได้ผล

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 ใช้ได้ผลสำหรับฉัน ขอบคุณ.
Wim Berchmans

คำตอบ:


115

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 pandasใช้s3fsโปรดดูคำตอบด้านล่าง


มีวิธีใช้ URL โดยไม่เปิดเผยต่อทุกคนหรือไม่? ไฟล์ต้องมีความเป็นส่วนตัว
alpalalpal

boto3เอกสารแสดงวิธีการรับรองความถูกต้องกำหนดค่าเพื่อให้คุณสามารถเข้าถึงไฟล์ส่วนตัวเช่นกัน: boto3.readthedocs.io/en/latest/guide/quickstart.html
สเตฟาน

1
มันกำลังขว้าง NoCredentialsError ฉันจะตั้งค่าข้อมูลประจำตัว s3 tl ได้อย่างไร ฉันยังใหม่กับ python และ boto
Sunil Rao

15
ฉันพบว่าฉันต้องทำสิ่งต่อไปนี้ในตัวอย่างสุดท้ายด้วย boto3: df = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
user394430

คำตอบนี้จากวันที่ โปรดดูคำตอบ Wesams
gerrit

84

ตอนนี้แพนด้าจัดการกับ 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

[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
Kyler Brown

การพิสูจน์ตัวตน .. ?
James Wierzba

1
@JamesWierzba ฉันได้เพิ่มรายละเอียดเพิ่มเติมเกี่ยวกับการรับรองความถูกต้องให้กับคำตอบของฉันด้านบน
Wesam

3
เมื่อจัดการกับโปรไฟล์ aws หลาย ๆ โปรไฟล์คุณจะเลือกได้อย่างไรว่าควรใช้โปรไฟล์ใด s3fs มีตัวเลือก profile_name แต่ฉันไม่แน่ใจว่ามันใช้ได้กับแพนด้าอย่างไร
Ivo Merchiers

1
@IanS ไม่จริงตอนนี้ฉันเปิดออบเจ็กต์ไฟล์ด้วย s3fs ก่อน (โดยใช้โปรไฟล์ที่ระบุ) จากนั้นฉันอ่านมันด้วยแพนด้าเหมือนที่พวกเขาทำที่นี่github.com/pandas-dev/pandas/issues/16692
Ivo Merchiers

16

ตอนนี้รองรับแพนด้าล่าสุดแล้ว ดู

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

เช่น.,

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

4
โปรดจำไว้ว่า 'S3 URLs ได้รับการจัดการเช่นกัน แต่ต้องติดตั้งไลบรารี S3Fs'
Julio Villane

สิ่งที่เกี่ยวกับการรับรองความถูกต้อง
James Wierzba

url ที่มี auth อาจทำได้ยากเว้นแต่ว่า url จะเปิดเผยเป็นสาธารณะไม่แน่ใจว่า http auth แบบธรรมดา / พื้นฐานจะใช้ได้หรือไม่
Raveen Beemsingh

10

ด้วย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
Sip

2

เนื่องจากไฟล์อาจมีขนาดใหญ่เกินไปจึงไม่ควรโหลดลงในดาต้าเฟรมทั้งหมด ดังนั้นอ่านทีละบรรทัดและบันทึกลงใน dataframe ใช่เรายังสามารถระบุขนาดชิ้นใน 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

สำหรับหลาม 3.6+ Amazon ตอนนี้มีห้องสมุดที่ดีจริงๆที่จะใช้นุ่นกับบริการของพวกเขาเรียกว่า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=['--'])

ในการติดตั้ง awswrangler: pip install awswrangler


-1
import s3fs
import pandas as pd
s3 = s3fs.S3FileSystem(profile='<profile_name>')
pd.read_csv(s3.open(<s3_path>))

1
โปรดเพิ่มคำอธิบายลงในโค้ดของคุณ
andrey.shedko
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.