Python图形化客户关联关系
图形用户界面展示客户关联关系
一.创作背景
金融行业反洗钱作业中会有自检自查的工作,例如通过下表作为交易数据判断:
1.张三和周扒皮之间是否有交易关联,如何关联
2.小利和小泉之间是否有交易关联,是否有反向关联
3…假设有10万条数据,找出A和B的关系
二.设计思路
pandas
数据处理,整合成可用于图计算的数据格式;networkx
构建图形和计算,其中关键方法有DiGraph
构建有向图,has_path
判断是否连通,shortest_path_length
获取最短路径长度 ,all_shortest_paths
最短路径获取,draw
图形设置;tkinter
设置用户界面;Pyinstaller
封装为exe文件。
三.导入所需要的库
import
导入库,其中 tkinter
用于图形用户界面(GUI)设计,networkx
用于图计算是核心工具包,matplotlib
用于画图,其他均为常规库在此不赘述。
import tkinter as tk
import tkinter.messagebox as tm
import pandas as pd
import networkx as nx
from collections import Counter
import os
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings('ignore')
四.图形用户界面代码实现
此处建立类旨在构建可复用的图形化界面,按键调用函数可根据不同需求任意修改。本文包含了button_query
button_draw
button_clearn
三个按键对于的操作,可作为参考。
初始用户界面
- 两个输入框及框名显示
- 三个按键(查询、图形、清空)
- 结果返回(放置在输入框上方)
查询用户界面
根据输入不同,查询结果及提示各有差异,具体展示可按照自己喜好设置
图形用户界面
用于一线人员的工具必须注意隐私保护,防止因个人疏忽导致信息泄露,除源节点及目标节点外其他节点信息用数字代替(且保证数字不会指向任何固定用户)
其他界面
其他界面及展示如有需要可自行尝试
代码实现
注释详细,不再赘述
class TkSet:
"""
窗口设置
"""
def __init__(self, init_window_name):
"""窗口大小及位置设置"""
self.init_window_name = init_window_name
# 得到屏幕宽度
self.sw = init_window_name.winfo_screenwidth()
# 得到屏幕高度
self.sh = init_window_name.winfo_screenheight()
# 窗口宽 和 高
self.ww = 320
self.wh = 120
# 窗口位于屏幕x轴 和 y轴
self.x = int((self.sw - self.ww) / 2.5)
self.y = int((self.sh - self.wh) / 3.5)
def set_window(self):
"""窗口内容设置"""
self.init_window_name.title("关联关系分析")
# 位置设置 窗口宽x窗口高+窗口位于屏幕x轴+窗口位于屏幕y轴
self.init_window_name.geometry('{}x{}+{}+{}'.format(self.ww, self.wh, self.x, self.y))
# 查询结果提示
self.query_result = tk.Label(self.init_window_name, text='')
self.query_result.grid(row=0, columnspan=2) # 跨越两列显示
# 第一行起始节点输入框
self.source_tips = tk.Label(self.init_window_name, text='起始端名称:')
self.source_tips.grid(row=1, sticky=tk.W)
self.source = tk.Entry(self.init_window_name)
self.source.grid(row=1, column=1, sticky=tk.E, padx=3)
# 第二行目标节点输入框
self.target_tips = tk.Label(self.init_window_name, text='结束端名称:')
self.target_tips.grid(row=2, sticky=tk.E)
self.target = tk.Entry(self.init_window_name)
self.target.grid(row=2, column=1, sticky=tk.E, padx=3)
# 第三行查询按钮
# Frame框架控件;在屏幕上显示一个矩形区域,多用来作为容器
self.f_btn = tk.Frame(self.init_window_name)
self.b_query = tk.Button(self.f_btn, text='查询', width=6, command=self.button_query)
self.b_query.grid(row=0, column=0)
# self.b_cancel = tk.Button(self.f_btn, text='取消', width=6, command=self.init_window_name.quit)
# self.b_cancel.grid(row=0, column=1)
self.b_draw = tk.Button(self.f_btn, text='图形', width=6, command=self.button_draw)
self.b_draw.grid(row=0, column=1)
self.b_clearn = tk.Button(self.f_btn, text='清空', width=6, command=self.button_clearn)
self.b_clearn.grid(row=0, column=2)
self.f_btn.grid(row=3, columnspan=2, pady=10)
self.init_window_name.mainloop()
def button_query(self):
"""按键返回信息设置"""
# 首先:输入的信息获取
self.source_node = self.source.get()
self.target_node = self.target.get()
# 然后:查询结果显示
try:
self.query_result['text'] = '查询成功!'
self.query_result['fg'] = 'green'
self.query_show()
except:
self.query_result.configure(text='名称输入错误!', fg='red')
def button_draw(self):
"""按键返回信息设置"""
# 首先:输入的信息获取
self.source_node = self.source.get()
self.target_node = self.target.get()
# 然后:查询结果显示
try:
self.query_result['text'] = '图形展示成功!'
self.query_result['fg'] = 'green'
self.draw_show()
except:
self.query_result.configure(text='无相关关系,无法展示图形!', fg='red')
def button_clearn(self):
"""清空输入框内容"""
self.source_node = self.source.get()
self.target_node = self.target.get()
self.len_source = len(self.source_node)
self.len_target = len(self.target_node)
self.source.delete(0, self.len_source)
self.target.delete(0, self.len_target)
self.query_result.configure(text='清理成功!', fg='green')
def query_show(self):
"""查询结果展示"""
# 关联关系判断
print_information = get_relation(graph, self.source_node, self.target_node)
# 文字信息
tm.showinfo("查询结果显示", print_information)
def draw_show(self):
"""图形结果展示"""
# 关系图绘制
draw_graph_sub(graph, self.source_node, self.target_node)
五.客户关联关系判断
数据获取并构建图
MultiDiGraph
构建多重有向图
add_edges_from
添加节点及边信息
全图不适合对一线作业人员展示,此处不做绘图操作
def get_data():
"""
获取数据并生成图
"""
# 数据获取
file_list = os.listdir()
file_path = [x for x in file_list if x.find('.xlsx') != -1][0]
data_str = pd.read_excel(file_path)
# Dataframe数据生成字典格式
# 情况一:数据为明细数据
data_list = data_str.iloc[:, 0:2].values.tolist()
data_list = [tuple(x) for x in data_list]
relation_dict = Counter(data_list)
# 情况二:数据为统计数据
# data_keys = data_str.iloc[:, 0:2].values.tolist()
# data_values = data_str.iloc[:, -1].values.tolist()
# relation_dict = {tuple(k): v for k, v in zip(data_keys, data_values)}
relation_data = {k: v for k, v in relation_dict.items() if v > 0}
relation_data = dict(sorted(relation_data.items(), key=lambda x: x[1], reverse=True))
# 图形建立
gh = nx.MultiDiGraph()
gh.add_edges_from(relation_data)
return gh, relation_data
关系判断并返回结果信息
has_path
判断是否有关联关系
shortest_path_length
获取最短路径长度
def get_relation(g, source, target):
"""
根据起始和终止节点返回信息
"""
# 判断是否有关联
has_path = '是' if nx.has_path(g, source, target) is True else '否'
has_path_re = '是' if nx.has_path(g, target, source) is True else '否'
if has_path == '是':
# 最短路径长度
shortest_path_length = nx.shortest_path_length(g, source, target) - 1
# 最短路径
shortest_path_length = '直连' if shortest_path_length == 0 else str(shortest_path_length)
print_information = '结果打印'.center(30, '=') \
+ '\n' + '{}和{}是否有关联:'.format(source, target) + has_path \
+ '\n' + '{}和{}关联经过几个人:'.format(source, target) + shortest_path_length \
+ '\n' + '{}和{}是否双向关联:'.format(source, target) + has_path_re \
+ '\n' + '打印完毕'.center(30, '=')
else:
print_information = '结果打印'.center(30, '=') \
+ '\n' + '{}和{}是否有关联:'.format(source, target) + has_path \
+ '\n' + '{}和{}是否反向关联:'.format(source, target) + has_path_re \
+ '\n' + '打印完毕'.center(30, '=')
return print_information
六.客户关联图形绘制
此部分的核心在于替换敏感信息的操作,图形颜色设置
画图操作用matplotlib
,可自由选择保存
def draw_graph_sub(g, source, target):
"""返回最短路径图"""
shortest_paths = list(nx.all_shortest_paths(g, source, target))
try:
shortest_paths_re = list(nx.all_shortest_paths(g, target, source))
shortest_paths = shortest_paths + shortest_paths_re
except nx.NetworkXNoPath:
pass
finally:
gs = nx.DiGraph()
# 数据集合
node_set = []
[node_set.extend(paths) for paths in shortest_paths]
node_unique = set(node_set)
# 生成字典
node_encode = []
i = 101
for nu in node_unique:
if nu == source or nu == target:
node_encode.append(nu)
else:
node_encode.append(str(i))
i += 1
node_encode_dict = {k: v for k, v in zip(node_unique, node_encode)}
# 添加节点和边信息
for paths in shortest_paths:
# 替换敏感信息
paths_encode = []
for pts in paths:
for k, v in node_encode_dict.items():
if pts == k:
node_rp = pts.replace(k, v)
paths_encode.append(node_rp)
paths_pro = [(x, y) for x, y in zip(paths_encode[:-1], paths_encode[1:])]
for edge in paths_pro:
gs.add_edge(edge[0], edge[1])
# 子图绘制
plt.figure(figsize=(5, 3))
nx.draw(gs,
with_labels=True,
node_color='#FFA500',
edge_color='#BC8F8F',
)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.title('{}和{}关联关系图:'.format(source, target))
plt.show()
七.代码的简单封装
Anaconda Prompt
下安装 pyinstaller
库,安装成功后可使用下方代码进行封装。
后续会专门写一个文章,解决封装后代码文件大、图标丑的问题
Pyinstaller -F -w relation.py
上述简单封装后文件较大,优化封装具体见本人另一文章 windows下python的pip安装和虚拟环境使用
八.总结
整体操作过程为 数据处理——图形构建——关键信息提取——子图绘制展示——图形化界面设置——代码封装。
*声明:本文所载信息为作者独立设计和编辑,不保证相关信息的准确性和完整性。文中所述内容和意见仅供参考,不构成实际商业建议,如有雷同纯属巧合。