Python应用 | 关联规则挖掘:让“影子账户”无所遁形 !
- 交易者账户和其控制的“影子账户”往往在时、空上有一定频率的交集。
- 从交易数据中,找到这些频繁出现的交易集合,也就能找到相应的“影子账户”。
- 数据挖掘中的关联规则挖掘就是这样一种挖掘频繁集的算法,可以让“影子账户”无所遁形。
好久不发技术贴,晚上被问到,就整理了下发出来。
代码很粗糙,专业人士请忽略!
重要声明
本文中的所有信息和数据都是虚拟的,仅为说明数据化审计的思路和过程,不代表真实的交易情况。
Python学习交流群:1004391443,这里是python学习者聚集地,有大牛答疑,有资源共享!小编也准备了一份python学习资料,有想学习python编程的,或是转行,或是大学生,还有工作中想提升自己能力的,正在学习的小伙伴欢迎加入学习。
所有的数据都是使用Python的faker库生成,非真实数据,并根据分析需要进行了调整,不涉及个人信息。
风控业务背景
在内部审计、风险预警、反欺诈等风险控制过程中,很多可疑交易往往不是使用交易者自己的账户进行,而是通过其控制的“影子账户”进行。
大量的业务实践也表明,交易者自己的账户和“影子账户”往往在时、空上有一定频率的交集。从大量的交易数据中,找到这些频繁出现的交易集合,也就能找到相应的“影子账户”。
数据挖掘中的关联规则挖掘就是这样一种挖掘频繁集的算法,可以让“影子账户”无所遁形。
关于数据挖掘的一些基本信息,可参见《审计思考 | 大数据分析技术在内部审计循环中的应用》一文。
什么是关联规则挖掘
1.学术定义
数据挖掘中的关联规则定义如下:
<tt-image data-tteditor-tag="tteditorTag" contenteditable="false" class="syl1560411637265 ql-align-center" data-render-status="finished" data-syl-blot="image" style="box-sizing: border-box; cursor: text; text-align: left; color: rgb(34, 34, 34); font-family: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", "Helvetica Neue", Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; display: block;"> image<input class="pgc-img-caption-ipt" placeholder="图片描述(最多50字)" value="" style="box-sizing: border-box; outline: 0px; color: rgb(102, 102, 102); position: absolute; left: 187.5px; transform: translateX(-50%); padding: 6px 7px; max-width: 100%; width: 375px; text-align: center; cursor: text; font-size: 12px; line-height: 1.5; background-color: rgb(255, 255, 255); background-image: none; border: 0px solid rgb(217, 217, 217); border-radius: 4px; transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) 0s;"></tt-image>
2.通俗表述
学术定义看上去比较复杂,简单一点,关联规则可以表述为“如果发生这种情况,则可能发生以下情况。”也就是在项目A发生的情况下,项目B有多大的概率可能发生。
在《审计思考 | 和数据化审计相关的几个“吓人”名词》一文中,也举个一个应用关联规则挖掘的例子:
<bi style="box-sizing: border-box; display: block;">拿到费用支出数据,整理出支付对象,分析单据交易对手之间的关系…… 嗯?甲客户怎么总是和表面没啥关系的乙客户出现在同一批次的单据中?确认一下这条关联规则的支持度(Support)和置信度(Confidence),聚焦力量检查。</bi>
3.重要参数
描述关联规则算法有三个参数:支持度、置信度与提升度。
<tt-image data-tteditor-tag="tteditorTag" contenteditable="false" class="syl1560411637286 ql-align-center" data-render-status="finished" data-syl-blot="image" style="box-sizing: border-box; cursor: text; text-align: left; color: rgb(34, 34, 34); font-family: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", "Helvetica Neue", Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; display: block;"> image<input class="pgc-img-caption-ipt" placeholder="图片描述(最多50字)" value="" style="box-sizing: border-box; outline: 0px; color: rgb(102, 102, 102); position: absolute; left: 187.5px; transform: translateX(-50%); padding: 6px 7px; max-width: 100%; width: 375px; text-align: center; cursor: text; font-size: 12px; line-height: 1.5; background-color: rgb(255, 255, 255); background-image: none; border: 0px solid rgb(217, 217, 217); border-radius: 4px; transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) 0s;"></tt-image>
数据分析环境
Python是一种解释型、面向对象、动态数据类型的高级程序设计语言,具有丰富、强大的库,功能全。基于Python环境引入Orange库,将交易时间接近的账号交易作为一个会话,并在此基础上,设置一定的置信度、支持度,提取关联规则,找到总是同时出现的账户。
本文分析所使用的环境具体如下:
软件或环境说明Win10 64位系统环境Python 2.7数据分析语言平台pandas 0.20.3Excel数据读取和处理Orange数据挖掘包
数据分析过程
代码可以按住屏幕,左右滑动查看
1.环境初始化和公共函数定义
<pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1# coding: utf-8
2
3# 引入库
4import pandas as pd
5import Orange
6import codecs
7
8# 函数logInfo
9# 写信息到指定的文本文件中,用于存放中间数据
10def logInfo(info,logfile=''):
11 if logfile == '':
12 logfile = 'result.basket'
13 logfp = codecs.open(logfile,mode='a',encoding='utf-8')
14 try:
15 logfp.write(info+'\r\n')
16 finally:
17 logfp.close()
</pre>
2.数据预处理
提取交易数据,包括如下字段:交易地点、交易日期、交易时间、账户。原始的交易数据是一条条记录,关联规则算法需要处理的是一个个会话,也就是所谓的购物篮数据。
所谓购物篮数据,就是类似于超市场的购物篮,每次购买的东西放在一个篮子里,每个篮子都是一组,基于此数据才能挖掘哪些商品是存在购买中的关联行为的。
所以,需要根据行式的交易记录,构建购物篮形式的数据。从业务实践看,在同一个地点交易,如果是同一个人持两个账户,交易间隔比较接近。
基于此,假设5分钟之内的所有账户交易为一个“购物篮”。(更优的算法是,两个没有交易的较大间隔中的交易为一个“购物篮”)
<pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1##
2## 将相近的交易(如5分钟)视为一个会话
3## 从交易记录文件生成购物篮数据
4##
5def Record2Basket(filename=''):
6 data = pd.read_csv(u'交易数据.txt',sep=',',header=0,usecols=[u'交易地点',u'交易日期',u'交易时间',u'账户'],dtype={u'交易地点':str,u'交易日期':str,u'交易时间':str,u'账户':str})
7 data = data.dropna(how='any')
8 datagrp = data.groupby([u'交易地点',u'交易日期'],sort=True)
9 for name,subgrp in datagrp:
10 subgrprowsnum = subgrp.count(0)[0]
11 if subgrprowsnum > 1:
12 subgrp = subgrp.sort_values(by=u'交易时间',ascending=True) #根据时间排序
13 begintime = subgrp.iloc[0,2]
14 Titems = subgrp.iloc[0,3]
15 for i in range(1,subgrprowsnum):
16 endtime = subgrp.iloc[i,2]
17 timegap = (pd.to_datetime(endtime) - pd.to_datetime(begintime)).total_seconds()
18 if abs(timegap) <=5*60:
19 Titems = Titems + ',' + subgrp.iloc[i,3]
20 else:
21 if len(Titems.split(','))>1 :
22 print Titems
23 logInfo(info=Titems,logfile = 'result.basket')
24 Titems = subgrp.iloc[i,3]
25 begintime = endtime
26 if len(Titems.split(','))>1 :
27 print Titems
28 logInfo(info=Titems,logfile = 'result.basket')
</pre>
3.提取关联规则
基于处理后的交易数据result.basket,提取关联规则。输出到屏幕,同时存入到规则文件rules.txt。
<pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1# 调用函数 生成购物篮数据
2Record2Basket(u'交易数据.txt')
3# 读入生成的购物篮数据
4data = Orange.data.Table('result.basket')
5# 三个参数可以根据实际数据进行调整
6rules = Orange.associate.AssociationRulesSparseInducer(data, support = 0.000166, confidence=0.8,maxItemSets=1500000)
7print "%4s %6s %5s %6s %s" % ("Index","Supp", "Conf", "Lift", "Rule")
8for index, r in enumerate(rules):
9 print "%03d %6.6f %5.3f %6.6f %s %s %s" % (index+1, r.support, r.confidence, r.lift, r, r.left.get_metas(str).items()[0][0], r.right.get_metas(str).items()[0][0])
10 logInfo(info="%03d %6.6f %5.3f %6.6f %s %s %s" % (index+1, r.support, r.confidence,
</pre>