如何基于pyspark dataframes中多个列的笛卡尔积创建新列

如何基于pyspark dataframes中多个列的笛卡尔积创建新列,dataframe,pyspark,cartesian,Dataframe,Pyspark,Cartesian,让我举一个简单的例子来解释我想做什么。假设我们有两个非常简单的数据帧,如下所示: Df1 +---+---+---+ | a1| a2| a3| +---+---+---+ | 2| 3| 7| | 1| 9| 6| +---+---+---+ Df2 +---+---+ | b1| b2| +---+---+ | 10| 2| | 9| 3| +---+---+ 从df1,df2,我们需要创建一个新的df,其中的列是来自df1,df2的原始列的笛卡尔乘积。特别是,新的df

让我举一个简单的例子来解释我想做什么。假设我们有两个非常简单的数据帧,如下所示:

Df1
+---+---+---+
| a1| a2| a3|
+---+---+---+
|  2|  3|  7|
|  1|  9|  6|
+---+---+---+

Df2
+---+---+
| b1| b2|
+---+---+
| 10|  2|
|  9|  3|
+---+---+
从df1,df2,我们需要创建一个新的df,其中的列是来自df1,df2的原始列的笛卡尔乘积。特别是,新的df将有'a1b1','a1b2','a2b1','a2b2','a3b1','a3b2',行将是df1,df2中相应列的乘积。结果df应如下所示:

Df3
+----+----+----+----+----+----+
|a1b1|a1b2|a2b1|a2b2|a3b1|a3b2|
+----+----+----+----+----+----+
|  20|   4|  30|   6|  70|  14|
|   9|   3|  81|  27|  54|  18|
+----+----+----+----+----+----+
我搜索过spark online文档以及这里发布的问题,但它们似乎都是关于行的笛卡尔积,而不是列。例如,rdd.cartesian()提供行中不同值组合的笛卡尔乘积,如以下代码所示:

r = sc.parallelize([1, 2])
r.cartesian(r).toDF().show()

+---+---+
| _1| _2|
+---+---+
|  1|  1|
|  1|  2|
|  2|  1|
|  2|  2|
+---+---+
但这不是我需要的。同样,我需要创建新的列而不是行。在我的问题中,行数将保持不变。我知道udf最终可以解决这个问题。然而,在我的实际应用程序中,我们有巨大的数据集,创建所有列(大约500个新列作为所有可能的列组合)所需的时间太长。我们更喜欢使用一些可以提高效率的向量运算。我可能错了,但spark udf似乎是基于行操作的,这可能是花了这么长时间才完成的原因

非常感谢您的建议/反馈/评论

为了方便起见,我在这里附加了简单的代码来创建上面所示的示例数据帧:

df1 = sqlContext.createDataFrame([[2,3,7],[1,9,6]],['a1','a2','a3'])
df1.show()

df2 = sqlContext.createDataFrame([[10,2],[9,3]],['b1','b2'])
df2.show()

据我所知,这并不简单。下面是使用eval对其进行的一次拍摄:

# function to add rownumbers in a dataframe
def addrownum(df):
    dff = df.rdd.zipWithIndex().toDF(['features','rownum'])
    odf = dff.map(lambda x : tuple(x.features)+tuple([x.rownum])).toDF(df.columns+['rownum'])
    return odf

df1_ = addrownum(df1)
df2_ = addrownum(df2)
# Join based on rownumbers
outputdf = df1_.rownum.join(df2_,df1_.rownum==df2_.rownum).drop(df1_.rownum).drop(df2_.rownum)

n1 = ['a1','a2','a3']  # columns in set1
n2 = ['b1','b2']       # columns in set2

# I create a string of expression that I want to execute
eval_list = ['x.'+l1+'*'+'x.'+l2 for l1 in n1 for l2 in n2]
eval_str = '('+','.join(eval_list)+')'
col_list = [l1+l2 for l1 in n1 for l2 in n2] 

dfcartesian = outputdf.map(lambda x:eval(eval_str)).toDF(col_list)

spark.ml.feature中的Elementwise产品可能对您有所帮助,但它同样复杂。您可以将一个列表中的元素以多个元素的方式添加到另一个列表中,并将特征向量展开回数据帧。

如何链接行?一般来说,订单不是你可以依赖的。嗨,Zero323,谢谢你的留言。我们有主键来链接行。这里,让我们简单地假设行是通过整数索引匹配的,并且所有数据帧都有相同数量的行。根据索引的不同,不是:)通常
df1.join(df2,['id'])。选择([df1[x]*df2[y]代表df1中的x.columns代表df2中的y.columns如果x!=“id”和y!=“id'])
id
是链接列时。嗨,zero323,你的pro代码工作得很好,伙计:)但是,生成的列名并不是我想要的。不过,我可以很容易地重命名它们。谢谢!你好,谢谢你的回复。同样,您使用的方法是行操作,这对于大型数据集来说非常缓慢。此外,mllib中的Elementwise乘积不起作用,因为它使用单独的权重向量乘以一行中的数组单元格。