一、Spark知识点
二、项目数据
三、项目代码
import java.util.Arrays;
import java.util.List;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.Optional;
import org.apache.spark.mllib.recommendation.ALS;
import org.apache.spark.mllib.recommendation.MatrixFactorizationModel;
import org.apache.spark.mllib.recommendation.Rating;
import scala.Tuple2;
public class HelloSpark {
//存放文件的路径,还没有采用hadoop存储
private static final String PATH_STRING = "/home/hadoop/ml-latest-small_2018/";
//main
public static void main(String[] args) {
try {
HelloSpark helloSpark = new HelloSpark();
helloSpark.recommendMovies();
} catch (Exception e) {
System.out.println("shenme");
}
}
//推荐电影
public void recommendMovies() throws Exception{
//如果多个spark任务运行在一台机器上,需要APPname对其进行分类
//setMaster(local[2])就是开启2个线程对该任务进行处理
SparkConf conf = new SparkConf().setAppName("Movies").setMaster("local[2]");
//一个程序只需要获得一个配置信息即可
JavaSparkContext sparkContext = new JavaSparkContext(conf);
//接下来就是函数的功能,具体功能下面详细解释
JavaRDD moviesRDD = preProcessMovies(sparkContext, "movies.csv");
JavaRDD ratRDD = preProcessRating(sparkContext, "ratings.csv");
JavaRDD rat = prePRocess(sparkContext, ratRDD, moviesRDD);
MatrixFactorizationModel mod = this.createModel(rat);
recommend(sparkContext, mod, 23, 10,rat);
//配置文件必须在程序结束时关闭
sparkContext.close();
System.out.println("Application is successed.");
}
//去掉错误的评分数据
public JavaRDD preProcessRating(JavaSparkContext sContext,String file) throws Exception{
JavaRDD ratingsJavaRDD = sContext.textFile(PATH_STRING+file).distinct();
JavaRDD ratingsJavaRDD1 = ratingsJavaRDD.filter(f -> {
String[] arr = f.split(",");
//ge shi jian cha
if (arr.length == 4) {
try {
Integer.valueOf(arr[0].trim());
Double val = Double.valueOf(arr[2].trim());
// ping fen zui gao 5.0
if (val< 5.0 && val >= 0) {
return true;
}else {
return false;
}
} catch (Exception e) {
return false;
}
}
return false;
});
//qu chu hao miao
JavaRDD ratingsJavaRDD2 = ratingsJavaRDD1.map(f -> {
String[] arr = f.split(",");
return arr[0]+","+arr[1]+","+arr[2];
});
return ratingsJavaRDD2;
}
//去掉错误的电影数据
public JavaRDD preProcessMovies(JavaSparkContext sContext,String file) throws Exception{
//quchongfu
JavaRDD ratingsJavaRDD = sContext.textFile(PATH_STRING+file).distinct();
JavaRDD ratingsJavaRDD1 = ratingsJavaRDD.filter(f -> {
String[] arr = f.split(",");
if (arr.length >= 3) {
try {
Integer.valueOf(arr[0].trim());
return true;
} catch (Exception e) {
return false;
}
}
return false;
});
return ratingsJavaRDD1;
}
//去掉用户评价的电影不存在的情况
public JavaRDD prePRocess(JavaSparkContext sc, JavaRDD rat, JavaRDD movise) throws Exception{
JavaPairRDD r1 = rat.mapToPair(f -> {
String[] arr = f.split(",");
//r1 =
return new Tuple2(Integer.valueOf(arr[1].trim()),arr[0]+","+arr[2]);
});
JavaPairRDD m1 = movise.mapToPair(f -> {
String[] arr = f.split(",", 2);
//m1 =
return new Tuple2(Integer.valueOf(arr[0].trim()),arr[1]);
});
/*
* Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
* Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
* Optional 类的引入很好的解决空指针异常。
*/
/*
* https://www.cnblogs.com/FG123/p/9747901.html
* leftOuterJoin:相当于mysql的LEFT JOIN,leftOuterJoin返回数据集左边的全部数据和数据集左边与右边有交集的数据
* rightOuterJoin:相当于mysql的RIGHT JOIN,rightOuterJoin返回数据集右边的全部数据和数据集右边与左边有交集的数据
* fullOuterJoin:返回左右数据集的全部数据,左右有一边不存在的数据以None填充
*/
//rk2 = >
JavaPairRDD>> rk2 = r1.leftOuterJoin(m1);
//所有用户评价过的电影数
System.out.println("rk2 count:"+rk2.count());
//去掉空的数据
JavaPairRDD>> rk3 = rk2.filter(f ->{
if (null == f._2()._1() || f._2()._1().isEmpty()) {
return false;
}
if (!f._2()._2().isPresent()) {
return false;
}
return true;
});
JavaRDD readyData = rk3.map(f -> {
//pid is movieID
int pid = f._1();
String[] arr = f._2()._1().split(",");
//uid is userID
int uid = Integer.valueOf(arr[0].trim());
//r is garde
double r = Double.valueOf(arr[1].trim());
return new Rating(uid, pid, r);
});
return readyData;
}
//通过ALS算法对数据进行分类,并形成模型
public MatrixFactorizationModel createModel(JavaRDD ra) throws Exception{
int rank = 50;
int interation = 6;
double lamda = 0.03;
System.out.println("ra count:"+ra.count());
/*
* weights: 是一个数组.
* 根据weight(权重值)将一个RDD划分成多个RDD,权重越高划分得到的元素较多的几率就越大.
* 数组的长度即为划分成RDD的数量.
* 需要注意的是weight数组内数据的加和应为1.
*/
JavaRDD[] data = ra.randomSplit(new double[]{0.7,0.3});
JavaRDD training = data[0];
JavaRDD testing = data[1];
/*
* ALS算法是2008年以来,用的比较多的协同过滤算法.它已经集成到Spark的Mllib库中,使用起来比较方便.
*/
/*
* 表示矩阵分解结果的模型。
* 注意:如果使用构造函数直接创建模型,请注意快速预测需要缓存的用户/产品功能及其关联的分区程序。
* param:rank此模型中功能的排名。 param:userFeatures元组的RDD,其中每个元组表示userId以及为此用户计算的功能。 param:productFeatures元组的RDD,其中每个元组代表productId和为此产品计算的特性。
*/
MatrixFactorizationModel mod = ALS.train(training.rdd(), rank, interation,lamda,-1);
//JavaPairRDD.fromJavaRDD将RDD转换成PairRDD,参数为Tuple2
//mod.predict通过JavaPairRDD的value以及上一句代码定义的算法,进行评级
JavaRDD predictTesting = mod.predict(JavaPairRDD.fromJavaRDD(testing.map(f ->{
//f is Rating
//就是一个评级的过程,根据评级,我们可以获得更接近的效果
return new Tuple2(f.user(), f.product());
})));
//从此开始---------------只是为了验证我们对ALS传入的参数是否合适,res最好要小于1.5--------------------
//将未评级的数据分为<用户和产品,用户评价>,这里的产品就是电影的ID
JavaPairRDD realityRating = JavaPairRDD.fromJavaRDD(testing.map(f ->{
return new Tuple2(f.user()+","+f.product(), f.rating());
}));
//将评级的数据分为<用户和产品,评级>
JavaPairRDD predictRating = JavaPairRDD.fromJavaRDD(predictTesting.map(f ->{
return new Tuple2(f.user()+","+f.product(), f.rating());
}));
JavaRDD> val = realityRating.join(predictRating).values();
//
double re = val.map(f -> {
return Math.pow(f._1() - f._2(), 2);
}).reduce((a,b) -> a+b)/(double)val.count();
double res = Math.sqrt(re);
//res:均方差
System.out.println("res:"+res);
//到此结尾-----------------------------------------------------------------------------------
return mod;
}
//根据用户的ID号给用户推荐电影
public void recommend(JavaSparkContext sparkContext,MatrixFactorizationModel mod, int uid,int number, JavaRDD rat) throws Exception{
/*
* public Rating [] recommendedProducts(int user,int num)
* 向用户推荐产品。
* 参数:
* user - 向其推荐产品的用户
* num - 要返回的产品数量。 返回的数字可能小于此数。
* 返回:
* 评级对象,每个对象包含给定的用户ID,产品ID和评级字段中的“得分”。 每个代表一个推荐产品,它们按分数排序,减少。
* 返回的第一个是预测最强烈推荐给用户的那个。
* 分数是一个不透明的值,表示产品的推荐强度。
*/
Rating[] recRatings = mod.recommendProducts(uid, number);
List lsRecommend = Arrays.asList(recRatings);
//从此开始-----------------------------将电影ID转换成电影名功能未完成---------------------------------------
//将list转换成JavaRDD
JavaRDD recommendRating = sparkContext.parallelize(lsRecommend);
//将JavaRDD转化成JavaPairRDD
JavaPairRDD recommendAllRDD = JavaPairRDD.fromJavaRDD(recommendRating.map(f ->{
return new Tuple2(f.user()+","+f.product(), f.rating());
}));
//将未评级JavaRDD转化成JavaPairRDD
JavaPairRDD ratingRDD = JavaPairRDD.fromJavaRDD(rat.map(f ->{
return new Tuple2(f.user()+","+f.product(), f.rating());
}));
JavaPairRDD>> recommendJoin = recommendAllRDD.leftOuterJoin(ratingRDD);
JavaRDD recMov = recommendJoin.filter(f ->{
if (!f._2()._2().isPresent())
return true;
return false;
}).map(f -> {
String[] arr = f._1().split(",");
return Integer.valueOf(arr[1]);
});
//从此结束-----------------------------将电影ID转换成电影名功能未完成---------------------------------------
//根据推荐度,推荐出电影ID,由于之前mod是从大到小以此排列的
for (Rating rating : recRatings) {
System.out.println("Recommend movie:"+rating.product());
}
}
}