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 三个按键对于的操作,可作为参考。

初始用户界面

  1. 两个输入框及框名显示
  2. 三个按键(查询、图形、清空)
  3. 结果返回(放置在输入框上方)

在这里插入图片描述

查询用户界面

根据输入不同,查询结果及提示各有差异,具体展示可按照自己喜好设置
在这里插入图片描述

图形用户界面

用于一线人员的工具必须注意隐私保护,防止因个人疏忽导致信息泄露,除源节点及目标节点外其他节点信息用数字代替(且保证数字不会指向任何固定用户)
在这里插入图片描述

其他界面

其他界面及展示如有需要可自行尝试

代码实现

注释详细,不再赘述

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安装和虚拟环境使用

八.总结

整体操作过程为 数据处理——图形构建——关键信息提取——子图绘制展示——图形化界面设置——代码封装。

*声明:本文所载信息为作者独立设计和编辑,不保证相关信息的准确性和完整性。文中所述内容和意见仅供参考,不构成实际商业建议,如有雷同纯属巧合。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值