如何在PySpark中进行范围查找和搜索

时间:2018-07-24 20:19:08

标签: dataframe pyspark rdd

我尝试在PySpark中编写一个函数,该函数可以在一定范围内进行组合搜索和查找值。以下是详细说明。

我有两个数据集。 一个数据集,例如D1,基本上是一个查找表,如下所示:

MinValue  MaxValue Value1 Value2
---------------------------------
1          1000      0.5     0.6
1001       2000      0.8     0.1
2001       4000      0.2     0.5
4001       9000      0.04    0.06

另一个数据集,例如D2,是一个包含数百万条记录的表,例如:

ID      InterestsRate       Days       
----------------------------------
1       19.99               29
2       11.99               49

对于每个ID,我需要根据可能的值为500, 1000, 2000, 3000, 5000的不同信用额度来计算最大收益。

返回值的计算方式例如

  

f(x)= InterestsRate *天数* Value1 * Value2。

Value1Value2通过在D1中查找信用额度来确定。例如,如果信用额度为3000,则将返回查询D1,0.2和0.5。

对于D2中的每条记录,我想计算不同信用额度的收益,并找出信用额度和收益,这给了我最大的收益。

到目前为止,我已经完成了两个功能:

我将查找功能定义为

def LookUp(value):
    filter_str = "MinValue <=" + str(value) + " and MaxValue >=" + str(value)
    return D1.filter(filter_str)

我还将搜索功能定义为

def Search(rate, day):
    credit_limit = [500, 1000, 2000, 3000, 5000]
    max=0;
    cl=-1;
    for i in range(1: len(credit_limit)):
       v1 = lookup(credit_limit[i]).select("value1")
       v2 = lookup(credit_limit[i]).select("value2")
       tmp = rate*day*value1*value2
       if max < tmp: 
          max=tmp 
          cl=credit_limit[i]

    return (cl, max)  

我将在D2上调用以下转换:

res = D2.mapValues(lambda row: Search(row[1], row[2]))

出乎意料的是,我遇到了错误,并且搜寻到我无法在RDD(D1)上进行转换时使用数据框(D2)。

我还用Google搜索可能的解决方案是广播D1。但是,我不知道如何使其工作。

请您谈谈如何在PySpark中实现此功能?

谢谢!

1 个答案:

答案 0 :(得分:1)

在使用spark时,您应该考虑SQL和表连接,而不是遍历列表。

所以我要做的第一件事是将您的信用额度列表变成一张表,我们称它为D3

credit_limit = [500, 1000, 2000, 3000, 5000]
D3 = spark.createDataFrame([[x] for x in credit_limit], ["CreditLimit"])
D3.show()
#+-----------+
#|CreditLimit|
#+-----------+
#|        500|
#|       1000|
#|       2000|
#|       3000|
#|       5000|
#+-----------+

现在,您可以将此表加入D1D2来计算每个信用额度的收益,然后使用Window函数选择最大收益来对每个收益进行排名。在您stated in the comments时,如果有平局,我们将选择最高信用额度。

import pyspark.sql.functions as f
from pyspark.sql import Window

w = Window.partitionBy("ID").orderBy(f.desc("Return"), f.desc("CreditLimit"))
D2.alias("D2").crossJoin(D3.alias("D3"))\
    .crossJoin(D1.alias("D1"))\
    .where("D3.CreditLimit BETWEEN D1.MinValue AND D1.MaxValue")\
    .withColumn("Return", f.expr("D2.InterestsRate*D2.Days*D1.Value1*D1.Value2"))\
    .withColumn("Rank", f.rank().over(w))\
    .where("Rank = 1")\
    .drop("Rank")\
    .show()
#+---+-------------+----+-----------+--------+--------+------+------+------------------+
#| ID|InterestsRate|Days|CreditLimit|MinValue|MaxValue|Value1|Value2|            Return|
#+---+-------------+----+-----------+--------+--------+------+------+------------------+
#|  1|        19.99|  29|       1000|       1|    1000|   0.5|   0.6|173.91299999999998|
#|  2|        11.99|  49|       1000|       1|    1000|   0.5|   0.6|           176.253|
#+---+-------------+----+-----------+--------+--------+------+------+------------------+

我们在这里做2种笛卡尔积,因此可能无法很好地扩展,但是请尝试一下。