分享

Python | 模型跑出结果了,还要自动生成报告?

 北方的白桦林 2023-09-24
内容导读
  • 疑点数据很多,能自动生成报告吗?

  • python-docx 库的介绍

  • “伪现金”交易批量核查单生成实例

本文只代表个人观点,不代表所在单位意见。

引子

很多审计同仁对数字化审计的梦想就是:点一个功能按钮,后台数据跑一下,分析报告就自动跑出来了

梦想还是要有的,万一实现了呢!

类似的问题被问过多次,一直想整理出来,拖更太久了!非常抱歉!

在数字化审计过程中,虽然直接将数据分析结果直接作为问题写成工作底稿不太可能,但将分析出来的疑点根据特定的维度(比如,分公司)分发下去核实,是经常会遇到的场景。

通常的流程就是:思路梳理 -> 数据分析 -> 结果整理 -> 撰写核查单。

在需要分发的核查单较多的情况下,“结果整理 -> 撰写核查单”会耗费大量的时间,进行很多典型的低价值密度工作

如何自动批量生成核查单呢?

python 的 python-docx 库可以生成docx文档,插入文字、表格、图片等文档要素,可以通过 python-docx 实现核查单批量生成。

python-docx

python-docx 可以创建、修改、编辑 docx 格式(不支持 doc 格式)的 word 文档,提供提供全套的 Word 操作。

python-docx 有三个基本的对象:

  • Document:docx 文档对象,打开或创建文件后建立。
  • Paragraph:文档中的段落,每个docx文档都是有多个段落组成的,通过document.add_paragraph()生成。
  • Run:段落中的节段,每个段落由多个节段组成。这是python-docx对文档进行各种操作的直接对象,通过Paragraph对象的 add_run() 生成。

对象之间的关系如下图:

图片

(图源:python-docx  documentation)

python-docx 库可以通过 pip install python-docx 安装,但需要注意的是:python-docx 在python引入时的库名是 docx 而不是安装时的 python-docx

python-docx 的官方文档地址:https://python-docx./en/latest/index.html

案例背景

在反洗钱领域,存在一种“伪现金”交易,通过该方式可以通过现金过桥的方式,割裂大额现金转账交易,改变交易性质。

《“伪现金”交易的洗钱风险及其防范》[1] 一文中的定义:

“伪现金”交易指未发生真实现金存取,而是通过利用现金业务方式和渠道提供资金划转和转账服务,实现资金在不同账户间流动的交易。该类交易人为割裂交易链条,改变交易性质,造成大额交易错报和漏报,影响可疑交易的分析和甄别,存在洗钱风险。

但由于客户个人偏好、经营需要等,客户的“伪现金”交易并非一定是洗钱行为,对此类数据进行分析后,往往需要下发核查单由交易机构进行核实。

本案例通过对根据“伪现金”交易提取的交易流水分析后,挖掘高风险的交易网络,根据不同机构生成“现金过账核查单”,核查单包括网络的节点、关键节点,以及可视化的关系网络,方便核查机构快速定位问题和关键点。

演示用的疑点清单中,每条记录包括机构号、交易日期、前一笔现金取款的客户号、交易金额、后一笔现金存款的客户号、交易金额等字段。(测试数据随机生成,不表示真实的交易情况!)

生成“现金过账核查单”如下图示意。

图片

实现代码

本文运行环境:Python 3.8 / Pandas 1.1.3 / networkx 2.5

1.初始化库

import matplotlib.pyplot as plt
import networkx as nx
import pandas as pd
# 编辑docx文件需要的各种库
from docx import Document
from docx.enum.text import  WD_ALIGN_PARAGRAPH
from docx.enum.style import WD_STYLE_TYPE
from docx.shared import Pt
from docx.oxml.ns import qn
from docx.shared import RGBColor

2.通用功能封装成函数

#定义函数
#设置中文字体函数
def setRun(run,size=9):
    run.font.name='微软雅黑'
    run.font.size = Pt(size)
    run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')

#根据子图生成关系网络图函数  
def genImg(subg):
    plt.clf()
    node_size = [subg.degree(i)*150 for i in subg.nodes()]
    node_colors = [subg.degree(i) for i in subg.nodes()]
    g_options = {
        'pos': nx.spring_layout(subg),
        'node_size': node_size,
        'node_color': node_colors,
        'cmap': plt.cm.hsv, 
        'edge_color''gray',
        'with_labels'True,
        'node_shape''o'
    }
    nx.draw(subg, **g_options)
    plt.savefig('tmp.jpg')

3.疑点数据自动生成报告

#主程序开始
if __name__=='__main__':
    #读取机构的交易数据 并将数字格式的字符型字段指定为字符
    ttdata = pd.read_excel('现金过账交易清单(测试).xlsx',
                             sheet_name='疑点清单',
                             converters={'交易日期': str,'取款客户号': str,'存款客户号': str}                         
                            )
    #对同一对客户的交易明细进行汇总 生成关系数据
    ttdata_grp = ttdata.groupby(['交易机构名称','取款客户号','存款客户号'],as_index=False).size()
    ttdata_grp = ttdata_grp.rename(columns={'size':u'笔数'})
    #建立一个无向图(最大连通子图函数只支持无向图)
    G = nx.Graph()
    #关系数据添加到图中 有更简单的函数 这样写更易理解
    for i in range(len(ttdata_grp)):
            item = ttdata_grp.iloc[i,:]
            G.add_edge(item['取款客户号'],item['存款客户号'],weight = item['笔数'])
    #当前机构名称
    brname = ttdata_grp.iloc[0,0]    
    #生成空白docx文档
    document = Document()
    #设置文档的属性
    document.core_properties.author = 'SmartAudit'
    document.core_properties.comments = 'Confidential'    
    #定义样式
    ##定义标题样式
    MainTitleStyle = document.styles.add_style('MainTitle', WD_STYLE_TYPE.PARAGRAPH)
    MainTitleStyle.font.name = 'SimSun'
    MainTitleStyle.font.size = Pt(22)
    ContentStyle = document.styles.add_style('Content', WD_STYLE_TYPE.PARAGRAPH)
    ContentStyle.font.name = u'宋体'
    ContentStyle.font.size = Pt(9)
    #添加标题
    p = document.add_paragraph(u'',style=MainTitleStyle)
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    #设置中文字体
    run = p.add_run(u'现金过账核查单(编号001)')
    setRun(run,18)    
    #添加机构的基本情况
    h = document.add_heading(u'',level=1)
    run = h.add_run(brname)
    setRun(run,14)   
    #建立一个空表格
    table = document.add_table(rows=1, cols=1)
    table.style = 'TableGrid'
    curCell = table.rows[0].cells[0]
    sText = u'全图涉及客户数:%d  客户往来关系数:%d'%(G.number_of_nodes(),G.number_of_edges())
    curCell.paragraphs[0].style = ContentStyle
    run = curCell.paragraphs[0].add_run(sText)
    setRun(run,9)  
    #提取所有的连通子图
    sub_graphs = list(nx.connected_components(G))
    #只处理客户数大于5的子图
    #有多个就在表格中生成多行
    for index,subgnodes in enumerate(sub_graphs):
        if len(subgnodes)>5:
            #根据子图的客户节点列表 从全图中提取出无向图
            subg = G.subgraph(subgnodes)
            rows = table.add_row()
            rows.height_rule = 0
            cells = rows.cells
            nodestr = ', '.join(subg.nodes())
            #节点重要性排序 根据客户中心度找出网络中的关键客户
            nodes = nx.degree_centrality(subg)
            nodeslist =  sorted(nodes.items(), key=lambda x: x[1], reverse=True)[0:4]
            #列出排序前4的客户及其中心度
            keynodestr = '\r关键客户(中心度Degree Centrality):'
            for node in nodeslist:
                keynodestr = keynodestr + '\r%s (%f)'%(node[0],node[1])  
            #拼接成段落文本内容
            run = cells[0].paragraphs[0].add_run('重点集群索引:%d  包含客户数:%d\r%s\r%s'%(index,subg.number_of_nodes(),nodestr,keynodestr))
            setRun(run,8)
            #根据当前子图生成关系网络图片
            genImg(subg)
            #将图片插入docx文档当前单元格
            cells[0].paragraphs[0].add_run().add_picture('tmp.jpg')
    #核查要求 红字显示
    rows = table.add_row()
    run = rows.cells[0].paragraphs[0].add_run('核查要求:\r请在2022年X月X日前返回核查结果!')
    run.font.color.rgb = RGBColor (255,0,0)
    setRun(run,18)
    #保存docx文件
    localfilename = '现金过账核查单(%s).docx'%(brname)
    document.save(localfilename)

参考文献

  1. “伪现金”交易的洗钱风险及其防范,[E/OL],https://finance.sina.com.cn/money/fund/jjzl/2021-11-11/doc-iktzqtyu6681354.shtml
  2. python-docx 0.8.11 documentation,[E/OL],https://python-docx./en/latest/index.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多