基于用户和基于物品的推荐方法
This commit is contained in:
@@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
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 {
|
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;
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user