Python环境下如何将Gurobi分支切割树日志转为CSV格式?
Gurobi日志转CSV的实现方法(Python)
一、Gurobi是否支持直接生成CSV格式日志?
Gurobi没有内置功能直接输出CSV格式的分支-切割树搜索日志,默认仅支持生成纯文本(TXT)格式日志文件。
二、Python下转CSV的简便实现方法
由于没有内置支持,需通过读取日志或实时收集数据的方式转换为CSV,以下是两种实用方案:
方法1:使用回调函数实时收集数据(推荐)
求解过程中直接捕获分支树关键数据,无需事后解析日志,准确性和灵活性更高。
import gurobipy as gp from gurobipy import GRB import csv # 初始化模型(替换为你的模型定义) model = gp.Model("mip_model") # ... 添加变量、约束、目标函数 ... # 存储日志数据的列表与表头 log_data = [] headers = [ "Expl", "Unexpl", "Obj", "Depth", "IntInf", "Incumbent", "BestBd", "Gap", "It/Node", "Time", "Flag" ] def mip_callback(model, where): if where == GRB.Callback.MIP: # 提取当前节点核心数据 expl = model.cbGet(GRB.Callback.MIP_NODES) unexpl = model.cbGet(GRB.Callback.MIP_OPENNODES) obj = model.cbGet(GRB.Callback.MIP_OBJBND) depth = model.cbGet(GRB.Callback.MIP_NODEDEPTH) intinf = model.cbGet(GRB.Callback.MIP_INTCOUNT) incumbent = model.cbGet(GRB.Callback.MIP_OBJBST) bestbd = model.cbGet(GRB.Callback.MIP_OBJBND) gap = model.cbGet(GRB.Callback.MIP_GAP) if incumbent != GRB.INFINITY else "" it_per_node = model.cbGet(GRB.Callback.MIP_ITRCOUNT) / (expl + 1) if expl > 0 else "" time = round(model.cbGet(GRB.Callback.RUNTIME), 1) flag = "" # 标记可行解(*) if model.cbGet(GRB.Callback.MIP_SOLCNT) > model._last_sol: model._last_sol = model.cbGet(GRB.Callback.MIP_SOLCNT) flag = "*" # 整理行数据,处理无穷值与空值 row = [ expl, unexpl, obj, depth, intinf, incumbent if incumbent != GRB.INFINITY else "", bestbd, gap, it_per_node, time, flag ] log_data.append(row) # 初始化可行解计数器 model._last_sol = 0 # 绑定回调并求解 model.optimize(mip_callback) # 写入CSV文件 with open("gurobi_mip_log.csv", "w", newline="") as f: writer = csv.writer(f) writer.writerow(headers) writer.writerows(log_data)
方法2:事后解析TXT日志文件
针对已生成的TXT日志,用正则表达式提取数据,适合回溯分析。
import re import csv log_path = "gurobi.log" headers = [ "Flag", "Expl", "Unexpl", "Obj", "Depth", "IntInf", "Incumbent", "BestBd", "Gap", "It/Node", "Time" ] log_data = [] # 匹配日志行的正则(适配Gurobi标准分支树日志格式) line_pattern = re.compile( r'^([H*]?)\s*(\d+)\s*(\d+)\s*([\d.-]*)\s*(\d*)\s*(\d*)\s*([\d.-]*)\s*([\d.-]*)\s*([\d.%]*)\s*([\d.]*)\s*(\d+s)$' ) with open(log_path, "r") as f: # 跳过前两行表头 for line in f.readlines()[2:]: line = line.strip() if not line: continue match = line_pattern.match(line) if match: row = [val if val else "" for val in match.groups()] log_data.append(row) # 写入CSV with open("gurobi_mip_log_parsed.csv", "w", newline="") as f: writer = csv.writer(f) writer.writerow(headers) writer.writerows(log_data)
注意事项
- 回调函数方案能避免日志格式变动带来的解析问题,更稳定
- 正则表达式需根据你的Gurobi版本微调,确保匹配所有日志行
- CSV中的
Gap列含百分比符号,可在写入前通过字符串处理去除,方便后续数据分析
内容的提问来源于stack exchange,提问作者mjsl
相关产品推荐
相关产品推荐

