本文记录了在工作中使用swing算法实现i2i的相关代码内容,如果做相关工作可以邮件和我联系 liangz1996@hotmail.com
itemcf相关内容参考之前spark实现itemcf-附scala代码
package *
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.sql._
import org.apache.spark.sql.expressions.UserDefinedFunction
import org.apache.spark.sql.functions._
import scala.math.{
pow, sqrt}
import scala.util.Random
object CollaborativeFilteringUtils extends Serializable {
/**
* 基于item之间的相似度,得到最相似的前N个结果
*
* @param orgDf item1 item2 similarity
* @param sign 排序方式,默认是1,即采用cos距离按照相似度越大越相似,如果采用欧式距离等越小越相似则该值为-1
* @param topN 每个item取最相似的前topN个
* @param minSimScore 最小的相似度分数,默认不做过滤
* @param whiteSet i2i右边的i可推荐的内容,如果为null,则全部可推荐
* @param blackSet i2i右边的i不可以推荐的内容,如果为null,则全部可推荐
* @return 当前的id,和最相似的前topN个结果与对应的相似度
*/
def getI2IMap(orgDf: DataFrame,
sign: Int = 1,
topN: Int = 100,
minSimScore: Double = -10000D,
whiteSet: Broadcast[Set[String]] = null,
blackSet: Broadcast[Set[String]] = null): RDD[(String, Map[String, Double])] = {
orgDf
.rdd
.flatMap(r => Array(
(r.getString(0), (r.getString(1), sign * r.getDouble(2))), // AB BA 都需要计算相似度
(r.getString(1), (r.getString(0), sign * r.getDouble(2)))
))
.filter(row => row._2._2 >= minSimScore)
.filter(row => {
var flag = true
if (whiteSet != null)
flag = whiteSet.value.contains(row._2._1)
flag
})
.filter(row => {
var flag = true
if (blackSet != null)
flag = !blackSet.value.contains(row._2._1)
flag
})
.distinct()
.groupByKey()
.map(row => {
val id = row._1
val map = row._2.toArray.sortBy(_._2).reverse.slice(0, topN).toMap
(id, map)
})
}
/**
* 对当前的i2i相似度计算进行评估
*
* @param input 计算i2i的原始日志输入,为了方便计算,需要将列名统一成user、item、score
* @param output i2i的输出结果,为item1、item2、sim,为防止AB BA只计算一遍的情况,所以在这里需要拆开
* @param i2iType 当前i2i计算方式
*/
def metrics(sparkSession: SparkSession,
input: DataFrame,
output: DataFrame,
i2iType: String): Unit = {
println(s"当前i2i的计算方式是${
i2iType}")
import sparkSession.implicits._
println(s"当前日志中共有${
input.selectExpr("user").distinct().count()}个user")
println(s"当前日志中共有${
input.selectExpr("item").distinct().count()}个item")
val temp1 = output
.rdd
.flatMap(row => Array(
(row.getString(0), row.getString(1), row.getDouble(2)),
(row.getString(1), row.getString(0), row.getDouble(2))
))
.toDF("item1", "item2", "sim")
println(s"有${
temp1.selectExpr("item1").distinct().count()}个item可以进行i2i召回")
val temp2 = temp1.groupBy("item1").agg(countDistinct("item2").as("count"), min("sim").as("minSim"), max("sim").as("maxSim"))
val maxMinItem2: Array[(Long, Long)] = temp2.agg(max("count"), min("count"))
.collect()
.map(row => (row.