Spark Python:如何计算RDD中每一行之间的Jaccard相似性?
我有一个大约50k行和2列的表。您可以将每一行视为一部电影,列视为该电影的属性——“ID”:该电影的ID,“Tags”:该电影的一些内容标签,以每部电影的字符串列表的形式出现 数据如下所示: 电影("浪漫","喜剧","英语"),; 电影2,[《动作》、《功夫》、《中国人》] 我的目标是首先根据每部电影对应的标签计算出它们之间的jacquard相似性,一旦计算完成,我将能够知道每部电影(例如,我选择了Movie_1),与这部电影(本例中为Movie_1)最相似的前5部电影是什么。我想要的结果不仅是电影1本身的前五名,而且是所有电影的前五名 我已经尝试使用Python来解决这个问题,但是运行时在这里是一个很大的挑战。即使我使用多处理,在6个内核上运行,总运行时间仍然超过20小时 Python代码如下:Spark Python:如何计算RDD中每一行之间的Jaccard相似性?,python,apache-spark,for-loop,pyspark,pairwise,Python,Apache Spark,For Loop,Pyspark,Pairwise,我有一个大约50k行和2列的表。您可以将每一行视为一部电影,列视为该电影的属性——“ID”:该电影的ID,“Tags”:该电影的一些内容标签,以每部电影的字符串列表的形式出现 数据如下所示: 电影("浪漫","喜剧","英语"),; 电影2,[《动作》、《功夫》、《中国人》] 我的目标是首先根据每部电影对应的标签计算出它们之间的jacquard相似性,一旦计算完成,我将能够知道每部电影(例如,我选择了Movie_1),与这部电影(本例中为Movie_1)最相似的前5部电影是什么。我想要的结果不仅
将熊猫作为pd导入
从收款进口柜台
将numpy作为np导入
来自多处理导入池
导入时间
col_name=['movie_id','tag_name']
df=pd.read\u csv(“movies.csv”,name=col\u name)
movie_id=df['movie_id'].tolist()
标签列表=df['tag\u name'].tolist()
def jaccard_相似性(string1、string2):
交叉点=集合(string1)。交叉点(集合(string2))
并集=集合(字符串1)。并集(集合(字符串2))
返回长度(交叉)/浮动(长度(联合))
def jc_结果(电影id):
结果=计数器()
此索引=电影id.index(电影id)
对于电影\u id中的另一个\u id:
该索引=电影id.index(另一个id)
如果另一个\u id==电影\u id:
持续
其他:
tag_1=tag_列表[此索引]
tag_2=tag_列表[该索引]
jaccard=jaccard\u相似性(标签1、标签2)
结果[(电影id,另一个id)]=jaccard
返回结果。最常见(10)
来自多处理导入池
游泳池=游泳池(6)
结果={}
对于电影id中的电影id:
结果[movie\u id]=池。应用异步(jc\u结果,args=(movie\u id,)
pool.close()
pool.join()
对于电影id,res in results.items():
results[movie_id]=res.get()
您可以尝试类似的解决方案,但由于您的数据已经标记化(字符串列表),因此不需要执行该步骤或ngram步骤
我还要提到pyspark中的approxSimilarityJoin计算的是Jaccard距离,而不是Jaccard相似度,但是如果您特别需要,您可以从1中减去以转换回相似度
您的代码最终看起来类似于:
from pyspark.ml import Pipeline
from pyspark.ml.feature import HashingTF, MinHashLSH
import pyspark.sql.functions as f
db = spark.createDataFrame([
('movie_1', ['romantic','comedy','English']),
('movie_2', ['action','kongfu','Chinese']),
('movie_3', ['romantic', 'action'])
], ['movie_id', 'genres'])
model = Pipeline(stages=[
HashingTF(inputCol="genres", outputCol="vectors"),
MinHashLSH(inputCol="vectors", outputCol="lsh", numHashTables=10)
]).fit(db)
db_hashed = model.transform(db)
db_matches = model.stages[-1].approxSimilarityJoin(db_hashed, db_hashed, 0.9)
#show all matches (including duplicates)
db_matches.select(f.col('datasetA.movie_id').alias('movie_id_A'),
f.col('datasetB.movie_id').alias('movie_id_B'),
f.col('distCol')).show()
#show non-duplicate matches
db_matches.select(f.col('datasetA.movie_id').alias('movie_id_A'),
f.col('datasetB.movie_id').alias('movie_id_B'),
f.col('distCol')).filter('movie_id_A < movie_id_B').show()
你找到解决这个问题的方法了吗?我也在寻找类似的问题。请让我知道,如果你找到任何解决方案this@AnilKumar请在下面查看我的答案。你能解释一下numHashTables表示什么以及为什么选择10吗?@ShirinYavari此链接对此有很好的解释:。总之,我选择了10个有点随意。10表示代码将从每个记录创建10个独立的minhash值,然后approxSimilarityJoin将比较每行的10个值,以查看每行中有多少相同或不同,从而得出相似性分数。如果选择小于10,则行之间计算的相似度将不太准确,如果选择大于10,则会更准确。非常感谢您的解释和链接。我一定会调查的。
+----------+----------+-------+
|movie_id_A|movie_id_B|distCol|
+----------+----------+-------+
| movie_3| movie_3| 0.0|
| movie_1| movie_3| 0.75|
| movie_2| movie_3| 0.75|
| movie_1| movie_1| 0.0|
| movie_2| movie_2| 0.0|
| movie_3| movie_2| 0.75|
| movie_3| movie_1| 0.75|
+----------+----------+-------+
+----------+----------+-------+
|movie_id_A|movie_id_B|distCol|
+----------+----------+-------+
| movie_1| movie_3| 0.75|
| movie_2| movie_3| 0.75|
+----------+----------+-------+