Как прочитать список паркетных файлов из S3 в виде фрейма данных pandas с помощью pyarrow?

у меня есть хакерский способ достижения этого с помощью boto3 (1.4.4),pyarrow (0.4.1) и pandas (0.20.3).

во-первых, я могу прочитать один файл паркета локально, как это:

import pyarrow.parquet as pq

path = 'parquet/part-r-00000-1e638be4-e31f-498a-a359-47d017a0059c.gz.parquet'
table = pq.read_table(path)
df = table.to_pandas()

я также могу прочитать каталог паркетных файлов локально, как это:

import pyarrow.parquet as pq

dataset = pq.ParquetDataset('parquet/')
table = dataset.read()
df = table.to_pandas()

оба работают как шарм. Теперь я хочу добиться того же удаленно с файлами, хранящимися в ведре S3. Я надеялся, что что-то вроде этого сработает:

dataset = pq.ParquetDataset('s3n://dsn/to/my/bucket')

но это нет:

OSError: Passed non-file path: s3n://dsn/to/my/bucket

после прочтения документация pyarrow тщательно, это кажется невозможным на данный момент. Поэтому я пришел к следующему решению:

чтение одного файла из S3 и получение фрейма данных pandas:

import io
import boto3
import pyarrow.parquet as pq

buffer = io.BytesIO()
s3 = boto3.resource('s3')
s3_object = s3.Object('bucket-name', 'key/to/parquet/file.gz.parquet')
s3_object.download_fileobj(buffer)
table = pq.read_table(buffer)
df = table.to_pandas()

и вот мое хакерское, не очень оптимизированное решение для создания фрейма данных pandas из пути к папке S3:

import io
import boto3
import pandas as pd
import pyarrow.parquet as pq

bucket_name = 'bucket-name'
def download_s3_parquet_file(s3, bucket, key):
    buffer = io.BytesIO()
    s3.Object(bucket, key).download_fileobj(buffer)
    return buffer

client = boto3.client('s3')
s3 = boto3.resource('s3')
objects_dict = client.list_objects_v2(Bucket=bucket_name, Prefix='my/folder/prefix')
s3_keys = [item['Key'] for item in objects_dict['Contents'] if item['Key'].endswith('.parquet')]
buffers = [download_s3_parquet_file(s3, bucket_name, key) for key in s3_keys]
dfs = [pq.read_table(buffer).to_pandas() for buffer in buffers]
df = pd.concat(dfs, ignore_index=True)

есть ли лучший способ для достижения это? Может какой-то разъем для панд, используя pyarrow? Я хотел бы избежать использования pyspark, но если нет другого решения, то я бы взял его.

4 ответов


вы должны использовать s3fs модуль, предложенный yjk21. Однако в результате вызова ParquetDataset вы получите pyarrow.паркет.ParquetDataset объект. Чтобы получить фрейм данных Pandas, вы скорее захотите применить .read_pandas().to_pandas() для этого:

import pyarrow.parquet as pq
import s3fs
s3 = s3fs.S3FileSystem()

pandas_dataframe = pq.ParquetDataset('s3://your-bucket/', filesystem=s3).read_pandas().to_pandas()

вы можете использовать s3fs из dask, который реализует интерфейс файловой системы для s3. Затем вы можете использовать аргумент файловой системы ParquetDataset следующим образом:

import s3fs
s3 = s3fs.S3FileSystem()
dataset = pq.ParquetDataset('s3n://dsn/to/my/bucket', filesystem=s3)

Это можно сделать с помощью boto3, а также без использования pyarrow

import boto3
import io
import pandas as pd

# Read the parquet file
buffer = io.BytesIO()
s3 = boto3.resource('s3')
object = s3.Object('bucket_name','key')
object.download_fileobj(buffer)
df = pd.read_parquet(buffer)

print(df.head())

вероятно, самый простой способ прочитать паркетные данные в облаке в dataframes-использовать ДАСК.таблицы данных таким образом:

import dask.dataframe as dd
df = dd.read_parquet('s3://bucket/path/to/data-*.parq')

dask.dataframe can чтение из Google Cloud Storage, Amazon S3, Hadoop файловой системы и многое другое!