写个菜鸟的入门级读物:如何利用weka进行文本聚类(一)(老鸟勿进,因为你会失望的。。。)
作者:finallyliuyu(转载请注明作者和出处)
哦,(一)忘记附上了去掉字符串首尾空格的小函数了,现在补上。
{
str.erase(0,str.find_first_not_of(val));
str.erase(str.find_last_not_of(val)+val.size());
}
在(一)中,我们已经建立了稳定词袋子模型,这个词袋子模型可是个宝贝家伙,我们一定要小心维护。为什么呢?因为 特征词选择模块,VSM(文档向量模型)的建立模块,我们都要用到它。另外我们也说了这个这个东西很占内存,所以我们不能在特征词选择的时候调用一次这个函数,然后在建立VSM模型的时候再调用一次这个函数,这样会影响程序的运行速度,所以最好的方法就是把这个宝贝家伙保存到硬盘上。序列化。对于序列化这个复杂的map,我问过嗷嗷,也问过同学,有人建议我用boost库,但是我觉得最简单的方法还是自己按某种规则自己序列化到硬盘,然后解序列化到内存。
至于格式,我是这么定义的。
首行,词的总个数
然后
词1的text
词1的DF,
然后是vector<pair<int,int> >
...
以此类推。上个图给大家看看就一目了然了。
下面给出序列化和反序列化的代码
void save(map<string,vector<pair<int,int> > >&mymap)
{ ofstream outfile("f:\\mydict.dat",ios::binary);
outfile<<mymap.size()<<endl;
map<string,vector<pair<int,int> > >::iterator it;
for (it=mymap.begin();it!=mymap.end();it++)
{ outfile<<it->first<<endl;
vector<pair<int,int>>::iterator subit;
outfile<<it->second.size()<<endl;
for(subit=(it->second).begin();subit!=(it->second).end();++subit)
{
outfile<<subit->first<<" "<<subit->second<<" "<<";"<<" ";
}
outfile<<endl;
}
//outfile.write((char *)&mymap,sizeof(mymap));
outfile.close();
}
{
//设置代码页为简体中文,936是简体中文的代码页。
std::locale loc1 = std::locale::global(std::locale(".936"));
{
// 在这里使用std::ifstream 或者 std::fstream
ifstream infile("F:\\mydict.dat",ios::binary);
int lenMyMap;//保存词典长度
int lenVector;//保存每个词出现的文章数目
string key;//保存读出的map的键值
int articleId;//文章标号
int count;//在该文章中刚出现的数目
string comma;
string semicolon;
infile>>lenMyMap;
while(!infile.eof())
{
infile>>key;
infile>>lenVector;
vector<pair<int,int> >temp;
for (int i=0;i<lenVector;i++)
{
infile>>articleId>>count>>semicolon;
temp.push_back(make_pair(articleId,count));
}
mymap[key]=temp;
}
infile.close();
}
std::locale::global(std::locale(loc1));
}
void print(map<string,vector<pair<int,int> > >&mymap)
{
cout<<mymap.size()<<endl;
map<string,vector<pair<int,int> > >::iterator it;
for (it=mymap.begin();it!=mymap.end();it++)
{ cout<<it->first<<endl;
vector<pair<int,int>>::iterator subit;
cout<<it->second.size()<<endl;
for(subit=(it->second).begin();subit!=(it->second).end();++subit)
{
cout<<subit->first<<','<<subit->second<<";";
}
cout<<endl;
}
}
下面开始介绍特征词选择模块:
首先是几个辅助函数,也就是用到泛型算法中的谓词函数
{
return pair1.second>pair2.second;
}
bool cntAssist(const pair<string,int> &pair1)
{
return pair1.second<=100;
}
void DFcharicteristicWordSelection(map<string,vector<pair<int,int>>> &mymap,int DFthreshold)
{ int finalKeyWordsCount=0;//计算共取了多少个关键词
vector<pair<string,int> >tempvector;
for(map<string,vector<pair<int,int>>>::iterator it=mymap.begin();it!=mymap.end();++it)
{
tempvector.push_back(make_pair(it->first,(it->second).size()));
}
stable_sort(tempvector.begin(),tempvector.end(),isLonger);
ofstream outfile("F:\\keywordsinfo.txt");
for(vector<pair<string,int> >::iterator it=tempvector.begin();it!=tempvector.end();it++)
{
if(it->second>=DFthreshold)
{
//outfile<<it->first<<" "<<it->second<<endl;
outfile<<it->first<<endl;
finalKeyWordsCount++;
}
}
outfile.close();
cout<<"最后共选择特征词"<<finalKeyWordsCount<<endl;
cout<<"by the way,DFthreshold equals"<<DFthreshold<<endl;
}
下面给出特征词文件的部分截图
有一点要提醒大家哈,特征词选择模块执行之前,要load 词袋子模型到内存哈。
下面我们开始给出建立VSM模型的代码。
建立VSM模型需要两个数据,词袋子就是前面所说的那个map,以及刚刚选出的特征词。load map的函数已经给出,下面给出load特征词集合的函数
vector<string> GetFinalKeyWords()
{
vector<string>myKeys;
ifstream infile("F:\\keywordsinfo.txt");
while(!infile.eof())
{
string temp;
infile>>temp;
if(temp!="")
{
myKeys.push_back(temp);
}
}
return myKeys;
}
方法就是:对于每一篇文章,检查我们选出的特征词集合,对于每个特征词,查map,看看里面有没有该篇文章的id出现过,以及该特征词在该篇文章中出现过几次。
未完待续。。。