基于用户和基于物品的推荐方法
This commit is contained in:
@@ -11,9 +11,12 @@ public class RecommendSystemApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
//SpringApplication.run(RecommendSystemApplication.class, args);
|
||||
List<ItemDTO> itemList= Recommend.guessUserLike(2);
|
||||
System.out.println("------猜你可能喜欢---------------下列电影");
|
||||
System.out.println("------基于用户协同过滤推荐---------------下列电影");
|
||||
List<ItemDTO> itemList= Recommend.userCfRecommend(2);
|
||||
itemList.forEach(e-> System.out.println(e.getName()));
|
||||
System.out.println("------基于物品协同过滤推荐---------------下列电影");
|
||||
List<ItemDTO> itemList1= Recommend.itemCfRecommend(2);
|
||||
itemList1.forEach(e-> System.out.println(e.getName()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.tarzan.recommend.dto.RelateDTO;
|
||||
import org.assertj.core.util.Lists;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
@@ -17,72 +16,54 @@ import java.util.stream.IntStream;
|
||||
*/
|
||||
public class CoreMath {
|
||||
|
||||
/**
|
||||
* 方法描述: 推荐电影id列表
|
||||
*
|
||||
* @param userId 当前用户
|
||||
* @param list 用户电影评分数据
|
||||
* @return {@link List<Integer>}
|
||||
* @date 2023年02月02日 14:51:42
|
||||
*/
|
||||
public List<Integer> recommend(Integer userId, List<RelateDTO> list) {
|
||||
//按用户分组
|
||||
Map<Integer, List<RelateDTO>> userMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId));
|
||||
//获取其他用户与当前用户的关系值
|
||||
Map<Integer,Double> userDisMap = computeNeighbor(userId, userMap);
|
||||
//获取关系最近的用户
|
||||
double maxvalue=Collections.max(userDisMap.values());
|
||||
Set<Integer> userIds=userDisMap.entrySet().stream().filter(e->e.getValue()==maxvalue).map(Map.Entry::getKey).collect(Collectors.toSet());
|
||||
//取关系最近的用户
|
||||
Integer nearestUserId = userIds.stream().findAny().get();
|
||||
//最近邻用户看过电影列表
|
||||
List<Integer> neighborItems = userMap.get(nearestUserId).stream().map(RelateDTO::getModuleId).collect(Collectors.toList());
|
||||
//指定用户看过电影列表
|
||||
List<Integer> userItems = userMap.get(userId).stream().map(RelateDTO::getModuleId).collect(Collectors.toList());
|
||||
//找到最近邻看过,但是该用户没看过的电影
|
||||
neighborItems.removeAll(userItems);
|
||||
return neighborItems;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 在给定userId的情况下,计算其他用户和它的相关系数并排序
|
||||
* @param userId 用户id
|
||||
* @param userMap 用户电影评分关系mqp
|
||||
* 计算相关系数并排序
|
||||
* @param key
|
||||
* @param map
|
||||
* @return Map<Integer,Double>
|
||||
*/
|
||||
private Map<Integer,Double> computeNeighbor(Integer userId, Map<Integer,List<RelateDTO>> userMap) {
|
||||
Map<Integer,Double> userDisMap = new TreeMap<>();
|
||||
List<RelateDTO> userItems=userMap.get(userId);
|
||||
userMap.forEach((k,v)->{
|
||||
public static Map<Integer,Double> computeNeighbor(Integer key, Map<Integer,List<RelateDTO>> map,int type) {
|
||||
Map<Integer,Double> distMap = new TreeMap<>();
|
||||
List<RelateDTO> userItems=map.get(key);
|
||||
map.forEach((k,v)->{
|
||||
//排除此用户
|
||||
if(!k.equals(userId)){
|
||||
if(!k.equals(key)){
|
||||
//关系系数
|
||||
double coefficient = pearsonDis(v,userItems);
|
||||
double coefficient = relateDist(v,userItems,type);
|
||||
//关系距离
|
||||
double distance=Math.abs(coefficient);
|
||||
userDisMap.put(k,distance);
|
||||
distMap.put(k,distance);
|
||||
}
|
||||
});
|
||||
return userDisMap;
|
||||
return distMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算两个序列间的相关系数
|
||||
*
|
||||
* @param xList 用户1喜欢的电影
|
||||
* @param yList 用户2喜欢的电影
|
||||
* @param xList
|
||||
* @param yList
|
||||
* @param type 类型0基于用户推荐 1基于物品推荐
|
||||
* @return double
|
||||
*/
|
||||
private double pearsonDis(List<RelateDTO> xList, List<RelateDTO> yList) {
|
||||
private static double relateDist(List<RelateDTO> xList, List<RelateDTO> yList,int type) {
|
||||
List<Integer> xs= Lists.newArrayList();
|
||||
List<Integer> ys= Lists.newArrayList();
|
||||
xList.forEach(x->{
|
||||
yList.forEach(y->{
|
||||
if(x.getModuleId().equals(y.getModuleId())){
|
||||
xs.add(x.getIndex());
|
||||
ys.add(y.getIndex());
|
||||
if(type==0){
|
||||
if(x.getItemId().equals(y.getItemId())){
|
||||
xs.add(x.getIndex());
|
||||
ys.add(y.getIndex());
|
||||
}
|
||||
}else{
|
||||
if(x.getUseId().equals(y.getUseId())){
|
||||
xs.add(x.getIndex());
|
||||
ys.add(y.getIndex());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -100,7 +81,8 @@ public class CoreMath {
|
||||
*/
|
||||
public static double getRelate(List<Integer> xs, List<Integer> ys){
|
||||
int n=xs.size();
|
||||
if (n==0) {
|
||||
//至少有两个元素
|
||||
if (n<2) {
|
||||
return 0D;
|
||||
}
|
||||
double Ex= xs.stream().mapToDouble(x->x).sum();
|
||||
|
||||
39
src/main/java/com/tarzan/recommend/core/ItemCF.java
Normal file
39
src/main/java/com/tarzan/recommend/core/ItemCF.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.tarzan.recommend.core;
|
||||
|
||||
import com.tarzan.recommend.dto.RelateDTO;
|
||||
import org.assertj.core.util.Lists;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* 核心算法
|
||||
*
|
||||
* @author tarzan
|
||||
* @version 1.0
|
||||
* @date 2020/7/31$ 15:21$
|
||||
* @since JDK1.8
|
||||
*/
|
||||
public class ItemCF {
|
||||
|
||||
/**
|
||||
* 方法描述: 推荐电影id列表
|
||||
*
|
||||
* @param itemId 当前电影id
|
||||
* @param list 用户电影评分数据
|
||||
* @return {@link List<Integer>}
|
||||
* @date 2023年02月02日 14:51:42
|
||||
*/
|
||||
public static List<Integer> recommend(Integer itemId, List<RelateDTO> list) {
|
||||
//按物品分组
|
||||
Map<Integer, List<RelateDTO>> itemMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getItemId));
|
||||
//获取其他物品与当前物品的关系值
|
||||
Map<Integer,Double> itemDisMap = CoreMath.computeNeighbor(itemId, itemMap,1);
|
||||
//获取关系最近物品
|
||||
double maxValue=Collections.max(itemDisMap.values());
|
||||
return itemDisMap.entrySet().stream().filter(e->e.getValue()==maxValue).map(Map.Entry::getKey).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
45
src/main/java/com/tarzan/recommend/core/UserCF.java
Normal file
45
src/main/java/com/tarzan/recommend/core/UserCF.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package com.tarzan.recommend.core;
|
||||
|
||||
import com.tarzan.recommend.dto.RelateDTO;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 核心算法
|
||||
*
|
||||
* @author tarzan
|
||||
* @version 1.0
|
||||
* @date 2020/7/31$ 15:21$
|
||||
* @since JDK1.8
|
||||
*/
|
||||
public class UserCF {
|
||||
|
||||
/**
|
||||
* 方法描述: 推荐电影id列表
|
||||
*
|
||||
* @param userId 当前用户
|
||||
* @param list 用户电影评分数据
|
||||
* @return {@link List<Integer>}
|
||||
* @date 2023年02月02日 14:51:42
|
||||
*/
|
||||
public static List<Integer> recommend(Integer userId, List<RelateDTO> list) {
|
||||
//按用户分组
|
||||
Map<Integer, List<RelateDTO>> userMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId));
|
||||
//获取其他用户与当前用户的关系值
|
||||
Map<Integer,Double> userDisMap = CoreMath.computeNeighbor(userId, userMap,0);
|
||||
//获取关系最近的用户
|
||||
double maxValue=Collections.max(userDisMap.values());
|
||||
Set<Integer> userIds=userDisMap.entrySet().stream().filter(e->e.getValue()==maxValue).map(Map.Entry::getKey).collect(Collectors.toSet());
|
||||
//取关系最近的用户
|
||||
Integer nearestUserId = userIds.stream().findAny().get();
|
||||
//最近邻用户看过电影列表
|
||||
List<Integer> neighborItems = userMap.get(nearestUserId).stream().map(RelateDTO::getItemId).collect(Collectors.toList());
|
||||
//指定用户看过电影列表
|
||||
List<Integer> userItems = userMap.get(userId).stream().map(RelateDTO::getItemId).collect(Collectors.toList());
|
||||
//找到最近邻看过,但是该用户没看过的电影
|
||||
neighborItems.removeAll(userItems);
|
||||
return neighborItems;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,8 +18,8 @@ import lombok.NoArgsConstructor;
|
||||
public class RelateDTO {
|
||||
/** 用户id */
|
||||
private Integer useId;
|
||||
/** 业务id */
|
||||
private Integer moduleId;
|
||||
/** 物品id */
|
||||
private Integer itemId;
|
||||
/** 指数 */
|
||||
private Integer index;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.tarzan.recommend.service;
|
||||
|
||||
import com.tarzan.recommend.core.CoreMath;
|
||||
import com.tarzan.recommend.core.ItemCF;
|
||||
import com.tarzan.recommend.core.UserCF;
|
||||
import com.tarzan.recommend.dto.ItemDTO;
|
||||
import com.tarzan.recommend.dto.RelateDTO;
|
||||
|
||||
@@ -25,10 +27,24 @@ public class Recommend{
|
||||
* @author tarzan
|
||||
* @date 2020年07月31日 17:28:06
|
||||
*/
|
||||
public static List<ItemDTO> guessUserLike(int userId){
|
||||
CoreMath coreMath = new CoreMath();
|
||||
public static List<ItemDTO> userCfRecommend(int userId){
|
||||
List<RelateDTO> data= FileDataSource.getData();
|
||||
List<Integer> recommendations = coreMath.recommend(userId, data);
|
||||
List<Integer> recommendations = UserCF.recommend(userId, data);
|
||||
return FileDataSource.getItemData().stream().filter(e->recommendations.contains(e.getId())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 方法描述: 猜你喜欢
|
||||
*
|
||||
* @param itemId 物品id
|
||||
* @Return {@link List<ItemDTO>}
|
||||
* @author tarzan
|
||||
* @date 2020年07月31日 17:28:06
|
||||
*/
|
||||
public static List<ItemDTO> itemCfRecommend(int itemId){
|
||||
List<RelateDTO> data= FileDataSource.getData();
|
||||
List<Integer> recommendations = ItemCF.recommend(itemId, data);
|
||||
return FileDataSource.getItemData().stream().filter(e->recommendations.contains(e.getId())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user