import pickle from sqlalchemy import text from orm import engine, connection import pandas as pd import networkx as nx import json import matplotlib.pyplot as plt # Prepare data Firm = pd.read_csv("input_data/input_firm_data/Firm_amended.csv") Firm['Code'] = Firm['Code'].astype('string') Firm.fillna(0, inplace=True) BomNodes = pd.read_csv('input_data/input_product_data/BomNodes.csv', index_col=0) # SQL query with open('SQL_analysis_risk.sql', 'r') as f: str_sql = text(f.read()) result = pd.read_sql(sql=str_sql, con=connection) result.to_csv('output_result/risk/count.csv', index=False, encoding='utf-8-sig') print(result) # G_bom plt.rcParams['font.sans-serif'] = 'SimHei' exp_id = 1 G_bom_df = pd.read_sql( sql=text(f'select g_bom from iiabmdb.without_exp_experiment where id = {exp_id};'), con=connection ) if G_bom_df.empty: raise ValueError(f"No g_bom found for exp_id = {exp_id}") G_bom_str = G_bom_df['g_bom'].tolist()[0] if G_bom_str is None: raise ValueError(f"g_bom data is None for exp_id = {exp_id}") G_bom = nx.adjacency_graph(json.loads(G_bom_str)) pos = nx.nx_agraph.graphviz_layout(G_bom, prog="twopi", args="") node_labels = nx.get_node_attributes(G_bom, 'Name') node_labels = { 7: 'Si Raw Mtl.', 8: 'Photoresist & Reagents', 9: 'Etch Solution', 10: 'SiF4', 11: 'Developer', 12: 'PCE Superplasticizer', 13: 'Metal Protectant', 14: 'Deep Hole Cu Plating', 15: 'Thinner', 16: 'HP Boric Acid (Nuc.)', 17: 'E-Grade Epoxy', 18: 'Stripper', 19: 'HP-MOC', 20: 'CMP Slurry & Consumables', 21: 'PR Remover', 22: 'Poly-Si Cutting Fluid', 23: 'Passivation', 24: 'E-Grade Phenolic', 25: 'Surfactant', 26: 'Mag. Carrier', 27: 'Wet Chems.', 28: 'Plating Chems.', 29: 'E-FR Materials', 30: 'LC Alignment Agent', 31: 'Func. Wet Chems.', 32: 'InP', 33: 'SiC', 34: 'GaAs', 35: 'GaN', 36: 'AlN', 37: 'Si3N4', 38: 'SiC Substrate', 39: 'GaN Substrate', 40: 'Si Wafer', 41: 'AlN Substrate', 42: 'DUV LED Substrate', 43: 'InP Substrate', 44: 'Mono-Si Wafer', 45: 'Poly-Si Wafer', 46: 'InP Cryst./Wafer', 47: 'SiC Cryst./Wafer', 48: 'GaAs Wafer', 49: 'GaN Cryst./Wafer', 50: 'Si Epi Wafer', 51: 'SiC Epi Wafer', 52: 'AlN Epi', 53: 'GaN Epi', 54: 'InP Epi', 55: 'LED Epi', 56: 'EDA/IP', 57: 'MPW Service', 58: 'IC Design', 59: 'Track System', 60: 'Wafer Grinder', 61: 'Etcher', 62: 'Ox/Diff Furnace', 63: 'Wafer Metrology', 64: 'Crystal Grower', 65: 'CMP Tool', 66: 'Stepper', 67: 'Wafer Dicer', 68: 'Deposition System', 69: 'Edge Profiler', 70: 'Descum Tool', 71: 'Clean System', 72: 'SAF', 73: 'Plating Eqpt.', 74: 'Implanter', 75: 'Trim/Form', 76: 'Probe Card', 77: 'ATE', 78: 'PCM Eqpt.', 79: 'Inspection Sys.', 80: 'Prober', 81: 'Dicing Saw', 82: 'Handler', 83: 'Backgrinder', 84: 'Die Bonder', 85: 'Reflow Oven', 86: 'FT Tester', 87: 'Wire Bonder', 88: 'BGA Mounter', 89: 'Molding Press', 90: 'Power Devices', 91: 'Diode', 92: 'Transistor', 93: 'Thyristor', 94: 'Rectifier', 95: 'IC Fab', 96: 'IC PKG', 97: 'DV', 98: 'IPM', 99: 'CP Test', 100: 'FT Test', 101: 'Bumping', 102: 'DA Materials', 103: 'Leadframe', 104: 'Solder Ball', 105: 'Substrate', 106: 'EMC', 107: 'Bond Wire', 108: 'Underfill', 109: 'Dicing Tape' } plt.figure(figsize=(12, 12), dpi=500) plt.axis('off') # 关闭坐标轴边框 # 优化节点绘制参数 nx.draw_networkx_nodes( G_bom, pos, node_size=100, # 优化节点尺寸 linewidths=0.0 # 去除节点边框 ) # 优化边绘制参数 nx.draw_networkx_edges( G_bom, pos, width=0.3, # 更细的边宽 alpha=0.5 # 半透明边 ) # 优化标签参数 nx.draw_networkx_labels( G_bom, pos, labels=node_labels, font_size=3, # 适当增大字号 font_family='sans-serif', # 使用无衬线字体 font_weight='bold', # 增强可读性 ) # 专业级保存参数设置 plt.savefig( f"output_result/risk/g_bom_exp_id_{exp_id}.png", bbox_inches='tight', # 去除图像白边 pad_inches=0.1, # 适当内边距 facecolor='white' # 保证背景纯白 ) plt.close() # G_firm plt.rcParams['font.sans-serif'] = 'SimHei' sample_id = 1 # G_firm_df = pd.read_sql( # sql=text(f'select g_firm from iiabmdb.without_exp_sample where id = {sample_id};'), # con=connection # ) # # if G_firm_df.empty: # raise ValueError(f"No g_firm found for sample_id = {sample_id}") # # G_firm_str = G_firm_df['g_firm'].tolist()[0] # if G_firm_str is None: # raise ValueError(f"g_firm data is None for sample_id = {sample_id}") # # G_firm = nx.adjacency_graph(json.loads(G_firm_str)) with open("firm_network.pkl", 'rb') as f: G_firm = pickle.load(f) print(f"Successfully loaded cached data from firm_network.pkl") # 1. 移除孤立节点 isolated_nodes = list(nx.isolates(G_firm)) # 找出所有没有连接的孤立节点 G_firm.remove_nodes_from(isolated_nodes) # 从图中移除这些节点 # 2. 重新布局和绘图 pos = nx.nx_agraph.graphviz_layout(G_firm, prog="twopi", args="") node_label = {key: key for key in nx.get_node_attributes(G_firm, 'Revenue_Log').keys()} # node_label = { # "7": "1", # "9": "2", # "829768": "4", # "863079": "5", # "1452048": "6", # "2010673": "7", # "2624175": "8", # "2728939": "9", # "5278074": "10", # "5849940": "11", # "7299120": "12", # "9746245": "13", # "11807506": "14", # "15613202": "15", # "24284343": "19", # "24673506": "20", # "25036634": "21", # "25685135": "24", # "25945288": "25", # "26162741": "26", # "26516263": "27", # "27075840": "28", # "27731896": "29", # "29954548": "30", # "43407343": "33", # "70634828": "36", # "71271700": "37", # "80158773": "39", # "118882692": "40", # "145511905": "42", # "151606446": "43", # "152008168": "44", # "159511306": "45", # "191912252": "46", # "194210021": "47", # "203314437": "48", # "213386023": "49", # "218633337": "50", # "251189644": "53", # "271860868": "55", # "278221281": "56", # "301209792": "57", # "343012684": "59", # "354897041": "60", # "400488703": "62", # "400692942": "63", # "413274977": "64", # "420984285": "65", # "448033045": "66", # "453289520": "67", # "474279224": "68", # "483081978": "69", # "495782506": "70", # "503176785": "73", # "549184982": "75", # "560866402": "76", # "561545339": "77", # "571058167": "78", # "581407487": "79", # "591452402": "80", # "593312758": "81", # "594378026": "82", # "607512171": "83", # "615763365": "84", # "620220747": "85", # "631449822": "86", # "644292599": "87", # "653528340": "88", # "654825436": "89", # "688155470": "92", # "695995052": "93", # "750610681": "95", # "762985858": "96", # "771821595": "97", # "857978527": "100", # "868012326": "101", # "887840774": "102", # "888356483": "103", # "888395016": "104", # "888478182": "105", # "930767828": "107", # "996174506": "108", # "1033972427": "110", # "1128343125": "111", # "1217957486": "113", # "1307012237": "115", # "1375606900": "116", # "1549474227": "118", # "1606833003": "120", # "1679596339": "121", # "2310825263": "122", # "2311838590": "124", # "2312490120": "125", # "2316990095": "128", # "2317245827": "129", # "2317841563": "131", # "2320102626": "132", # "2320475044": "133", # "2321109759": "134", # "2324787028": "137", # "2324844174": "138", # "2326478786": "139", # "2327031723": "140", # "2327979389": "141", # "2329375731": "142", # "2333843479": "143", # "2337952436": "146", # "2339188563": "147", # "2339684065": "148", # "2341555098": "149", # "2343704209": "150", # "2348941764": "151", # "2352036411": "155", # "2354145351": "157", # "2424229017": "159", # "2545430247": "161", # "2820140348": "163", # "2944892892": "165", # "3025036704": "168", # "3026382513": "169", # "3045721313": "171", # "3047163873": "172", # "3048263744": "173", # "3069206426": "174", # "3070859372": "175", # "3072715478": "176", # "3103797386": "177", # "3111033905": "178", # "3113895788": "179", # "3120341363": "180", # "3122923980": "181", # "3127420424": "182", # "3133307899": "183", # "3147511625": "184", # "3177507356": "185", # "3188903709": "186", # "3195502499": "187", # "3203777710": "188", # "3211956484": "189", # "3215814536": "190", # "3221190269": "191", # "3226664625": "192", # "3267688490": "193", # "3269039233": "194", # "3269940677": "195", # "3271705843": "196", # "3299144127": "197", # "3312358902": "198", # "3344297292": "200", # "3372913783": "201", # "3373311444": "202", # "3384021594": "203", # "3395900897": "205", # "3398677646": "206", # "3407754893": "207", # "3433628561": "209", # "3445244192": "212", # "3445928818": "213", # "4208851809": "216", # "5007015990": "218", # "11164476478": "219", # "517717050": "223", # "737770776": "224", # "872394725": "225", # "2311581270": "226", # "2313209417": "227", # "2347013470": "228", # "2350418059": "229", # "3031009366": "234", # "3089095447": "235", # "3100891962": "236", # "3188352290": "238", # "3288105727": "239", # "3462551351": "240" # } node_size = [value * 5 for value in nx.get_node_attributes(G_firm, 'Revenue_Log').values()] edge_label = {(n1, n2): label for (n1, n2, _), label in nx.get_edge_attributes(G_firm, "Product").items()} plt.figure(figsize=(15, 15), dpi=500) plt.axis('off') # 完全关闭坐标轴系统 # 分层绘制网络组件 nodes = nx.draw_networkx_nodes( G_firm, pos, node_size=node_size, # 保持原始尺寸设置 ) edges = nx.draw_networkx_edges( G_firm, pos, width=0.3, # 保持原始线宽设置 ) # 优化节点标签 labels = nx.draw_networkx_labels( G_firm, pos, labels=node_label, font_size=6, # 保持原始字号 ) # 增强边标签可读性 edge_labels = nx.draw_networkx_edge_labels( G_firm, pos, edge_labels=edge_label, font_size=2, label_pos=0.5, # 标签沿边偏移量 rotate=False, # 禁止自动旋转 ) # 专业级输出配置 plt.savefig( f"output_result/risk/g_firm_sample_id_{sample_id}_de.png", bbox_inches='tight', pad_inches=0.05, # 更紧凑的边距 facecolor='white', # 强制白色背景 metadata={ 'Title': f"Supply Chain Risk Map - Sample {sample_id}", 'Author': 'USTB Risk Analytics', 'Copyright': 'Confidential' } ) plt.close() # Count firm product count_firm_prod = result.value_counts(subset=['id_firm', 'id_product']) count_firm_prod.name = 'count' count_firm_prod = count_firm_prod.to_frame().reset_index() count_firm_prod.to_csv('output_result/risk/count_firm_prod.csv', index=False, encoding='utf-8-sig') print(count_firm_prod) # Count firm count_firm = count_firm_prod.groupby('id_firm')['count'].sum() count_firm = count_firm.to_frame().reset_index() count_firm.sort_values('count', inplace=True, ascending=False) count_firm.to_csv('output_result/risk/count_firm.csv', index=False, encoding='utf-8-sig') print(count_firm) # Count product count_prod = count_firm_prod.groupby('id_product')['count'].sum() count_prod = count_prod.to_frame().reset_index() count_prod.sort_values('count', inplace=True, ascending=False) count_prod.to_csv('output_result/risk/count_prod.csv', index=False, encoding='utf-8-sig') print(count_prod) # DCP disruption causing probability result_disrupt_ts_above_0 = result[result['ts'] > 0] print(result_disrupt_ts_above_0) result_dcp = pd.DataFrame(columns=[ 's_id', 'up_id_firm', 'up_id_product', 'down_id_firm', 'down_id_product' ]) result_dcp_list = [] # 用列表收集数据,避免DataFrame逐行增长的问题 for sid, group in result.groupby('s_id'): ts_start = max(group['ts']) while ts_start >= 1: ts_end = ts_start - 1 while ts_end >= 0: up = group.loc[group['ts'] == ts_end, ['id_firm', 'id_product']] down = group.loc[group['ts'] == ts_start, ['id_firm', 'id_product']] for _, up_row in up.iterrows(): for _, down_row in down.iterrows(): result_dcp_list.append([sid] + up_row.tolist() + down_row.tolist()) ts_end -= 1 ts_start -= 1 # 转换为DataFrame result_dcp = pd.DataFrame(result_dcp_list, columns=[ 's_id', 'up_id_firm', 'up_id_product', 'down_id_firm', 'down_id_product' ]) # 统计 count_dcp = result_dcp.value_counts( subset=['up_id_firm', 'up_id_product', 'down_id_firm', 'down_id_product'] ).reset_index(name='count') # 保存文件 count_dcp.to_csv('output_result/risk/count_dcp.csv', index=False, encoding='utf-8-sig') # 输出结果 print(count_dcp)