Python 如何优化Spark SQL中的非相等联接?

Python 如何优化Spark SQL中的非相等联接?,python,apache-spark,apache-spark-sql,Python,Apache Spark,Apache Spark Sql,我有两个数据帧需要使用具有两个连接谓词的非相等连接(即不等式连接)连接在一起 一个数据帧是直方图数据帧[bin:bigint,下限:double,上限:double] 另一个数据帧是观察值的集合dataframe[id:bigint,observation:double] 我需要确定每个观察结果属于我的直方图中的哪一个区域,如下所示: observations_df.join(histogram_df, ( (observations_df.observation &g

我有两个数据帧需要使用具有两个连接谓词的非相等连接(即不等式连接)连接在一起

一个数据帧是直方图
数据帧[bin:bigint,下限:double,上限:double]

另一个数据帧是观察值的集合
dataframe[id:bigint,observation:double]

我需要确定每个观察结果属于我的直方图中的哪一个区域,如下所示:

observations_df.join(histogram_df, 
    (
        (observations_df.observation >= histogram_df.lower_bound) &
        (observations_df.observation < histogram_df.upper_bound)
    )
   )

火花连接不建议使用不等连接。通常,我会生成一个新列作为此类操作的连接键。 但是,对于您的情况,您不需要加入来确定每个观察值属于直方图的哪个仓位,因为每个仓位的上限和下限都可以预先计算,并且您可以使用观察值计算仓位

您可以做的是编写一个UDF,它为您查找bin并将bin作为新列返回。
您可以参考

,也可以提供一些提示。谢谢,是的,您的第一个链接看起来确实有用。我会尝试一下,然后再报告。我还没有让我的场景工作,但肯定是一个类似的问题,因此我会接受这个答案。谢谢@user6910411。谢谢你的回复。在这个人为的演示中,是的,每个箱子的上限和下限都可以推导出来,但它只是一个演示。在我的现实世界场景中,情况并非如此。如果您坚持需要不相等的联接,那么您需要提供您想要联接的两个表的大小。而调谐将取决于尺寸。如果直方图_df很小(我假设),您可以缓存它并进行广播连接。如果直方图_df更小,您可以将其放入字典(或其他数据结构),您可以广播字典并使用二进制搜索编写UDF来查找bin。上面的演示代码故意代表了我的真实场景,事实上,真实世界的情况实际上更糟,因为
直方图中的
箱数实际上>1000000。因此,不幸的是,广播连接不适合(我已经尝试过:))
observations\u df
在我的真实场景中大约有100000行,因此这就是我在上面的演示中填充的行数。我自己也在想,如果你使用非等联接,Catalyst如何处理它。但是如果你真的想要非等联接呢?
from pyspark.sql.functions import lit, col, lead
from pyspark.sql.types import *
from pyspark.sql import SparkSession
from pyspark.sql.types import *
from pyspark.sql.functions import rand
from pyspark.sql import Window
spark = SparkSession \
    .builder \
    .getOrCreate()

number_of_bins = 500000

bin_width = 1.0 / number_of_bins
window = Window.orderBy('bin')
histogram_df = spark.range(0, number_of_bins)\
    .withColumnRenamed('id', 'bin')\
    .withColumn('lower_bound', 0 + lit(bin_width) * col('bin'))\
    .select('bin', 'lower_bound', lead('lower_bound', 1, 1.0).over(window).alias('upper_bound'))
observations_df = spark.range(0, 100000).withColumn('observation', rand())
observations_df.join(histogram_df, 
        (
            (observations_df.observation >= histogram_df.lower_bound) &
            (observations_df.observation < histogram_df.upper_bound)
        )
       ).groupBy('bin').count().head(15)