让我举一个简单的例子来解释我想要做的事情。我们假设我们有两个非常简单的数据框如下:
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|
+----+----+----+----+----+----+
我搜索了火花在线文档以及此处发布的问题,但似乎它们都是关于行的笛卡尔积,而不是列。例如,rdd.cartesian()提供了行中不同值组合的笛卡尔积,如下面的代码:
r = sc.parallelize([1, 2])
r.cartesian(r).toDF().show()
+---+---+
| _1| _2|
+---+---+
| 1| 1|
| 1| 2|
| 2| 1|
| 2| 2|
+---+---+
但这不是我需要的。同样,我需要创建新列而不是行。在我的问题中,行数将保持不变。我理解udf最终可以解决问题。然而,在我的实际应用程序中,我们有庞大的数据集,创建所有列需要很长时间(大约500个新列作为列的所有可能组合)。我们更喜欢有一些矢量操作,这可能会提高效率。我可能错了,但是火花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()
答案 0 :(得分:0)
据我所知,它并不简单。以下是使用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产品,但它也同样复杂。您可以将元素从一个列表中逐个元素地添加到另一个列表中,然后将特征向量扩展回数据帧。