如何结构化解析codeml输出文件并转换为表格格式
报错根因
- 原代码循环遍历到lines列表末尾时,直接访问
i+1/i+3/i+4位置的元素,当i距离列表末尾小于4时,这些索引不存在,直接触发列表越界报错 - ID判定逻辑过于宽松:只要行内包含"OG"字符串就认定为目标ID行,很容易把结果块内其他带OG字符的说明文字误判为ID行,后续取数完全错位
- 空行判定逻辑错误:代码写的是判断
"\\n" not in lines[i+1],实际匹配的是字面量\n字符串,不是空行,判定条件完全失效 - 依赖固定行偏移取数:不同OG对应的结果块之间空行数量、说明行长度可能有差异,固定取ID行后第3、4行的内容,很容易取到非目标行
修正后的解析方案
放弃固定行偏移取数的逻辑,先过滤空行,再逐行扫描匹配ID和对应参数,从根源避免越界和错位问题,可直接运行:
import pandas as pd import re # 读取文件并提前过滤纯空行,避免空行打乱匹配逻辑 lines = [] with open("/home/Selection_Analysis/M0_res.txt", mode='r') as f: for line in f: stripped_line = line.strip() if stripped_line: lines.append(stripped_line) res = [] current_record = None # 预编译正则匹配规则,适配codeml标准输出格式 og_id_pattern = re.compile(r'^(OG\d+_M0)') lnl_pattern = re.compile(r'lnL\s*=\s*([-\d.]+)') base_param_pattern = re.compile(r't=\s*([\d.]+)\s+S=\s*([\d.]+)\s+N=\s*([\d.]+)') omega_pattern = re.compile(r'dN/dS\s*=\s*([\d.]+)') dn_pattern = re.compile(r'dN\s*=\s*([\d.]+)') ds_pattern = re.compile(r'dS\s*=\s*([\d.]+)') for line in lines: # 匹配到新的OG ID时,先把上一个采集完成的记录存入结果,再初始化新记录 id_match = og_id_pattern.search(line) if id_match: if current_record is not None: res.append(current_record) current_record = { "ID": id_match.group(1), "lnL": None, "t": None, "S": None, "N": None, "dN/dS": None, "dN": None, "dS": None } continue # 未匹配到第一个ID前跳过参数匹配 if current_record is None: continue # 逐行匹配当前ID对应块内的各个参数 lnl_match = lnl_pattern.search(line) if lnl_match: current_record["lnL"] = float(lnl_match.group(1)) continue base_match = base_param_pattern.search(line) if base_match: current_record["t"] = float(base_match.group(1)) current_record["S"] = float(base_match.group(2)) current_record["N"] = float(base_match.group(3)) continue omega_match = omega_pattern.search(line) if omega_match: current_record["dN/dS"] = float(omega_match.group(1)) continue dn_match = dn_pattern.search(line) if dn_match: current_record["dN"] = float(dn_match.group(1)) continue ds_match = ds_pattern.search(line) if ds_match: current_record["dS"] = float(ds_match.group(1)) continue # 循环结束后存入最后一条记录 if current_record is not None: res.append(current_record) # 转换为结构化表格并导出 df = pd.DataFrame(res) df.to_csv("codeml_M0_parsed_result.csv", index=False, encoding="utf-8")
方案说明
- 提前过滤所有纯空行,避免空行导致的行号错位问题
- 用正则严格匹配OG ID格式,不会把带OG字符的普通说明行误判为ID行
- 不依赖固定行偏移,按参数本身的文本特征匹配取值,适配codeml不同版本输出的微小格式差异
- 全程不会访问超出列表长度的索引,从根源解决IndexError问题
- 最终输出为标准pandas DataFrame,可直接导出为csv文件做后续统计分析
内容的提问来源于stack exchange,提问作者peg97
相关产品推荐
相关产品推荐

