电影推荐系统-[离线统计部分](二)创建Package、类+编写代码

3)按类别统计平均分最高的10个电影

(1)电影流程图

(2)求Top10的大致思路

平均分表  join  电影表 得到 电影表带平均分

带平均分的电影表  笛卡儿积  所有的类别---》  过滤掉不包含的类别 ---》group by 得到数据形式:[类别,评分(mid,A ),(mid,B),(mid,C),(mid ,D)]

(3)Scala代码:

自定数据类

package staticRecommender

/**
  * @Author : ASUS and xinrong
  * @Version : 2020/9/4
  *  数据格式转换类
  *  ---------------电影表------------------------
  *  1
  *  Toy Story (1995)
  *
  *  81 minutes
  *  March 20, 2001
  *  1995
  *  English
  *  Adventure|Animation|Children|Comedy|Fantasy
  *  Tom Hanks|Tim Allen|Don Rickles|Jim Varney|Wallace Shawn|John Ratzenberger|Annie Potts|John Morris|Erik von Detten|Laurie Metcalf|R. Lee Ermey|Sarah Freeman|Penn Jillette|Tom Hanks|Tim Allen|Don Rickles|Jim Varney|Wallace Shawn
  *  John Lasseter
  */
case class Movie(val mid:Int,val name:String,val descri:String,
                 val timelong:String,val cal_issue:String,val shoot:String,
                 val language:String,val genres :String,val actors:String,val directors:String)
/**
  * -----用户对电影的评分数据集--------
  * 1,31,2.5,1260759144
  */
case class Rating(val uid:Int,val mid:Int,val score:Double,val timastamp:Int)

/**
  * --------用户对电影的标签数据集--------
  * 15,339,sandra 'boring' bullock,1138537770
  */
case class Tag(val uid:Int,val mid:Int,val tag:String,val timestamp:Int)

/**
  *
  * MongoDB配置对象
  * @param uri
  * @param db
  */
case class MongoConfig(val uri:String,val db:String)

/**
  * ES配置对象
  * @param httpHosts
  * @param transportHosts:保存的是所有ES节点的信息
  * @param clusterName
  */
case class EsConfig(val httpHosts:String,val transportHosts:String,val index:String,val clusterName:String)

/**
  * recs的二次封装数据类
  * @param mid
  * @param res
  */
case class Recommendation(mid:Int,res:Double)

/**
  * Key-Value封装数据类
  * @param genres
  * @param recs
  */
case class GenresRecommendation(genres:String,recs:Seq[Recommendation])

实际执行类

//三、按类别统计平均分最高的10个电影(第4个目标包含第3个目标)
 //可以将Movie当作参数直接传进来,也可以将movies当成视图去写
 //在学闭包柯里化的时候(柯里化解决了多个参数的问题)--可以将多个参数拆成多个括号的形式,调用的时候需要注意-要写多个括号
def genresTop10(spark:SparkSession)(movies:Dataset[Movie])(implicit mongoConfig: MongoConfig):Unit={
   //1.定义所有电影类别
   val genres = List("Action","Adventure","Animation","Comedy","Ccrime","Documentary","Drama","Family","Fantasy","Foreign","History","Horror","Music","Mystery"
     ,"Romance","Science","Tv","Thriller","War","Western")
   //2.统计电影平均分-averageMovieScore(DataFrame类型的)
   val averageMovieScore=spark.sql("select mid,avg(score) as avg from ratings group by mid").cache()
   //3.统计类别中分数最高的10部电影
   //将电影平均分、电影两个表进行Join得到一个合并后的表
                                                //第一个表的mid等于第二个表的mid
   val moviesWithScoreDF=movies.join(averageMovieScore,Seq("mid","mid")).select("mid","avg","genres").cache()
   //4.做笛卡儿积、Filter、模式匹配-GroupBy、排序+take
    //1)genres一开始是一个List,将其转成RDD才能使用
   val genresRDD=spark.sparkContext.makeRDD(genres)
    //2)操作笛卡儿积注意引入(需要的时候再引)
   import spark.implicits._
    //3)进行Filter(将名字转换成小写)
      //注意要将合并后的表-moviesWithScoreDF转换成RDD形式
   val genresTopMovies=genresRDD.cartesian(moviesWithScoreDF.rdd).filter{
        //geners:是指做笛卡儿积之后所得表的genres列;row则是指此表中属于原合并表(moviesWithScoreDF)的每行数据
        case(genres,row)=>{
          //如果geners在row中geners包括的电影种类里,就返回true,它就被保留了
          //否则此行数据就会被清除(不删除结构,只是删除数据)
          row.getAs[String]("genres").toLowerCase.contains(genres.toLowerCase)
        }
      }.map{
      //4)GroupBy
      case(genres,row)=>{
        //genres作为Key,(Int,Double)作为value
        (genres,(row.getAs[Int]("mid"),row.getAs[Double]("avg")))
      }
    }.groupByKey()
        .map{
          //5)排序+take
          case(genres,items)=>{
            //将items从Iterable转换成List,然后进行遍历
            //用第二位-平均分进行排序
            GenresRecommendation(genres,items.toList.sortWith(_._2>_._2).take(10).map(x=>Recommendation(x._1,x._2))) //取前10位
          }
        }.toDF //注意:调用toDF,需要引入:import spark.implicits._
   //5.将统计好的top10电影数据写入到MongoDB里面(注意转换成DataFrame的类型)
   genresTopMovies
     .write
     .option("uri",mongoConfig.uri)
     .option("collection",GENRES_TOP_MOVIES)
     .mode("overwrite")
     .format("com.mongodb.spark.sql")
     .save()
   //6.将统计好的电影平均分写入MongoDB里面
   averageMovieScore
     .write
     .option("uri",mongoConfig.uri)
     .option("collection",AVERAGE_MOVIES_SCORE)
     .mode("overwrite")
     .format("com.mongodb.spark.sql")
     .save()
}

调用类

//七、按类别统计平均分最高的10个电影(第4个目标包含第3个目标)
StatisticsRecommender.genresTop10(spark)(movies)

(3)启动MongoDB、执行程序、打开Robo可视化工具查看

 

 

 

博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3