diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/src/main/java/com/tarzan/recommend/RecommendSystemApplication.java b/src/main/java/com/tarzan/recommend/RecommendSystemApplication.java index 01dc0f9..ff5d5a9 100644 --- a/src/main/java/com/tarzan/recommend/RecommendSystemApplication.java +++ b/src/main/java/com/tarzan/recommend/RecommendSystemApplication.java @@ -1,8 +1,7 @@ package com.tarzan.recommend; -import com.tarzan.recommend.Service.Recommend; +import com.tarzan.recommend.service.Recommend; import com.tarzan.recommend.dto.ItemDTO; -import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.List; diff --git a/src/main/java/com/tarzan/recommend/core/CoreMath.java b/src/main/java/com/tarzan/recommend/core/CoreMath.java index 0a8bebf..a669df2 100644 --- a/src/main/java/com/tarzan/recommend/core/CoreMath.java +++ b/src/main/java/com/tarzan/recommend/core/CoreMath.java @@ -12,49 +12,57 @@ import java.util.stream.IntStream; * * @author tarzan * @version 1.0 - * @company 洛阳图联科技有限公司 - * @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved. * @date 2020/7/31$ 15:21$ * @since JDK1.8 */ public class CoreMath { + /** + * 方法描述: 推荐电影id列表 + * + * @param userId 当前用户 + * @param list 用户电影评分数据 + * @return {@link List} + * @date 2023年02月02日 14:51:42 + */ public List recommend(Integer userId, List list) { - //找到最近邻用户id - Map distances = computeNearestNeighbor(userId, list); - Integer nearest = distances.values().iterator().next(); - + //按用户分组 Map> userMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId)); - + //获取其他用户与当前用户的关系值 + Map distances = computeNeighbor(userId, userMap); + //取关系最近的用户 + Integer nearestUserId = distances.values().iterator().next(); //最近邻用户看过电影列表 - List neighborItemList = userMap.get(nearest).stream().map(e->e.getModuleId()).collect(Collectors.toList()); + List neighborItems = userMap.get(nearestUserId).stream().map(RelateDTO::getModuleId).collect(Collectors.toList()); //指定用户看过电影列表 - List userItemList = userMap.get(userId).stream().map(e->e.getModuleId()).collect(Collectors.toList());; + List userItems = userMap.get(userId).stream().map(RelateDTO::getModuleId).collect(Collectors.toList()); //找到最近邻看过,但是该用户没看过的电影,计算推荐,放入推荐列表 List recommendList = new ArrayList<>(); - for (Integer item : neighborItemList) { - if (!userItemList.contains(item)) { + for (Integer item : neighborItems) { + if (!userItems.contains(item)) { recommendList.add(item); } } - Collections.sort(recommendList); + // Collections.sort(recommendList); return recommendList; } /** * 在给定userId的情况下,计算其他用户和它的相关系数并排序 - * @param userId - * @param list - * @return + * @param userId 用户id + * @param userMap 用户电影评分关系mqp + * @return Map */ - private Map computeNearestNeighbor(Integer userId, List list) { - Map> userMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId)); + private Map computeNeighbor(Integer userId, Map> userMap) { Map distances = new TreeMap<>(); + List userItems=userMap.get(userId); userMap.forEach((k,v)->{ - if(k!=userId){ - double distance = pearson_dis(v,userMap.get(userId)); + //排除此用户 + if(!k.equals(userId)){ + //关系距离 + double distance = pearsonDis(v,userItems); distances.put(distance, k); } }); @@ -65,16 +73,16 @@ public class CoreMath { /** * 计算两个序列间的相关系数 * - * @param xList - * @param yList - * @return + * @param xList 用户1喜欢的电影 + * @param yList 用户2喜欢的电影 + * @return double */ - private double pearson_dis(List xList, List yList) { + private double pearsonDis(List xList, List yList) { List xs= Lists.newArrayList(); List ys= Lists.newArrayList(); xList.forEach(x->{ yList.forEach(y->{ - if(x.getModuleId()==y.getModuleId()){ + if(x.getModuleId().equals(y.getModuleId())){ xs.add(x.getIndex()); ys.add(y.getIndex()); } @@ -86,14 +94,13 @@ public class CoreMath { /** * 方法描述: 皮尔森(pearson)相关系数计算 * - * @param xs - * @param ys - * @Return {@link Double} - * @throws + * @param xs x集合 + * @param ys y集合 + * @Return {@link double} * @author tarzan * @date 2020年07月31日 17:03:20 */ - public static Double getRelate(List xs, List ys){ + public static double getRelate(List xs, List ys){ int n=xs.size(); double Ex= xs.stream().mapToDouble(x->x).sum(); double Ey=ys.stream().mapToDouble(y->y).sum(); @@ -102,7 +109,9 @@ public class CoreMath { double Exy= IntStream.range(0,n).mapToDouble(i->xs.get(i)*ys.get(i)).sum(); double numerator=Exy-Ex*Ey/n; double denominator=Math.sqrt((Ex2-Math.pow(Ex,2)/n)*(Ey2-Math.pow(Ey,2)/n)); - if (denominator==0) return 0.0; + if (denominator==0) { + return 0D; + } return numerator/denominator; } diff --git a/src/main/java/com/tarzan/recommend/dto/ItemDTO.java b/src/main/java/com/tarzan/recommend/dto/ItemDTO.java index 52cd488..66092b8 100644 --- a/src/main/java/com/tarzan/recommend/dto/ItemDTO.java +++ b/src/main/java/com/tarzan/recommend/dto/ItemDTO.java @@ -7,10 +7,8 @@ import lombok.NoArgsConstructor; /** * 业务项 * - * @author liu yapeng + * @author TARZAN * @version 1.0 - * @company 洛阳图联科技有限公司 - * @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved. * @date 2020/7/31$ 15:02$ * @since JDK1.8 */ @@ -18,9 +16,13 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor public class ItemDTO { + /** 主键 */ private Integer id; + /** 名称 */ private String name; + /** 日期 */ private String date; + /** 链接 */ private String link; } diff --git a/src/main/java/com/tarzan/recommend/dto/RelateDTO.java b/src/main/java/com/tarzan/recommend/dto/RelateDTO.java index 7b94197..f2ff728 100644 --- a/src/main/java/com/tarzan/recommend/dto/RelateDTO.java +++ b/src/main/java/com/tarzan/recommend/dto/RelateDTO.java @@ -7,10 +7,8 @@ import lombok.NoArgsConstructor; /** * 关系数据 * - * @author liu yapeng + * @author tarzan * @version 1.0 - * @company 洛阳图联科技有限公司 - * @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved. * @date 2020/7/31$ 14:51$ * @since JDK1.8 */ @@ -18,11 +16,11 @@ import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor public class RelateDTO { - //用户id + /** 用户id */ private Integer useId; - //业务id + /** 业务id */ private Integer moduleId; - //指数 + /** 指数 */ private Integer index; diff --git a/src/main/java/com/tarzan/recommend/dto/UserDTO.java b/src/main/java/com/tarzan/recommend/dto/UserDTO.java index 80087b3..b272edd 100644 --- a/src/main/java/com/tarzan/recommend/dto/UserDTO.java +++ b/src/main/java/com/tarzan/recommend/dto/UserDTO.java @@ -7,7 +7,7 @@ import lombok.NoArgsConstructor; /** * 用户对象 * - * @author liu yapeng + * @author TARZAN * @version 1.0 * @company 洛阳图联科技有限公司 * @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved. @@ -18,15 +18,15 @@ import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor public class UserDTO { - //主键 + /** 主键 */ private Integer id; - //年纪 + /** 年纪 */ private Integer age; - //性别 + /** 性别 */ private String sex; - //职业 + /** 职业 */ private String profession; - //邮编 + /** 邮编 */ private String postcode; } diff --git a/src/main/java/com/tarzan/recommend/Service/FileDataSource.java b/src/main/java/com/tarzan/recommend/service/FileDataSource.java similarity index 85% rename from src/main/java/com/tarzan/recommend/Service/FileDataSource.java rename to src/main/java/com/tarzan/recommend/service/FileDataSource.java index 4888f73..24f3827 100644 --- a/src/main/java/com/tarzan/recommend/Service/FileDataSource.java +++ b/src/main/java/com/tarzan/recommend/service/FileDataSource.java @@ -1,4 +1,4 @@ -package com.tarzan.recommend.Service; +package com.tarzan.recommend.service; import com.tarzan.recommend.dto.ItemDTO; import com.tarzan.recommend.dto.RelateDTO; @@ -10,7 +10,11 @@ import org.assertj.core.util.Lists; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Objects; +/** + * @author tarzan + */ @Data @Slf4j public class FileDataSource { @@ -22,20 +26,18 @@ public class FileDataSource { /** * 方法描述: 读取基础数据 * - * @param - * @Return {@link List< RelateDTO>} - * @throws + * @Return {@link List} * @author tarzan * @date 2020年07月31日 16:53:40 */ public static List getData() { - folderPath=new FileDataSource().getClass().getResource("/ml-100k").getPath(); + folderPath= Objects.requireNonNull(FileDataSource.class.getResource("/ml-100k")).getPath(); List relateList = Lists.newArrayList(); try { FileInputStream out = new FileInputStream(folderPath+"\\u.data"); InputStreamReader reader = new InputStreamReader(out, StandardCharsets.UTF_8); BufferedReader in = new BufferedReader(reader); - String line = null; + String line; while ((line = in.readLine()) != null) { String newline = line.replaceAll("[\t]", " "); String[] ht = newline.split(" "); @@ -54,20 +56,18 @@ public class FileDataSource { /** * 方法描述: 读取用户数据 * - * @param - * @Return {@link List< UserDTO>} - * @throws + * @Return {@link List} * @author tarzan * @date 2020年07月31日 16:54:51 */ public static List getUserData() { - folderPath=new FileDataSource().getClass().getResource("/ml-100k").getPath(); + folderPath= Objects.requireNonNull(FileDataSource.class.getResource("/ml-100k")).getPath(); List userList = Lists.newArrayList(); try { FileInputStream out = new FileInputStream(folderPath+"\\u.user"); InputStreamReader reader = new InputStreamReader(out, StandardCharsets.UTF_8); BufferedReader in = new BufferedReader(reader); - String line = null; + String line; while ((line = in.readLine()) != null) { String newline = line.replaceAll("[\t]", " "); String[] ht = newline.split("\\|"); @@ -89,20 +89,18 @@ public class FileDataSource { /** * 方法描述: 读取电影数据 * - * @param - * @Return {@link List< ItemDTO>} - * @throws + * @Return {@link List} * @author tarzan * @date 2020年07月31日 16:54:22 */ public static List getItemData() { - folderPath=new FileDataSource().getClass().getResource("/ml-100k").getPath(); + folderPath= Objects.requireNonNull(FileDataSource.class.getResource("/ml-100k")).getPath(); List itemList = Lists.newArrayList(); try { FileInputStream out = new FileInputStream(folderPath+"\\u.item"); InputStreamReader reader = new InputStreamReader(out, StandardCharsets.UTF_8); BufferedReader in = new BufferedReader(reader); - String line = null; + String line; while ((line = in.readLine()) != null) { String newline = line.replaceAll("[\t]", " "); String[] ht = newline.split("\\|"); diff --git a/src/main/java/com/tarzan/recommend/Service/Recommend.java b/src/main/java/com/tarzan/recommend/service/Recommend.java similarity index 67% rename from src/main/java/com/tarzan/recommend/Service/Recommend.java rename to src/main/java/com/tarzan/recommend/service/Recommend.java index 3dee032..f5e4821 100644 --- a/src/main/java/com/tarzan/recommend/Service/Recommend.java +++ b/src/main/java/com/tarzan/recommend/service/Recommend.java @@ -1,4 +1,4 @@ -package com.tarzan.recommend.Service; +package com.tarzan.recommend.service; import com.tarzan.recommend.core.CoreMath; import com.tarzan.recommend.dto.ItemDTO; @@ -10,9 +10,8 @@ import java.util.stream.Collectors; /** * 推荐服务 * - * @author liu yapeng + * @author TARZAN * @version 1.0 - * @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved. * @date 2020/7/31$ 16:18$ * @since JDK1.8 */ @@ -21,9 +20,8 @@ public class Recommend{ /** * 方法描述: 猜你喜欢 * - * @param + * @param userId 用户id * @Return {@link List} - * @throws * @author tarzan * @date 2020年07月31日 17:28:06 */ @@ -31,8 +29,7 @@ public class Recommend{ CoreMath coreMath = new CoreMath(); List data= FileDataSource.getData(); List recommendations = coreMath.recommend(userId, data); - List itemList= FileDataSource.getItemData().stream().filter(e->recommendations.contains(e.getId())).collect(Collectors.toList()); - return itemList; + return FileDataSource.getItemData().stream().filter(e->recommendations.contains(e.getId())).collect(Collectors.toList()); }