今天在编写项目时,需要得到“某演员所擅长的电影题材搭配”,最先想到的就是关联算法,再想到Apriori算法中的支持度指标很符合这一要求。
支持度(Support):support({X -> Y}) = 集合X与集合Y中的项在一条记录中同时出现的次数 / 数据记录的个数
只要找到满足“最低支持度指标”的电影题材搭配,就满足了项目需求。
1.先查看手头的数据
每个演员有多部电影,每部电影都有多个题材。项目中只需要对单个演员进行分析。
2.直接查找出某个演员的所有电影,进行整合
public List getPreferGenres(String actorId) {
List<Movie> movieList = movieRepository.findMovieByActorId(actorId);
List<List<String>> genresList = new ArrayList<>();
for (Movie movie : movieList) {
List genres = new ArrayList();
String movieGenres = movie.getGenres();
//过滤掉无题材电影和真人秀
if (movieGenres.equals("")|| movieGenres.equals("真人秀"))
continue;
genres = Arrays.asList(movieGenres.split(","));
genresList.add(genres);
}
return AprioriUtil.getPreferGenres(genresList);
}
每部电影的题材都被存储在一个List中,而这些List也存储在一个大的List中。OK,数据整理完成,来到AprioriUtil中。
3.对原始数据进行输出
//*************读取数据集**************
record = genresList;
System.out.println("以矩阵形式读取数据集record");
for(int i=0;i<record.size();i++){
List<String> list= new ArrayList<String>(record.get(i));
for(int j=0;j<list.size();j++){
System.out.print(list.get(j)+" ");
}
System.out.println();
}
读取数据集
动作 战争
喜剧 爱情
剧情 动作 犯罪
剧情 动作 战争
科幻 灾难
喜剧 爱情 奇幻
动作 战争
喜剧 奇幻
剧情
剧情
输出的结果与数据库中一致。
4.得到候选1项集
候选1项集是对集合中所有元素进行罗列,为了不重复,使用HashSet作为媒介。
private static List<List<String>> findFirstCandidate() {
List<List<String>> tableList = new ArrayList<>();
HashSet hashSet = new HashSet();
for (int i = 0;i<record.size();i++){
List list = record.get(i);
for (int j = 0;j<list.size();j++){
hashSet.add(list.get(j));
}
}
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()){
List tempList = new ArrayList();
tempList.add(iterator.next());
tableList.add(tempList);
}
return tableList;
}
对候选1项集进行打印(代码与之前打印代码类似)
候选1项集
喜剧
剧情
犯罪
科幻
爱情
战争
动作
灾难
奇幻
与期望的结果一致
5.剪枝 候选项集->频繁项集
private static List<List<String>> getSupprotedItemset(List<List<String>> candidateItemset) {
boolean end = true;
List<List<String>> supportedItemset = new ArrayList<List<String>>();
int k = 0;
for (int i = 0;i<candidateItemset.size();i++){
int count = countFrequent(candidateItemset.get(i));//统计记录数
if (count >= MIN_SUPPORT*record.size()){
supportedItemset.add(candidateItemset.get(i));
end = false;
}
}
endTag = end;
if(endTag==true)
System.out.println("无满足支持度项集,结束连接");
return supportedItemset;
}
剪枝的依据在于最小支持度,当该题材组合不满足最小支持度(即占电影总数的比例小于最小支持度),则会被淘汰。最小支持度的值可设置在(0,1),本项目设为0.2
对频繁1项集进行打印
频繁1项集
喜剧
剧情
爱情
战争
动作
奇幻
与期望的结果一致
6.频繁1项集->候选2项集
候选2项集中的所有项满足以下条件:
均为2个题材
来自于频繁1项集
是原始数据中的项(或子集)
private static List<List<String>> getNextCandidate(List<List<String>> frequentItemset) {
List<List<String>> nextCandidateItemset = new ArrayList<List<String>>();
for (int i = 0; i<frequentItemset.size();i++){
List tempList = frequentItemset.get(i);
HashSet hashSet = new HashSet();
HashSet tempHashSet = new HashSet();
for (int j = 0;j<tempList.size();j++){
hashSet.add(tempList.get(j));
}
int beforeSize = hashSet.size();
tempHashSet = (HashSet) hashSet.clone();
for (int k = i+1;k<frequentItemset.size();k++){
hashSet = (HashSet)tempHashSet.clone();
for (int m = 0;m<frequentItemset.get(k).size();m++){
hashSet.add(frequentItemset.get(k).get(m));
}
int afterSize = hashSet.size();
if(afterSize == beforeSize+1 && isSubsetOf(hashSet,record)==1 && isnotHave(hashSet,nextCandidateItemset)){
Iterator<String> itr = hashSet.iterator();
List<String> temphsList = new ArrayList<String>();
while(itr.hasNext()){
String Item = (String) itr.next();
temphsList.add(Item);
}
nextCandidateItemset.add(temphsList);
}
}
}
return nextCandidateItemset;
}
组合算法思想:从频繁i项集的第一项开始对频繁i项集进行扫描,使用当前项与其他项中的某一元素进行组合,则得到新项。如果新项满足下列条件,则加入候选i+1项集:
新项中有i+1个元素(意味着当前项中的元素与“某一元素”不能有重复);
新项是原始数据的项(或子集);
新项未加入候选i+1项集(不能重复加入新项);
对候选i+1项集进行输出
扫描后备选集
喜剧 爱情
喜剧 奇幻
剧情 战争
剧情 动作
爱情 奇幻
战争 动作
满足预期的结果
7.从第5步开始重复迭代,直至最后的组合不能满足最小支持度
迭代过程输出(接第6步的输出)
扫描后频繁集
喜剧 爱情
喜剧 奇幻
剧情 动作
战争 动作
扫描后备选集
喜剧 爱情 奇幻
剧情 战争 动作
无满足支持度项集,结束连接
扫描后频繁集
Apriori算法结果集
喜剧 爱情
喜剧 奇幻
剧情 动作
战争 动作
满足预期结果
完成整个算法流程。