{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":87793,"databundleVersionId":12276181,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":10855324,"sourceType":"datasetVersion","datasetId":6742586},{"sourceId":11519231,"sourceType":"datasetVersion","datasetId":7224412},{"sourceId":11748963,"sourceType":"datasetVersion","datasetId":7374573}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"![trRosettaRNA logo](https://raw.githubusercontent.com/ammomahdavikia/asset-holding/main/trRosettaRNA_logo.png)\n\nIn this notebook, we are implementing [trRosettaRNA](hhttps://yanglab.qd.sdu.edu.cn/trRosettaRNA/download/) RNA folding framework for inference. We will be using CASP15 RNA targets as validation set. It is too heavy for folding long sequences (usually more than 400 nt) and submitting the notebook to Staford RNA 3D Folding challenge to evaluate its real-world performance will lead to Runtime limit but you can use it in your pipeline for certain short RNAs.\n\ntrRosettaRNA shares the basic framework of deep learning architecture as AlphaFold, which consists of three main modules:\n1. **Preprocessing**: trRosettaRNA incorporates predicted secondary structure information along with an MSA for a target RNA.\n    + **Secondary Structure**: predicted using SPOT-RNA, deep learning based method.\n    + **Multiple Sequence Alignment**: generated by rMSA pipeline.\n2. **RNAFormer**: A transformer network similar to the network Evoformer in AlphaFold2 to predict 1D and 2D\ngeometries.\n3. **Structure**: Similar to trRosetta, trRosettaRNA generates full-atom structure models by energy minimization with deep learning potentials and physics-based energy terms in Rosetta.\n\n![trRosettaRNA architecture](https://raw.githubusercontent.com/ammomahdavikia/asset-holding/main/trRosettaRNA_architecture.png)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"### ↓ Libraries","metadata":{}},{"cell_type":"code","source":"%%capture\n!pip install /kaggle/input/trrosettarna-v1-1/trRosettaRNA_v1.1/pyrosetta_cache/pyrosetta-2025.19+release.fabfc12491-cp311-cp311-linux_x86_64.whl\n!pip uninstall -y tensorflow tensorflow-gpu\n!pip install --no-index --find-links=/kaggle/input/trrosettarna-v1-1/trRosettaRNA_v1.1/dependencies tensorflow==2.15.0\n!pip install /kaggle/input/biopython/biopython-1.85-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:26:27.378118Z","iopub.execute_input":"2025-05-18T14:26:27.378444Z","execution_failed":"2025-05-18T15:19:59.273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!cp -r /kaggle/input/trrosettarna-v1-1/trRosettaRNA_v1.1 /kaggle/working/\n!rm -r /kaggle/working/trRosettaRNA_v1.1/pyrosetta_cache\n%cd /kaggle/working/trRosettaRNA_v1.1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:22:08.002100Z","iopub.execute_input":"2025-05-18T14:22:08.002494Z","iopub.status.idle":"2025-05-18T14:22:11.728592Z","shell.execute_reply.started":"2025-05-18T14:22:08.002383Z","shell.execute_reply":"2025-05-18T14:22:11.727560Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Keep in mind that the version_v1.1 we are using is modified from the original version_v1.1.","metadata":{}},{"cell_type":"markdown","source":"### ↓ Imports","metadata":{}},{"cell_type":"code","source":"import os\nimport re\nimport gc\nimport subprocess\nfrom datetime import datetime\nfrom glob import glob\nfrom tqdm import tqdm\nimport numpy as np\nimport pandas as pd\nimport torch\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom Bio.PDB import PDBParser\nfrom Bio import SeqIO","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:54:36.929652Z","iopub.execute_input":"2025-05-18T14:54:36.929958Z","iopub.status.idle":"2025-05-18T14:54:36.935109Z","shell.execute_reply.started":"2025-05-18T14:54:36.929938Z","shell.execute_reply":"2025-05-18T14:54:36.934246Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"colors = ['#c1121f', '#ffb703', '#003049']\nprint('\\n----- Color -----\\n')\nsns.palplot(sns.color_palette(colors))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:54:37.153866Z","iopub.execute_input":"2025-05-18T14:54:37.154172Z","iopub.status.idle":"2025-05-18T14:54:37.221195Z","shell.execute_reply.started":"2025-05-18T14:54:37.154151Z","shell.execute_reply":"2025-05-18T14:54:37.220424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"PATH = '/kaggle/input/stanford-rna-3d-folding'\nSS = '/kaggle/working/secondary_structure'\nOUTPUT = '/kaggle/working/trRosettaRNA_pdb'\n\nos.makedirs(OUTPUT, exist_ok=True)\nos.makedirs(SS, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:27:46.618022Z","iopub.execute_input":"2025-05-18T14:27:46.618357Z","iopub.status.idle":"2025-05-18T14:27:46.624057Z","shell.execute_reply.started":"2025-05-18T14:27:46.618333Z","shell.execute_reply":"2025-05-18T14:27:46.623128Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Load the data","metadata":{}},{"cell_type":"code","source":"test_seq = pd.read_csv(os.path.join(PATH, 'test_sequences.csv'))\ntest_seq.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:24:10.061719Z","iopub.execute_input":"2025-05-18T14:24:10.062101Z","iopub.status.idle":"2025-05-18T14:24:10.119913Z","shell.execute_reply.started":"2025-05-18T14:24:10.062075Z","shell.execute_reply":"2025-05-18T14:24:10.118959Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Preprocessing","metadata":{}},{"cell_type":"code","source":"def fasta_to_a3m_msa(input_file, output_file):\n    records = list(SeqIO.parse(input_file, \"fasta\"))\n    if not records:\n        raise ValueError(\"Empty MSA\")\n\n    query = str(records[0].seq)\n    output_lines = [f\">{records[0].id}\\n{query}\"]  # Keep query as-is\n\n    for record in records[1:]:\n        aligned = str(record.seq)\n        # Remove characters aligned to gaps in the query\n        compressed = ''.join([\n            res for res, q in zip(aligned, query) if q != '-'\n        ])\n        output_lines.append(f\">{record.id}\\n{compressed}\")\n\n    with open(output_file, \"w\") as f:\n        f.write(\"\\n\".join(output_lines) + \"\\n\")\n\ndef write_fasta(sequence, target_id, output_file):\n    with open(output_file, \"w\") as f:\n        f.write(f\">{target_id}\\n{sequence}\\n\")\n\nfor idx, rna in tqdm(test_seq.iterrows(), total=len(test_seq)):\n    target_id = rna.target_id\n    sequence = rna.sequence\n\n    os.makedirs(os.path.join(SS, target_id), exist_ok=True)\n    \n    fasta_path = os.path.join(SS, target_id, f\"{target_id}.fasta\")\n    msa_path = os.path.join(PATH, 'MSA', f'{target_id}.MSA.fasta')\n    a3m_path = os.path.join(SS, target_id, f'{target_id}.a3m')\n    \n    write_fasta(sequence, target_id, fasta_path)\n    fasta_to_a3m_msa(msa_path, a3m_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:27:50.182501Z","iopub.execute_input":"2025-05-18T14:27:50.182799Z","iopub.status.idle":"2025-05-18T14:27:50.650421Z","shell.execute_reply.started":"2025-05-18T14:27:50.182780Z","shell.execute_reply":"2025-05-18T14:27:50.649492Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for idx, rna in test_seq.iterrows():\n    target_id = rna.target_id\n    length = len(rna.sequence)\n    input_msa = os.path.join(SS, target_id, f'{target_id}.a3m')\n    output_dir = os.path.join(OUTPUT, target_id)\n    os.makedirs(output_dir, exist_ok=True)\n    seq_npz = os.path.join(output_dir, f'{target_id}.npz')\n        \n    print(f'\\n----- Predicting 2D Structure: {target_id} (#{length}) -----\\n')\n        \n    cmd = [\n        'python', 'predict.py',\n        '-i', input_msa,\n        '-o', seq_npz,\n        '-mdir', './params/model_1',\n        '-gpu', '0'\n    ]\n    print(f'Running: {\" \".join(cmd)}')\n        \n    result = subprocess.run(cmd, capture_output=True, text=True)\n        \n    # Log stdout and stderr\n    print(f'[STDOUT for {target_id}]\\n{result.stdout}')\n    #print(f'[STDERR for {target_id}]\\n{result.stderr}')\n        \n    # Check for errors\n    if result.returncode != 0:\n        print(f'[ERROR] predict.py failed for {target_id} with return code {result.returncode}')\n    \n    gc.collect()\n    torch.cuda.empty_cache()\n    del result","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:30:05.491132Z","iopub.execute_input":"2025-05-18T14:30:05.492246Z","iopub.status.idle":"2025-05-18T14:38:52.383557Z","shell.execute_reply.started":"2025-05-18T14:30:05.492211Z","shell.execute_reply":"2025-05-18T14:38:52.379685Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Folding","metadata":{}},{"cell_type":"code","source":"for idx, rna in test_seq.iterrows():\n    \n    target_id = rna.target_id\n    length = len(rna.sequence)\n    output_dir = os.path.join(OUTPUT, target_id)\n    fastafile = os.path.join(SS, target_id, f'{target_id}.fasta')\n            \n    print(f'\\n{datetime.now()} [{idx+1}] Folding 3D Structure: {target_id} (#{length}) using tRrosettaRNA\\n')\n            \n    npz_path = os.path.join(output_dir, f'{target_id}.npz')\n    pdb_path = os.path.join(output_dir, f'{target_id}.pdb')\n        \n    cmd = [\n        \"python\", \"fold.py\",\n        \"-npz\", npz_path,\n        \"-fa\", fastafile,\n        \"-out\", pdb_path,\n        \"-nm\", \"5\",\n        \"-dcut\", \"0.45\",\n        \"-cpu\", \"4\"\n    ]\n            \n    try:\n        result = subprocess.run(cmd, capture_output=True, text=True, timeout=1800)\n    except subprocess.TimeoutExpired as e:\n        print(f'\\n{datetime.now()} [ERROR] tRrosettaRNA failed on {target_id} (#{length})\\nException: {e}\\n')\n        \n    print(f'[STDOUT for {target_id}]\\n{result.stdout}')\n    print(f'[STDERR for {target_id}]\\n{result.stderr}')\n    gc.collect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:39:17.448490Z","iopub.execute_input":"2025-05-18T14:39:17.448909Z","iopub.status.idle":"2025-05-18T14:47:13.391310Z","shell.execute_reply.started":"2025-05-18T14:39:17.448882Z","shell.execute_reply":"2025-05-18T14:47:13.390329Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -r /kaggle/working/trRosettaRNA_v1.1\n!rm -r {SS}\n%cd /kaggle/working/","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:55:44.634332Z","iopub.execute_input":"2025-05-18T14:55:44.634686Z","iopub.status.idle":"2025-05-18T14:55:44.849758Z","shell.execute_reply.started":"2025-05-18T14:55:44.634661Z","shell.execute_reply":"2025-05-18T14:55:44.848711Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"submission = pd.read_csv(os.path.join(PATH, 'sample_submission.csv'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:48:12.869359Z","iopub.execute_input":"2025-05-18T14:48:12.869691Z","iopub.status.idle":"2025-05-18T14:48:12.903865Z","shell.execute_reply.started":"2025-05-18T14:48:12.869668Z","shell.execute_reply":"2025-05-18T14:48:12.902897Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for idx, rna in test_seq.iterrows():\n    target_id = rna.target_id\n    prediction_path = os.path.join(OUTPUT, target_id)\n    predictions = glob(os.path.join(prediction_path, '*.pdb'))\n\n    for i, sample in enumerate(predictions):\n        parser = PDBParser(QUIET=True)\n        structure = parser.get_structure(\"rna\", sample)\n        for model in structure:\n            for chain in model:\n                for residue in chain:\n                    res_number = residue.get_id()[1]\n                    res_id = f'{target_id}_{res_number}'\n                    for atom in residue:\n                        if atom.get_name() == \"C1'\":\n                            x, y, z = atom.coord\n                            submission.loc[submission.ID == res_id, f'x_{i+1}'] = float(x)\n                            submission.loc[submission.ID == res_id, f'y_{i+1}'] = float(y)\n                            submission.loc[submission.ID == res_id, f'z_{i+1}'] = float(z)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:48:13.673763Z","iopub.execute_input":"2025-05-18T14:48:13.674032Z","iopub.status.idle":"2025-05-18T14:48:14.400219Z","shell.execute_reply.started":"2025-05-18T14:48:13.674014Z","shell.execute_reply":"2025-05-18T14:48:14.399331Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Submission.csv shape:', submission.shape)\nsubmission.to_csv('/kaggle/working/submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:48:16.864629Z","iopub.execute_input":"2025-05-18T14:48:16.865024Z","iopub.status.idle":"2025-05-18T14:48:16.908941Z","shell.execute_reply.started":"2025-05-18T14:48:16.864988Z","shell.execute_reply":"2025-05-18T14:48:16.908003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:48:24.953932Z","iopub.execute_input":"2025-05-18T14:48:24.954218Z","iopub.status.idle":"2025-05-18T14:48:24.974394Z","shell.execute_reply.started":"2025-05-18T14:48:24.954198Z","shell.execute_reply":"2025-05-18T14:48:24.973728Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -r {OUTPUT}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:56:57.056767Z","iopub.execute_input":"2025-05-18T14:56:57.057223Z","iopub.status.idle":"2025-05-18T14:56:57.211838Z","shell.execute_reply.started":"2025-05-18T14:56:57.057192Z","shell.execute_reply":"2025-05-18T14:56:57.210370Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Evaluation","metadata":{}},{"cell_type":"code","source":"solution = pd.read_csv(os.path.join(PATH, 'validation_labels.csv'))\nsolution.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:50:56.478019Z","iopub.execute_input":"2025-05-18T14:50:56.478421Z","iopub.status.idle":"2025-05-18T14:50:56.562020Z","shell.execute_reply.started":"2025-05-18T14:50:56.478395Z","shell.execute_reply":"2025-05-18T14:50:56.561007Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def parse_tmscore_output(output):\n    # Extract TM-score based on length of reference structure (second)\n    tm_score_match = re.findall(r'TM-score=\\s+([\\d.]+)', output)[1]\n    if not tm_score_match:\n        raise ValueError('No TM score found')\n    return float(tm_score_match)\n\n\ndef write_target_line(\n    atom_name, atom_serial, residue_name, chain_id, residue_num, x_coord, y_coord, z_coord, occupancy=1.0, b_factor=0.0, atom_type='P'\n) -> str:\n    \"\"\"\n    Writes a single line of PDB format based on provided atom information.\n\n    Args:\n        atom_name (str): Name of the atom (e.g., \"N\", \"CA\").\n        atom_serial (int): Atom serial number.\n        residue_name (str): Residue name (e.g., \"ALA\").\n        chain_id (str): Chain identifier.\n        residue_num (int): Residue number.\n        x_coord (float): X coordinate.\n        y_coord (float): Y coordinate.\n        z_coord (float): Z coordinate.\n        occupancy (float, optional): Occupancy value (default: 1.0).\n        b_factor (float, optional): B-factor value (default: 0.0).\n\n    Returns:\n        str: A single line of PDB string.\n    \"\"\"\n    return f'ATOM  {atom_serial:>5d}  {atom_name:<5s} {residue_name:<3s} {residue_num:>3d}    {x_coord:>8.3f}{y_coord:>8.3f}{z_coord:>8.3f}{occupancy:>6.2f}{b_factor:>6.2f}           {atom_type}\\n'\n\n\ndef write2pdb(df: pd.DataFrame, xyz_id: str, target_path: str) -> int:\n    resolved_cnt = 0\n    with open(target_path, 'w') as target_file:\n        for _, row in df.iterrows():\n            x_coord = row[f'x_{xyz_id}']\n            y_coord = row[f'y_{xyz_id}']\n            z_coord = row[f'z_{xyz_id}']\n\n            if x_coord > -1e17 and y_coord > -1e17 and z_coord > -1e17:\n                # if True:\n                resolved_cnt += 1\n                target_line = write_target_line(\n                    atom_name=\"C1'\",\n                    atom_serial=int(row['resid']),\n                    residue_name=row['resname'],\n                    chain_id='0',\n                    residue_num=int(row['resid']),\n                    x_coord=x_coord,\n                    y_coord=y_coord,\n                    z_coord=z_coord,\n                    atom_type='C',\n                )\n                target_file.write(target_line)\n    return resolved_cnt\n\n\ndef score(solution: pd.DataFrame, submission: pd.DataFrame, row_id_column_name: str) -> float:\n    \"\"\"\n    Computes the TM-score between predicted and native RNA structures using USalign.\n\n    This function evaluates the structural similarity of RNA predictions to native structures\n    by computing the TM-score. It uses USalign, a structural alignment tool, to compare\n    the predicted structures with the native structures.\n\n    Workflow:\n    1. Copies the USalign binary to the working directory and grants execution permissions.\n    2. Extracts the `target_id` from the `ID` column of both the solution and submission DataFrames.\n    3. Iterates over each unique `target_id`, grouping the native and predicted structures.\n    4. Writes PDB files for native and predicted structures.\n    5. Runs USalign on each predicted-native pair and extracts the TM-score.\n    6. Computes the highest TM-score per target and returns aggregated results.\n\n    Args:\n        solution (pd.DataFrame): A DataFrame containing the native RNA structures.\n        submission (pd.DataFrame): A DataFrame containing the predicted RNA structures.\n        row_id_column_name (str): The name of the column containing unique row identifiers.\n\n    Returns:\n        float: the average highest TM-scores.\n    \"\"\"\n\n    os.system('cp //kaggle/input/usalign/USalign /kaggle/working/')\n    os.system('sudo chmod u+x /kaggle/working//USalign')\n\n    # Extract target_id from ID (target_resid)\n    solution['target_id'] = solution['ID'].apply(lambda x: x.split('_')[0])\n    submission['target_id'] = submission['ID'].apply(lambda x: x.split('_')[0])\n\n    results = {}\n    # Iterate through each target_id and generate PDB files for both clean and corrupted data\n    for target_id, group_native in tqdm(solution.groupby('target_id'), desc='TM-scoring'):\n        group_predicted = submission[submission['target_id'] == target_id]\n        native_pdb = 'native.pdb'\n        predicted_pdb = 'predicted.pdb'\n\n        target_id_scores = []\n        for pred_cnt in range(1, 6):\n            prediction_scores = []\n            for native_cnt in range(1, 41):\n                # Write solution PDB\n                resolved_cnt = write2pdb(group_native, native_cnt, native_pdb)\n\n                # Write predicted PDB\n                _ = write2pdb(group_predicted, pred_cnt, predicted_pdb)\n\n                if resolved_cnt > 0:\n                    command = f'/kaggle/working/USalign {predicted_pdb} {native_pdb} -atom \" C1\\'\"'\n                    usalign_output = os.popen(command).read()\n                    prediction_scores.append(parse_tmscore_output(usalign_output))\n\n            target_id_scores.append(max(prediction_scores))\n        results[target_id] = max(target_id_scores)\n\n    return results","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:50:58.648923Z","iopub.execute_input":"2025-05-18T14:50:58.649233Z","iopub.status.idle":"2025-05-18T14:50:58.662949Z","shell.execute_reply.started":"2025-05-18T14:50:58.649210Z","shell.execute_reply":"2025-05-18T14:50:58.661763Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"scores = score(solution, submission, 'ID')\nresult = {'target_id': [],\n          'length': [],\n          'tm_score': []}\n\nfor idx, rna in test_seq.iterrows():\n    \n    target_id = rna.target_id\n    length = len(rna.sequence)\n    tm_score = scores[target_id]\n    \n    result['target_id'].append(target_id)\n    result['length'].append(length)\n    result['tm_score'].append(tm_score)\n\nresult_df = pd.DataFrame(result)\nresult_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:51:02.453535Z","iopub.execute_input":"2025-05-18T14:51:02.454319Z","iopub.status.idle":"2025-05-18T14:52:29.587811Z","shell.execute_reply.started":"2025-05-18T14:51:02.454296Z","shell.execute_reply":"2025-05-18T14:52:29.586682Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_results(df, desc, axs=None):\n    if axs is None:\n        fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(15, 5), gridspec_kw={'width_ratios': [1, 2]})\n    else:\n        fig = None\n\n    df['color'] = df['length'].apply(lambda x: colors[0] if x > 400 else colors[-1])\n\n    sns.scatterplot(data=df, x='length', y='tm_score', hue='color', palette={colors[0]: colors[0], colors[-1]: colors[-1]}, ax=axs[0], legend=False)\n    axs[0].set_title('TM-score per length')\n    axs[0].set_xlabel('RNA length')\n    axs[0].set_ylabel('TM-score')\n    axs[0].set_ylim(0., 1.)\n\n    mean_tm = df['tm_score'].mean()\n    axs[0].axhline(mean_tm, color=colors[1], linestyle='--', label=f'Mean = {mean_tm:.2f}')\n    axs[0].legend()\n\n    sns.barplot(data=df, x='target_id', y='tm_score', palette=df['color'].to_list(), ax=axs[1])\n    axs[1].set_title('TM-score per target')\n    axs[1].set_xlabel('Target id')\n    axs[1].set_ylabel('TM-score')\n    axs[1].set_ylim(0., 1.)\n    axs[1].axhline(mean_tm, color=colors[1], linestyle='--', label=f'Mean = {mean_tm:.2f}')\n    axs[1].legend()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:54:52.856965Z","iopub.execute_input":"2025-05-18T14:54:52.857386Z","iopub.status.idle":"2025-05-18T14:54:52.868239Z","shell.execute_reply.started":"2025-05-18T14:54:52.857362Z","shell.execute_reply":"2025-05-18T14:54:52.866808Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_results(result_df, desc='Results')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:54:53.351941Z","iopub.execute_input":"2025-05-18T14:54:53.352323Z","iopub.status.idle":"2025-05-18T14:54:54.093464Z","shell.execute_reply.started":"2025-05-18T14:54:53.352290Z","shell.execute_reply":"2025-05-18T14:54:54.092408Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def log_resluts(result_df):\n    long_score = result_df.loc[result_df.length > 400, 'tm_score'].mean()\n    short_score = result_df.loc[result_df.length <= 400, 'tm_score'].mean()\n    all_score = result_df.tm_score.mean()\n\n    print(f'[TM-score] > 400: {long_score:.4f}')\n    print(f'[TM-score] <= 400: {short_score:.4f}')\n    print(f'[TM-score] all: {all_score:.4f}')\n\nlog_resluts(result_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-18T14:55:02.832639Z","iopub.execute_input":"2025-05-18T14:55:02.833036Z","iopub.status.idle":"2025-05-18T14:55:02.842431Z","shell.execute_reply.started":"2025-05-18T14:55:02.833012Z","shell.execute_reply":"2025-05-18T14:55:02.841459Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Resource\n\nWang et al, trRosettaRNA: automated prediction of RNA 3D structure with transformer network, Nature Communications, 14: 7266 (2023).","metadata":{}}]}