【资源聚合平台】6/7日工作日志
邵长旭
今天的工作是做了前几天说的label问题:
为以后知识图谱系统做铺垫,首先在修改Knowledge表结构:
class AddLabelToKnowledge < ActiveRecord::Migration[5.0]
def change
add_column :knowledges, :label, :string # 为以后扩展留下余地 标签
end
end
在view中,写输入框和添加按钮:
<div class="row-fluid">
<div class="span8 offset2" style="margin-top:10px">
<span class="span3">标签(最多添加5个):</span>
<div id="label_div" class="span8">
<%= render :partial => "knowledges/label_show",:locals => {:label_string => @blog.label } %>
</div>
</div>
</div>
<div class="row-fluid"> <div class="span8 offset2" id="input_label" style="margin-top:10px"> <%= f.text_field :label,id:"label_for_form" %> <input id="labelInput" type="text" style="width:100px"/> <button id="label_sub_button" type="button" onClick="label_show_click()">添加</button> <%if @blog.label.nil?||@blog.label.empty?%> <p style="display:none" id="label_count" value="0"></p> <p style="display:none" id="label_string" value=""></p> <%else%> <p style="display:none" id="label_count"><%aaa@qq.com(";").size()%></p> <p style="display:none" id="label_string" value="<%aaa@qq.com%>"></p> <%end%> </div> </div></pre><p>其中label_show局部渲染如下:</p><pre class="html"><%if !label_string.nil?%> <%label_list = label_string.split(";")%> <div> <%for label in label_list%> <div style="display:inline"> <span class="label label-info"><%=label%></span> <!--<button class="btn btn-small btn-danger del_btn" onClick="del_label()" style="margin-right:15px;height:20px;width:15px;padding:0px" type="button"></button>--> </div> <%end%> </div>
<%end%>
下面写按钮的响应:
function label_show_click(){
var label = $("#labelInput").val();
if(label != ""){
var count = Number($("#label_count").val())+1;
var label_string = $("#label_string").html();
$("#label_count").val(count);
if(count == 1){
label_string = label;
}
else if(count == 5){
label_string = label_string+";"+label;
$("#input_label").css("display","none");
}
else{
label_string = label_string+";"+label;
}
$("#label_string").html(label_string);
$("#label_for_form").val(label_string);
}
$.ajax({
url:"../knowledges/render_label",
type: 'POST',
data: {label_string: label_string}
});
}
拿到各个相关值,将计算值加一、然后将关键词连成一个string对象,中间利用;隔开,以便分析使用时提取,然后将修改好的值设回对象,利用ajax发送请求局部渲染页面。
效果如下:
梁惠欣
用近似真实数据的方式重写了数据生成文件seed的大部分内容,并重新整理了seed文件的格式。
有兴趣可以看一看。
王子悦
上回说到可能有好多人匹配不到学习路径,需要一个其他内容的推荐,所以今天就做了这个:
生成部分的主要逻辑是查询用户最近的学习记录,对记录中的每一个项,查询它到达知识图谱中其他项的支持度,并且分别累加,这样就得到了用户最近学习序列到图中所有项的支持度,按降序排列,筛选前n个且不在最近学习序列中的元素作为推荐事件推荐给用户,实现如下:
/**
* 根据有向图生成推荐的项(事件、课程)
* @param recentSeq 用户的最近访问序列
* @param recNum 要推荐的项数
* @return 包含字符串代表的推荐项的向量
*/
private Vector<String> generateRecItemsFromGraph(Vector<Vector<String>> recentSeq, int recNum) {
Vector<String> recItems = new Vector<>();
int itemCount = ap.itemList.size();
Map<String, Integer> itemToIndex = ap.itemMaps;
// 对最近项集中的项的相关联项计数
int[] allItems = new int[itemCount];
Vector<String> recentItems = new Vector<>();
// 对最近项集中每个项集
for (Vector<String> items : recentSeq) {
// 每个项集中的每个项
for (String item : items) {
// 添加到列表中
recentItems.add(item);
}
}
for (String item : recentItems) {
// 从图中找到各个其他项的支持度 累加
int recentItemIndex = itemToIndex.get(item);
for (int assItemIndex = 0; assItemIndex < itemCount; assItemIndex++) {
if (recentItemIndex == assItemIndex) {
continue;
}
allItems[assItemIndex] += graph[recentItemIndex][assItemIndex];
}
}
int[] indexs = sortedIndexByValue(allItems);
for (int i = 0; i < indexs.length && i < recNum; i++) {
String recItem = ap.itemList.get(indexs[i]);
// Vector<Vector<String>> allUserItems = new Vector<>();
// for (Vector<String> vector : sr.data) {
//
// }
if (recentItems.contains(recItem)) {
continue;
}
recItems.add(recItem);
}
return recItems;
}
/**
* 把数组内数据按降序排列 并返回每个数对应的原下标
* @param nums 要排序的数组
* @return 数组存放的排序后的下标
*/
private int[] sortedIndexByValue(int[] nums) {
int[] indexs = new int[nums.length];
for (int i = 0; i < indexs.length; i++) {
indexs[i] = i;
}
for (int i = 0; i < indexs.length; i++) {
for (int j = 1; j < indexs.length-i; j++) {
if (nums[j-1] > nums[j]) {
int temp;
temp = nums[j-1];
nums[j-1] = nums[j];
nums[j]=temp;
// int temp;
temp = indexs[j-1];
indexs[j-1] = indexs[j];
indexs[j]=temp;
}
}
}
return indexs;
}
然后是显示,这部分并没有什么技术含量了,只是把之前各个部分整合起来,然后写的规整一点。其中稍微研究了一下的是有一个序列和子序列的问题,就是有可能会有【【1】【2】【1,2】】这种情况,这种其实是有意义的,表示的是先学1,再学2,然后同时复习1,2,但是说实话直接这么显示出来看起来有点蠢,所以想了个算法把频繁序列里这种东西都去掉了,算法在代码注释中:
package aprioriAll;
import java.util.Vector;
public class RecomendInfo {
static String noSeqInfo = "抱歉,系统并没有匹配到合适的学习路径。";
static String hasSeqInfo = "系统根据您最近的学习内容,为您推荐的学习路径是:\n";
boolean hasSeq = false;
String seqInfo = "";
String recItemInfo = "";
public String generateSeqInfo(Vector<Vector<String>> seq,Vector<Vector<String>> recentItems) {
hasSeq = true;
seqInfo = seqToString(seq)
+ ",\n其中您学过了:\n"
+ seqToString(recentItems)
+ "\n请查漏补缺,并进行接下来的学习。"
+ "";
return seqInfo;
}
public String getSeqInfo() {
if (hasSeq) {
return hasSeqInfo + seqInfo;
}else {
return noSeqInfo;
}
}
private String seqToString(Vector<Vector<String>> longseq) {
// 不修正
// Vector<Vector<String>> seq = longseq;
Vector<Vector<String>> seq = refineSeq(longseq);
String result = "";
for (int i = 0; i < seq.size()-1; i++) {
result += (seq.get(i).toString() + " ==> ");
}
result += (seq.lastElement().toString());
return result;
}
/**
* 修正序列中多个子项之后跟着一个和项集的问题 虽然这是有意义的(学习顺序) 但是看起来有点蠢 可以不采用
* @param seq 需要修正的序列
* @return 修正后的序列
*/
private Vector<Vector<String>> refineSeq(Vector<Vector<String>> seq) {
// 被抛弃的算法 找每一个事件集是否被其他事件集包含
// Vector<Vector<String>> tempSeq = new Vector<>();
// for (int i = 0; i < seq.size(); i++) {
// Vector<String> curItemSet = seq.get(i);
// boolean containInOther = false;
// for (int j = i+1; j < seq.size(); j++) {
// Vector<String> testItemSet = seq.get(j);
// if (testItemSet.containsAll(curItemSet)) {
// containInOther = true;
// break;
// }
// }
// if (!containInOther) {
// tempSeq.add(curItemSet);
// }
// }
// return tempSeq;
// 更快的算法 从后向前检查是否当前项集包含前一个
Vector<Vector<String>> tempSeq = new Vector<>(seq);
for (int i = tempSeq.size()-1; i > 0; i--) {
Vector<String> latter = tempSeq.get(i);
Vector<String> former = tempSeq.get(i-1);
if (latter.containsAll(former)) {
tempSeq.remove(i-1);
}
}
return tempSeq;
}
public String generateRecItemInfo(Vector<String> recItems) {
// TODO Auto-generated method stub
if (recItems.isEmpty()) {
recItemInfo = "暂无推荐信息,请*探索!";
} else {
recItemInfo = ""
+ "系统根据您最近的学习内容,为您推荐的学习内容是:\n"
+ recItems
+ "";
}
return recItemInfo;
}
public String getRecItemInfo() {
return recItemInfo;
}
}
/**
* 生成推荐并显示
*/
private void generateRecommend() {
// TODO Auto-generated method stub
sr = new SequenceRecommender(ap.resultSet);
Vector<Integer> freqSeq = sr.matchSequence(ap.data);
System.out.println(freqSeq);
// 对每个用户
for (int i = 0; i < freqSeq.size(); i++) {
RecomendInfo ri = new RecomendInfo();
if (freqSeq.get(i) == -1) {
ri.hasSeq = false;
Vector<String> recItems = generateRecItemsFromGraph(sr.data.get(i),2);
ri.generateRecItemInfo(recItems);
} else {
ri.hasSeq = true;
ri.generateSeqInfo(ap.resultSet.get(freqSeq.get(i)), sr.data.get(i));
Vector<String> recItems = generateRecItemsFromGraph(sr.data.get(i),2);
ri.generateRecItemInfo(recItems);
}
outputRecommend(ri);
}
}
private void outputRecommend(RecomendInfo ri) {
System.out.println("==============================");
System.out.println(ri.getSeqInfo());
System.out.println(ri.getRecItemInfo());
System.out.println("==============================");
}
最后的输出结果大概是这样的:
[Java, 高等数学, 数据结构, 数据库, 计算机网络, C++, 智能软件, 线性代数, 非关系型数据库]
8 2 4 2 2 0 0 0 0
1 14 4 2 1 0 0 4 1
3 3 18 5 2 1 0 2 1
1 1 1 14 6 1 1 2 1
0 0 0 6 12 0 0 0 0
0 0 1 1 0 0 1 0 0
0 0 0 0 0 1 0 0 0
0 5 1 1 0 0 0 12 1
0 0 0 1 0 0 0 1 0
[-1, -1, -1, 6, -1]
抱歉,系统并没有匹配到合适的学习路径。
系统根据您最近的学习内容,为您推荐的学习内容是:
[智能软件, C++]
==============================
抱歉,系统并没有匹配到合适的学习路径。
系统根据您最近的学习内容,为您推荐的学习内容是:
[Java, 高等数学]
==============================
抱歉,系统并没有匹配到合适的学习路径。
系统根据您最近的学习内容,为您推荐的学习内容是:
[Java]
==============================
系统根据您最近的学习内容,为您推荐的学习路径是:
[Java] ==> [高等数学] ==> [数据库, 计算机网络],
其中您学过了:
[高等数学] ==> [数据库, 计算机网络]
请查漏补缺,并进行接下来的学习。
系统根据您最近的学习内容,为您推荐的学习内容是:
[C++]
==============================
抱歉,系统并没有匹配到合适的学习路径。
系统根据您最近的学习内容,为您推荐的学习内容是:
[C++, 智能软件]
==============================当然了,这只是输出到控制台的,也可以调整一下打到json里,这样就可以接到网页上去了。还是用的之前的测试数据集,所以可以看到依然有昨天说的那个问题,很多都匹配不到序列,但是已经有针对各自的推荐了。让我比较惊喜的是实验结果看起来并不是单纯的推荐数据库里频繁的东西,的确每个人都不太一样,而且有的人因为已经学的多,也的确能推荐到比较冷门的东西,比如智能软件,看来这几天编的算法还有点成效。
上一篇: 【资源聚合平台】6/8-9日工作日志
下一篇: 【资源聚合平台】6/5日工作日志