【问题标题】:Best way to consolidate fragments within a pyarrow dataset?在 pyarrow 数据集中整合片段的最佳方法?
【发布时间】:2021-01-17 05:11:59
【问题描述】:

下面的代码有没有更好的方法来实现我的目标:

  1. 我想从包含很多小片段的数据集 A 中读取基于过滤器的数据(这个数据集中的文件很多,因为我经常下载数据)
  2. 我想在循环中根据分区合并片段(当我无法将所有过滤器都放入内存时,我正在使用它,因此我一个一个地处理它们)
  3. 我想将此数据写入一个新数据集(数据集 B)到一个合并文件中,该文件由我们的 BI 工具读取 - 遗憾的是没有 partition_filename_cb 函数,因此我需要为此使用旧的 write_to_dataset - 这个文件通常是分区名称
  4. 我真的很想清理 Dataset A。随着时间的推移,越来越多的文件被添加到分区中,因为我经常下载数据并且可以更新行(其中一些片段文件只有 1 或 2 条记录)李>

以下是我目前的流程。 我使用 ds.Scanner 应用我的过滤器并从原始数据集中选择我的列

def retrieve_fragments(dataset, filter_expression, columns):
    """Creates a dictionary of file fragments and filters from a pyarrow dataset"""
    fragment_partitions = {}
    scanner = ds.Scanner.from_dataset(dataset, columns=columns, filter=filter_expression)
    fragments = scanner.get_fragments()
    for frag in fragments:
        keys = ds._get_partition_keys(frag.partition_expression)
        fragment_partitions[frag] = keys
    return fragment_partitions

下面我创建了所有具有相同过滤器表达式的片段的小列表。然后我可以将这些新数据集写入一个合并文件中,我假设我也可以删除单个片段文件并编写一个新的合并版本?

fragments = retrieve_fragments(
    dataset=dataset, filter_expression=filter_expression, columns=read_columns
)
unique_filters = []
dfs = []
for fragment, filter_value in fragments.items():
    if filter_value not in unique_filters:
        unique_filters.append(filter_value)


#each chunk is a list of all of the fragments with the same partition_expression / filter which we turn into a new dataset that we can then process or resave into a consolidated file
for unique_filter in unique_filters:
    chunks = []
    for frag, filter_value in fragments.items():
        if filter_value == unique_filter:
            chunks.append(frag.path)
    logging.info(
        f"Combining {len(chunks)} fragments with filter {unique_filter} into a single table"
    )
    table = ds.dataset(chunks, partitioning=partitioning, filesystem=filesystem).to_table(columns=read_columns)


    #ignoring metadata due to some issues with columns having a boolean type even though they were never boolean
    df = table.to_pandas(ignore_metadata=True)
    #this function would just sort and drop duplicates on a unique constraint key
    df = prepare_dataframe(df)
    table = pa.Table.from_pandas(df=df, schema=dataset.schema, preserve_index=False)

    #write dataset to Dataset B (using partition_filename_cb)
    #I believe I could now also write the dataset back to Dataset A in a consolidated parquet file and then delete all of the fragment.paths. This would leave me with only a single file in the partition "folder"


此操作的输出会将每个分区的单个文件保存到新数据集中 (/dev/interactions-final/created_date=2019-11-13/2019-11-13.parquet)

INFO - Combining 78 fragments with filter {'created_date': datetime.date(2019, 11, 13)} into a single table
INFO - Saving 172657 rows and 36 columns (70.36 MB to dev/interactions-final)
INFO - Combining 57 fragments with filter {'created_date': datetime.date(2019, 11, 18)} into a single table
INFO - Saving 67036 rows and 36 columns (29.63 MB to dev/interactions-final)
INFO - Combining 55 fragments with filter {'created_date': datetime.date(2019, 11, 19)} into a single table
INFO - Saving 65035 rows and 36 columns (29.62 MB to dev/interactions-final)
INFO - Combining 63 fragments with filter {'created_date': datetime.date(2019, 11, 20)} into a single table
INFO - Saving 63613 rows and 36 columns (30.76 MB to dev/interactions-final)

【问题讨论】:

    标签: pyarrow


    【解决方案1】:

    你试过write_dataset(代码here)吗?它会重新分区,我认为它会在此过程中收集小碎片。

    【讨论】:

    • 会重新分区吗?我确实使用了 write_dataset,但我看不到任何可以处理分区中已保存文件(guid 文件名)的事实。我认为理想情况下我希望清理每个分区并将数据合并到更少的文件中(可能每个分区 1 个或基于最大大小或行数)
    • 它从源重新分区数据,但不涉及目标目录中的任何文件。
    • ahh - 无论如何“清理”原始文件夹?想象一下,我每 30 分钟下载一次数据,并且每月对数据进行分区,我最终会得到很多看起来有点……混乱的小文件?也许这没什么大不了的。但与数百个小文件相比,我更愿意拥有一个 50-100MB 的文件。
    • 如果是我,我会从一个目录读取并写入另一个目录,而不是覆盖源数据。您可以定义一个跨越多个数据集/源(可能是不同的文件格式、分区等)的 Arrow 数据集,这样您仍然可以将整个事物作为一个实体进行查询。
    • 是的,这就是我现在正在做的事情(我有一个“历史”数据集,然后是一个“最终”数据集,我也在其中排序和删除重复项)。好吧,看来我不应该担心这么多小文件的混乱。我不知道我可以将多个数据集作为一个实体读取 - 打算探索一下
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-22
    • 2016-01-25
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    • 2016-07-06
    相关资源
    最近更新 更多