重要声明
本文中的所有信息和数据都是虚拟的,仅为说明数据化审计的思路和过程,不代表真实的交易情况。所有的数据都是使用Python的faker库生成,非真实数据,并根据分析需要进行了调整。
欢迎大家加入小编创建的Python行业交流群,有大牛答疑,有资源共享,有企业招人!是一个非常不错的交流基地!群号:683380553
审计业务背景
在内部审计过程,财务资源支出对应的各项物品、服务采购和基建装修等事项往往都是内部审计的重点。一般情况下,超过一定金额的采购都需要进行招投标,按照一套标准的流程和规则,选择一家性价比最优的公司作为中标单位进行合作。招投标这种制度设计,本来是为了方式内部人舞弊,但却被有心人巧妙地利用了规则,用形式上的合规掩盖了不当操作。“三重一大”中的很多违规舞弊问题都与围标有关。
1.法律法规要求
《招标投标法》规定“投标人不得相互串通投标报价,不得排挤其他投标人的公平竞争,损害招标人或者其他投标人的合法权益。投标人不得与招标人串通投标,损害国家利益、社会公共利益或者他人的合法权益。”《招标投标法实施条例》对“串通投标情形”作了界定和细化,如,“属于同集团、协会、商会等组织成员的投标人按照该组织要求协同投标;投标人之间为谋取中标或者排斥特定投标人而采取的其他联合行动”、“不同投标人委托同一单位或者个人办理投标事宜”等等。简而言之,“围标”是指投标人之间的串通,或通过某种私下约定,共同针对招标人采取的一种博弈策略和手段。“串标”是招标人与投标人之间串通,操纵招标过程、谋定招标结果的行为。
2.内部控制要求
公司相关的招投标管理制度一般都会要求,在招投标过程中要严格审核投标人资质,确保准入审查不走过场。各级机构要严格执行供应商准入审查制度,加强对竞标(选)供应商法人、实际控制人、采购经办人、采购文件编制人、保证金转出人及报价等内容的横向对比审核,严防供应商串标、围标。
3.案例特征提炼
分析内外部公开的案例发现,存在围标的公司之间,总会在一些关键信息上存在交集,往往构成一个隐蔽的网络。如果将参加投标的公司作为一个节点、该公司相关的联系人、银行账号等等静态信息也分别作为节点,可以考虑用 社交网络分析(Social Network Analysis) 的方法,找出隐含的网络,进而识别出关键的节点,挖出围标线索。
什么是社交网络
1.社交网络是复杂网络的一个特例
社交网络分析(Social Network Analysis),也译为社会网络分析,是指对人、组织、计算机或者其他信息或知识处理实体之间的关系和流动信息的映射和测量。一个社交网络由很多节点(Node)和连接这些节点的一种或多种特定的链接(Link,也成为边Edge)所组成。节点往往表示了个人或团体,也即传统数据挖掘中的数据实例,链接则表示了他们之间存在的各种关系(Relation),如股权关系、高管交叉任职关系、朋友关系、亲属关系、贸易关系、资金关系等。
社交网络是复杂网络的一个特例。社会网络分析关注的焦点是关系和关系的模式,采用的方式和方法从概念上有别于传统的统计分析方法。
2.社交网络相关的概念
社交网络分析中涉及很多概念:网络、节点、边、度、路径等。具体如下图所示:
数据分析环境
Python是一种解释型、面向对象、动态数据类型的高级程序设计语言,具有丰富、强大的库,功能全。其中networkx库可用于创建、操作和研究复杂网络的结构、动力学和功能。基于Python环境引入networkx库,基于关系数据创建复杂网络,并在此基础上进行社交网络分析,挖掘围标网络。还可以调用Graphviz生成可视化的网络图,进行直观展现。
本文分析所使用的环境具体如下:
软件或环境说明
Win10 64位系统环境
Python 2.7数据分析语言平台
pandas 0.20.3Excel数据读取和处理
Networkx 1.11社交网络分析
graphviz-2.38网络可视化图生成
数据源和整理
1.内部数据
招标可以区分为自行招标或者委托专业公司公开招标。不管是哪种形式,一般都会有如下的过程资料:
投标人相关信息。公司名称、联系人姓名、银行账号、联系电话。
其他内部信息,比如供应商库、财务系统中的付款对象信息等。
2.外部数据
对参与投标的公司,可以通过天眼查等APP查询到公司的股东、高管、联系电话等信息。
电子投标系统的网络访问信息,如网卡的MAC地址、IP地址、操作员信息等。
3.数据准备
将以上信息整理为如下格式的Excel表。
投标人相关信息。公司名称、联系人姓名、银行账号、联系电话。
投标人工商信息。字段名称包括:公司名称、联系人姓名、联系电话、股东1、股东2、法人代表、董事。
4.模拟数据生成代码
本文就以投标人相关信息为例,挖掘隐含的围标网络。为了演示数据化审计分析过程,通过Python的faker库生成模拟数据进行分析。生成模拟数据的Python代码如下:
1fromfakerimportFaker
2
3fake = Faker(locale='zh_CN')
4# 生成20个公司名称 联系人 联系电话 银行账号
5i =0
6whilei<20:
7info ='%s%s%s\t%s\t%s\t%s\t%s'%(fake.city(),
8fake.company_prefix(),fake.company_suffix(),
9fake.name(),fake.phone_number(),
10fake.bban(),fake.email())
11printinfo
12i +=1
最后形成投标人基本信息的模拟数据,如下图所示:
数据分析过程
1.数据预处理
由于社交网络分析处理的是一对对节点之间的关系,而投标人信息数据中,同一个投标人有多个信息点,需要分别提取,最终形成每对节点“投标公司名称-公司信息点”一一对应的唯一数据集合,将数据处理为三个字段“投标公司名称、信息点、关系描述”的对照表。
1importpandasaspd
2
3# 通过pandas读取Excel文件 联系电话为全数字需要指定为字符型
4df = pd.read_excel(ur"F:\投标公司信息.xlsx",
5dtype = {u'联系电话':str},
6encoding='gbk')
7# 新建一个空白的数据框 用于存放关系数据集合
8dfdetail = pd.DataFrame(columns=[u'投标公司名称',u'信息点',u'关系描述'])
9# 分别从源数据中读取“投标公司名称-公司信息点” 形成关系数据对
10# 追加到结果数据集dfdetail中
11curdf = df[[u'投标公司名称',u'联系人姓名']]
12curdf[u'关系描述'] =u'联系人姓名'
13curdf.columns = dfdetail.columns
14dfdetail = dfdetail.append(curdf,ignore_index=True)
15
16curdf = df[[u'投标公司名称',u'联系电话']]
17curdf[u'关系描述'] =u'联系电话'
18curdf.columns = dfdetail.columns
19dfdetail = dfdetail.append(curdf,ignore_index=True)
20
21curdf = df[[u'投标公司名称',u'银行账号']]
22curdf[u'关系描述'] =u'银行账号'
23curdf.columns = dfdetail.columns
24dfdetail = dfdetail.append(curdf,ignore_index=True)
25
26curdf = df[[u'投标公司名称',u'联系邮箱']]
27curdf[u'关系描述'] =u'联系邮箱'
28curdf.columns = dfdetail.columns
29dfdetail = dfdetail.append(curdf,ignore_index=True)
2.将关系数据转换到社交网络中
1importnetworkxasnx
2# 新建一个有向图(网络)
3G = nx.DiGraph()
4# 循环读取关系数据 关系描述作为关系标签
5foriinrange(0,dfdetail.count(0)[0]):
6G.add_edge(dfdetail.iloc[i,0], dfdetail.iloc[i,1], attrtype=dfdetail.iloc[i,2])
3.找出网络中的关键节点
PageRank算法是Google搜索引擎用来衡量网页重要性的算法,在社交网络中也可以用PageRank值来刻画节点在网络中的重要性程度,帮助寻找重要性节点,也即中心节点。
Networkx自带了PageRank算法模块,可以使用该模块列出可疑围标网络中的关键信息控制点,也就是重点关注的疑点。
1# 使用Pagerank算法查找关键节点
2pr = nx.pagerank(G)
3prdict = sorted(pr.items(), key=lambdad:d[1], reverse =True)
4# 列出前十个最重要的节点
5foridx,curprinenumerate(prdict[:10]):
6print'序号:',idx,'节点:',curpr[0],'节点的PageRank:',curpr[1]
结果输出如下:
序号节点节点的PageRank值
0JBSZ23451484999840.0255622752756
1145831671290.0255622752756
2guiyingqiu@yahoo.com0.023174537151
3186659509850.0207867990265
4卿丽华0.0207867990265
5BQUU02651582531760.0183990609019
6minghuang@yahoo.com0.0183990609019
7BTQW32485505667750.0183990609019
8白建军0.0160113227774
9郭旭0.0160113227774
4.可视化生成关系网络图
networkx自带draw函数,可以画出关系图,但比较简陋,在节点比较多的时候,非常不直观。考虑通过第三方工具进行展现,如商业软件I2、或者开源的graphviz实现。
本文的实现思路是:读取社交网络数据->生成dot文件->利用graphviz提供的功能将dot文件转换为svg图片。
SVG是一种矢量图格式,可以无极缩放,可以进行关键字查找 。生成的SVG图片可以用IE 9.0及以上浏览器、firefox、chrome浏览器打开。
1importcodecs
2importos
3# 定义一个函数用于提取G的内容 写成dot文件
4# 为graphviz生成svg图片提供源
5defG2Dot(g,dotfilename):
6ifdotfilename =='':
7dotfilename ='g2dot.dot'
8fp = codecs.open(dotfilename,mode='w',encoding='utf-8')
9try:
10# 写dot表头
11fp.write('strict digraph {\r\n')
12fp.write(' node [shape="box",fontname="simsun",fontsize="9",margin="0.04",width="0.1",height="0.1"]\r\n')
13fp.write(' edge [fontname="simsun",fontsize="8",arrowhead="open",arrowtail="dot",arrowsize=0.5]\r\n')
14#将节点转换为dict
15gnodes=g.nodes()
16nodenumber=dict(zip(gnodes,range(1,len(gnodes)+1)))
17# 写node数据
18forningnodes:
19na=g.node.get(n,{})
20fontcolor=na.get('fontcolor','black')
21color = na.get('color','lightgray')
22style='filled'
23id=int(na.get('id',nodenumber[n]))
24fp.write(' %d [label="%s",color=%s,fontcolor=%s,style=%s];\r\n'% (id,n,color,fontcolor,style))
25# 写edge数据
26fork,vinnx.get_edge_attributes(g,'attrtype').items():
27fp.write(' %d->%d [label="%s",color=red,fontcolor=red];\r\n'% (nodenumber[k[0]],nodenumber[k[1]],v) )
28
29fp.write('}\r\n')
30finally:
31fp.close()
32
33# 在关系数据很多的情况下 大的社交网络中可能还存在多个不连通的子网络
34# 只生成至少有4个节点的网络的可视化图
35sub_graphs = nx.weakly_connected_component_subgraphs(G)
36forindex,subginenumerate(sub_graphs):
37iflen(subg)>3:
38lDotFileName ='%d.dot'% (index)
39G2Dot(subg,lDotFileName)
40os.system('fdp -Tsvg %s -o %s'% (lDotFileName,lDotFileName.replace('.dot','.svg')))
生成的0号网络图,如下图所示:
数据分析结果应用
重点关注找出来的的关键信息控制点,结合可视化的关系网络,对重点关注的疑点进行延伸检查。
网友评论