feat: 初始提交 - 科创企业特有风险的识别与管理 (数智风控系统)
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
知识图谱构建引擎
|
||||
基于 NetworkX 构建科创企业供应链风险传染图谱
|
||||
节点类型:企业、供应商、客户、核心人员、制裁实体
|
||||
边类型:供应关系、客户关系、任职关系、投资关系
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import networkx as nx
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DATA_DIR = Path(__file__).parent.parent / "data"
|
||||
|
||||
|
||||
def build_graph() -> nx.DiGraph:
|
||||
"""
|
||||
构建完整的科创企业供应链风险知识图谱
|
||||
"""
|
||||
G = nx.DiGraph()
|
||||
|
||||
# 1. 加载企业数据,添加企业节点
|
||||
_add_company_nodes(G)
|
||||
|
||||
# 2. 加载供应链数据,添加关系边
|
||||
_add_supply_chain_edges(G)
|
||||
|
||||
# 3. 加载实体清单,标记受制裁节点
|
||||
_mark_sanctioned_nodes(G)
|
||||
|
||||
logger.info(f"图谱构建完成: {G.number_of_nodes()} 节点, {G.number_of_edges()} 边")
|
||||
return G
|
||||
|
||||
|
||||
def _add_company_nodes(G: nx.DiGraph):
|
||||
"""添加科创板企业节点及其关联的核心人员节点"""
|
||||
filepath = DATA_DIR / "sample_companies.json"
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
companies = json.load(f)
|
||||
|
||||
for company in companies:
|
||||
name = company["short_name"]
|
||||
G.add_node(
|
||||
name,
|
||||
node_type="科创企业",
|
||||
stock_code=company["stock_code"],
|
||||
industry=company["industry"],
|
||||
sector=company["sector"],
|
||||
is_sanctioned=company["compliance"]["entity_list_status"] != "未列入",
|
||||
risk_level="正常",
|
||||
color="#4CAF50", # 默认绿色
|
||||
)
|
||||
|
||||
# 添加核心技术人员节点
|
||||
for person in company.get("core_tech_personnel", []):
|
||||
person_id = f"{person['name']}@{name}"
|
||||
G.add_node(
|
||||
person_id,
|
||||
node_type="核心人员",
|
||||
real_name=person["name"],
|
||||
title=person["title"],
|
||||
status=person["status"],
|
||||
importance=person["importance"],
|
||||
company=name,
|
||||
color="#2196F3", # 蓝色
|
||||
)
|
||||
G.add_edge(
|
||||
person_id, name,
|
||||
relation="任职于",
|
||||
edge_type="personnel",
|
||||
)
|
||||
|
||||
# 添加供应商节点
|
||||
for supplier in company.get("supply_chain", {}).get("key_suppliers", []):
|
||||
supplier_name = supplier.split("(")[0].split("(")[0].strip()
|
||||
if not G.has_node(supplier_name):
|
||||
G.add_node(
|
||||
supplier_name,
|
||||
node_type="供应商",
|
||||
is_sanctioned=False,
|
||||
risk_level="正常",
|
||||
color="#FF9800", # 橙色
|
||||
)
|
||||
G.add_edge(
|
||||
supplier_name, name,
|
||||
relation="供应",
|
||||
detail=supplier,
|
||||
edge_type="supply",
|
||||
)
|
||||
|
||||
# 添加客户节点
|
||||
for customer in company.get("supply_chain", {}).get("key_customers", []):
|
||||
customer_name = customer.split("(")[0].split("(")[0].strip()
|
||||
if not G.has_node(customer_name):
|
||||
G.add_node(
|
||||
customer_name,
|
||||
node_type="客户",
|
||||
is_sanctioned=False,
|
||||
risk_level="正常",
|
||||
color="#9C27B0", # 紫色
|
||||
)
|
||||
G.add_edge(
|
||||
name, customer_name,
|
||||
relation="供货给",
|
||||
edge_type="customer",
|
||||
)
|
||||
|
||||
|
||||
def _add_supply_chain_edges(G: nx.DiGraph):
|
||||
"""从供应链关系文件添加更详细的边"""
|
||||
filepath = DATA_DIR / "supply_chain.json"
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
# 添加供应关系
|
||||
for rel in data.get("supply_relations", []):
|
||||
from_node = rel["from"]
|
||||
to_node = rel["to"]
|
||||
|
||||
# 确保节点存在
|
||||
if not G.has_node(from_node):
|
||||
G.add_node(from_node, node_type="供应商", is_sanctioned=False,
|
||||
risk_level="正常", color="#FF9800")
|
||||
if not G.has_node(to_node):
|
||||
G.add_node(to_node, node_type="企业", is_sanctioned=False,
|
||||
risk_level="正常", color="#4CAF50")
|
||||
|
||||
G.add_edge(
|
||||
from_node, to_node,
|
||||
relation=rel["relation"],
|
||||
critical=rel.get("critical", False),
|
||||
status=rel.get("status", "正常"),
|
||||
edge_type="supply",
|
||||
)
|
||||
|
||||
# 添加投资关系
|
||||
for rel in data.get("investment_relations", []):
|
||||
from_node = rel["from"]
|
||||
to_node = rel["to"]
|
||||
|
||||
if not G.has_node(from_node):
|
||||
G.add_node(from_node, node_type="投资方", is_sanctioned=False,
|
||||
risk_level="正常", color="#607D8B")
|
||||
|
||||
G.add_edge(
|
||||
from_node, to_node,
|
||||
relation=rel["relation"],
|
||||
share_ratio=rel.get("share_ratio", 0),
|
||||
edge_type="investment",
|
||||
)
|
||||
|
||||
|
||||
def _mark_sanctioned_nodes(G: nx.DiGraph):
|
||||
"""标记受制裁的节点,并向下游传播风险"""
|
||||
filepath = DATA_DIR / "entity_list.json"
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
entities = json.load(f)
|
||||
|
||||
# 收集所有受制裁实体的名称和别名
|
||||
sanctioned_names = set()
|
||||
for entity in entities:
|
||||
sanctioned_names.add(entity["entity_name"])
|
||||
for alias in entity.get("aliases", []):
|
||||
sanctioned_names.add(alias)
|
||||
|
||||
# 标记图谱中的受制裁节点
|
||||
for node in G.nodes():
|
||||
for sname in sanctioned_names:
|
||||
if node in sname or sname in node:
|
||||
G.nodes[node]["is_sanctioned"] = True
|
||||
G.nodes[node]["risk_level"] = "高危"
|
||||
G.nodes[node]["color"] = "#F44336" # 红色
|
||||
break
|
||||
|
||||
|
||||
def get_node_info(G: nx.DiGraph, node_name: str) -> dict:
|
||||
"""获取节点详细信息"""
|
||||
if node_name not in G:
|
||||
return {"error": f"节点 '{node_name}' 不存在"}
|
||||
|
||||
node_data = dict(G.nodes[node_name])
|
||||
predecessors = list(G.predecessors(node_name))
|
||||
successors = list(G.successors(node_name))
|
||||
|
||||
return {
|
||||
"name": node_name,
|
||||
"attributes": node_data,
|
||||
"upstream": predecessors,
|
||||
"downstream": successors,
|
||||
"degree": G.degree(node_name),
|
||||
}
|
||||
|
||||
|
||||
def get_graph_stats(G: nx.DiGraph) -> dict:
|
||||
"""获取图谱统计信息"""
|
||||
node_types = {}
|
||||
for _, data in G.nodes(data=True):
|
||||
t = data.get("node_type", "未知")
|
||||
node_types[t] = node_types.get(t, 0) + 1
|
||||
|
||||
sanctioned_count = sum(
|
||||
1 for _, data in G.nodes(data=True) if data.get("is_sanctioned", False)
|
||||
)
|
||||
|
||||
return {
|
||||
"total_nodes": G.number_of_nodes(),
|
||||
"total_edges": G.number_of_edges(),
|
||||
"node_types": node_types,
|
||||
"sanctioned_nodes": sanctioned_count,
|
||||
"density": nx.density(G),
|
||||
}
|
||||
Reference in New Issue
Block a user