Apache spark Pyspark:如何过滤MapType列上的数据帧?(与isin()的样式相同)
当我想以Apache spark Pyspark:如何过滤MapType列上的数据帧?(与isin()的样式相同),apache-spark,pyspark,apache-spark-sql,pyspark-dataframes,Apache Spark,Pyspark,Apache Spark Sql,Pyspark Dataframes,当我想以isin()的样式过滤MapType列上的数据帧时,最好的策略是什么 因此,基本上我想获得数据帧的所有行,其中MapType列的内容匹配MapType列表中的一个条目——“实例”。也可以是该列上的联接,但迄今为止我尝试的所有方法都失败了,因为EqualTo不支持对map类型进行排序 除了使用isin()或join()的直接方法外,我还想到了使用to_json()将映射转储到json,然后对json字符串进行过滤,但这似乎会随机排列键,因此字符串比较也不可靠 有什么容易的东西我错过了吗?您
isin()
的样式过滤MapType列上的数据帧时,最好的策略是什么
因此,基本上我想获得数据帧的所有行,其中MapType列的内容匹配MapType列表中的一个条目——“实例”。也可以是该列上的联接,但迄今为止我尝试的所有方法都失败了,因为EqualTo不支持对map类型进行排序
除了使用isin()或join()的直接方法外,我还想到了使用to_json()
将映射转储到json,然后对json字符串进行过滤,但这似乎会随机排列键,因此字符串比较也不可靠
有什么容易的东西我错过了吗?您建议如何解决这个问题
示例df:
+----+---------------------------------------------------------+
|key |metric |
+----+---------------------------------------------------------+
|123k|Map(metric1 -> 1.3, metric2 -> 6.3, metric3 -> 7.6) |
|d23d|Map(metric1 -> 1.5, metric2 -> 2.0, metric3 -> 2.2) |
|as3d|Map(metric1 -> 2.2, metric2 -> 4.3, metric3 -> 9.0) |
+----+---------------------------------------------------------+
过滤器(伪代码):
期望输出:
----+---------------------------------------------------------+
|key |metric |
+----+---------------------------------------------------------+
|123k|Map(metric1 -> 1.3, metric2 -> 6.3, metric3 -> 7.6) |
|d23d|Map(metric1 -> 1.5, metric2 -> 2.0, metric3 -> 2.2) |
+----+---------------------------------------------------------+
这不是比较映射相等性的最优雅的方法:您可以收集映射键,比较两个映射中每个键的值,并确保所有值都相同。我想最好构造一个过滤器df并进行半连接,而不是使用isin
传递它们:
样本df和过滤器df:
df.show(truncate=False)
+----+------------------------------------------------+
|key |metric |
+----+------------------------------------------------+
|123k|[metric1 -> 1.3, metric2 -> 6.3, metric3 -> 7.6]|
|d23d|[metric1 -> 1.5, metric2 -> 2.0, metric3 -> 2.2]|
|as3d|[metric1 -> 2.2, metric2 -> 4.3, metric3 -> 9.0]|
+----+------------------------------------------------+
filter_df = df.select('metric').limit(2)
filter_df.show(truncate=False)
+------------------------------------------------+
|metric |
+------------------------------------------------+
|[metric1 -> 1.3, metric2 -> 6.3, metric3 -> 7.6]|
|[metric1 -> 1.5, metric2 -> 2.0, metric3 -> 2.2]|
+------------------------------------------------+
过滤方法:
import pyspark.sql.functions as F
result = df.alias('df').join(
filter_df.alias('filter_df'),
F.expr("""
aggregate(
transform(
concat(map_keys(df.metric), map_keys(filter_df.metric)),
x -> filter_df.metric[x] = df.metric[x]
),
true,
(acc, x) -> acc and x
)"""),
'left_semi'
)
result.show(truncate=False)
+----+------------------------------------------------+
|key |metric |
+----+------------------------------------------------+
|123k|[metric1 -> 1.3, metric2 -> 6.3, metric3 -> 7.6]|
|d23d|[metric1 -> 1.5, metric2 -> 2.0, metric3 -> 2.2]|
+----+------------------------------------------------+
比较spark中的两个贴图列并不是很明显。对于第一个映射中的每个键,需要检查第二个映射中的值是否相同。钥匙也一样
使用UDF可能更简单,因为在Python中,您可以检查dict相等性:
from pyspark.sql import functions as F
map_equals = F.udf(lambda x, y: x == y, BooleanType())
# create map1 literal to filter with
map1 = F.create_map(*[
F.lit(x) for x in chain(*{"metric1": 1.3, "metric2": 6.3, "metric3": 7.6}.items())
])
df1 = df.filter(map_equals("metric", map1))
df1.show(truncate=False)
#+----+------------------------------------------------+
#|key |metric |
#+----+------------------------------------------------+
#|123k|[metric1 -> 1.3, metric2 -> 6.3, metric3 -> 7.6]|
#+----+------------------------------------------------+
另一种方法是添加要作为列过滤的映射文本,并检查metric
中的每个键是否从该文本映射中获得相同的值
下面是一个使用映射键数组上的transfrom
和array\u min
创建过滤器表达式的示例。(如果array\u min
返回true,则表示所有值相等):
匹配意味着什么?按键匹配,还是按值匹配?或者两者都有?
from pyspark.sql import functions as F
map_equals = F.udf(lambda x, y: x == y, BooleanType())
# create map1 literal to filter with
map1 = F.create_map(*[
F.lit(x) for x in chain(*{"metric1": 1.3, "metric2": 6.3, "metric3": 7.6}.items())
])
df1 = df.filter(map_equals("metric", map1))
df1.show(truncate=False)
#+----+------------------------------------------------+
#|key |metric |
#+----+------------------------------------------------+
#|123k|[metric1 -> 1.3, metric2 -> 6.3, metric3 -> 7.6]|
#+----+------------------------------------------------+
filter_map_literal = F.create_map(*[
F.lit(x) for x in chain(*{"metric1": 1.3, "metric2": 6.3, "metric3": 7.6}.items())
])
df1 = df.withColumn("filter_map", filter_map_literal).filter(
F.array_min(F.expr("""transform(map_keys(metric),
x -> if(filter_map[x] = metric[x], true, false)
)""")
)
).drop("filter_map")