使用 Array<Map<String,String>> 列读取 Parquet 文件

Posted

技术标签:

【中文标题】使用 Array<Map<String,String>> 列读取 Parquet 文件【英文标题】:Reading Parquet File with Array<Map<String,String>> Column 【发布时间】:2019-11-23 05:31:39 【问题描述】:

我正在使用 Dask 读取由 PySpark 生成的 Parquet 文件,其中一列是字典列表(即array&lt;map&lt;string,string&gt;&gt;')。 df 的一个例子是:

import pandas as pd

df = pd.DataFrame.from_records([ 
    (1, ['job_id': 1, 'started': '2019-07-04', 'job_id': 2, 'started': '2019-05-04'], 100), 
    (5, ['job_id': 3, 'started': '2015-06-04', 'job_id': 9, 'started': '2019-02-02'], 540)], 
    columns=['uid', 'job_history', 'latency'] 
) 

使用engine='fastparquet 时,Dask 可以正常读取所有其他列,但会为复杂类型的列返回Nones 列。当我设置engine='pyarrow' 时,出现以下异常:

ArrowNotImplementedError: lists with structs are not supported.

很多谷歌搜索已经清楚地表明,现在并不真正支持读取带有嵌套数组的列,而且我不完全确定处理这个问题的最佳方法是什么。我认为我的选择是:

告诉 dask/fastparquet 使用标准 json 库解析列的一些方法。架构很简单,如果可能的话就可以完成这项工作 看看我是否可以重新运行生成输出的 Spark 作业并将其另存为其他内容,尽管这几乎不是一个可接受的解决方案,因为我的公司到处都使用镶木地板 将映射的键转换为列,并使用 dtype list 将数据分成几列,并注意这些列中的数据通过索引相互关联/映射(例如,idx 中的元素0这些键/列都来自同一来源)。这会奏效,但坦率地说,让我心碎:(

我很想听听其他人是如何绕过这个限制的。我的公司经常在他们的 parquest 中使用嵌套数组,因此我不想放弃使用 Dask。

【问题讨论】:

【参考方案1】:

当我尝试使用 Pandas 阅读时,我正在处理 pyarrow.lib.ArrowNotImplementedError: Reading lists of structs from Parquet files not yet supported;但是,当我使用 pyspark 阅读然后转换为 pandas 时,数据至少会加载:

import pyspark
spark = pyspark.sql.SparkSession.builder.getOrCreate()
df = spark.read.load(path)
pdf = df.toPandas()

并且违规字段现在呈现为 pyspark Row 对象,该对象具有一些结构化解析,但您可能必须编写自定义 pandas 函数来从中提取数据:

>>> pdf["user"][0]["sessions"][0]["views"]
[Row(is_search=True, price=None, search_string='ABC', segment='listing', time=1571250719.393951), Row(is_search=True, price=None, search_string='ZYX', segment='homepage', time=1571250791.588197), Row(is_search=True, price=None, search_string='XYZ', segment='listing', time=1571250824.106184)]

单个记录可以呈现为字典,只需在所需的 Row 对象上调用 .asDict(recursive=True)

不幸的是,启动 SparkSession 上下文大约需要 5 秒,而且每个 spark 操作也比 pandas 操作(对于中小型数据集)花费更长的时间,所以我更喜欢更原生的 python 选项

【讨论】:

我也尝试使用 Spark 读取并转换为 pd.Dataframe,但在我的情况下它没有成功。调用 toPandas() 会得到与尝试使用 fastparquet 读取相同的结果;对于任何具有复杂类型的列,我最终都会得到一列 Nones。 您找到解决方案了吗?我正在处理同样的事情。有没有其他方法可以读取镶木地板文件而不将它们转换为熊猫数据框@Jon.H @NilanSaha 我们最终将列转换为 JSON 字符串,然后在读取时调用 .loads() 将其转换回来。它一点也不漂亮,但它确实有效。您可以关注此问题的进展,但请在此处观看相关票证:issues.apache.org/jira/browse/ARROW-1644。看起来有积极的发展。希望我们能尽快找到解决方案。 对不起。我没明白。你能详细说明一下吗?您最终是否使用了 PySpark 并且您碰巧有 POC ? @Jon.H【参考方案2】:

更公平地说,pandas(目前)不太支持非简单类型。 pyarrow 可能会在没有转换为 pandas 的情况下,并且作为未来的某个点,pandas 将直接使用这些箭头结构。

确实,我认为您可以使用的最直接的方法是将列重写为 B/JSON 编码的文本,然后使用 fastparquet 加载,指定使用 B/JSON 加载。您应该在列中获得 dicts 列表,但性能会很慢。

请注意,旧项目 oamap 及其后继项目 awkward 提供了一种使用 Python 语法迭代和聚合嵌套列表/映射/结构树的方法,但使用 Numba 编译,因此您无需实例化中间蟒蛇对象。它们不是为镶木地板设计的,但具有镶木地板的兼容性,因此可能对您有用。

【讨论】:

以上是关于使用 Array<Map<String,String>> 列读取 Parquet 文件的主要内容,如果未能解决你的问题,请参考以下文章

java,根据一个动态的map如何动态 往 Object[] array=new Object[];添加值

map的本质

使用 Array.map (TypeScript) 返回一个通用数组

数组去重复及记录重复个数(以及遍历map的四种方法)

hive的特殊函数named_structinline及特殊类型map<string, array<string>>

Hive建表时,使用Array和Map类型以及数据导入