Mam plik tekstowy zapisany na S3, który jest tabelą rozdzielaną tabulatorami. Chcę załadować go do pandy, ale nie mogę go najpierw zapisać, ponieważ pracuję na serwerze heroku. Oto, co mam do tej pory.
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)
błąd jest
OSError: Expected file path name or file-like object, got <class 'bytes'> type
Jak przekonwertować treść odpowiedzi na format, który przyjmą pandy?
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 - Korzystanie z następujących działało
file = response["Body"].read()
i
pd.read_csv(io.BytesIO(file), header=14, delimiter="\t", low_memory=False)
io.BytesIO(file)
lubio.StringIO(file)
zamiastfile
w trakcieread_csv()
rozmowyio.StringIO
jak w tej odpowiedzi .Odpowiedzi:
pandas
używaboto
doread_csv
, więc powinieneś być w stanie:import boto data = pd.read_csv('s3://bucket....csv')
Jeśli potrzebujesz,
boto3
bo jesteś włączonypython3.4+
, możeszimport boto3 import io s3 = boto3.client('s3') obj = s3.get_object(Bucket='bucket', Key='key') df = pd.read_csv(io.BytesIO(obj['Body'].read()))
Od wersji 0.20.1
pandas
zastosowańs3fs
, patrz odpowiedź poniżej.źródło
boto3
pokazuje, jak skonfigurować uwierzytelnianie, aby mieć również dostęp do prywatnych plików: boto3.readthedocs.io/en/latest/guide/quickstart.htmldf = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
Teraz pandy mogą obsługiwać adresy URL S3 . Możesz po prostu zrobić:
import pandas as pd import s3fs df = pd.read_csv('s3://bucket-name/file.csv')
Musisz zainstalować,
s3fs
jeśli go nie masz.pip install s3fs
Poświadczenie
Jeśli Twój zasobnik S3 jest prywatny i wymaga uwierzytelnienia, masz dwie możliwości:
1- Dodaj poświadczenia dostępu do
~/.aws/credentials
pliku konfiguracyjnegoLub
2- Ustaw następujące zmienne środowiskowe z ich odpowiednimi wartościami:
aws_access_key_id
aws_secret_access_key
aws_session_token
źródło
Jest to teraz obsługiwane w najnowszych pandach. Widzieć
http://pandas.pydata.org/pandas-docs/stable/io.html#reading-remote-files
na przykład.,
df = pd.read_csv('s3://pandas-test/tips.csv')
źródło
Z s3fs można to zrobić w następujący sposób:
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)
źródło
df = pd.read_csv('s3://mybucket/path/to/object/foo.pkl')
Ponieważ pliki mogą być zbyt duże, nie jest rozsądne ładowanie ich w całości do ramki danych. Dlatego czytaj wiersz po wierszu i zapisz go w ramce danych. Tak, możemy również podać rozmiar porcji w read_csv, ale wtedy musimy zachować liczbę odczytanych wierszy.
Dlatego wymyśliłem tę inżynierię:
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=",")
Po zakończeniu pracy usuwam także plik df.
del df
źródło
W przypadku plików tekstowych możesz użyć poniższego kodu z plikiem rozdzielanym pionową kreską, na przykład: -
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)
źródło
Opcją jest przekonwertowanie
df.to_dict()
pliku csv na json za pośrednictwem, a następnie zapisanie go jako ciągu. Zauważ, że ma to znaczenie tylko wtedy, gdy plik CSV nie jest wymagany, ale chcesz po prostu szybko umieścić ramkę danych w zasobniku S3 i odzyskać ją ponownie.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()))
Spowoduje to przekonwertowanie df na ciąg dict, a następnie zapisanie go jako json w S3. Możesz później przeczytać go w tym samym formacie json:
Inne rozwiązania też są dobre, ale to trochę prostsze. Yaml może niekoniecznie być wymagane, ale potrzebujesz czegoś do przeanalizowania łańcucha json. Jeśli plik S3 niekoniecznie musi być plikiem CSV, może to być szybka naprawa.
źródło