基于用户和基于物品的推荐方法

This commit is contained in:
tarzan
2023-02-07 13:24:04 +08:00
parent 006a73bb46
commit b924b78dbd
6 changed files with 137 additions and 52 deletions

View File

@@ -11,9 +11,12 @@ public class RecommendSystemApplication {
public static void main(String[] args) { public static void main(String[] args) {
//SpringApplication.run(RecommendSystemApplication.class, 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())); 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()));
} }
} }

View File

@@ -4,7 +4,6 @@ import com.tarzan.recommend.dto.RelateDTO;
import org.assertj.core.util.Lists; import org.assertj.core.util.Lists;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
/** /**
@@ -17,72 +16,54 @@ import java.util.stream.IntStream;
*/ */
public class CoreMath { 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 key
* @param userMap 用户电影评分关系mqp * @param map
* @return Map<Integer,Double> * @return Map<Integer,Double>
*/ */
private Map<Integer,Double> computeNeighbor(Integer userId, Map<Integer,List<RelateDTO>> userMap) { public static Map<Integer,Double> computeNeighbor(Integer key, Map<Integer,List<RelateDTO>> map,int type) {
Map<Integer,Double> userDisMap = new TreeMap<>(); Map<Integer,Double> distMap = new TreeMap<>();
List<RelateDTO> userItems=userMap.get(userId); List<RelateDTO> userItems=map.get(key);
userMap.forEach((k,v)->{ 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); double distance=Math.abs(coefficient);
userDisMap.put(k,distance); distMap.put(k,distance);
} }
}); });
return userDisMap; return distMap;
} }
/** /**
* 计算两个序列间的相关系数 * 计算两个序列间的相关系数
* *
* @param xList 用户1喜欢的电影 * @param xList
* @param yList 用户2喜欢的电影 * @param yList
* @param type 类型0基于用户推荐 1基于物品推荐
* @return double * @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> xs= Lists.newArrayList();
List<Integer> ys= Lists.newArrayList(); List<Integer> ys= Lists.newArrayList();
xList.forEach(x->{ xList.forEach(x->{
yList.forEach(y->{ yList.forEach(y->{
if(x.getModuleId().equals(y.getModuleId())){ if(type==0){
xs.add(x.getIndex()); if(x.getItemId().equals(y.getItemId())){
ys.add(y.getIndex()); 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){ public static double getRelate(List<Integer> xs, List<Integer> ys){
int n=xs.size(); int n=xs.size();
if (n==0) { //至少有两个元素
if (n<2) {
return 0D; return 0D;
} }
double Ex= xs.stream().mapToDouble(x->x).sum(); double Ex= xs.stream().mapToDouble(x->x).sum();

View 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());
}
}

View 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;
}
}

View File

@@ -18,8 +18,8 @@ import lombok.NoArgsConstructor;
public class RelateDTO { public class RelateDTO {
/** 用户id */ /** 用户id */
private Integer useId; private Integer useId;
/** 业务id */ /** 物品id */
private Integer moduleId; private Integer itemId;
/** 指数 */ /** 指数 */
private Integer index; private Integer index;

View File

@@ -1,6 +1,8 @@
package com.tarzan.recommend.service; package com.tarzan.recommend.service;
import com.tarzan.recommend.core.CoreMath; 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.ItemDTO;
import com.tarzan.recommend.dto.RelateDTO; import com.tarzan.recommend.dto.RelateDTO;
@@ -25,10 +27,24 @@ public class Recommend{
* @author tarzan * @author tarzan
* @date 2020年07月31日 17:28:06 * @date 2020年07月31日 17:28:06
*/ */
public static List<ItemDTO> guessUserLike(int userId){ public static List<ItemDTO> userCfRecommend(int userId){
CoreMath coreMath = new CoreMath();
List<RelateDTO> data= FileDataSource.getData(); 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()); return FileDataSource.getItemData().stream().filter(e->recommendations.contains(e.getId())).collect(Collectors.toList());
} }