我有一个PySpark DataFrame df1,看起来像:
CustomerID CustomerValue CustomerValue2
12 .17 .08
我有第二个PySpark DataFrame df2
CustomerID CustomerValue CustomerValue
15 .17 .14
16 .40 .43
18 .86 .09
我要考虑两个数据帧的余弦相似度。还有类似的东西
CustomerID CustomerID CosineCustVal CosineCustVal
15 12 1 .90
16 12 .45 .67
18 12 .8 .04
答案 0 :(得分:1)
您只能计算两个向量的余弦相似度,而不能计算两个数字。就是说,如果称为CustomerValue的列是表示要获得两个客户之间相似性的功能的向量的不同组成部分,则可以通过转置数据框然后在CuatomerValues上进行联接来实现。 >
可以通过爆炸完成转置(有关转置数据帧here的更多详细信息):
from pyspark.sql import functions as F
kvs = F.explode(F.array([
F.struct(F.lit(c).alias('key'), F.columm(c).alias('value')) for c in ['CustomerValue1', 'CustomerValue2']
])).alias('kvs')
dft1 = (df1.select(['CustomerID', kvs])
.select('CustomerID', F.column('kvs.name').alias('column_name'), F.column('kvs.value').alias('column_value'))
)
dft2 = (df2.select(['CustomerID', kvs])
.select('CustomerID', F.column('kvs.name').alias('column_name'), F.column('kvs.value').alias('column_value'))
)
其中dft1
和dft2
表示转置的数据帧。换位后,可以将它们加入列名:
dft2 = (dft2.withColumnRenamed('CustomerID', 'CustomerID2')
.withColumnRenamed('column_value', 'column_value2')
)
cosine = (dft1.join(dft2, dft1.column_name = dft2.column_name)
.groupBy('CustomerID' , 'CustomerID2')
.agg(F.sum(F.column('column_value')*F.column('column_value2')).alias('cosine_similarity'))
)
现在在cosine
中,您具有三列:来自第一个和第二个数据帧的CustomerID和余弦相似度(前提是首先对值进行了归一化)。这样做的好处是,对于相似性非零的CustomerID对,只有一行(如果某些CustomerID的值为零)。例如:
df1:
CustomerID CustomerValue CustomerValue2
12 .17 .08
df2:
CustomerID CustomerValue CustomerValue
15 .17 .14
16 .40 .43
18 .86 .09
余弦:
CustomID CustomID2 cosine_similarity
12 15 .0401
12 16 .1024
12 18 .1534
当然,这些还不是真正的余弦相似度,您需要首先将这些值标准化。您可以通过以下方式与一群人一起做到这一点:
(df.groupBy('CustomerID')
.agg(F.sqrt(F.sum(F.column('column_value')*F.column('column_value'))).alias('norm'))
.select('CustomerID', F.column('column_name'), (F.column('column_value')/F.column('norm')).alias('column_value_norm'))
)
对列进行归一化后,您的余弦相似度变为:
CustomID CustomID2 cosine_similarity
12 15 .970
12 16 .928
12 18 .945
相似度值较大是由于维数较低(仅两个分量)。