Dask pyarrow-识别写入拼花地板数据集时使用的片段或过滤器?
我的用例是,我希望将文件路径或过滤器作为xcom传递给Airflow中的任务,以便我的下一个任务可以读取刚刚处理的数据 任务A将表写入分区数据集,并生成大量拼花文件片段-->任务B稍后将这些片段作为数据集读取。不过,我只需要读取相关数据,而不需要读取可能有数百万行的整个数据集 我测试了两种方法:Dask pyarrow-识别写入拼花地板数据集时使用的片段或过滤器?,dask,pyarrow,Dask,Pyarrow,我的用例是,我希望将文件路径或过滤器作为xcom传递给Airflow中的任务,以便我的下一个任务可以读取刚刚处理的数据 任务A将表写入分区数据集,并生成大量拼花文件片段-->任务B稍后将这些片段作为数据集读取。不过,我只需要读取相关数据,而不需要读取可能有数百万行的整个数据集 我测试了两种方法: 在我完成写入数据集后立即列出修改过的文件。这将为我提供一个路径列表,我可以在下一个任务中调用ds.dataset(路径)。我可以在这些路径上使用partitioning.parse(),或者检查片段以获
partitioning.parse()
,或者检查片段以获得所用过滤器的列表(frag.partition\u expression
) def create_filter_list(df, partition_columns):
"""Creates a list of pyarrow filters to be sent through an xcom and evaluated as an expression. Xcom disables pickling, so we need to save timestamp and date values as strings and convert downstream"""
filter_list = []
value_list = []
partition_keys = [df[col] for col in partition_columns]
for keys, _ in df[partition_columns].groupby(partition_keys):
if len(partition_columns) == 1:
if is_jsonable(keys):
value_list.append(keys)
elif keys is not None:
value_list.append(str(keys))
else:
if not isinstance(keys, tuple):
keys = (keys,)
read_filter = []
for name, val in zip(partition_columns, keys):
if type(val) == np.int_:
read_filter.append((name, "==", int(val)))
elif val is not None:
read_filter.append((name, "==", str(val)))
filter_list.append(read_filter)
if len(partition_columns) == 1:
if len(value_list) > 0:
filter_list = [(name, "in", value_list) for name in partition_columns]
return filter_list
关于我应该采取哪种方法,或者是否有更好的方法来实现我的目标,有什么建议吗?建议(不确定这是否适合您的用例):
关键问题是需要正确选择数据的子集,这可以在上游“修复”。更新big dataframe的函数/脚本可以包含一个条件,用于保存已修改的临时数据副本,并在单独(临时)路径中满足某些要求。然后,该文件将被传递给下游任务,这些任务可以在处理临时文件后将其删除。您可以查看此问题(),我相信它会满足您的需要。同时,您可以使用basename\u模板
作为解决方法
import glob
import os
import pyarrow as pa
import pyarrow.dataset as pads
class TrackingWriter:
def __init__(self):
self.counter = 0
part_schema = pa.schema({'part': pa.int64()})
self.partitioning = pads.HivePartitioning(part_schema)
def next_counter(self):
result = self.counter
self.counter += 1
return result
def write_dataset(self, table, base_dir):
counter = self.next_counter()
pads.write_dataset(table, base_dir, format='parquet', partitioning=self.partitioning, basename_template=f'batch-{counter}-part-{{i}}')
files_written = glob.glob(os.path.join(base_dir, '**', f'batch-{counter}-*'))
return files_written
table_one = pa.table({'part': [0, 0, 1, 1], 'val': [1, 2, 3, 4]})
table_two = pa.table({'part': [0, 0, 1, 1], 'val': [5, 6, 7, 8]})
writer = TrackingWriter()
print(writer.write_dataset(table_one, '/tmp/mydataset'))
print(writer.write_dataset(table_two, '/tmp/mydataset'))
这只是一个粗略的草图。您可能还希望代码在启动时运行,以查看
计数器的下一个自由值是多少。或者你可以使用uuid
而不是计数器。Nice,所以我用adlfs替换操作系统,用aiffair中的“{task\u id}-{execution}”替换“batch-{counter}”,它根据唯一的时间表命名每个文件(这很好,因为我可以并行地将数据加载到同一个数据集中,并且可以使用glob查找我想要的文件)。因此,我的一些数据集也是使用pq.write_to_数据集编写的(有时我希望根据partition_filename_cb函数将数据重新分区到单个拼花地板文件中)。没有basename_模板,因此在编写数据集时,我的工作是使用元数据_收集器:files.append(fs.sep.join([root_path,piece.row_group(0).column(0).file_path])。这似乎相当方便,并且不需要我列出blob。我正在使用你的方法来处理ds.write_dataset()数据集-希望有一天我能有一个标准的方法。酷-这对我来说很好,非常感谢-我不认为没有你我会走上这条路。因为我正在传递文件路径,所以我可以用它们生成一个数据集,然后使用fragment.partition_表达式生成一个过滤器表达式列表。。。做了很多工作,但它做到了我想要的