具有混合值类型的MapType的PySpark UDF

具有混合值类型的MapType的PySpark UDF,pyspark,pyspark-sql,Pyspark,Pyspark Sql,我有一个类似这样的JSON输入 { "1": { "id": 1, "value": 5 }, "2": { "id": 2, "list": { "10": { "id": 10 }, "11": { "id": 11 }, "20": {

我有一个类似这样的JSON输入

{ "1": { "id": 1, "value": 5 }, "2": { "id": 2, "list": { "10": { "id": 10 }, "11": { "id": 11 }, "20": { "id": 20 } } }, "3": { "id": 3, "key": "a" } } 我需要合并3列并提取每列所需的值,这是我需要的输出:

{ "out": { "1": 5, "2": [10, 11, 20], "3": "a" } } 我试图创建一个UDF来将这3列转换为1,但我不知道如何使用混合值类型定义MapType—IntegerType、ArrayTypeIntegerType和StringType


提前谢谢

您需要使用StructType而不是MapType来定义UDF的结果类型,如下所示:

from pyspark.sql.types import *

udf_result = StructType([
    StructField('1', IntegerType()),
    StructField('2', ArrayType(StringType())),
    StructField('3', StringType())
])

您需要使用StructType而不是MapType定义UDF的结果类型,如下所示:

from pyspark.sql.types import *

udf_result = StructType([
    StructField('1', IntegerType()),
    StructField('2', ArrayType(StringType())),
    StructField('3', StringType())
])
MapType用于键、值对定义,不用于嵌套数据帧。你要找的是StructType

您可以使用createDataFrame直接加载它,但必须传递一个架构,因此这种方式更容易:

导入json 数据_json={ 1: { id:1, 价值:5 }, 2: { id:2, 名单:{ 10: { 身份证号码:10 }, 11: { 身份证号码:11 }, 20: { 身份证号码:20 } } }, 3: { id:3, 关键字:a } } a=[json.dumpsdata_json] jsonRDD=sc.parallelizea df=spark.read.jsonrdd 打印模式 根 |-1:struct nullable=true ||-id:long nullable=true ||-value:long nullable=true |-2:struct nullable=true ||-id:long nullable=true ||-list:struct nullable=true || |-10:struct nullable=true || | |-id:long nullable=true || |-11:struct nullable=true || | |-id:long nullable=true || |-20:struct nullable=true || | |-id:long nullable=true |-3:struct nullable=true ||-id:long nullable=true ||-key:string nullable=true 现在访问嵌套的数据帧。请注意,第2列比其他列嵌套得多:

嵌套的_cols=[2] cols=[1,3] 将pyspark.sql.functions作为psf导入 df=df.select cols+[psf.arraypsf.colc+.list.*.aliasc表示嵌套的_cols中的c] df=df.select [df[c].id.aliasc代表df.columns中的c] 根 |-1:long nullable=true |-3:long nullable=true |-2:array nullable=false ||-元素:long containsnall=true 它不完全是您的最终输出,因为您希望它嵌套在out列中:

将pyspark.sql.functions作为psf导入 df.selectpsf.struct*.aliasout.printSchema 根 |-out:struct nullable=false ||-1:long nullable=true ||-3:long nullable=true ||-2:array nullable=false || |-元素:long containsnall=true 最后回到JSON:

df.toJSON.first '{1:1,3:3,2:[10,11,20]}' MapType用于键、值对定义,不用于嵌套数据帧。你要找的是StructType

您可以使用createDataFrame直接加载它,但必须传递一个架构,因此这种方式更容易:

导入json 数据_json={ 1: { id:1, 价值:5 }, 2: { id:2, 名单:{ 10: { 身份证号码:10 }, 11: { 身份证号码:11 }, 20: { 身份证号码:20 } } }, 3: { id:3, 关键字:a } } a=[json.dumpsdata_json] jsonRDD=sc.parallelizea df=spark.read.jsonrdd 打印模式 根 |-1:struct nullable=true ||-id:long nullable=true ||-value:long nullable=true |-2:struct nullable=true ||-id:long nullable=true ||-list:struct nullable=true || |-10:struct nullable=true || | |-id:long nullable=true || |-11:struct nullable=true || | |-id:long nullable=true || |-20:struct nullable=true || | |-id:long nullable=true |-3:struct nullable=true ||-id:long nullable=true ||-key:string nullable=true 现在访问嵌套的数据帧。请注意,第2列比其他列嵌套得多:

嵌套的_cols=[2] cols=[1,3] 将pyspark.sql.functions作为psf导入 df=df.select cols+[psf.arraypsf.colc+.list.*.aliasc表示嵌套的_cols中的c] df=df.select [df[c].id.aliasc代表df.columns中的c] 根 |-1:long nullable=true |-3:long nullable=true |-2:array nullable=false ||-元素:long containsnall=true 它不完全是您的最终输出,因为您希望它嵌套在out列中:

将pyspark.sql.functions作为psf导入 df.selectpsf.struct*.aliasout.printSchema 根 |-out:struct nullable=false ||-1:long nullable=true ||-3:long nullable=true ||-2:array nullable=false || |-元素:long containsnall=true 最后回到JSON:

df.toJSON.first '{1:1,3 :3,2:[10,11,20]}'
很抱歉,我没有提到这一点,但是结构2可能有可为空的项,例如一些记录可能只有10个。如果可能的话,有没有办法使Structure2的扁平化成为动态的,而不管嵌套的元素是什么?2:{id:2,列表:{10:{id:10}}}谢谢!我尝试了col2.list.*.getFieldid,但我猜getField不能与*一起使用,因为错误是表达式“unsolvedExtractValue”中“*”的有效用法;我已经更新了我的答案,所以它是动态的。2有可为空的项这一事实不会改变任何东西。当你使用*时,它会使结构变得平坦,从而创建多个列,因此当你尝试访问id时,实际上是在告诉它一次访问多个列的子列,这是不可能的。希望这有帮助,别忘了将问题标记为solvedSorry我没有提到这一点,但是结构2可以有可为空的项,例如一些记录可能只有10个。如果可能的话,有没有办法使Structure2的扁平化成为动态的,而不管嵌套的元素是什么?2:{id:2,列表:{10:{id:10}}}谢谢!我尝试了col2.list.*.getFieldid,但我猜getField不能与*一起使用,因为错误是表达式“unsolvedExtractValue”中“*”的有效用法;我已经更新了我的答案,所以它是动态的。2具有可为空的项这一事实不会改变任何东西。当您使用*时,它会使结构变得平坦,从而创建多个列,因此当您尝试访问id时,实际上是在告诉它一次访问多个列的子列,这是不可能的。希望这有所帮助,不要忘记将问题标记为solvedGotcha,这是可行的。只是想知道,如果没有UDF,是否有任何方法可以实现这一点?明白了,这是有效的。只是想知道,如果没有UDF,是否还有实现这一目标的方法?