我有一个存储在s3上的实木复合地板数据集,我想查询数据集中的特定行。我可以使用petastorm
来做到这一点,但现在我只想使用pyarrow
来做到这一点。
这是我的尝试:
import pyarrow.parquet as pq
import s3fs
fs = s3fs.S3FileSystem()
dataset = pq.ParquetDataset(
'analytics.xxx',
filesystem=fs,
validate_schema=False,
filters=[('event_name', '=', 'SomeEvent')]
)
df = dataset.read_pandas().to_pandas()
但是这将返回pandas DataFrame,就像过滤器不起作用一样,也就是说,我的行具有各种值event_name
。有什么我想念的东西或我误解了的东西吗?在获得pandas DataFrame之后,我可以进行过滤,但是我会使用比所需更多的内存空间。
答案 0 :(得分:10)
对于从Google到达这里的任何人,您现在都可以在读取Parquet文件时过滤PyArrow中的行。不管您是通过pandas还是pyarrow.parquet阅读。
过滤器(列表[元组]或列表[列表[元组]]或无(默认))– 与过滤谓词不匹配的行将从扫描数据中删除。如果嵌套键结构中不包含匹配的行,则将利用这些键来避免完全加载文件。如果use_legacy_dataset为True,则过滤器只能引用分区键,并且仅支持配置单元样式的目录结构。将use_legacy_dataset设置为False时,还支持文件内过滤和不同的分区方案。
谓词以析取范式(DNF)表示,例如[[('x','=',0),...],...]。 DNF允许单列谓词的任意布尔逻辑组合。最里面的元组每个描述一个列谓词。内部谓词列表被解释为一个连词(AND),形成一个更具选择性和多列的谓词。最后,最外面的列表将这些过滤器组合为一个析取(OR)。
谓词也可以作为List [Tuple]传递。此形式被解释为单个连词。要在谓词中表达OR,必须使用(首选)List [List [Tuple]]表示法。
答案 1 :(得分:6)
注意:我已经在this post
中将其扩展为Python和Parquet的综合指南。要使用过滤器,您需要使用分区以Parquet格式存储数据。加载Parquet列中的几个和分区中的许多列,可以使Parquet和CSV大大提高I / O性能。 Parquet可以基于一个或多个字段的值对文件进行分区,并为嵌套值的唯一组合创建目录树,或者为一个分区列仅创建一组目录。 PySpark Parquet documentation解释了Parquet如何运作良好。
关于性别和国家的划分看起来像this:
path
└── to
└── table
├── gender=male
│ ├── ...
│ │
│ ├── country=US
│ │ └── data.parquet
│ ├── country=CN
│ │ └── data.parquet
│ └── ...
如果需要进一步对数据进行分区,还可以进行行组分区,但是大多数工具仅支持指定行组大小,并且您必须自己进行key-->row group
查找,这很丑陋(很乐意回答此问题另一个问题)。
您需要使用Parquet对数据进行分区,然后才能使用过滤器加载数据。您可以使用PyArrow,pandas或用于大型数据集的Dask或PySpark将数据写入分区。
例如,要在熊猫中写入分区:
df.to_parquet(
path='analytics.xxx',
engine='pyarrow',
compression='snappy',
columns=['col1', 'col5'],
partition_cols=['event_name', 'event_category']
)
这会将文件布置如下:
analytics.xxx/event_name=SomeEvent/event_category=SomeCategory/part-0001.c000.snappy.parquet
analytics.xxx/event_name=SomeEvent/event_category=OtherCategory/part-0001.c000.snappy.parquet
analytics.xxx/event_name=OtherEvent/event_category=SomeCategory/part-0001.c000.snappy.parquet
analytics.xxx/event_name=OtherEvent/event_category=OtherCategory/part-0001.c000.snappy.parquet
要使用分区列通过一个属性来捕获事件,请在列表中放置一个元组过滤器:
import pyarrow.parquet as pq
import s3fs
fs = s3fs.S3FileSystem()
dataset = pq.ParquetDataset(
's3://analytics.xxx',
filesystem=fs,
validate_schema=False,
filters=[('event_name', '=', 'SomeEvent')]
)
df = dataset.to_table(
columns=['col1', 'col5']
).to_pandas()
要使用AND抓取具有两个或多个属性的事件,您只需创建一个过滤器元组列表:
import pyarrow.parquet as pq
import s3fs
fs = s3fs.S3FileSystem()
dataset = pq.ParquetDataset(
's3://analytics.xxx',
filesystem=fs,
validate_schema=False,
filters=[
('event_name', '=', 'SomeEvent'),
('event_category', '=', 'SomeCategory')
]
)
df = dataset.to_table(
columns=['col1', 'col5']
).to_pandas()
要使用OR抓取两个事件,您需要将过滤器元组嵌套在它们自己的列表中:
import pyarrow.parquet as pq
import s3fs
fs = s3fs.S3FileSystem()
dataset = pq.ParquetDataset(
's3://analytics.xxx',
filesystem=fs,
validate_schema=False,
filters=[
[('event_name', '=', 'SomeEvent')],
[('event_name', '=', 'OtherEvent')]
]
)
df = dataset.to_table(
columns=['col1', 'col5']
).to_pandas()
作为另一个答案,将数据过滤仅加载到数据所在位置(本地或云中)中某些分区中某些列的最简单方法是使用awswrangler
模块。如果您使用的是S3,请查看awswrangler.s3.read_parquet()
和awswrangler.s3.to_parquet()
的文档。过滤的作用与上面的示例相同。
import awswrangler as wr
df = wr.s3.read_parquet(
path="analytics.xxx",
columns=["event_name"],
filters=[('event_name', '=', 'SomeEvent')]
)
pyarrow.parquet.read_table()
加载镶木地板分区如果您使用的是PyArrow,也可以使用pyarrow.parquet.read_table()
:
import pyarrow.parquet as pq
fp = pq.read_table(
source='analytics.xxx',
use_threads=True,
columns=['some_event', 'some_category'],
filters=[('event_name', '=', 'SomeEvent')]
)
df = fp.to_pandas()
最后,您可以在PySpark中使用pyspark.sql.DataFrameReader.read_parquet()
import pyspark.sql.functions as F
from pyspark.sql import SparkSession
spark = SparkSession.builder.master("local[1]") \
.appName('Stack Overflow Example Parquet Column Load') \
.getOrCreate()
# I automagically employ Parquet structure to load the selected columns and partitions
df = spark.read.parquet('s3://analytics.xxx') \
.select('event_name', 'event_category') \
.filter(F.col('event_name') == 'SomeEvent')
希望这可以帮助您使用Parquet:)
答案 2 :(得分:3)
对于python 3.6 +,AWS有一个名为aws-data-wrangler的库,该库有助于Pandas / S3 / Parquet之间的集成,并允许您筛选分区的S3键。
安装do;
pip install awswrangler
要减少读取的数据,您可以根据存储在s3上的实木复合地板文件中的分区列来过滤行。
要使用值event_name
过滤分区列"SomeEvent"
中的行,请执行以下操作;
awswrangler <1.0.0
import awswrangler as wr
df = wr.pandas.read_parquet(
path="s3://my-bucket/my/path/to/parquet-file.parquet",
columns=["event_name"],
filters=[('event_name', '=', 'SomeEvent')]
)
awswrangler> 1.0.0做;
import awswrangler as wr
df = wr.s3.read_parquet(
path="s3://my-bucket/my/path/to/parquet-file.parquet",
columns=["event_name"],
filters=[('event_name', '=', 'SomeEvent')]
)
答案 3 :(得分:1)
当前,filters
功能仅在文件级别实现,尚未在行级别实现。
因此,如果您有一个数据集作为嵌套层次结构中多个分区镶花文件的集合(此处描述的分区数据集的类型:official doc here),则可以使用filters
参数仅读取文件的子集。
但是,您还不能用它来读取单个文件的行组的子集(请参见https://arrow.apache.org/docs/python/parquet.html#partitioned-datasets-multiple-files)。
但是,您会收到一条错误消息,指出了这样一个无效的过滤器,这是很好的。我为此打开了一个问题:https://issues.apache.org/jira/browse/ARROW-1796