精华内容
下载资源
问答
  • python数据挖掘系列教程 今天我们使用python实现一个网站搜索引擎。主要包含两个部分。网站数据库的生成、搜索引擎。其中搜索引擎部分我们使用单词频度算法、单词距离算法、外部回值算法、链接文本算法、pagerank...
    
        ad1.jpg
    

    全栈工程师开发手册 (作者:栾鹏)

    python数据挖掘系列教程

    今天我们使用python实现一个网站搜索引擎。主要包含两个部分。网站数据库的生成、搜索引擎。其中搜索引擎部分我们使用单词频度算法、单词距离算法、外部回值算法、链接文本算法、pagerank算法和神经网络学习等6种算法来实现搜索排名。

    我们这里将http://blog.csdn.net/luanpeng825485697站点下的所有网站当做一个小型服务器。对该网站域名下的网页进行搜索引擎设计。

    由于需要获取博客文章的单词作为每篇博客的特征属性,并且我们的博客都是中文,所以在学习获取博客数据前,需要学习结巴中文分词库的使用。参考:
    http://blog.csdn.net/luanpeng825485697/article/details/78757563

    在我们的代码中,我们将模型固话在sqlite数据库中。所以在学习获取博客数据前,还需要学习sqlite3库的使用,参考:
    http://blog.csdn.net/luanpeng825485697/article/details/78361168

    由于我们会将所有的数据都存储在数据库中以便今后常用,所以代码中会有部分sqlite数据库的相关代码操作。

    调试环境python3.6

    gitup网址:https://github.com/626626cdllp/data-mining/tree/master/Search-Engines

    第一部分、爬取网站生成网站数据库

    这一部分,我们的目标是将域名下的所有网页的信息生成几个数据集。

    这里写图片描述

    sqlite数据库默认的主键索引名为rowid,mysql数据库默认的主键索引名为id。

    第一张表urllist、保存的是已经url列表,字段hascrawl表示该网页是否已经爬取过。
    第二张表wordlist、保存的是整个网站的单词列表,不包含重复单词。
    第三张表wordlocation、保存的是单词在文档中所处位置的列表。单词的位置为单词在该文档内容所形成的单词列表中的索引。
    第四张表link、保存了两个urlid,指明从一张表到另一张表的跳转关系。
    第五张表linkwords、则利用wordid和linkid记录链接的描述。

    下面我们就来爬取整个网站,生成这五张表,这里我们使用urllib获取响应,所以不能爬取js动态加载的网站。如果想爬取动态加载的网站,可以参考http://blog.csdn.net/luanpeng825485697/article/details/78436963

    将下面的代码存储成spyder.py

    # 根据连接爬取中文网站,获取标题、子连接、子连接数目、连接描述、中文分词列表,
    import urllib
    from bs4 import BeautifulSoup
    import bs4
    import sqlite3
    import os
    import jieba   #对中文进行分词
    import traceback
    
    # 分词时忽略下列词,即不为这些单词建立数据库
    biaodian = '[!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~]+ ,。?‘’“”!;:\r\n、()…'     #所有的标点符号
    ignorewords = list(set(biaodian))   #去重
    ignorewords.append('\r\n')   #另外添加一个忽略的项
    
    
    # 定义爬虫类。获取链接的标题、描述、分词、深度
    class crawler:
        def __init__(self,dbname):
            self.urls={}              #创建爬取列表
            self.con = sqlite3.connect(dbname)
            self.curs = self.con.cursor()
            try:
                self.createindextables()
            except:
                pass
    
        def __del__(self):  # 关闭数据库
            self.curs.close()
            self.con.close()
    
        def dbcommit(self):  # 保存数据库
            self.con.commit()
    
        # 创建数据库表
        def createindextables(self):
            self.curs.execute('create table urllist(url,hascrawl)')  # 创建url列表数据表
            self.curs.execute('create table wordlist(word)')  # 创建word列表数据表
            self.curs.execute('create table wordlocation(urlid,wordid,location)')  # 创建url-word-location数据表(每个链接下出现的所有单词和单词出现的位置)
            self.curs.execute('create table link(fromid integer,toid integer)')  # 创建url-url数据表(当前链接和目标链接对)
            self.curs.execute('create table linkwords(wordid,linkid)')  # 创建word-url数据表(链接描述)
            self.curs.execute('create index wordidx on wordlist(word)')  # 创建索引
            self.curs.execute('create index urlidx on urllist(url)')  # 创建索引
            self.curs.execute('create index wordurlidx on wordlocation(wordid)')  # 创建索引
            self.curs.execute('create index urltoidx on link(toid)')  # 创建索引
            self.curs.execute('create index urlfromidx on link(fromid)')  # 创建索引
            self.dbcommit()
    
    	#获取一个dom内的所有单词。soup为dom元素
        def getword(self,soup):
            # 获取每个单词
            text=self.gettextonly(soup)   #提取所有显示出来的文本
            allword=self.separatewords(text)  #使用分词器进行分词
            return allword
    
        # 根据一个dom元素提取文字(不带标签的)。由外至内获取文本元素。style和script内的文字忽略
        def gettextonly(self,soup):
            v=soup.string
            if v==None:
                c=soup.contents   # 直接子节点的列表,将<tag>所有儿子节点存入列表
                resulttext=''
                for t in c:
                    if t.name=='style' or t.name=='script':   #当元素为style和script和None时不获取内容
                        continue
                    subtext=self.gettextonly(t)
                    resulttext+=subtext+'\n'
                return resulttext.strip()
            else:
                if isinstance(v,bs4.element.Comment):   #代码中的注释不获取
                    return ''
                return v.strip()
    
        # 使用结巴分词,同时去除标点符号
        def separatewords(self,text):
            seg_list = jieba.cut(text, cut_all=False)  #使用结巴进行中文分词
            allword = []
            for word in seg_list:
                if word not in ignorewords:
                    allword.append(word)
                    # print(allword)
            return allword
    
        #爬虫主函数
        def crawl(self,url,host):
            if url in self.urls and self.urls[url]['hascrawl']:return  # 如果网址已经存在于爬取列表中,并且已经爬取过,则直接返回
            try:
                if url in self.urls:    
                    if self.urls[url]['hascrawl']:return
                    else: self.urls[url]['hascrawl']=True  # 如果网址已存在,但是并没有爬取过,就设置hascrawl为True
                else:
                    self.urls[url]={}
                    self.urls[url]['hascrawl']=True
                response=urllib.request.urlopen(url)   #获取响应流
                text = str(response.read(), encoding='utf-8')   #转化为utf-8编码的字符串
                soup = BeautifulSoup(text, 'html.parser')   #解析成dom树
    
                links = soup('a')  #获取所有链接
                for link in links:
                    if ('href' in dict(link.attrs)):   #如果链接元素存在href属性
                        newurl = urllib.parse.urljoin(url, link['href'])  #获取链接的绝对网址
                        if not host in newurl: continue  # 非服务范围网址不爬取,不记录
                        if newurl == url: continue  # 如果网址是当前网址,不爬取,不记录
                        if newurl.find("'") != -1: continue  # 包含'的链接不爬取,不记录
                        newurl = newurl.split('#')[0]  # 去掉#后的数据部分
                        if newurl[0:4] == 'http':  # 只处理http协议
                            if newurl not in self.urls:  # 将链接加入爬取列表
                                self.urls[newurl] = {}
                                self.urls[newurl]['hascrawl'] = False 
                            # 添加链接描述
                            linkText = self.gettextonly(link).strip()  # 获取链接的描述
                            self.addlinkref(url, newurl, linkText)  # 添加链接跳转对和链接描述
    
                self.addtoindex(url, soup.body)  # 创建链接列表库和链接-分词库
                self.dbcommit()    #保存
                return True
            except:
                traceback.print_exc()
                return False
                # print("Could not parse page %s" % url)
    
    
        # 添加链接列表库和链接-分词索引
        def addtoindex(self, url, soup):
            # 查询-增加网址获得urlid
            urlid = self.get_add_id('urllist', 'url', url)
            # 提取所有单词
            allword = self.getword(soup)  # 提取所有显示出来的文本
            print(allword)
            # 将每个单词与该url关联,写入到数据库
            index = 0
            for word in allword:
                index += 1
                # 查询-增加单词获得wordid 
                wordid = self.get_add_id('wordlist', 'word', word)
                self.curs.execute("insert into wordlocation(urlid,wordid,location) values (%d,%d,%d)" % (urlid, wordid, index))
    
        # 添加链接跳转对,和链接-描述文本。
        def addlinkref(self, urlFrom, urlTo, linkText):
            words = self.separatewords(linkText)
            fromid = self.get_add_id('urllist', 'url', urlFrom)   #参数:表名、列名、值
            toid = self.get_add_id('urllist', 'url', urlTo)   #参数:表名、列名、值
            if fromid == toid: return
            cur = self.curs.execute("insert into link(fromid,toid) values (%d,%d)" % (fromid, toid))
            linkid = cur.lastrowid
            for word in words:
                wordid = self.get_add_id('wordlist', 'word', word)   #参数:表名、列名、值
                self.curs.execute("insert into linkwords(linkid,wordid) values (%d,%d)" % (linkid, wordid))
    
        # 辅助函数,用于获取数据库中记录的id,并且如果记录不存在,就将其加入数据库中,再返回id
        def get_add_id(self, table, field, value):
            command = "select rowid from %s where %s='%s'" % (table, field, value)
            cur = self.curs.execute(command)
            res = cur.fetchall()
            if res == None or len(res) == 0:
                cur = self.curs.execute("insert into %s (%s) values ('%s')" % (table, field, value))
                return cur.lastrowid
            else:
                return res[0][0]   #返回第一行第一列
    
    

    上面就完成了一个基本的爬虫类,当然今天我们还会在这个类中添加一些功能,以适应更加丰富的搜索。

    下面我们就可以借用这个类来爬取一个网站了。

    # 爬取指定域名范围内的所有网页,beginurl为开始网址,host为根网址
    def crawlerhost(beginurl,host,dbname):
        mycrawler = crawler(dbname)      #定义爬虫对象
        mycrawler.crawl(beginurl,host)  #爬取主页
        for url in list(mycrawler.urls.keys()):
            print(url)
            mycrawler.crawl(url, host)  # 爬取子网页
           
        # 获取pageRank数据
        # mycrawler.calculatepagerank()
    

    爬取部分算是完成了。下面就可以来测试一下了。

    # 读取数据库信息,检验是否成功建立了搜索数据库
    def readdb(dbname):
        con = sqlite3.connect(dbname)
        curs = con.cursor()
        command = "select fromid,toid from link"   #'select * from link'
        cur = curs.execute(command)
        res = cur.fetchall()
        allurl=[]
        if res != None and len(res) > 0:
            for row in res:
                print(row)
                command = "select url from urllist where rowid=%d" % row[0]
                fromurl = curs.execute(command).fetchall()[0][0]
                command = "select url from urllist where rowid=%d" % row[1]
                tourl = curs.execute(command).fetchall()[0][0]
                # print(fromurl,tourl)
                if fromurl not in allurl:
                    allurl.append(fromurl)
                if tourl not in allurl:
                    allurl.append(tourl)
    
    
    
    
    
    if __name__ == '__main__':
        # 爬取服务器建立数据库
        url = 'http://blog.csdn.net/luanpeng825485697'
        # if os._exists('csdn.db'):
        #     os.remove('csdn.db')    #删除旧的数据库
        crawlerhost(url, url,'csdn.db')
    
        #读取数据库
        readdb('csdn.db')
    

    第二部分、搜索

    这一部分我们根据用户的输入,从数据库中获取相关的链接集。在下一部分链接集进行排序使用。

    我们为搜索引擎新建一个模块,命名为searchengine.py

    先引入一些必要的模块和变量

    # 搜索和排名
    import urllib
    from bs4 import BeautifulSoup
    import re
    import sqlite3
    import nn
    import os
    import spyder   #获取爬虫数据集
    import jieba
    
    # 分词时忽略下列词
    biaodian = '[!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~]+ ,。?‘’“”!;:\r\n、()…'     #所有的标点符号
    ignorewords = list(set(biaodian))   #去重
    ignorewords.append('\r\n')   #添加一个不忽略的项
    
    

    在模块中我们先建立一个搜索引擎类。

    
    # 定义搜索引擎类
    class searcher:
        def __init__(self,dbname):
            self.con=sqlite3.connect(dbname)  #链接数据库
            self.curs = self.con.cursor()
    
        def __del__(self):
            self.curs.close()
            self.con.close()
    

    搜索引擎类的一个任务就是能根据用户搜索的字符串分割成多个关键词,再查询到相关的网址。而只要文章中出现了每个关键词,就认为网页相关。这一部分代码比较难懂,建议读者边运行边学习。

    查询过程:

    1、对输入字符串进行分词获得多个关键词。例如我们查询“腾讯股票”,会分词成“腾讯”、“股票”
    2、查询每个关键词在单词库wordlist数据库中的索引。例如我们会查询到“腾讯”在单词库中的索引为126,“股票在单词库中的索引为127”
    3、利用这两个关键词索引把所有包含这两个关键词的链接以及这两个关键词在链接中出现的位置查询出来。形成以下的数据格式

    [
    	 (urlid1,wordlocation1_1,wordlocation2_1),  # 链接1中,单词1的位置1,单词2的位置1
    	 (urlid1,wordlocation1_1,wordlocation2_2),  # 链接1中,单词1的位置1,单词2的位置2
    	 (urlid1,wordlocation1_1,wordlocation2_3),  # 链接1中,单词1的位置1,单词2的位置3
    	 (urlid1,wordlocation1_2,wordlocation2_1),  # 链接1中,单词1的位置2,单词2的位置1
    	 (urlid1,wordlocation1_2,wordlocation2_2),  # 链接1中,单词1的位置2,单词2的位置2
    	 (urlid1,wordlocation1_2,wordlocation2_3),  # 链接1中,单词1的位置2,单词2的位置3
    	 ...
    	 (urlid2,wordlocation1_1,wordlocation2_1),  # 链接2中,单词1的位置1,单词2的位置1
    	 (urlid2,wordlocation1_1,wordlocation2_2),  # 链接2中,单词1的位置1,单词2的位置2
    	 ...
    ]
    

    在下面的语句中我们会用到一个数据库查询语句。
    例如我们查询了字符串中包含“腾讯”、“股票”两个关键词,在单词列表wordlist中的索引分别为126、和127。

    则我们使用下面的语法查询出数据集。

    select w0.urlid,w0.location,w1.location from wordlocation w0,wordlocation w1 where w0.wordid=126 and w0.urlid=w1.urlid and w1.wordid=127
    

    上面的 代码先看where后面的语句。w0表示一个数据表,w1表示另一个数据表。
    下面的语句表示满足条件:
    w0表的wordid字段为126的所有w0中的记录
    w1表的wordid字段为127的所有w1中的记录
    w0表的urlid字段与w1表中urlid字段存在相同值的所有w0中的记录和w1中的所有记录。
    where后的语句查询上述数据集的交集。

    where w0.wordid=126 and w0.urlid=w1.urlid and w1.wordid=127
    

    再来看from后面的语句。表示wordlocation表可以用w0这个名称代替,wordlocation这个表也可以用w1这个名称代替。

    from wordlocation w0,wordlocation w1 
    

    最后来看select后的语句。上面查询到的数据集既有w0中的又有w1中的,把w0中记录的urlid字段、w0中记录的location字段、w1中记录的location字段提取出来。

    select w0.urlid,w0.location,w1.location 
    

    最终形成了我们想要的数据集格式。

    [
    	 (urlid1,wordlocation1_1,wordlocation2_1),  # 链接1中,单词1的位置1,单词2的位置1
    	 (urlid1,wordlocation1_1,wordlocation2_2),  # 链接1中,单词1的位置1,单词2的位置2
    	 (urlid1,wordlocation1_1,wordlocation2_3),  # 链接1中,单词1的位置1,单词2的位置3
    	 (urlid1,wordlocation1_2,wordlocation2_1),  # 链接1中,单词1的位置2,单词2的位置1
    	 (urlid1,wordlocation1_2,wordlocation2_2),  # 链接1中,单词1的位置2,单词2的位置2
    	 (urlid1,wordlocation1_2,wordlocation2_3),  # 链接1中,单词1的位置2,单词2的位置3
    	 ...
    	 (urlid2,wordlocation1_1,wordlocation2_1),  # 链接2中,单词1的位置1,单词2的位置1
    	 (urlid2,wordlocation1_1,wordlocation2_2),  # 链接2中,单词1的位置1,单词2的位置2
    	 ...
    ]
    

    查询实现代码如下:

    # 使用结巴分词,去除标点符号
        def separatewords(self, text):
            seg_list = jieba.cut(text, cut_all=False)  # 使用结巴进行中文分词
            allword = []
            for word in seg_list:
                if word not in ignorewords:
                    allword.append(word)
            # print(allword)
            return allword
    
        # 根据搜索字符串分词后获取查询到的链接
        def getmatchrows(self,querystr):
            # 构造数据库的查询字符串(搜索字符串根据空格分割成查询字符串列表)
            fieldlist='w0.urlid'
            tablelist=''
            clauselist=''
            wordids=[]
    
            # words=querystr.strip().split(' ')   # 根据空格分割单词
            words = self.separatewords(querystr)  #使用结巴进行中文分词
            tablenumber=0
            for word in words:
                # 获取单词的id
                wordrow=self.curs.execute("select rowid from wordlist where word='%s'" % word).fetchall()
                if wordrow!=None and len(wordrow)> 0:
                    wordid=wordrow[0][0]  #获取单词id
                    wordids.append(wordid)
                    if tablenumber>0:
                        tablelist+=','
                        clauselist+=' and '
                        clauselist+='w%d.urlid=w%d.urlid and ' % (tablenumber-1,tablenumber)
                    fieldlist+=',w%d.location' % tablenumber
                    tablelist+='wordlocation w%d' % tablenumber
                    clauselist+='w%d.wordid=%d' % (tablenumber,wordid)
                    tablenumber+=1
    
            # 根据各个组分,建立查询。为列表中的每个单词,建立指向wordlocation表的引用,并根据对应的urlid将它们连接起来进行联合查询
            fullquery='select %s from %s where %s' % (fieldlist,tablelist,clauselist)
            # print(fullquery)
            cur=self.curs.execute(fullquery)
            rows=[row for row in cur.fetchall()]
    
            return rows,wordids
    

    经过上面的查询语句,返回的rows,wordids。其中
    wordids样式如[126, 127],表示每个查询关键词在单词库wordlist数据库中的索引。这个比较简单。

    而rows的样式比较复杂,但他是理解后续代码的关键。

    下面假设搜索字符串解析出了两个关键词。并且这两个关键词在某一个网页中可能都出现了很多次。
    rows的样式为

    [
    	 (urlid1,wordlocation1_1,wordlocation2_1),  # 链接1中,单词1的位置1,单词2的位置1
    	 (urlid1,wordlocation1_1,wordlocation2_2),  # 链接1中,单词1的位置1,单词2的位置2
    	 (urlid1,wordlocation1_1,wordlocation2_3),  # 链接1中,单词1的位置1,单词2的位置3
    	 (urlid1,wordlocation1_2,wordlocation2_1),  # 链接1中,单词1的位置2,单词2的位置1
    	 (urlid1,wordlocation1_2,wordlocation2_2),  # 链接1中,单词1的位置2,单词2的位置2
    	 (urlid1,wordlocation1_2,wordlocation2_3),  # 链接1中,单词1的位置2,单词2的位置3
    	 ...
    	 (urlid2,wordlocation1_1,wordlocation2_1),  # 链接2中,单词1的位置1,单词2的位置1
    	 (urlid2,wordlocation1_1,wordlocation2_2),  # 链接2中,单词1的位置1,单词2的位置2
    	 ...
    ]
    

    可以看出列表中的每个元组中,第一列代表是网址id,后面的列是每个关键词出现的位置的一种组合。

    第三部分、排名算法

    上面已经根据用户的输入获取到了相关的网址数据。
    获取到的数据中rows的形式如下
    [(urlid1,wordlocation1_1,wordlocation1_2,wordlocation1_3…),(urlid2,wordlocation2_1,wordlocation2_2,wordlocation2_3…)]
    列表的每个元素是一个元组,每个元素的内容是urlid和每个关键词在该文档中的位置。

    wordids形式为[wordid1, wordid2, wordid3…],即每个关键词所对应的单词id

    我们将会介绍几种排名算法,所谓排名也就是根据各自的规则为每个链接评分,评分越好。并且最终我们会将几种排名算法综合利用起来,给出最终的排名。既然要综合利用,那么我们就要先实现每种算法。在综合利用时会遇到几个问题。

    1、每种排名算法评分机制不同,给出的评分尺度和含义也不尽相同
    2、如何综合利用,要考虑每种算法的效果。为效果好的给与较大的权重。

    我们先来考虑第一个问题,如何消除每种评分算法所给出的评分尺度和含义不相同的问题。
    第2个问题,等研究完所有的算法以后再来考虑。

    简单,使用归一化,将每个评分值缩放到0-1上,1代表最高,0代表最低。

    # 评价值归一化:因为不同的评价方法的返回值和含义不同。这里所有的评价值归一化到0-1,默认越大越好
        def normalizescores(self,scores,smallIsBetter=0):
            vsmall=0.00001 #避免被0整除
            if smallIsBetter:
                minscore=min(scores.values())
                return dict([(u,float(minscore)/max(vsmall,l)) for (u,l) in scores.items()])
            else:
                maxscore=max(scores.values())
                if maxscore==0: maxscore=vsmall
                return dict([(u,float(c)/maxscore) for (u,c) in scores.items()])
    

    下面我们来研究每一种算法。

    第1个排名算法:根据单词位置进行评分的函数

    我们可以认为对用户输入的多个关键词,在文档中,这些关键词出现的位置越靠前越好。比如我们往往习惯在文章的前面添加一些摘要性、概括性的描述。

        # 根据单词位置进行评分的函数.
        # rows是[(urlid1,wordlocation1_1,wordlocation1_2,wordlocation1_3...),(urlid2,wordlocation2_1,wordlocation2_2,wordlocation2_3...)]
        def locationscore(self,rows):
            locations=dict([(row[0],1000000) for row in rows])
            for row in rows:
                loc=sum(row[1:]) #计算每个链接的单词位置总和,越小说明越靠前
                if loc<locations[row[0]]:  #记录每个链接最小的一种位置组合
    	            locations[row[0]]=loc
    
            return self.normalizescores(locations,smallIsBetter=1)
    

    第2个排名算法:根据单词频度进行评价的函数

    我们可以认为对用户输入的多个关键词,在文档中,这些关键词出现的次数越多越好。比如我们在指定主题的文章中会反复提到这个主题。

      # 根据单词频度进行评价的函数
        # rows是[(urlid1,wordlocation1_1,wordlocation1_2,wordlocation1_3...),(urlid2,wordlocation2_1,wordlocation2_2,wordlocation2_3...)]
        def frequencyscore(self,rows):
            counts=dict([(row[0],0) for row in rows])
            for row in rows: 
    	        counts[row[0]]+=1   #统计每个链接出现的组合数目。 每个链接只要有一种位置组合就会保存一个元组。所以链接所拥有的组合数,能一定程度上表示单词出现的多少。
            return self.normalizescores(counts)
    

    第3个排名算法:根据单词距离进行评价的函数

    我们可以认为对用户输入的多个关键词,在文档中,这些关键词出现的越紧凑越好。这是因为我们更希望所有单词出现在一句话中,而不是不同的关键词出现在不同段落或语句中。

      # 根据单词距离进行评价的函数。
        # rows是[(urlid1,wordlocation1_1,wordlocation1_2,wordlocation1_3...),(urlid2,wordlocation2_1,wordlocation2_2,wordlocation2_3...)]
        def distancescore(self,rows):
            # 如果仅查询了一个单词,则得分都一样
            if len(rows[0])<=2: return dict([(row[0],1.0) for row in rows])
    
            # 初始化字典,并填入一个很大的值
            mindistance=dict([(row[0],1000000) for row in rows])
    
            for row in rows:
                dist=sum([abs(row[i]-row[i-1]) for i in range(2,len(row))]) # 计算每种组合中每个单词之间的距离
                if dist<mindistance[row[0]]:  # 计算每个链接所有组合的距离。并为每个链接记录最小的距离
    	            mindistance[row[0]]=dist
            return self.normalizescores(mindistance,smallIsBetter=1)
    

    第4个排名算法:利用指向该链接的链接数目进行评价

    我们可以认为查询到的相关链接中,如果有较多的其他链接在文档中指向了当前链接,这该链接内容质量比较好。

      # 利用指向该链接的链接数目进行评价(仅计算回指数目)。
        # rows是[(urlid1,wordlocation1_1,wordlocation1_2,wordlocation1_3...),(urlid2,wordlocation2_1,wordlocation2_2,wordlocation2_3...)]
        def inboundlinkscore(self,rows):
            uniqueurls=dict([(row[0],1) for row in rows])
            inboundcount=dict([(u,self.curs.execute('select count(*) from link where toid=%d' % u).fetchall()[0]) for u in uniqueurls])
            return self.normalizescores(inboundcount)
    

    第5个排名算法:pagerank算法

    互联网中的网页可以看出是一个有向图,其中网页是结点,如果网页A有链接到网页B,则存在一条有向边A->B,下面是一个简单的示例:

    这里写图片描述

    这个例子中只有四个网页,如果当前在A网页,那么悠闲的上网者将会各以1/3的概率跳转到B、C、D,这里的3表示A有3条出链,如果一个网页有k条出链,那么跳转任意一个出链上的概率是1/k,同理D到B、C的概率各为1/2,而B到C的概率为0。

    在跳转有向图中不考虑指向自己的链接

    由于上网者并不一定会点击网络中的链接跳转到新的网址,所以我们需要设置阻尼系数,代表用户点击链接进入新网址的概率。

    另外每一个网页本身的重要性也不同,被不同重要性的网址所引用自然也就代表了不同的重要性。比如网址D是一个非常重要的网址,在D中指向了网址B和C,则我们可以认为网址B和网址C也很重要。而网址的重要性又是通过pagerank所表示的,所以这是一个迭代收敛获取每个网址pagerank的过程。

    由于每个网址的链接情况是相对固定的,每个网址的pagerank我们可以在用户请求前就计算好,将每个网址的pagerank值保存到数据库中,待用户请求时,直接查询相关网址的pagerank值就可以了。

    第一步、提前迭代计算每个网址的pagerank的值。

    每个链接的pagerank=每个指向此链接的网页的pagerank/该网页中的链接总数*0.85+0.15,其中0.85表示阻尼因子,表示网页是否点击该网页中的链接。

    先初始化每个网页的pagerank的值为1,然后按照上面的方法迭代n次计算每个网页的pagerank。

    我们在spyder.py模块的crawler类中添加下面的函数

    # (每个链接的pagerank=指向此链接的网页的pagerank/网页中的链接总数*0.85+0.15,其中0.85表示阻尼因子,表示网页是否点击该网页中的链接)
        # pagerank算法,离线迭代计算,形成每个链接的稳定pagerank值。iterations为迭代计算的次数
        def calculatepagerank(self, iterations=20):
            # 清除您当前的pagerank表
            self.curs.execute('drop table if exists pagerank')
            self.curs.execute('create table pagerank(urlid primary key,score)')
    
            # 初始化每个url,令其pagerank的值为1
            for (urlid,) in self.curs.execute('select rowid from urllist').fetchall():
                self.curs.execute('insert into pagerank(urlid,score) values (%d,1.0)' % urlid)
            self.dbcommit()
    
            for i in range(iterations):
                print("Iteration %d" % (i))
                for (urlid,) in self.curs.execute('select rowid from urllist').fetchall():
                    pr = 0.15
                    # 循环遍历指向当前网页的所有其他网页
                    for (linker,) in self.curs.execute('select distinct fromid from link where toid=%d' % urlid).fetchall():
                        # 得到链接源对应网页的pagerank值
                        linkingpr = self.curs.execute('select score from pagerank where urlid=%d' % linker).fetchall()[0][0]
                        # 根据链接源求总的链接数
                        linkingcount = self.curs.execute('select count(*) from link where fromid=%d' % linker).fetchall()[0][0]
                        pr += 0.85 * (linkingpr / linkingcount)
                    self.curs.execute('update pagerank set score=%f where urlid=%d' % (pr, urlid))
                self.dbcommit()
    

    第二步、查询网址的pagerank的值为相关网址进行排名

    调用函数生成网址pagerank数据库以后,就可以通过pagerank对查询到的网址进行排名了。

    # 根据pagerank值进行评价的函数。(利用外部回值链接进行评价)。
    # rows是[(urlid1,wordlocation1_1,wordlocation1_2,wordlocation1_3...),(urlid2,wordlocation2_1,wordlocation2_2,wordlocation2_3...)]
        def pagerankscore(self,rows):
            pageranks=dict([(row[0],self.curs.execute('select score from pagerank where urlid=%d' % row[0]).fetchall()[0][0]) for row in rows])
            maxrank=max(pageranks.values())   #求最大的pagerank值
            for urlid in pageranks:
                pageranks[urlid] /= maxrank   #归一化
            return pageranks   #返回归一化的url的pagerank
    

    第6个排名算法:利用链接文本进行评价的函数

    有时,相比于被链接的网址自身所提供的信息而言,我们从指向该网页的链接中所得到的信息会更有价值。因为针对其所指向的网页,网站的开发者会倾向于提供一些解释其内容的简短描述。

    # 利用链接文本进行评价的函数。
        # rows是[(urlid1,wordlocation1_1,wordlocation1_2,wordlocation1_3...),(urlid2,wordlocation2_1,wordlocation2_2,wordlocation2_3...)]
        def linktextscore(self,rows,wordids):
            linkscores=dict([(row[0],0) for row in rows])
            for wordid in wordids:
                cur=self.curs.execute('select link.fromid,link.toid from linkwords,link where wordid=%d and linkwords.linkid=link.rowid' % wordid)
                for (fromid,toid) in cur.fetchall():
                    if toid in linkscores:
                        pr=self.curs.execute('select score from pagerank where urlid=%d' % fromid).fetchall()[0][0]
                        linkscores[toid]+=pr
            maxscore=max(linkscores.values())   #求最大的pagerank值
            for urlid in linkscores:
                linkscores[urlid] /= maxscore   #归一化
            return linkscores
    

    第五部分、综合利用各种评分算法,将查询到的网址进行评分排名

    # 对查询到的链接进行排名。参数:rows,wordids查询字符串id列表
        def getscoredlist(self,rows,wordids):
            totalscores=dict([(row[0],0) for row in rows])
            # 对链接进行评价的函数。(权重和评价值),使用了多种评价函数
            weights=[(1.0,self.locationscore(rows)),   #根据关键词出现的位置获取权重
                     (1.0,self.frequencyscore(rows)),  #根据关键词出现的频率获取权重
                     (1.0,self.pagerankscore(rows)),   #根据pagerank获取权重
                     (1.0,self.linktextscore(rows,wordids)), #根据链接描述获取权重
                     #(5.0,self.nnscore(rows,wordids))   #根据神经网络获取权重
                     ] 
            for (weight,scores) in weights:
                for urlid in totalscores:
                    totalscores[urlid]+=weight*scores[urlid]
    
            return totalscores  #返回每个链接的评价值
    

    现在我们已经能根据用户输入字符串,分词成多个关键词,查询相关网址,对网址进行评分。那只要按照分数进行排序,给出排名靠前的搜索引擎就完成了。

    
        #搜索函数:将上面的搜索、评价、排名合并在一起。querystr为用户输入字符串
        def query(self,querystr):
            rows,wordids=self.getmatchrows(querystr)  #rows是[(urlid1,wordlocation1_1,wordlocation1_2,wordlocation1_3...),(urlid2,wordlocation2_1,wordlocation2_2,wordlocation2_3...)]
            # print(rows)
            rows = list(set(rows))   #去重,位置组合重复计算没有意义,虽然说,所有的都重复了不影响效果。但是计算量增大了。
            if rows==None or len(rows)==0:
                print('无法查询到,请使用空格分隔查询关键词')
                return
    
            scores=self.getscoredlist(rows,wordids)
            rankedscores=[(score,url) for (url,score) in scores.items()]
            rankedscores.sort()
            rankedscores.reverse()
            for (score,urlid) in rankedscores[0:10]:
                print('%f\t%d\t%s' % (score,urlid,self.geturlname(urlid)))
            return wordids,[r[1] for r in rankedscores[0:10]]
    
    
    #根据urlid查询url
        def geturlname(self,id):
            return self.curs.execute("select url from urllist where rowid=%d" % id).fetchall()[0][0]
    
    

    测试

    mynet=nn.searchnet('csdn.db')
    if __name__ == '__main__':
        mysearcher= searcher('csdn.db')
        searchkey = input("搜索关键词>")
        wordids,urlids=mysearcher.query(searchkey)
        print(wordids,urlids)
    

    我们输入“腾讯股票爬虫”

    返回的结果为

    3.421882	56	http://blog.csdn.net/luanpeng825485697/article/details/78451057
    3.026871	58	http://blog.csdn.net/luanpeng825485697/article/details/78442062
    1.472765	1	http://blog.csdn.net/luanpeng825485697
    1.386459	2	http://blog.csdn.net/luanpeng825485697?viewmode=contents
    1.220016	51	http://blog.csdn.net/luanpeng825485697/article/category/7225092
    1.220016	48	http://blog.csdn.net/luanpeng825485697/article/category/7083783
    1.167393	43	http://blog.csdn.net/luanpeng825485697/article/category/7060769
    1.167161	50	http://blog.csdn.net/luanpeng825485697/article/category/7190343
    1.152465	47	http://blog.csdn.net/luanpeng825485697/article/category/7145481
    1.105118	7	http://blog.csdn.net/luanpeng825485697/article/details/78154417
    
    

    搜索引擎设计成功。

    通过神经网络学习用户点击行为

    在上面的搜索引擎设计中我们实现了6种排名算法。下面我们要再介绍一种排名算法。由于这种算法的设计本身并不对文档内容进行解析,而只是对用户行为进行解析,内容较多,知识较难,所以我们单独来讲。

    下面要讲的是通过神经网络来实现搜索引擎。没有学习过深度学习的同学可以先不用看这一部分。

    神经网络设计搜索引擎,简单的说就是会记忆用户的选择行为。比如搜索引擎根据已有的算法为用户的搜索返回了一些网址。但是这些网址究竟好不好呢。网站并没有这样一个反馈机制。而搜索引擎就是为这样一个反馈机制存在的。当用户选择了一个推荐的网址,神经网络就会为这个网址添加一定的权重,下次搜索相同的内容,更加情愿优先推荐权重大的网址。

    将下面的代码存储成nn.py

    # 神经网络学习用户点击行为,实现搜索排名
    from math import tanh
    import sqlite3
    
    # 指定任何输出值的斜率
    def dtanh(y):
        return 1.0-y*y
    
    class searchnet:
        def __init__(self,dbname):
            self.con=sqlite3.connect(dbname)  #链接数据库
            self.curs = self.con.cursor()
            try:
                self.maketables()
            except:
                pass
    
        def __del__(self):
            self.curs.close()
            self.con.close()
    
        def dbcommit(self):  # 保存数据库
            self.con.commit()
    
        def maketables(self):
            self.curs.execute('create table hiddennode(create_key)')              #创建神经元节点
            self.curs.execute('create table wordhidden(fromid,toid,strength)')   #输入节点(单词)到神经元的权重,默认-0.2
            self.curs.execute('create table hiddenurl(fromid,toid,strength)')    #神经元到输出节点(链接)的权重,默认为0
            self.dbcommit()
    
            # 获取节点间权重。layer为0表示输入到神经元之间,为1表示神经元到输出之间
        def getstrength(self,fromid,toid,layer):
            if layer==0: table='wordhidden'
            else: table='hiddenurl'
            res=self.curs.execute('select strength from %s where fromid=%d and toid=%d' % (table,fromid,toid)).fetchall()
            if res==None or len(res)==0:
                if layer==0: return -0.2  # 输入到神经元之间默认为-0.2
                if layer==1: return 0   # 神经元到输出节点间默认为0
            return res[0][0]
    
        # 设置数据库中节点间权重。layer为0表示输入到神经元之间,为1表示神经元到输出之间
        def setstrength(self,fromid,toid,layer,strength):
            if layer==0: table='wordhidden'
            else: table='hiddenurl'
            res=self.curs.execute('select rowid from %s where fromid=%d and toid=%d' % (table,fromid,toid)).fetchall()
            if res==None or len(res)==0:
                self.curs.execute('insert into %s (fromid,toid,strength) values (%d,%d,%f)' % (table,fromid,toid,strength))
            else:
                rowid=res[0][0]
                self.curs.execute('update %s set strength=%f where rowid=%d' % (table,strength,rowid))
    
         # 根据已知正确的输入输出结果,创建神经元,建立输入与神经元间里连接,神经元与输出间连接。每一种输入组合都创建一个神经节点
        def generatehiddennode(self,wordids,urlids):
            # if len(wordids)>3: return None   #对于有3个输出单词的我们不做处理,太复杂
            # 检测我们是否已经为这组输入建好了一个节点
            sorted_words=[str(id) for id in wordids]
            sorted_words.sort()
            createkey='_'.join(sorted_words)
            res=self.curs.execute("select rowid from hiddennode where create_key='%s'" % createkey).fetchall()
    
            # 如果没有,就创建
            if res==None or len(res)==0:
                cur=self.curs.execute("insert into hiddennode (create_key) values ('%s')" % createkey)
                hiddenid=cur.lastrowid
                # 设置默认权重
                for wordid in wordids:
                    self.setstrength(wordid,hiddenid,0,1.0/len(wordids))   #设置输入节点到神经元间的权重为1/输入数量
                for urlid in urlids:
                    self.setstrength(hiddenid,urlid,1,0.1)   #设置神经元到输出节点间权重为0.1
                self.dbcommit()
    
        # 根据输入关键词id和相关的链接的id,获取数据库中神经元节点的id。(相关的链接也就是初步查询到的链接,只要链接的网页中出现过关键词就会被认为相关)
        def getallhiddenids(self,wordids,urlids):
            hiddenids=[]
            for wordid in wordids:
                cur=self.curs.execute('select toid from wordhidden where fromid=%d' % wordid).fetchall()
                for row in cur:
                    hiddenids.append(row[0])
            for urlid in urlids:
                cur=self.curs.execute('select fromid from hiddenurl where toid=%d' % urlid).fetchall()
                for row in cur:
                    hiddenids.append(row[0])
            return hiddenids  # 返回神经元节点
    
        # 构建一个神经网络
        def setupnetwork(self,wordids,urlids):
            # 值列表:输入:神经元、输出
            self.wordids=wordids
            self.hiddenids=self.getallhiddenids(wordids,urlids)
            self.urlids=urlids
    
            # 构建输入节点、神经元、输出节点。就是前面的输入、神经元、输出。这里用了一个更加普遍的名称
            self.ai = [1.0]*len(self.wordids)
            self.ah = [1.0]*len(self.hiddenids)
            self.ao = [1.0]*len(self.urlids)
    
            # 建立权重矩阵(线性组合系数矩阵):输入-神经元,  神经元-输出
            self.wi = [[self.getstrength(wordid,hiddenid,0)
                        for hiddenid in self.hiddenids]
                        for wordid in self.wordids]
            self.wo = [[self.getstrength(hiddenid,urlid,1)
                        for urlid in self.urlids]
                        for hiddenid in self.hiddenids]
    
        # 前馈算法:一列输入,进入神经网络,返回所有输出结果的活跃程度。越活跃也好。因为神经网络每向下传播一层就会衰弱一层。衰弱函数使用tanh这种0时陡峭,无限大或无限小时平稳的函数
        def feedforward(self):
            # 查询的单词是仅有的输入
            for i in range(len(self.wordids)):
                self.ai[i] = 1.0   #输入节点的活跃程度就设为1
    
            # 根据输入节点活跃程度,获取神经元节点的活跃程度
            for j in range(len(self.hiddenids)):
                sum = 0.0
                for i in range(len(self.wordids)):
                    sum = sum + self.ai[i] * self.wi[i][j]  # 线性组合
                self.ah[j] = tanh(sum)   #使用tanh表示神经元对输入的反应强度。(因为tanh是一个在0附近震荡强烈,在远离0时趋于稳定的函数)
    
            # 根据神经元节点活跃程度,获取输出节点的活跃程度
            for k in range(len(self.urlids)):
                sum = 0.0
                for j in range(len(self.hiddenids)):
                    sum = sum + self.ah[j] * self.wo[j][k]  # 线性组合
                self.ao[k] = tanh(sum)
    
            return self.ao[:]
    
    # =============================以上是公共实例函数=======================================
    
    #=======================getresult是应用神经网络进行搜索的函数==============================
    
        #  针对一组单词和url给出输出
        def getresult(self,wordids,urlids):
            self.setupnetwork(wordids,urlids)
            return self.feedforward()
    
    
    
    
    # ============================下面是使用反向传播法进行神经网络训练============================
    
    
     #前馈训练法:依据当前权重预测输出,计算误差,更正权重。用户每选择一次,进行一次训练
    
      #用户每选择一次链接,就调整一次权重。targets表示正确的输出结果。即用户选择的链接
        def backPropagate(self, targets, N=0.5):
            # 计算输出层误差
            output_deltas = [0.0] * len(self.urlids)
            for k in range(len(self.urlids)):
                error = targets[k]-self.ao[k]    #计算正确输出和预测输出之间的误差
                output_deltas[k] = dtanh(self.ao[k]) * error   #确定总输入需要如何改变
    
            # 计算神经元误差:
            hidden_deltas = [0.0] * len(self.hiddenids)
            for j in range(len(self.hiddenids)):
                error = 0.0
                for k in range(len(self.urlids)):
                    error = error + output_deltas[k]*self.wo[j][k]  #将每个神经元-输出间的权重值乘以输出节点的改变量,再累加求和,从而改变节点的输出结果
                hidden_deltas[j] = dtanh(self.ah[j]) * error  #确定节点的总输入所需的该变量
    
            # 更新神经元-输出间权重
            for j in range(len(self.hiddenids)):
                for k in range(len(self.urlids)):
                    change = output_deltas[k]*self.ah[j]
                    self.wo[j][k] = self.wo[j][k] + N*change
    
            # 更新输入-神经元间权重
            for i in range(len(self.wordids)):
                for j in range(len(self.hiddenids)):
                    change = hidden_deltas[j]*self.ai[i]
                    self.wi[i][j] = self.wi[i][j] + N*change
    
    
        # 更新数据库
        def updatedatabase(self):
            # 将值写入数据库
            for i in range(len(self.wordids)):
                for j in range(len(self.hiddenids)):
                    self.setstrength(self.wordids[i],self. hiddenids[j],0,self.wi[i][j])
            for j in range(len(self.hiddenids)):
                for k in range(len(self.urlids)):
                    self.setstrength(self.hiddenids[j],self.urlids[k],1,self.wo[j][k])
            self.dbcommit()
    
    
       # 对神经网络进行一次训练。wordids为查询关键词id,urlids为查找到相关的url的id,selectedurl为选中的url的id
        def trainquery(self,wordids,urlids,selectedurlid):
            # 第一次运行时,在数据库中创建表。以默认权重赋值
            self.generatehiddennode(wordids,urlids)
            # 创建神经网络类的属性。(输入、神经元、输出、两个权重)
            self.setupnetwork(wordids,urlids)
            self.feedforward()   #执行前馈算法,根据输入获取输出
            targets=[0.0]*len(urlids)
            targets[urlids.index(selectedurlid)]=1.0  #获取用户选择的正确链接
            error = self.backPropagate(targets)  #执行反向传播法修正网络
            self.updatedatabase()   #更新数据库
    
    
    if __name__ == '__main__':
        con = sqlite3.connect('csdn.db')
        curs = con.cursor()
        command = 'select * from hiddennode'  # "select fromid,toid from link"
        cur = curs.execute(command)
        res = cur.fetchall()
        if res != None and len(res) > 0:
            for row in res:
                print(row)
    

    根据长时间的用户行为,为新搜索相关的网址进行排名

    # 根据神经网络(用户点击行为学习)进行评价的函数。神经网络在nn.py中实现。
        # rows是[(urlid1,wordlocation1_1,wordlocation1_2,wordlocation1_3...),(urlid2,wordlocation2_1,wordlocation2_2,wordlocation2_3...)]
        def nnscore(self,rows,wordids):
            # 获得一个由唯一的url id构成的有序列表
            urlids=[urlid for urlid in dict([(row[0],1) for row in rows])]
            nnres=mynet.getresult(wordids,urlids)
            scores=dict([(urlids[i],nnres[i]) for i in range(len(urlids))])
            return self.normalizescores(scores)
    

    有了上面的排名算法,就可以添加到之前的综合评分函数中去了。

    展开全文
  • 安装Anaconda Python集成环境下载环境anaconda下载选择安装环境下载过程中使用默认,但有一个页面需要确认,如下图。anaconda选择页面第一个勾是是否把 Anaconda 加入环境变量,这涉及到能否直接在 cmd中使用 conda...

    友情提示:此篇文章大约需要阅读 7分钟57秒,不足之处请多指教,感谢你的阅读。

    安装Anaconda Python集成环境

    下载环境

    anaconda下载选择

    安装环境

    下载过程中使用默认,但有一个页面需要确认,如下图。

    anaconda选择页面

    第一个勾是是否把 Anaconda 加入环境变量,这涉及到能否直接在 cmd中使用 conda、jupyter、 ipython 等命令,推荐打勾。

    第二个是是否设置 Anaconda 所带的 Python 3.6 为系统默认的 Python 版本,可以打勾。

    安装完成后,在开始菜单中显示“Anaconda2”如下图所示。

    安装显示界面

    安装第三方程序包 Graphviz

    目的是在决策树算法中八进制最终的树结构。

    1、打开 Anaconda Prompt ,输入 conda install python-graphviz,回车即可完成安装,如下图所示,本图所示已经安装 了 graphviz包,若之前没有安装,这时会花点时间安装,安装不用干预。

    安装决策树依赖包

    安装完成后先输入 python,然后再输入 import graphviz,测试是否成功安装,如上图所示。

    需要设置环境变量,才能使用新安装的 graphviz。

    Anaconda及依赖包环境变量设置

    首先查看 anaconda安装在哪个目录下,可以打开 Spyder的属性,看一看目标是什么目 录。例如本机的 anaconda安装路径为 C:\Users\lenovo\Anaconda2。

    下面设置环境变量

    (1) 在用户变量“path”里添加 C:\Users\lenovo\Anaconda2\Library\bin\graphviz

    (2) 在系统变量的“path”里添加 C:\Users\lenovo\Anaconda2\Library\bin\graphviz\dot.exe

    (3) 如果现在有正在打开的 anaconda 程序,例如正在 Spyder,那么关闭 Spyder,再启动,这 样刚才设置的环境变量生效。

    决策树分析

    格式化原始数据

    将下图的表 demo输入到 Excel中,保存为.csv 文件(.csv为逗号分隔值文件格式)。

    注意将表 demo中的汉字值转换成数据字值,例如“是否是公司职员”列中的“是”为“1”, “否”为“0”。转换后的表中数据如下图所示。

    学习表

    编写数据分析代码

    编写程序对上面的数据进行决策树分类,采用信息熵(entropy)作为度量标准。参考代码如下所示:

    from sklearn.tree import DecisionTreeClassifier,export_graphviz

    import graphviz

    import csv

    dataset = []

    reader = csv.reader(open("demo.csv"))

    for line in reader:

    if reader.line_num == 1:

    continue

    dataset.append(line)

    X = [x[0:4] for x in dataset]

    y = [x[4] for x in dataset]

    clf = DecisionTreeClassifier(criterion='entropy').fit(X, y)

    dot_data = export_graphviz(clf, out_file=None)

    graph = graphviz.Source(dot_data)

    graph.render("table");

    digraph Tree {

    node [shape=box] ;

    0 [label="X[0] <= 0.5\nentropy = 0.94\nsamples = 14\nvalue = [9, 5]"] ;

    1 [label="X[1] <= 1.5\nentropy = 0.985\nsamples = 7\nvalue = [3, 4]"] ;

    0 -> 1 [labeldistance=2.5, labelangle=45, headlabel="True"] ;

    2 [label="entropy = 0.0\nsamples = 3\nvalue = [0, 3]"] ;

    1 -> 2 ;

    3 [label="X[1] <= 2.5\nentropy = 0.811\nsamples = 4\nvalue = [3, 1]"] ;

    1 -> 3 ;

    4 [label="entropy = 0.0\nsamples = 2\nvalue = [2, 0]"] ;

    3 -> 4 ;

    5 [label="X[3] <= 0.5\nentropy = 1.0\nsamples = 2\nvalue = [1, 1]"] ;

    3 -> 5 ;

    6 [label="entropy = 0.0\nsamples = 1\nvalue = [1, 0]"] ;

    5 -> 6 ;

    7 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 1]"] ;

    5 -> 7 ;

    8 [label="X[1] <= 2.5\nentropy = 0.592\nsamples = 7\nvalue = [6, 1]"] ;

    0 -> 8 [labeldistance=2.5, labelangle=-45, headlabel="False"] ;

    9 [label="entropy = 0.0\nsamples = 4\nvalue = [4, 0]"] ;

    8 -> 9 ;

    10 [label="X[3] <= 0.5\nentropy = 0.918\nsamples = 3\nvalue = [2, 1]"] ;

    8 -> 10 ;

    11 [label="entropy = 0.0\nsamples = 2\nvalue = [2, 0]"] ;

    10 -> 11 ;

    12 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 1]"] ;

    10 -> 12 ;

    }

    数据分析结果

    程序运行结果在与该程序在同一目录下的 table.pdf 文件中,将每一个叶子结点转换成IF-THEN 规则。

    决策树分析结果

    IF-THEN分类规则

    (1)IF"不是公司员工" AND "年龄大于等于40", THEN "不买保险"。

    (2)IF"不是公司员工" AND "年龄小于40", THEN "买保险"。

    (3)IF"不是公司员工" AND "年龄大于50" AND "信用为良", THEN "不买保险"。

    (4)IF"不是公司员工" AND "年龄大于40" AND "信用为优", THEN "买保险"。

    (5)IF"是公司员工" AND "年龄小于50", THEN "不买保险"。

    (6)IF"是公司员工" AND "年龄小于50" AND "信用为优", THEN "买保险"。

    (7)IF"是公司员工" AND "年龄小于50" AND "信用为良", THEN "不买保险"。

    展开全文
  • 作者:Nature出品:AI机器思维“从数据处理基础扎实练习是数据分析与数据挖掘的第一步”做一道好菜需要食材,好的食材需要经过优质的萃取提炼。食材的提炼过程包括选型、运输保鲜、加工清洗、按要求切菜等才能按菜谱...

    ed8cc99d3584573839da5bed8702b0ed.gif

    4eb81b8e5e43a535d03e19f239c2320f.png

    作者:Nature

    出品:AI机器思维

    从数据处理基础扎实练习是数据分析与数据挖掘的第一步

    做一道好菜需要食材,好的食材需要经过优质的萃取提炼。食材的提炼过程包括选型、运输保鲜、加工清洗、按要求切菜等才能按菜谱进行真正的做出一道口感美味的菜。大数据时代数据分析与数据挖掘关键的一步在处理食材,这里的各类数据就是我们的食材,选择优质的数据,经过深加工清洗,去伪纯真这个过程需要耗费很长时间,也需要更多的实践经验。根据多年经验,要想运用好数据,首先要研究学习对各种类型的数据进行处理(如各类数据库数据、EXCEL数据、TXT数据、DAT数据等等)。无论用那种语言做数据分析与数据挖掘,首先得有数据食材,对于数据分析与数据挖掘工具只是帮助我们更好的做出一道好菜,正如开车,那种车都能到达目的地,技术不同效率不同,安全系数也不同。数据分析与数据挖掘的工具目前主流的是SAS、python、R等。这些数据分析挖掘工具能对海量的数据进行处理分析与挖掘。业界中用到的数据量都超过GB级别,研究数据处理,学好数据处理是万里长征第一步,也是学习数据分析与数据挖掘的核心。练习基本功需要耐心和毅力,也需要学习方式。建议学习方式如下:○对各类数据每一种类型的数据处理根据案例学习并总结数据处理经验。○对数据处理生成能够被数据分析与数据挖掘工具的数据集进行二次加工处理,需要理解业务衍生业务数据指标,这是一个深加工的过程。根据数据分析和数据挖掘目标找几个案例真正落实,多加练习。○数据分析与数据挖掘的过程就是运用工具中已经内置好的数据分析工具以及函数包的过程,这个过程只是占了整个数据分析与数据挖掘的百分之三十的工作量,大部分时间都是在做数据处理的清洗工作。○数据分析报告可以研习案例写作,数据挖掘结果分析根据研习的案例学习。数据分析与数据挖掘真正上手的方式就是加大案例学习与实践,跟着案例手把手练习,才能真正掌握数据分析与数据挖掘的真谛。案例如下面是零售客户数据,文件名buy.txt,目前存放在我的个人文件夹    E:/testdata/buy.txt

    f63dda2ddb3ee61cda8896f9d7a42cef.png

    数据说明:数据中每列为面包、牛奶、奶酪、苹果和香蕉对此类数据研究客户交叉销售,根据客户购买行为判断客户在买其中一种食物时会购买那类食物,这样把客户可能购买概率最高的放在货架上相同的位置。因此处理数据是核心环节:下面是python3.7版本下数据处理程序,数据文件以”|”分割,处理数据时程序指明了分隔符。这里的x=np.loadtxt(dataset_filename,delimiter='|')语句中delimiter=’|’指明了数据以|分割,默认是以空格,如果以空格就需要这个参数了。
    importtnumpyas np
    dataset_filename=
    "E:/testdata/buy.txt"x=np.loadtxt(dataset_filename,delimiter='|')print(x[:6])
    查看数据处理的数据如下:

    3d2d949ca182f950f28d6c4c916b6625.png

    输出结果从横向和纵向看都可以,横着看,每次只看一行。第一行(0, 0, 0, 1,1)表示 第一条交易数据所包含的商品。竖着看,每一列代表一种商品。在我们这个例子中,这五种商品分别是面包、牛奶、奶酪、苹果和香蕉。从第一条交易数据中,我们可以看到顾客购买了苹果和香蕉,但是没有买面包、牛奶和奶酪。每个特征只有两个可能的值,1或0,表示是否购买了某种商品,而不是购买商品的数量。1 表示顾客至少买了该商品,0表示顾客没有买该种商品。我们要找出“如果顾客购买了商品X,那么他们可能愿意购买商品Y”这样 的规则。简单粗暴的做法是,找出数据集中所有同时购买的两件商品。找出规则后,还需要判断其优劣,我们挑好的规则用。规则的优劣有多种衡量方法,常用的是支持度(support)和置信度(confidence)。 支持度指数据集中规则应验的次数,统计起来很简单。有时候,还需要对支持度进行规范化,即再除以规则有效前提下的总数量。我们这里只是简单统计规则应验的次数。支持度衡量的是给定规则应验的比例,而置信度衡量的则是规则准确率如何,即符合给定条件(即规则的“如果”语句所表示的前提条件)的所有规则里,跟当前规则结论一致的比例有多大。计算方法为首先统计当前规则的出现次数,再用它来除以条件(“如果”语句)相同的规则数量。 接下来,通过一个例子来说明支持度和置信度的计算方法,我们看一下怎么求“如果顾客购买了苹果,他们也会购买香蕉”这条规则的支持度和置信度。如下面的代码所示,通过判断交易数据中sample[3]的值,就能知道一个顾客是否买了苹果。 这里,sample表示一条交易信息,也就是数据集里的一行数据。
    num_apple_purchases=0for sample in x:if sample[3]==1:
            num_apple_purchases+=
    1print("{0}people bought apples".format(num_apple_purchases))
    下面是程序执行后统计购买苹果的结果:

    fd2611fb078b2f744160a1ab94b74293.png

    同理,检测sample[4]的值是否为1,就能确定顾客有没有买香蕉。现在可以计算题目给定规则在数据集中的出现次数,从而计算置信度和支持度。 我们需要统计数据集中所有规则的相关数据。首先分别为规则应验和规则无效这两种情况创 建字典。字典的键是由条件和结论组成的元组,元组元素为特征在特征列表中的索引值,不要用 实际特征名,比如“如果顾客购买了苹果,他们也会买香蕉”就用(3, 4)表示。如果某个个体的条件和结论均与给定规则相符,就表示给定规则对该个体适用,否则如果通过给定条件推出的结论与给定规则的结论不符,则表示给定规则对该个体无效。 为了计算所有规则的置信度和支持度,首先创建几个字典,用来存放计算结果。这里使用defaultdict,好处是如果查找的键不存在,返回一个默认值。附件:以空格分隔数据案例,整个程序源代码,数据可以自己多创造几条,如需要数据文件请留言,跟着案例学习可以更好的体验应用价值,提供学习兴趣:

    624ec871f825cdd57a052a7849568f53.png

    运行结果如下:

    32203a95f2d2f37242492cb4c2f0c58b.png

    从排序结果来看,“顾客买苹果,也会买奶酪”和“顾客买奶酪,也会买香蕉”,这两条规 则的支持度和置信度都很高。超市可以根据这些规则来调整商品摆放位置。例如如果苹果促销,就在旁边摆上奶酪。但是香蕉和奶酪同时搞促销就没有多大意义了,因为我们发现购买奶酪的顾客中,接近66%的人即使不搞促销也会买香蕉——即使搞促销,也不会给销量带来多大提升。 从上面这个例子就能看出从海量的数据中进行数据挖掘的洞察力有多强大,帮助我们发现数据背后的信息提升决策依据,指导经营提升营收。人们可以用数据挖掘技术探索数据集中各变量之间的关系,寻找商业价值,商品组合价值,营销价值等。学好数据挖掘,数据处理是关键,其他就是引用统计学的方法以及机器学习的算法等!

    ——往期精彩——

    额度授予模型

    开启数据挖掘之旅——以Python应用案例讲解(NO1)

    信用风险评分卡研究(1)

    品玩SAS:电商平台销售数据分析——以英国电商平台为例

    P2P网贷用户数据分析——以拍拍贷为例

    a57bd288983c8a274325837259dcdeee.png

    如果您觉得文章不错,请点击右下角的在看支持我们!

    展开全文
  • 在这个案例开始之前,希望大家回忆一下大学里讲过的线性回归的知识,这是数据挖掘里非常重要的一部分知识。当然,鉴于大家都学过,本篇就不再赘述。一.数据集介绍diabetesdataset数据集这是一个糖尿病的数据集,主要...

    今天给大家讲解一个实战案例:如何根据现有数据预测糖尿病。在这个案例开始之前,希望大家回忆一下大学里讲过的线性回归的知识,这是数据挖掘里非常重要的一部分知识。当然,鉴于大家都学过,本篇就不再赘述。

    一.数据集介绍diabetesdataset数据集

    这是一个糖尿病的数据集,主要包括442行数据,10个属性值,分别是:Age(年龄)、性别(Sex)、Bodymassindex(体质指数)、AverageBloodPressure(平均血压)、S1~S6一年后疾病级数指标。Target为一年后患疾病的定量指标。

    输出如下所示:

    二、LinearRegression使用方法

    LinearRegression模型在Sklearn.linear_model下,它主要是通过fit(x,y)的方法来训练模型,其中x为数据的属性,y为所属类型。

    sklearn中引用回归模型的代码如下:

    输出的函数原型如下所示:

    fit(x,y):训练。分析模型参数,填充数据集。其中x为特征,y位标记或类属性。

    predict():预测。它通过fit()算出的模型参数构成的模型,对解释变量进行预测其类属性。预测方法将返回预测值y_pred。

    引用搬砖小工053″大神的例子:

    运行结果如下所示,首先输出数据集,同时调用sklearn包中的LinearRegression()回归函数,fit(X,Y)载入数据集进行训练,然后通过predict()预测数据12尺寸的匹萨价格,最后定义X2数组,预测它的价格。

    输出的图形如下所示:

    线性模型的回归系数W会保存在他的coef_方法中,截距保存在intercept_中。score(X,y,sample_weight=None)评分函数,返回一个小于1的得分,可能会小于0。

    三、线性回归判断糖尿病

    1.Diabetes数据集(糖尿病数据集)

    糖尿病数据集包含442个患者的10个生理特征(年龄,性别、体重、血压)和一年以后疾病级数指标。

    然后载入数据,同时将diabetes糖尿病数据集分为测试数据和训练数据,其中测试数据为最后20行,训练数据从0到-20行(不包含最后20行),即diabetes.data[:-20]。

    输出结果如下所示,可以看到442个数据划分为422行进行训练回归模型,20行数据用于预测。输出的diabetes_x_test共20行数据,每行仅一个特征。

    2.完整代码

    改代码的任务是从生理特征预测疾病级数,但仅获取了一维特征,即一元线性回归。【线性回归】的最简单形式给数据集拟合一个线性模型,主要是通过调整一系列的参以使得模型的残差平方和尽量小。

    线性模型:y=βX+b

    X:数据y:目标变量β:回归系数b:观测噪声(bias,偏差)

    运行结果如下所示,包括系数、残差平方和、方差分数。

    绘制图形如下所示,每个点表示真实的值,而直线表示预测的结果,比较接近吧。

    同时绘制图形时,想去掉坐标具体的值,可增加如下代码:

    四、优化代码

    下面是优化后的代码,增加了斜率、截距的计算,同时增加了点图到线性方程的距离,保存图片设置像素。

    运行结果如下所示:

    绘制图形如下所示:

    展开全文
  • 在这个案例开始之前,希望大家回忆一下大学里讲过的线性回归的知识,这是数据挖掘里非常重要的一部分知识。当然,鉴于大家都学过,本篇就不再赘述。一. 数据集介绍diabetes dataset数据集这是一个糖尿病的数据集,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,001
精华内容 400
关键字:

python数据挖掘案例

python 订阅