from Bio import PDB
from Bio.PDB.Polypeptide import PPBuilder
import sys

def parse_pdb(pdb_file):
    """PDBファイルを解析して構造オブジェクトを取得"""
    parser = PDB.PDBParser(QUIET=True)
    structure = parser.get_structure('model', pdb_file)
    return structure

def get_chain_info(structure):
    """各チェインの情報(配列、長さ、残基番号)を取得"""
    ppb = PPBuilder()
    chain_info = {}
    for model in structure:
        for chain in model:
            seq = ""
            res_nums = []
            for pp in ppb.build_peptides(chain):
                seq += pp.get_sequence()
                res_nums += [res.get_id()[1] for res in pp]
            if not seq:
                # PPBuilderが失敗した場合、手動で配列を取得
                seq = ''.join([PDB.Polypeptide.three_to_one(res.get_resname()) 
                              for res in chain if PDB.is_aa(res)])
                res_nums = [res.get_id()[1] for res in chain if PDB.is_aa(res)]
            if seq:
                chain_id = chain.get_id()
                chain_info[chain_id] = {
                    'sequence': str(seq),
                    'length': len(seq),
                    'res_nums': res_nums,
                    'start': min(res_nums) if res_nums else None,
                    'end': max(res_nums) if res_nums else None,
                }
    return chain_info

def filter_chains(chain_info):
    """短いチェインの削除と重複チェインの削除"""
    # 長さが10以下のチェインを削除
    filtered = {cid: info for cid, info in chain_info.items() if info['length'] > 10}
    
    # チェインをグループ化(例えば、Aa, Ab, Ac は同じグループA)
    groups = {}
    for cid, info in filtered.items():
        group_key = cid[0]  # 最初の文字でグループ化
        if group_key not in groups:
            groups[group_key] = []
        groups[group_key].append((cid, info))
    
    # 各グループ内で重複チェインを削除
    final_chains = set()
    for group, chains in groups.items():
        # チェインを長さの降順でソート
        chains_sorted = sorted(chains, key=lambda x: x[1]['length'], reverse=True)
        kept = []
        for cid, info in chains_sorted:
            overlap = False
            for kept_cid, kept_info in kept:
                # 残基番号の範囲が重複するかチェック
                if info['start'] <= kept_info['end'] and info['end'] >= kept_info['start']:
                    overlap = True
                    break
            if not overlap:
                kept.append((cid, info))
        # 保持するチェインIDを追加
        for cid, info in kept:
            final_chains.add(cid)
    return final_chains

def write_clean_pdb(pdb_file, output_file, kept_chains):
    """クリーンアップされたチェインのみを含む新しいPDBファイルを作成"""
    parser = PDB.PDBParser(QUIET=True)
    io = PDB.PDBIO()
    structure = parser.get_structure('model', pdb_file)
    
    class ChainSelect(PDB.Select):
        def accept_chain(self, chain):
            return chain.get_id() in kept_chains
    
    io.set_structure(structure)
    io.save(output_file, select=ChainSelect())

def main():
    if len(sys.argv) != 3:
        print("使用法: python filter_modelangelo_chains.py input.pdb output_cleaned.pdb")
        sys.exit(1)
    
    input_pdb = sys.argv[1]
    output_pdb = sys.argv[2]
    
    print("PDBファイルを解析中...")
    structure = parse_pdb(input_pdb)
    
    print("チェイン情報を取得中...")
    chain_info = get_chain_info(structure)
    
    print("チェインをフィルタリング中...")
    kept_chains = filter_chains(chain_info)
    
    print(f"保持するチェイン: {', '.join(kept_chains)}")
    
    print("クリーンアップされたPDBファイルを作成中...")
    write_clean_pdb(input_pdb, output_pdb, kept_chains)
    
    print(f"フィルタリング完了。クリーンアップされたPDBは {output_pdb} に保存されました。")

if __name__ == "__main__":
    main()

コメント