使用pyspark,如何将一列添加到数据帧中,作为同一数据帧中多个已知列(不包括null)的键值映射?
给出以下示例:使用pyspark,如何将一列添加到数据帧中,作为同一数据帧中多个已知列(不包括null)的键值映射?,pyspark,apache-spark-sql,pyspark-dataframes,Pyspark,Apache Spark Sql,Pyspark Dataframes,给出以下示例: d = [{'asset': '2', 'ts': 6, 'B':'123','C':'234'}, {'asset': '1', 'ts': 5, 'C.1':'999', 'B':'888','F':'999'}] df = spark.createDataFrame(d) df.show(truncate=False) +---+----+-----+---+----+----+ |B |C |asset|ts |C.1 |F | +---+---
d = [{'asset': '2', 'ts': 6, 'B':'123','C':'234'},
{'asset': '1', 'ts': 5, 'C.1':'999', 'B':'888','F':'999'}]
df = spark.createDataFrame(d)
df.show(truncate=False)
+---+----+-----+---+----+----+
|B |C |asset|ts |C.1 |F |
+---+----+-----+---+----+----+
|123|234 |2 |6 |null|null|
|888|null|1 |5 |999 |999 |
+---+----+-----+---+----+----+
我要创建以下输出:
+-----+---+--------------------------------+
|asset|ts |signals |
+-----+---+--------------------------------+
|2 |6 |[B -> 123, C -> 234] |
|1 |5 |[B -> 888, C.1 -> 999, F -> 999]|
+-----+---+--------------------------------+
我尝试了以下方法:
from itertools import chain
from pyspark.sql.functions import *
all_signals=['B','C','C.1','F']
key_values = create_map(*(chain(*[(lit(name), col("`"+name+"`"))
for name in all_signals])))
new_df = df.withColumn('signals',key_values).drop(*all_signals).show(truncate=False)
+-----+---+--------------------------------------+
|asset|ts |signals |
+-----+---+--------------------------------------+
|2 |6 |[B -> 123, C -> 234, C.1 ->, F ->] |
|1 |5 |[B -> 888, C ->, C.1 -> 999, F -> 999]|
+-----+---+--------------------------------------+
但是我不想要带有空值的键。所以我尝试了很多方法来排除null或None。
我试过如果条件,当/否则,但似乎都不起作用。这里有一个尝试:
key_values = create_map(*(chain(*[(lit(name), col("`"+name+"`"))
for name in all_signals
if col("`"+name+"`").isNotNull()])))
new_df = df.withColumn('signals',key_values).drop(*all_signals).show(truncate=False)
ValueError: Cannot convert column into bool: please use '&' for 'and', '|' for 'or', '~' for 'not' when building DataFrame boolean expressions.
我用一种我不满意的循环方式让它工作:
new_df= df.withColumn("signals", from_json(
to_json(struct(["`"+x+"`" for x in all_signals])),"MAP<STRING,STRING>"))
new_df = new_df.drop(*all_signals)
new_df.show(truncate=False)
+-----+---+--------------------------------+
|asset|ts |signals |
+-----+---+--------------------------------+
|2 |6 |[B -> 123, C -> 234] |
|1 |5 |[B -> 888, C.1 -> 999, F -> 999]|
+-----+---+--------------------------------+
但是必须有一种排除null的方法,而不必来回访问json 我有另一个解决方案。首先使用空值构建映射,然后删除空值 从pyspark.sql.types导入映射类型,StringType 从pyspark.sql导入函数为F 原始数据帧 数据=[{'asset':'2','ts':6',B':'123',C':'234'}, {'asset':'1','ts':5,'C.1':'999','B':'888','F':'999'}] df=spark.createDataFramedata df.showtruncate=False 创建包含空值的映射 因为spark很奇怪,所以需要背景标记 https://stackoverflow.com/questions/44367019/column-name-with-dot-spark 名称=['B','C','C.1','F'] 键值列表=[] 对于名称中的名称: 键值列表+=[F.litname] 键值列表+=[df[`{}`.formatname]] 映射列=F.创建映射*键值列表 删除空值的UDF 删除\u null\u值\u udf=F.udf lambda d:{k:v代表k,d项中的v如果v不是None}, MapTypeStringType,StringType 应用以上两种方法 df=df.withColumn'map',删除\u null\u值\u udfmap\u column.drop*名称 df.show +---+--+----------+ |资产| ts |地图| +---+--+----------+ |2 | 6 |[B->123,C->234]| |1 | 5 |[B->888,F->9| +---+--+----------+
我有另一个解决方案。首先用空值构建映射,然后删除空值 从pyspark.sql.types导入映射类型,StringType 从pyspark.sql导入函数为F 原始数据帧 数据=[{'asset':'2','ts':6',B':'123',C':'234'}, {'asset':'1','ts':5,'C.1':'999','B':'888','F':'999'}] df=spark.createDataFramedata df.showtruncate=False 创建包含空值的映射 因为spark很奇怪,所以需要背景标记 https://stackoverflow.com/questions/44367019/column-name-with-dot-spark 名称=['B','C','C.1','F'] 键值列表=[] 对于名称中的名称: 键值列表+=[F.litname] 键值列表+=[df[`{}`.formatname]] 映射列=F.创建映射*键值列表 删除空值的UDF 删除\u null\u值\u udf=F.udf lambda d:{k:v代表k,d项中的v如果v不是None}, MapTypeStringType,StringType 应用以上两种方法 df=df.withColumn'map',删除\u null\u值\u udfmap\u column.drop*名称 df.show +---+--+----------+ |资产| ts |地图| +---+--+----------+ |2 | 6 |[B->123,C->234]| |1 | 5 |[B->888,F->9| +---+--+----------+ 不需要UDF,使用高阶函数过滤器,使用数组\u zip和映射\u from\u条目来获得所需的输出。spark2.4+ 不需要UDF,使用高阶函数过滤器,使用数组\u zip和映射\u from\u条目来获得所需的输出。spark2.4+
这看起来棒极了。我会测试它的性能并让你知道。你太棒了。谢谢!这看起来棒极了。我会测试它的性能并让你知道。你太棒了。谢谢!我忍不住对你的解决方案微笑。这让我觉得很傻。令人印象深刻!出于某种原因,从_json到_json提供了更好的性能,但这确实回答了我的问题n、 我忍不住对你的解决方案笑了笑。这让我觉得很傻。令人印象深刻!出于某种原因,从_json和to_json提供了更好的性能,但这确实回答了我的问题。
from pyspark.sql import functions as F
all_singals=['B','C','C.1','F']
df.withColumn("all_one", F.array(*[F.lit(x) for x in all_signals]))\
.withColumn("all_two", F.array(*["`"+x+"`" for x in all_signals]))\
.withColumn("signals", F.expr("""map_from_entries(filter(arrays_zip(all_one,all_two),x-> x.all_two is not null))"""))\
.drop("all_one","all_two").show(truncate=False)
#+---+----+-----+---+----+----+--------------------------------+
#|B |C |asset|ts |C.1 |F |signals |
#+---+----+-----+---+----+----+--------------------------------+
#|123|234 |2 |6 |null|null|[B -> 123, C -> 234] |
#|888|null|1 |5 |999 |999 |[B -> 888, C.1 -> 999, F -> 999]|
#+---+----+-----+---+----+----+--------------------------------+