如何用Bash/Python高效处理百万级结果文件:合并行、提取内容并排序
高效处理百万级结果文件的解决方案
针对你的需求,优先推荐Bash工具链方案,因为awk+sort都是经过高度优化的C语言实现,处理百万级文件的速度远快于Python;如果需要更灵活的逻辑,也提供Python的内存友好型方案。
一、Bash快速处理方案
这个命令可以一站式完成格式转换、字段提取和排序,完全适配你的文件结构:
awk '/^Ligand:/ { # 提取配体文件名(去掉路径前缀) ligand = $2; sub(/.*\//, "", ligand) } /^1/ { # 提取数值字段,和配体名组合输出 print $2, ligand }' results.txt | sort -n
工作原理:
awk处理逻辑:- 匹配以
Ligand:开头的行,提取第二个字段(完整路径),用sub去掉所有路径前缀,只保留文件名(比如ZINC00001677.pdbqt),存到变量ligand中。 - 匹配以
1开头的数值行,提取第二个字段(即你的评分值),和之前保存的配体名一起打印。
- 匹配以
sort -n排序:-n参数表示按数值升序排序,正好满足你“从最负到最正”的需求(负数数值越小,排序越靠前)。
性能优势:
awk逐流式处理文件,不需要加载整个文件到内存,对大文件友好。- GNU
sort针对超大文件做了外部排序优化,能高效处理数百万行数据,比Python的排序速度快数倍。
二、Python内存友好型方案
如果你更熟悉Python,或者需要后续扩展更多逻辑,可以用生成器逐行处理,避免内存溢出:
def process_and_sort_results(input_path, output_path): def yield_score_ligand(): with open(input_path, 'r') as f: line_iter = iter(f) for line in line_iter: line = line.strip() if not line.startswith('Ligand:'): continue # 提取配体文件名(分割路径取最后一段) ligand_full_path = line.split()[-1] ligand_name = ligand_full_path.split('/')[-1] # 读取对应的数值行 try: score_line = next(line_iter).strip() except StopIteration: break # 处理文件末尾不完整的情况 # 提取评分值 score_parts = score_line.split() if len(score_parts) < 2: continue try: score = float(score_parts[1]) except ValueError: continue yield (score, ligand_name) # 按数值升序排序(最负的在前) sorted_results = sorted(yield_score_ligand(), key=lambda x: x[0]) # 写入结果 with open(output_path, 'w') as f: for score, ligand in sorted_results: f.write(f"{score} {ligand}\n") # 调用示例 process_and_sort_results('results.txt', 'final_results.txt')
工作原理:
- 用生成器
yield_score_ligand()逐行读取文件,成对处理配体行和数值行,每次只在内存中保留一组数据。 sorted()函数会自动处理大数量的排序(Python的Timsort算法效率很高),最后批量写入结果文件。
额外优化建议:并行脚本的IO优化
你当前的并行脚本中,每个进程都用tee -a results.txt写入同一个文件,会导致严重的IO竞争,不仅速度慢,还可能出现数据错乱。更高效的做法是:
- 每个Python脚本将自己的输出写入独立的临时文件(比如
result_${PID}.txt)。 - 所有任务完成后,用
cat result_*.txt合并所有临时文件,再用上面的处理命令生成最终结果。
这样能大幅提升并行任务的IO效率,避免多进程写同一个文件的冲突。
内容的提问来源于stack exchange,提问作者darrowboat
相关产品推荐
相关产品推荐

