【数据库|电子商务|ERP】基于PyQt5、SQLite和Apriori算法实现CRM商品推荐

实验目的

熟悉关联算法、相邻方法,能应用关联算法或相邻方法设计个性化推荐系统,如推荐相应的优惠折扣或用户可能感兴趣的内容。

实验内容

1、输入客户数据
2、应用关联算法或相邻方法计算该用户适宜的优惠政策或可能感兴趣的内容
3、输出相应的推荐内容

实验工具

① IDE:PyCharm
Pycharm是一个用于计算机编程的集成开发环境,主要用于Python语言开发,提供代码分析、图形化调试器、集成测试器、集成版本控制系统,并支持Django进行开发。提供卓越的生产力推进工具如自动代码格式化、代码完成、重构、自动导入和一键代码导入,能够方便开发人员高效开发程序。此外,Pycharm支持跨平台,在Windows、MacOSX和linux系统等主流系统都发布了相应的版本。

② 开发语言:Python3
Python3是一种用于通用编程的解释型高级编程语言,Python的设计理念强调代码可读性,提供了在小规模和大规模上实现清晰变成的构造。Python具有动态类型系统和自动内存管理功能,它支持多种编程示例,包括面向对象,功能性和程序性,并且具有大型且全面的标准库。作为一种动态类型语言,Python非常灵活,支持使用不同的方法灵活地解决问题。Python可以在每个主要的操作系统和平台,以及大多数的小系统上运行。此外,Python非常适合后端web开发、数据分析、人工智能和科学计算。

③ 数据库:SQLite
SQLite是一个轻量级、跨平台的关系型数据库,实现了自给自足的、无服务器的、零配置的、事务性的SQL数据库引擎,SQLite是世界上最广泛部署的SQL数据库引擎,被广泛适用于浏览器、操作系统和嵌入式系统等。SQLite是ACID兼容的,使用动态和弱类型的SQL语法来实现大多数SQL标准。SQLite的核芯引擎本身不依赖第三方软件,能够在开发部署时省去不少麻烦。

④ GUI实现库:PyQt5
PyQt是一个Python结合的跨平台的GUI工具包的Qt,属于一个Python实现插件,PyQt支持Microsoft Windows以及各种类型的UNIX,包括Linux和MacOS。PyQt实现了大约440个类和超过6000个函数和方法,包括一组GUI部件、用于访问SQL数据库的类、基于Scintilla的富文本编辑器小部件、从数据库自动填充的数据感知小部件、XML解释器等。Qt框架除了界面库之外还提供了音频库、3D库、数据库SDK、WebEngine、网络库能,使开发能够变得更加简便。

实现步骤

  1. 整体框架设计
    【数据库|电子商务|ERP】基于PyQt5、SQLite和Apriori算法实现CRM商品推荐

  2. 数据表的设计
    数据表名:goods 中文描述:商品表
    字段名称 字段描述 数据类型 长度 是否允许空 缺省值 备注
    Id 商品编号 char 10 N NULL 主键
    Name 商品名称 Varchar 50 N NULL
    company 生产厂商 Varchar 50 N NULL
    Date 上市日期 Date N 2000/1/1
    Price 商品价格 float N 0.0
    img 商品图片 Char 200 Y NULL
    screen 屏幕 Varchar 100 Y NULL
    CPU CPU Varchar 100 Y NULL
    GPU GPU Varchar 100 Y NULL
    advantage 优点 Varchar 200 Y NULL
    disadvantage 缺点 varchar 200 Y NULL

  3. 算法

支持度:支持度(Support)的公式是:Support(A->B)=P(A U B)。支持度揭示了A与B同时出现的概率。如果A与B同时出现的概率小,说明A与B的关系不大;如果A与B同时出现的非常频繁,则说明A与B总是相关的。

置信度: 置信度(Confidence)的公式:Confidence(A->B)=P(A | B)。置信度揭示了A出现时,B是否也会出现或有多大概率出现。如果置信度度为100%,则A和B可以捆绑销售了。如果置信度太低,则说明A的出现与B是否出现关系不大。

Apriori算法:
Apriori算法是一种最有影响的挖掘布尔关联规则频繁项集的算法Apriori使用一种称作逐层搜索的迭代方法,“K-1项集”用于搜索“K项集”。
首先,找出频繁“1项集”的集合,该集合记作L1。L1用于找频繁“2项集”的集合L2,而L2用于找L3。如此下去,直到不能找到“K项集”。找每个Lk都需要一次数据库扫描。
核心思想是:连接步和剪枝步。连接步是自连接,原则是保证前k-2项相同,并按照字典顺序连接。剪枝步,是使任一频繁项集的所有非空子集也必须是频繁的。反之,如果某个候选的非空子集不是频繁的,那么该候选肯定不是频繁的,从而可以将其从CK中删除。
简单的讲,1、发现频繁项集,过程为(1)扫描(2)计数(3)比较(4)产生频繁项集(5)连接、剪枝,产生候选项集 重复步骤(1)~(5)直到不能发现更大的频集
Apriori算法是反单调的,即一个集合如果不能通过测试,则该集合的所有超集也不能通过相同的测试。(剪枝步核心)

系统运行情况界面

  1. 主界面
    【数据库|电子商务|ERP】基于PyQt5、SQLite和Apriori算法实现CRM商品推荐
  2. 商品浏览界面
    【数据库|电子商务|ERP】基于PyQt5、SQLite和Apriori算法实现CRM商品推荐
  3. 导入商品界面
    【数据库|电子商务|ERP】基于PyQt5、SQLite和Apriori算法实现CRM商品推荐
  4. 商品详情界面
    【数据库|电子商务|ERP】基于PyQt5、SQLite和Apriori算法实现CRM商品推荐

程序设计代码

  1. 商品查询功能
    def recordQuery(self, index):
        conditionChoice = self.condisionComboBox.currentText()
        if (conditionChoice == "按商品编号查询"):
            conditionChoice = 'id'
        elif (conditionChoice == "按商品名称查询"):
            conditionChoice = 'name'
        elif (conditionChoice == "按商品厂家查询"):
            conditionChoice = 'company'
        elif (conditionChoice == "按上市时间查询"):
            conditionChoice = 'date'
        else:
            conditionChoice = 'price'

        if (self.searchEdit.text() == ""):
            queryCondition = "select id,name,company,date,price from goods"
            self.queryModel.setQuery(queryCondition)
            self.totalRecord = self.queryModel.rowCount()
            self.totalPage = int((self.totalRecord + self.pageRecord - 1) / self.pageRecord)
            label = "/" + str(int(self.totalPage)) + "页"
            self.pageLabel.setText(label)
            queryCondition = ("select id,name,company,date,price from goods order by %s  limit %d,%d " % (
            conditionChoice, index, self.pageRecord))
            self.queryModel.setQuery(queryCondition)
            self.setButtonStatus()
            return

        temp = self.searchEdit.text()
        s = '%'
        for i in range(0, len(temp)):
            s = s + temp[i] + "%"
        queryCondition = ("select id,name,company,date,price from goods where %s like '%s' order by %s " % (
            conditionChoice, s, conditionChoice))
        self.queryModel.setQuery(queryCondition)
        self.totalRecord = self.queryModel.rowCount()
        if (self.totalRecord == 0):
            print(QMessageBox.information(self, "提醒", "查询无记录", QMessageBox.Yes, QMessageBox.Yes))
            queryCondition = "select id,name,company,date,price from goods"
            self.queryModel.setQuery(queryCondition)
            self.totalRecord = self.queryModel.rowCount()
            self.totalPage = int((self.totalRecord + self.pageRecord - 1) / self.pageRecord)
            label = "/" + str(int(self.totalPage)) + "页"
            self.pageLabel.setText(label)
            queryCondition = ("select id,name,company,date,price from goods order by %s  limit %d,%d " % (
            conditionChoice, index, self.pageRecord))
            self.queryModel.setQuery(queryCondition)
            self.setButtonStatus()
            return
        self.totalPage = int((self.totalRecord + self.pageRecord - 1) / self.pageRecord)
        label = "/" + str(int(self.totalPage)) + "页"
        self.pageLabel.setText(label)
        queryCondition = ("select id,name,company,date,price from goods where %s like '%s' order by %s limit %d,%d " % (
            conditionChoice, s, conditionChoice, index, self.pageRecord))
        self.queryModel.setQuery(queryCondition)
        self.setButtonStatus()
        return
  1. 商品视图
        self.db = QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName('./db/myERP.db')
        self.db.open()
        self.tableView = QTableView()
        self.tableView.horizontalHeader().setStretchLastSection(True)
        self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.tableView.setFont(QFont("苏新诗柳楷繁", 12))
        self.tableView.horizontalHeader().setFont(QFont("苏新诗柳楷繁", 12))
        self.queryModel = QSqlQueryModel()
        self.searchButtonClicked()
        self.tableView.setModel(self.queryModel)

        self.queryModel.setHeaderData(0, Qt.Horizontal, "商品编号")
        self.queryModel.setHeaderData(1, Qt.Horizontal, "商品名称")
        self.queryModel.setHeaderData(2, Qt.Horizontal, "商品厂家")
        self.queryModel.setHeaderData(3, Qt.Horizontal, "上市时间")
        self.queryModel.setHeaderData(4, Qt.Horizontal, "商品价格")
  1. 置信度计算
class ConfidenceCalculate():
    def verifyExist(self,x,array):
        for i in range(len(array)):
            if(x == array[i]):
                return True
        return False

    def readFile(self,itemCount):
        f = open('./data/record.DAT','r')
        f1 = f.read().split('\n')
        recordCount = len(f1)
        for i in range(recordCount):
            f1[i] = f1[i].split(',')
        y = []
        for i in range(itemCount):
            x = []
            for j in range(itemCount):
                if(i+1<10):
                    i1 = '0' + str(i+1)
                else:
                    i1 = str(i+1)
                if(j+1<10):
                    j1 = '0' + str(j+1)
                else:
                    j1 = str(j+1)
                w1 = 0
                w2 = 0
                for k in range(recordCount):
                    if(self.verifyExist(i1,f1[k]) == True):
                        w1 = w1 + 1
                        if(self.verifyExist(j1,f1[k]) == True):
                            w2 = w2 + 1
                c = float(w2)/float(w1)
                oc = [j1,c]
                x.append(oc)
            y.append(x)

        for i in range(len(y)):
            print(y[i])

        s = []
        for i in range(len(y)):
            t = []
            if (i < 9):
                iw = '0' + str(i + 1)
            else:
                iw = str(i + 1)
            for m in range(3):
                max = 0
                flag = 0
                for j in range(len(y[i])):
                    if(y[i][j][0] == iw):
                        continue
                    elif(y[i][j][1] > max):
                        max = y[i][j][1]
                        flag = j
                t.append(y[i][flag][0])
                y[i][flag][1] = -1
            s.append(t)

        for i in range(len(s)):
            print(s[i])

        return s
  1. Apriori算法
1.	class Apriori():  
2.	  
3.	    def createC1(dataSet):  
4.	        C1 = []  
5.	        for transaction in dataSet:  
6.	            for item in transaction:  
7.	                if not [item] in C1:  
8.	                    C1.append([item])  
9.	        C1.sort()  
10.	        return list(map(frozenset, C1))  
11.	  
12.	    def scanD(D, Ck, minSupport):  
13.	        ssCnt = {}  
14.	        for tid in D:  
15.	            for can in Ck:  
16.	                if can.issubset(tid):  
17.	                    if can not in ssCnt:  
18.	                        ssCnt[can] = 1  
19.	                    else:  
20.	                        ssCnt[can] += 1  
21.	        numItems = float(len(D))  
22.	        retList = []   
23.	        supportData = {}   
24.	        for key in ssCnt:  
25.	            support = ssCnt[key] / numItems  
26.	            if support >= minSupport:  
27.	                retList.insert(0, key)  
28.	            supportData[key] = support  
29.	        return retList, supportData  
30.	 
31.	    def aprioriGen(Lk, k):  
32.	        retList = []  
33.	        lenLk = len(Lk)  
34.	        for i in range(lenLk):  
35.	            for j in range(i + 1, lenLk):  
36.	                L1 = list(Lk[i])[:k - 2] 
37.	                L2 = list(Lk[j])[:k - 2]  
38.	                L1.sort();  
39.	                L2.sort()  
40.	                if L1 == L2:  
41.	                    retList.append(Lk[i] | Lk[j])  
42.	        return retList    
43.	  
44.	    def apriori(dataSet, minSupport=0.3):  
45.	        D = list(map(set, dataSet))  
46.	        C1 = Apriori.createC1(  
47.	            dataSet)  
48.	        L1, supportData = Apriori.scanD(D, C1, minSupport)   
49.	        L = [L1]  
50.	        k = 2  
51.	        while (len(L[k - 2]) > 0): 
52.	            Ck = Apriori.aprioriGen(L[k - 2], k)   
53.	            Lk, supK = Apriori.scanD(D, Ck, minSupport)   
54.	            supportData.update(supK) 
55.	            L.append(Lk)  
56.	            k += 1  
57.	        return L, supportData  
58.	  
59.	  
60.	    def loadData(f):  
61.	        f = open('./data/record.DAT','r')  
62.	        t = f.read().split('\n')  
63.	        for i in range(len(t)):  
64.	            t[i] = t[i].split(',')  
65.	        for i in range(len(t)):  
66.	            for j in range(len(t[i])):  
67.	                t[i][j] = int(t[i][j])  
68.	        f.close()  
69.	        return t  
70.	  
71.	    def printData(L,suppData):  
72.	        f = open('result\\result.DAT','w')  
73.	        for j in range(len(L[1])):  
74.	            f.write(str(L[1][j])[11:][:-2]+'\n')  
75.	        f.close()  

实验体会

CRM算法我实现的较快,较为复杂的是数据集的制作,我选择了和老师给的示例一样的电脑商品,对电脑属性选择了商品编号、商品名称、生产厂商、上市日期、商品价格、商品图片、电脑屏幕、CPU、显卡、优点和缺点,选择的是中关村电脑网站上的商品,电脑的属性也是如实的。本来是想创建三四十个商品的数据表,但是最后觉得有点麻烦我就只选择了当前电脑排行榜前十五的商品。开始时使用的Apriori算法,计算每两个商品之间的支持度,然后在满足最小支持度的二元频繁集中随机选择三个商品,如果满足最小支持度的二元频繁集的较少,则显示仅有的商品。后来上课时老师提到了关联度的计算,我觉得我的算法并不靠谱,因为当有三个以上满足最小支持度的二元频繁集时,应当计算每一个关联度,然后从大到小排列关联度显示前三的商品,这样更加的靠谱些。然后最后就改成了先计算满足最小支持度的商品,再对关联度进行排序。