我有一个带有MapType
列的pyspark DataFrame,并想通过键名将其分解为所有列
root
|-- a: map (nullable = true)
| |-- key: string
| |-- value: long (valueCOntainsNull= true)
我想做sp_test.select('a.*')
但是出现错误:
AnalysisException:'只能加星展开结构数据类型。属性:ArrayBuffer(a)
;'
如果我们知道所有的关键,可以通过
sp_test.select(['a.%s'%item for item in ['a','b']]).show()
但我想删除关键的依赖
如果我们有StrucType列,可以通过执行以下操作轻松实现 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)
我有些疑惑:
可以MapType
铸成StructType
?
我们可以直接从中查询子键MapType
吗?
pault..
6
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)
可以MapType
铸成StructType
?
除非您提前知道按键,否则简单的答案是“否”(至少不是很有效)。
之间的差异MapType
和StructType
是,对地图的键值对是逐行独立。对于StructType
struct列中的列,情况并非如此,所有行都具有相同的struct字段。
因此,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]|
#+-------------+
新的架构是:
df_new.printSchema()
#root
# |-- a: struct (nullable = false)
# | |-- a: long (nullable = true)
# | |-- b: long (nullable = true)
# | |-- c: long (nullable = true)
我们可以直接从MapType查询子键吗?
是的,(如上所示),您可以使用getItem()
来从列表中的索引处或通过地图中的键获取项。
如果您不知道键,则唯一的选择是explode
将地图分为行groupby
和pivot
。
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
这里可以是昂贵的,这取决于你的数据的大小。
1> pault..:
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)
可以MapType
铸成StructType
?
除非您提前知道按键,否则简单的答案是“否”(至少不是很有效)。
之间的差异MapType
和StructType
是,对地图的键值对是逐行独立。对于StructType
struct列中的列,情况并非如此,所有行都具有相同的struct字段。
因此,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]|
#+-------------+
新的架构是:
df_new.printSchema()
#root
# |-- a: struct (nullable = false)
# | |-- a: long (nullable = true)
# | |-- b: long (nullable = true)
# | |-- c: long (nullable = true)
我们可以直接从MapType查询子键吗?
是的,(如上所示),您可以使用getItem()
来从列表中的索引处或通过地图中的键获取项。
如果您不知道键,则唯一的选择是explode
将地图分为行groupby
和pivot
。
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
这里可以是昂贵的,这取决于你的数据的大小。