#-*- coding: UTF-8 -*- from numpy import * def loadDataSet():#创建包含文档的训练集和各文档对应的标签列表 postinglist = [['my','dog','has','flea','problems','help','please'], ['maybe','not','take','him','to','dog','park','stupid'], ['my','dalmation','is','so','cute','I','love','him'], ['stop','posting','stupid','worthless','garbage'], ['mr','licks','ate','my','steak','how','to','stop','him'], ['quit','buying','worthless','dog','food','stupid']] classVec = [0,1,0,1,0,1] return postinglist,classVec#返回创建的文档训练集和标签列表 def createVocablist(dataSet):#创建总词汇列表,列表中元素为训练集元素之和,且每个元素只出现一次 vocabSet = set([])#创建空集 for document in dataSet:#遍历输入的文档词汇集 vocabSet = vocabSet | set(document)#取并集 return list (vocabSet) def setOfWords2Vec(vocabList,inputSet):#将某文档(词向量)中的单词以是否在总词汇列表中出现为特征,转换为数字矩阵,输入总词汇列表和待测词汇向量 returnVec = [0] * len(vocabList)#初始化返回矩阵,创建与参考词汇集等长的0矩阵 for word in inputSet:#遍历待测词汇列表 if word in vocabList:#如果待测词汇列表中的单词出现在总词汇列表中 returnVec[vocabList.index(word)] = 1 #获取该单词在参考词汇列表中的索引值,并传递给返回矩阵,使得返回矩阵与该单词对应的位置置1 else: print "the word: %s is not in my Vocabulary!" % word return returnVec listOPosts,listClasses = loadDataSet() myVocabList = createVocablist(listOPosts) #print (myVocabList) #print (setOfWords2Vec(myVocabList,listOPosts[0])) #print (setOfWords2Vec(myVocabList,listOPosts[3])) def trainNB0(trainMatrix,trainCategory):#输入为由文档矩阵转换成的数字矩阵!!!由i个行向量(文档)组成,格式见p60,另一输入参数为与输入文档矩阵对应的类别列表 numTrainDocs = len(trainMatrix)#获取输入数字矩阵中列表个数(文档个数) numWords = len(trainMatrix[0])#获取列表中单词个数,即列表中元素个数 pAbusive = sum(trainCategory) / float(numTrainDocs)#计算类别概率,sum函数对类别列表元素求和,所得值为非正常的文档(列表)个数(即将其中的1求和) p0Num = ones(numWords)#向量的初始化(拉普拉斯平滑,初始值置1,长度与文档中元素个数相同) p1Num = ones(numWords) p0Denom = 2.0#分母初始化。即正常文档中出现的非正常词汇的总数(这里做拉普拉斯平滑初始值为 类别数 * 1) p1Denom = 2.0#非正常文档中出现的非正常词汇的的总数(拉普拉斯平滑) for i in range(numTrainDocs):#遍历输入矩阵,对于其中的列表 if trainCategory[i] == 1:#类别为1,即正常的文档 p1Num += trainMatrix[i]#第i 个文档的向量加到p1,最终得到各特征出现的总次数的数字矩阵 p1Denom += sum(trainMatrix[i])#将第i 个文档中非正常词汇的个数加入到总词汇数中,得到分母 else:#二分类中,除去正常文档,剩下的为非正常文档 p0Num += trainMatrix[i]#类似正常文档操作 p0Denom += sum(trainMatrix[i]) p1Vect = log(p1Num / p1Denom)#计算条件概率,由于概率值很小且logx与x同增减,故可以用log函数将条件概率放大 p0Vect = log(p0Num / p0Denom) return p0Vect,p1Vect,pAbusive#返回正常文档类各特征的的条件概率,非正常文档各特征的条件概率,非正常文档的概率 def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):#预测待分类文档所属的类别,输入为p0类别各特征条件概率向量(列表),p1类别各特征的条件概率向量,非正常文档噗p1的概率 p1 = sum(vec2Classify * p1Vec) + log(pClass1)#这里需要注意的是使用loga + logb = logab,即原式为概率相乘,使用log函数放大之后等价于概率相加 p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)#二分类,两类概率相加为1 if p1 > p0:#比较概率值,返回比较大的概率所对应的类别 return 1 else: return 0 def testingNB():#封装函数 listOPosts,listClasses = loadDataSet()#获得数据集,类别标签向量列表 myVocabList = createVocablist(listOPosts)#创建总词汇列表 trainMat = [] for postinDoc in listOPosts: trainMat.append(setOfWords2Vec(myVocabList,postinDoc))#将各文档转换为数字向量并放入列表中,构成训练集数字矩阵 p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))#获得p0类别中各概率矩阵,p1类别中各概率矩阵,以及非正常文档的概率,(输入为矩阵!!!) testEntry = ['love','my','dalmation']#测试词汇向量(文档) thisDoc = array(setOfWords2Vec(myVocabList,testEntry))#将测试词汇向量转换为与参考向量等特征数的数字向量,并转换为numpy数组 print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)#输出结果,测试向量的类别 testEntry = ['stupid','garbage']#测试词汇向量 thisDoc = array(setOfWords2Vec(myVocabList,testEntry))# print testEntry,'classfied as: ',classifyNB(thisDoc,p0V,p1V,pAb) print testingNB() def textParse(bigString):#切分文当,输入为一个包含数字,字母,符号字符的字符串 import re ListOftokens = re.split(r'\W*',bigString)#正则表达式 return [tok.lower() for tok in ListOftokens if len(tok) > 2]#过滤掉长度小于3的字符 def spamTest(): docList = []#初始化中间变量 classList= []#初始化类别列表 fullText = []#初始化总词汇列表 for i in range(1,26):#遍历ham和spam中的25个文件 wordList = textParse(open('email/spam/%d.txt' % i).read())#读取非正常文档的第i 个文件,并得到词汇向量,放入列表中 docList.append(wordList)#将得到的词汇向量放入大的训练集列表 fullText.extend(wordList)#每次都将得到的字符放入总词汇列表,fulltext列表包含的元素为词汇(可以重复) classList.append(1)#打开的是spam文件,每打开一个 不正常文档数的类别加1 wordList = textParse(open('email/ham/%d.txt' % i).read())#读取正常文档的第i个文件,并切分后得到词汇向量放入列表 docList.append(append(wordList))#将词汇向量放入文档列表 fullText.extend(wordList)#得到的所有词汇加入到总词汇列表 classList.append(0)#打开的是ham文件,每在文档列表中添加一个词汇向量,类别0加一 vocabList = createVocablist(docList)#得到唯一的总词汇列表 trainingSet = range(50)#生成一个元素为[0,1,2,L,50]的列表(索引列表) testSet = []#定义空的测试集 for i in range(10):#选出10个词汇向量作为测试向量组成测试集 randIndex = int(random.uniform(0,len(trainingSet)))#numpy中random函数的uniform功能是从一个均匀分布[low,high)中随机采样,得到一个随机索引值 testSet.append(trainingSet[randIndex])#将得到的值作为从训练集中选取好的测试向量的索引值并添加到测试集列表 del(trainingSet[randIndex])#为了避免重复,将选出的作为测试向量的索引值从训练集中删除,防止二次选出和在交叉验证中被选入训练集 trainMat = []#新训练集初始化 trainClasses = []#新训练集中词汇向量对应表标签列表的初始化 for docIndex in trainingSet:#遍历训练集的索引值(剩余40个,测试集已经取出) trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))#将旧的训练集中的词汇向量全部取出,转化成为数字向量 trainClasses.append(classList[docIndex])#对应的标签添加到类别列表 p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))#将训练集转换成的数字列表和对应标签列表变成numpy数组,作为输入 errorCount = 0#错分类数量初试化 for docIndex in testSet:#遍历测试词汇集列表 wordVector = setOfWords2Vec(vocabList,docList[docIndex])#将测试集词汇向量转换成数字向量 if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:#如果测试得到的类别的与实际类别不一致,错分类数目加1 errorCount += 1 print 'the error rate is: ',float(errorCount)/len(testSet)#计算错误率 #使用朴素贝叶斯分类器从个人广告中获取区域倾向 def calcMostFreq(vocabList,fullText):#找出出现频数最多的词汇 import operator freqDict = {}#定义新的频数字典 for token in vocabList: freqDict[token] = fullText.count(token)#对于每个cocabList中的单词,计算其在总词汇列表中出现的次数,比作为值赋值给对应的键 sortedFreq = sorted(freqDict.iteritems(),key=operator.itemgetter(1),reverse=True)#对得到的字典按降序排序 return sortedFreq[:30]#取出现频数排名前30的单词 def localWords(feed1,feed0): import feedparser docList = [] classList = [] fullText = [] minLen = min(len(feed1['entries']),len(feed0['entries']))#比较两个类别文件所含文件个数,并取最小的个数作为两类文件分别包含的文件个数 for i in range(minLen):#此for循环将两类的每个字符串文档切分并放入词汇集列表,格式为[[],L,[]].其中每个字符串文档对应的类别放入类别列表 wordList = textParse(feed1['entries'][i]['summary']) docList.append(wordList) fullText.extend(wordList) classList.append(1) wordList = textParse(feed0['entries'][i]['summary']) docList.append(wordList) fullText.extend(wordList) classList.append(0) vocabList = createVocablist(docList)#得到独一无二的词汇列表,即参考词汇集列表 top30Words = calcMostFreq(vocabList,fullText)#选出出现频数最高的前30个词汇 for pairW in top30Words:#遍历频数列表,如果其中的单词出现在参考集词汇列表中,将该词删掉 if pairW[0] in vocabList: vocabList.remove(pairW[0]) trainingSet = range(2 * minLen)#生成与词汇集列表文档数等长的列表 testSet = [] for i in range(20):#生成20个随机索引 randIndex = int(random.uniform(0,len(trainingSet))) testSet.append(trainingSet[randIndex])#将随机索引对应的训练集中的文档添加到测试集 del(trainingSet[randIndex])#将添加到测试集中的文档从训练集中删去 trainMat = [] trainingClassess = [] for docIndex in trainingSet:#训练集剩下的文档作为新的训练集,将其中的文档转换成数字向量(词袋模型) trainMat.append(bagofwords2VecMN(vocabList,docList[docIndex])) trainingClassess.append(classList[docIndex]) p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainingClassess))#将训练集和训练列表转换成numpy数组,训练得到模型 errorCount = 0#初始化错分类数量 for docIndex in testSet: wordVector = bagOfWords2VecMN(vocabList,docList[docIndex])#将测试集中的向量转换成数字向量(词袋模型) if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:#将分类器得到的分类结果与文档的实际类别做对比,如果不等,错误数加1 errorCount += 1 print 'the error rate is:',float(errorCount) / len(testSet)#计算错误率 return vocabList,p0V,p1V #选出出现频率较高的词汇 def getTopWords(ny,sf): import operator vocabList,p0V,p1V = localWords(ny,sf) topNY = [] topSF = [] for i in range(len(p0V)):#设定阈值,选出大于该阈值的概率 if p0V[i] > -6.0: topSF.append((vocabList[i],p0V[i]))#将大于该阈值的词汇和概率值以元祖的方式添加到一个列表中 if p1V[i] > -6.0: topSF.append((vocabList[i],p1V[i])) sortedSF = sorted(topSF,key=lambda pair:pair[1],reverse = True)#降序排序 print "SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**" for item in sortedSF: print item[0] sortedNY = sorted(topNY,key=lambda pair:pair[1],reverse=True) print "NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**" for item in sortedNY: print item[0]