web-dev-qa-db-ja.com

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 dataframe:

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

そして、ここで、S3フォルダーパスからpandasデータフレームを作成する、あまり最適化されていない私のハッキングソリューション:

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)

これを達成するためのより良い方法はありますか? pandas pyarrowを使用するための何らかの種類のコネクタですか?pysparkの使用を避けたいのですが、他に解決策がない場合はそれを採用します。

22

yjk21 で提案されている_s3fs_モジュールを使用する必要があります。ただし、ParquetDatasetを呼び出すと、pyarrow.parquet.ParquetDatasetオブジェクトが取得されます。 Pandas DataFrame)を取得するには、.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()
_
25
vak

Pyarrowを使用せずにboto3を使用しても実行できます。

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

S3のファイルシステムインターフェイスを実装するdaskのs3fsを使用できます。次に、ParquetDatasetのファイルシステム引数を次のように使用できます。

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

ありがとう!あなたの質問は実際に私に多くを教えてくれます。これは、pandas(0.21.1)を使ってpyarrowboto3(1.3.1)を呼び出す方法です。

import boto3
import io
import pandas as pd

# Read single parquet file from S3
def pd_read_s3_parquet(key, bucket, s3_client=None, **args):
    if s3_client is None:
        s3_client = boto3.client('s3')
    obj = s3_client.get_object(Bucket=bucket, Key=key)
    return pd.read_parquet(io.BytesIO(obj['Body'].read()), **args)

# Read multiple parquets from a folder on S3 generated by spark
def pd_read_s3_multiple_parquets(filepath, bucket, s3=None, 
                                 s3_client=None, verbose=False, **args):
    if not filepath.endswith('/'):
        filepath = filepath + '/'  # Add '/' to the end
    if s3_client is None:
        s3_client = boto3.client('s3')
    if s3 is None:
        s3 = boto3.resource('s3')
    s3_keys = [item.key for item in s3.Bucket(bucket).objects.filter(Prefix=filepath)
               if item.key.endswith('.parquet')]
    if not s3_keys:
        print('No parquet found in', bucket, filepath)
    Elif verbose:
        print('Load parquets:')
        for p in s3_keys: 
            print(p)
    dfs = [pd_read_s3_parquet(key, bucket=bucket, s3_client=s3_client, **args) 
           for key in s3_keys]
    return pd.concat(dfs, ignore_index=True)

次に、S3のフォルダーの下にある複数の寄木細工を読むことができます。

df = pd_read_s3_multiple_parquets('path/to/folder', 'my_bucket')

(このコードをかなり単純化できると思います。)

4
Louis Yang

おそらく、クラウド上の寄木細工のデータをデータフレームに読み込む最も簡単な方法は、この方法で dask.dataframe を使用することです。

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

dask.dataframe can Google Cloud Storage、Amazon S3、Hadoopファイルシステムなどから読み取り

3
Rich Signell