python-3.x 在pyspark中展平Maptype列

x6h2sr28  于 2023-04-08  发布在  Python
关注(0)|答案(1)|浏览(112)

我有一个带有MapType列的pyspark DataFrame,我想将其分解为所有列(按键的名称)

root
 |-- a: map (nullable = true)
 |    |-- key: string
 |    |-- value: long (valueContainsNull = true)

我想做sp_test.select('a.*'),但得到一个错误:
分析异常:'只能星星展开结构数据类型。属性:ArrayBuffer(a) ;'
如果我们知道所有的钥匙,这可以通过做来实现

sp_test.select(['a.%s'%item for item in ['a','b']]).show()

但我想删除关键依赖
如果我们有一个StructType列,这可以通过执行display(nested_df.select('*', 'nested_array.*'))轻松实现

root
 |-- _corrupt_record: string (nullable = true)
 |-- field1: long (nullable = true)
 |-- field2: long (nullable = true)
 |-- nested_array: struct (nullable = true)
 |    |-- nested_field1: long (nullable = true)
 |    |-- nested_field2: long (nullable = true)

我有一些疑问:

  1. MapType可以转换为StructType吗?
    1.我们可以直接从MapType查询子键吗?
4nkexdtk

4nkexdtk1#

**TL;DR:**除非你提前知道可能的密钥,否则没有简单的方法来完成你的要求。

让我用一个例子来解释为什么和你的选择是什么。
首先,创建以下DataFrame:

data = [({'a': 1, 'b': 2},), ({'c':3},), ({'a': 4, 'c': 5},)]
df = spark.createDataFrame(data, ["a"])
df.show()
#+-------------------+
#|                  a|
#+-------------------+
#|Map(a -> 1, b -> 2)|
#|        Map(c -> 3)|
#|Map(a -> 4, c -> 5)|
#+-------------------+

它具有以下模式:

df.printSchema()
#root
# |-- a: map (nullable = true)
# |    |-- key: string
# |    |-- value: long (valueContainsNull = true)
  1. MapType可以转换为StructType吗?
    简单的答案是否定的(至少不是没有效率的),除非你提前知道钥匙。
    MapTypeStructType之间的区别在于Map的键值对是行独立的。StructType列不是这样的-在结构体列中,所有行都有相同的结构体字段。
    由于这个原因,spark不能很容易地从map中推断出要创建哪些列。(记住spark是并行操作每一行的)另一方面,将结构体分解成列是很简单的,因为所有的列都是提前知道的。
    所以如果你知道键,你可以通过以下方式创建一个结构类型:
import pyspark.sql.functions as f

df_new = df.select(
    f.struct(*[f.col("a").getItem(c).alias(c) for c in ["a", "b", "c"]]).alias("a")
)
df_new.show()
#+-------------+
#|            a|
#+-------------+
#|   [1,2,null]|
#|[null,null,3]|
#|   [4,null,5]|
#+-------------+

新的schema是:

df_new.printSchema()
#root
# |-- a: struct (nullable = false)
# |    |-- a: long (nullable = true)
# |    |-- b: long (nullable = true)
# |    |-- c: long (nullable = true)

1.我们可以直接从MapType查询子键吗?
是的,(如上所示)你可以使用getItem(),它将从列表中的索引处获取一个项,或者从Map中通过键获取一个项。
如果你不知道这些键,你唯一的选择就是将Mapexplode分成行groupbypivot

df.withColumn("id", f.monotonically_increasing_id())\
    .select("id", f.explode("a"))\
    .groupby("id")\
    .pivot("key")\
    .agg(f.first("value"))\
    .drop("id")\
    .show()
#+----+----+----+
#|   a|   b|   c|
#+----+----+----+
#|null|null|   3|
#|   1|   2|null|
#|   4|null|   5|
#+----+----+----+

在本例中,我们需要首先创建一个id列,以便有分组依据。
这里的pivot可能很昂贵,具体取决于数据的大小。

相关问题