mcpField
V
viennarna-structure-prediction

viennarna-structure-prediction

github.com/jaechang-hits数据与数据库38469 次安装6 天前更新
代码仓库

关于 viennarna-structure-prediction

viennarna-structure-prediction 是面向 RNA 研究者的 ViennaRNA 结构预测技能,涵盖 Python 和命令行操作,可用于热力学折叠、概率分析及 RNA 相互作用预测。

主要功能

  1. 01预测最小自由能结构
  2. 02分析碱基配对概率
  3. 03预测 RNA 共折叠与双链
  4. 04进行约束及次优折叠
  5. 05使用 RNAfold 命令行

适用场景

  1. 01分析 RNA 序列二级结构
  2. 02比较不同序列的结构不确定性
  3. 03评估 siRNA 靶区可及性
  4. 04预测 miRNA 与靶 RNA 相互作用

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill viennarna-structure-prediction

安装后在 Claude Code 中输入/viennarna-structure-prediction确认已加载。

详细介绍

ViennaRNA 结构预测

概述

ViennaRNA 是基于热力学最近邻参数进行 RNA 二级结构预测的黄金标准工具包。对于给定的 RNA 序列,它可以预测最小自由能(MFE)结构和点括号表示法,计算完整的配分函数以获得碱基对概率,并通过共折叠和双链体预测对 RNA-RNA 相互作用建模。Python 绑定(import RNA)通过序列级 API 和折叠化合物 API 暴露完整的 ViennaRNA C 库。这里还提供并演示了命令行程序(RNAfold、RNAalifold、RNAduplex)。

适用场景

  • 预测 RNA 序列(mRNA、lncRNA、miRNA 前体、适配体)的最小自由能二级结构
  • 计算碱基对概率矩阵,以评估结构不确定性并识别结构明确的茎环
  • 通过折叠靶 mRNA 区域并检查其双链结构,设计或评估 siRNA 的可及性
  • 通过预测可能降低靶向活性的向导 RNA 二级结构,评估 sgRNA 的靶向效率
  • 对 RNA-RNA 相互作用建模(共折叠或双链体预测),用于 miRNA-靶标结合或反义寡核苷酸设计
  • 计算一组序列的折叠自由能,以比较热力学稳定性
  • 只有在需要专门使用 Mfold 算法进行预测或需要 Efold 配分函数时,才使用 mfold(Web 服务)或 RNAstructure;ViennaRNA 使用 Turner 2004 最近邻参数,是研究级热力学预测的标准工具

先决条件

  • Python 包:ViennaRNA(Python 绑定)、matplotlib、numpy
  • 数据要求:RNA 序列字符串(ACGU 字母表;ViennaRNA 会自动将 T 转换为 U)
  • 环境:Python 3.8+;强烈建议使用 conda 安装(可处理 C 库依赖项)
# Install via conda (recommended)
conda install -c conda-forge -c bioconda viennarna

# Verify installation
python -c "import RNA; print(RNA.__version__)"
# 2.6.4

# Install additional Python dependencies
pip install matplotlib numpy pandas

# Optional: verify CLI tools are available
RNAfold --version
# RNAfold 2.6.4

快速开始

import RNA

# Predict MFE structure for an RNA sequence
sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"
structure, mfe = RNA.fold(sequence)

print(f"Sequence:  {sequence}")
print(f"Structure: {structure}")
print(f"MFE:       {mfe:.2f} kcal/mol")
# Sequence:  GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA
# Structure: (((((((..((((........)))).(((((.......))))).....(((((.......))))))))))))....
# MFE:       -31.30 kcal/mol

工作流

步骤 1:序列准备和 MFE 折叠

加载 RNA 序列并使用 RNA.fold() 计算其最小自由能二级结构。验证输入并检查点括号输出。

import RNA

def prepare_sequence(seq: str) -> str:
    """Normalize sequence: uppercase, replace T→U, validate alphabet."""
    seq = seq.upper().replace("T", "U").strip()
    invalid = set(seq) - set("ACGUNX")
    if invalid:
        raise ValueError(f"Invalid characters in sequence: {invalid}")
    return seq

# E. coli tRNA-Phe (GenBank: M10217)
raw_seq = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"
sequence = prepare_sequence(raw_seq)

structure, mfe = RNA.fold(sequence)

print(f"Sequence length: {len(sequence)} nt")
print(f"Structure:       {structure}")
print(f"MFE:             {mfe:.2f} kcal/mol")

# Validate: structure length must equal sequence length
assert len(structure) == len(sequence), "Structure and sequence length mismatch"

# Count stems (paired bases)
n_paired   = structure.count("(") + structure.count(")")
n_unpaired = structure.count(".")
print(f"Paired bases: {n_paired}  |  Unpaired bases: {n_unpaired}")
print(f"Stem fraction: {n_paired/len(sequence):.2f}")
步骤 2:创建折叠化合物以进行高级分析

RNA.fold_compound 对象是配分函数、碱基对概率和约束折叠的核心 API。

import RNA

sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"

# Create fold compound (wraps the sequence with model parameters)
fc = RNA.fold_compound(sequence)

# Compute MFE structure via the fold compound API
structure, mfe = fc.mfe()
print(f"MFE structure: {structure}")
print(f"MFE:           {mfe:.2f} kcal/mol")

# Evaluate free energy of an alternative structure
alt_structure = "." * len(sequence)   # fully unfolded
energy = fc.eval_structure(alt_structure)
print(f"Fully unfolded energy: {energy:.2f} kcal/mol")
print(f"Folding stabilization:  {energy - mfe:.2f} kcal/mol")
步骤 3:配分函数和碱基对概率

计算热力学配分函数以获得集合层面的碱基对概率。高概率配对表示结构明确的结构元件。

import RNA
import numpy as np

sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"
n = len(sequence)

fc = RNA.fold_compound(sequence)

# Step 1: MFE folding (required before pf for proper initialization)
structure_mfe, mfe = fc.mfe()

# Step 2: Rescale Boltzmann factors for numerical stability (optional but recommended)
fc.exp_params_rescale(mfe)

# Step 3: Compute partition function
structure_pf, gibbs_free_energy = fc.pf()
print(f"Gibbs free energy (ensemble): {gibbs_free_energy:.2f} kcal/mol")
print(f"MFE structure:  {structure_mfe}")
print(f"Centroid (pf):  {structure_pf}")

# Step 4: Retrieve base pair probability matrix
bpp = fc.bpp()   # returns (n+1)x(n+1) matrix; 1-indexed

# Convert to 0-indexed numpy array for analysis
probs = np.zeros((n, n))
for i in range(1, n + 1):
    for j in range(i + 1, n + 1):
        if bpp[i][j] > 0.0:
            probs[i - 1][j - 1] = bpp[i][j]
            probs[j - 1][i - 1] = bpp[i][j]

# Identify high-confidence pairs (p > 0.9)
high_conf = [(i, j, probs[i, j]) for i in range(n) for j in range(i + 1, n) if probs[i, j] > 0.9]
print(f"\nHigh-confidence base pairs (p > 0.9): {len(high_conf)}")
for i, j, p in high_conf[:5]:
    print(f"  {sequence[i]}{i+1} — {sequence[j]}{j+1}: p={p:.3f}")
步骤 4:可视化碱基对概率矩阵

将碱基对概率矩阵绘制为热图,以直观呈现结构区域。

import RNA
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"
n = len(sequence)

fc = RNA.fold_compound(sequence)
structure_mfe, mfe = fc.mfe()
fc.exp_params_rescale(mfe)
fc.pf()
bpp = fc.bpp()

# Build matrix
probs = np.zeros((n, n))
for i in range(1, n + 1):
    for j in range(i + 1, n + 1):
        if bpp[i][j] > 0.0:
            probs[i - 1][j - 1] = bpp[i][j]
            probs[j - 1][i - 1] = bpp[i][j]

fig, ax = plt.subplots(figsize=(8, 7))
im = ax.imshow(probs, cmap="hot_r", vmin=0, vmax=1, origin="upper", aspect="equal")
plt.colorbar(im, ax=ax, label="Base pair probability")

ax.set_xlabel("Nucleotide position")
ax.set_ylabel("Nucleotide position")
ax.set_title(f"Base Pair Probability Matrix\n(n={n} nt, MFE={mfe:.2f} kcal/mol)")
plt.tight_layout()
plt.savefig("bpp_matrix.png", dpi=150, bbox_inches="tight")
print("Saved: bpp_matrix.png")
步骤 5:RNA-RNA 双链体预测(共折叠)

使用 RNA.cofold() 预测两条 RNA 序列之间的相互作用,方法是用 & 分隔符将它们连接起来。

import RNA

# miRNA (hsa-miR-21-5p) and its target sequence in mRNA (PTEN 3'UTR region)
mirna_seq  = "UAGCUUAUCAGACUGAUGUUGA"
target_seq = "UCAACAUCAGUCUGAUAAGCUA"   # approximate complementary target

# Co-fold: concatenate with & separator
cofold_seq = mirna_seq + "&" + target_seq
structure, mfe = RNA.cofold(cofold_seq)

print(f"miRNA:          {mirna_seq}")
print(f"Target:         {target_seq}")
print(f"Co-fold MFE:    {mfe:.2f} kcal/mol")

# Parse the structure — & is retained in output
n1, n2 = len(mirna_seq), len(target_seq)
struct_mirna  = structure[:n1]
struct_ampersand = structure[n1]
struct_target = structure[n1 + 1:]

print(f"miRNA structure:  {struct_mirna}")
print(f"Target structure: {struct_target}")

paired_in_duplex = struct_mirna.count("(") + struct_mirna.count(")")
print(f"Bases paired across the duplex: {paired_in_duplex}")
步骤 6:用于 siRNA 设计的 RNA 可及性分析

计算较长 mRNA 序列中靶区域的可及性——这对 siRNA 和反义寡核苷酸的效率至关重要。

import RNA
import numpy as np

def compute_accessibility(mrna_seq: str, window: int = 40) -> list:
    """
    Compute per-position probability of being unpaired (accessible) using
    a sliding-window approach on the partition function.
    Returns list of (position, accessibility) tuples.
    """
    n = len(mrna_seq)
    fc = RNA.fold_compound(mrna_seq)
    _, mfe = fc.mfe()
    fc.exp_params_rescale(mfe)
    fc.pf()
    bpp = fc.bpp()

    # Probability of being paired at each position
    p_paired = np.zeros(n)
    for i in range(1, n + 1):
        for j in range(1, n + 1):
            if i != j:
                p = bpp[min(i,j)][max(i,j)]
                p_paired[i - 1] += p

    p_unpaired = 1.0 - np.clip(p_paired, 0, 1)
    return p_unpaired

# Example: 80-nt mRNA segment with a known accessible region
mrna = "AUGCUAGCUAGCUAGCUAUGCUAGCUAGCUUUUUUUUUUUUAUGCUAGCUAGCUAGCUAGCUAGCUAGCUAGCUAGC"
p_unpaired = compute_accessibility(mrna)

import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 3))
ax.bar(range(1, len(mrna) + 1), p_unpaired, color="#2166ac", alpha=0.8)
ax.axhline(0.5, color="red", lw=1, ls="--", label="50% unpaired")
ax.set_xlabel("Position (nt)")
ax.set_ylabel("P(unpaired)")
ax.set_title("RNA Accessibility Profile")
ax.legend()
plt.tight_layout()
plt.savefig("rna_accessibility.png", dpi=150, bbox_inches="tight")
print("Saved: rna_accessibility.png")

# Top 5 most accessible positions (siRNA target candidates)
best = sorted(enumerate(p_unpaired, 1), key=lambda x: -x[1])[:5]
print("\nMost accessible positions:")
for pos, prob in best:
    print(f"  Position {pos}: P(unpaired) = {prob:.3f}  ({mrna[pos-1]})")
步骤 7:命令行 RNAfold 和输出解析

通过 subprocess 使用 RNAfold CLI 批量折叠,然后解析输出。

# Fold a single sequence from stdin
echo "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA" | RNAfold

# Output:
# GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA
# (((((((..((((........)))).(((((.......))))).....(((((.......)))))))))))).... (-31.30)

# Batch fold from FASTA file
RNAfold < sequences.fasta > structures.txt

# Generate base pair probability dot plot (PostScript)
RNAfold --noPS < sequences.fasta   # suppress PostScript output
RNAfold -p < sequences.fasta       # save dot plot as rna.ps
# Python: run RNAfold via subprocess and parse output
import subprocess
import re

def run_rnafold(sequence: str) -> tuple:
    """Run RNAfold CLI and return (structure, mfe) tuple."""
    result = subprocess.run(
        ["RNAfold", "--noPS"],
        input=sequence,
        capture_output=True, text=True, timeout=30
    )
    if result.returncode != 0:
        raise RuntimeError(f"RNAfold failed: {result.stderr}")

    lines = result.stdout.strip().split("\n")
    # Last line: structure and energy, e.g. "((....)) (-5.40)"
    match = re.match(r"^([.()\[\]{}<>|]+)\s+\((-?\d+\.\d+)\)$", lines[-1])
    if not match:
        raise ValueError(f"Could not parse RNAfold output: {lines[-1]}")
    structure = match.group(1)
    mfe       = float(match.group(2))
    return structure, mfe

sequences = [
    ("tRNA-Phe", "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"),
    ("miR-21",   "UAGCUUAUCAGACUGAUGUUGA"),
]

for name, seq in sequences:
    struct, mfe = run_rnafold(seq)
    print(f"{name}: {mfe:.2f} kcal/mol  |  {struct}")
步骤 8:约束折叠和次优结构

应用硬约束(强制或禁止特定碱基对)并枚举次优结构。

import RNA

sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"

# --- Constrained folding: force specific pairs ---
fc = RNA.fold_compound(sequence)

# Add hard constraint: force positions 1-7 to be paired (known stem)
hc = RNA.hc_add_bp(fc, 1, 72)  # force pair between position 1 and 72 (0-indexed in C API)
structure_c, mfe_c = fc.mfe()
print(f"Constrained MFE:   {mfe_c:.2f} kcal/mol")
print(f"Constrained struct: {structure_c}")

# --- Enumerate suboptimal structures (delta_mfe threshold) ---
fc_sub = RNA.fold_compound(sequence)
_, mfe_opt = fc_sub.mfe()

# Get suboptimal structures within 5 kcal/mol of MFE
delta = 5.0  # kcal/mol window
subopt_list = RNA.subopt(sequence, int(delta * 100))   # energy in 10-cal units
print(f"\nSuboptimal structures within {delta} kcal/mol of MFE:")
print(f"Total suboptimal structures: {len(subopt_list)}")
for s in subopt_list[:3]:
    e = s.energy / 100.0  # convert from 10-cal to kcal/mol
    print(f"  {s.structure}  ({e:.2f} kcal/mol)")

关键参数

参数默认值范围 / 选项作用
sequence (RNA.fold)—ACGU 字符串输入 RNA 序列;T 会自动转换为 U
temperature (模型详情)37.0 °C0–100 °C折叠温度;温度越低,结构越稳定
dangles (模型详情)20、1、2、3悬垂末端处理方式;2 = 平均值,0 = 不处理,大多数应用建议使用 2
noGU (模型详情)FalseTrue/False为 True 时禁止 G-U 摇摆配对
noLP (模型详情)FalseTrue/False禁止孤立碱基对(单碱基对茎);减少噪声
delta (RNA.subopt)—浮点数 kcal/mol枚举次优结构时高于 MFE 的能量窗口
window(滑动窗口)—整数 nt用于长序列可及性分析的滑动窗口大小

常见示例

示例:从 FASTA 文件批量进行 MFE 折叠

适用情况:折叠一组 RNA 序列(例如候选适配体、向导 RNA)并比较能量。

import RNA
from pathlib import Path

def read_fasta(fasta_path: str) -> list:
    """Parse FASTA file, return list of (name, sequence) tuples."""
    records = []
    name, seq = None, []
    for line in Path(fasta_path).read_text().splitlines():
        if line.startswith(">"):
            if name:
                records.append((name, "".join(seq).upper().replace("T", "U")))
            name, seq = line[1:].split()[0], []
        else:
            seq.append(line.strip())
    if name:
        records.append((name, "".join(seq).upper().replace("T", "U")))
    return records

# Example: fold sequences from a FASTA file
sequences = [
    ("aptamer_1", "GGGUUUUGAAACUAAACUAGGCUCUAGCGCUGGUGUCCCUUCCCGGCUCUAGCCUCAGCAGAAGCUUGAAAAAACCC"),
    ("aptamer_2", "GGGAGACAAGAAUAAACGCUCAACGUCUACCAUGAUCGAAUGCUAGCCUUCUAGCUUGCUUCGGCAGCACUAUAGGG"),
    ("aptamer_3", "GGGCGACCCUGAUGAGUCCCAAGUCGAAACGAUUCCUUUUUAAACUCAUGGUGCCCAGCCUCGCUCAGCA"),
]

print(f"{'Name':<15} {'Length':>8} {'MFE':>10} {'Structure'}")
print("-" * 80)
for name, seq in sequences:
    struct, mfe = RNA.fold(seq)
    print(f"{name:<15} {len(seq):>8} {mfe:>10.2f}  {struct[:50]}...")
示例:检查 sgRNA 二级结构

适用情况:评估 CRISPR sgRNA 向导序列是否会折叠成降低 Cas9 结合效率的二级结构。

import RNA

def assess_sgrna(guide_seq: str, scaffold: str = None) -> dict:
    """
    Assess sgRNA secondary structure.
    guide_seq: 20-nt spacer sequence (RNA)
    scaffold: constant sgRNA scaffold sequence (default: SpCas9)
    """
    if scaffold is None:
        # SpCas9 sgRNA scaffold (Addgene standard)
        scaffold = "GUUUUAGAGCUAGAAAUAGCAAGUUAAAAUAAGGCUAGUCCGUUAUCAACUUGAAAAAGUGGCACCGAGUCGGUGCUUU"

    full_sgrna = guide_seq.upper().replace("T", "U") + scaffold
    fc = RNA.fold_compound(full_sgrna)
    structure, mfe = fc.mfe()
    fc.exp_params_rescale(mfe)
    fc.pf()
    bpp = fc.bpp()

    n_guide = len(guide_seq)
    # Check if any guide bases are paired (bad for targeting)
    guide_paired = sum(
        bpp[min(i, j)][max(i, j)]
        for i in range(1, n_guide + 1)
        for j in range(1, n_guide + 1)
        if i != j
    )
    guide_accessibility = 1.0 - min(1.0, guide_paired / n_guide)
    return {
        "guide_seq":    guide_seq,
        "mfe":          mfe,
        "structure":    structure[:n_guide],
        "guide_access": guide_accessibility,
        "predicted_ok": guide_accessibility > 0.7,
    }

guides = ["GCACUAGUGACGCAUGGCAC", "GGGCAUAGCUAGCUAGCUAU", "AAAUUCGCACUAGUGACGCA"]
for g in guides:
    result = assess_sgrna(g)
    status = "OK" if result["predicted_ok"] else "WARN (self-paired)"
    print(f"{g}: accessibility={result['guide_access']:.2f}, MFE={result['mfe']:.1f} kcal/mol  [{status}]")
示例:RNA 结构山形图可视化

适用情况:绘制山形图——一种经典的 RNA 结构可视化图,展示沿序列变化的茎高度。

import RNA
import matplotlib.pyplot as plt

def dot_bracket_to_mountain(structure: str) -> list:
    """Convert dot-bracket structure to mountain plot heights."""
    heights = []
    level = 0
    for c in structure:
        if c == "(":
            level += 1
        heights.append(level)
        if c == ")":
            level -= 1
    return heights

sequence = "GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"
structure, mfe = RNA.fold(sequence)
heights = dot_bracket_to_mountain(structure)

fig, axes = plt.subplots(2, 1, figsize=(12, 5), sharex=True,
                          gridspec_kw={"height_ratios": [1, 3]})

# Top: sequence text
axes[0].text(0.5, 0.5, "tRNA-Phe  |  E. coli", ha="center", va="center",
             fontsize=10, transform=axes[0].transAxes)
axes[0].axis("off")

# Bottom: mountain plot
axes[1].fill_between(range(len(sequence)), heights, step="mid",
                      color="#2c7bb6", alpha=0.7, label=f"MFE = {mfe:.2f} kcal/mol")
axes[1].set_xlabel("Nucleotide position")
axes[1].set_ylabel("Stem height")
axes[1].set_title("Mountain Plot")
axes[1].legend()
plt.tight_layout()
plt.savefig("mountain_plot.png", dpi=150, bbox_inches="tight")
print(f"Saved: mountain_plot.png  (MFE structure: {structure[:40]}...)")

预期输出

输出类型描述
structure字符串MFE 二级结构的点括号表示法;( 和 ) 表示配对碱基,. 表示未配对碱基
mfe浮点数(kcal/mol)预测结构的最小自由能;数值越负,结构越稳定
bpp(n+1)×(n+1) 矩阵由配分函数得出的碱基配对概率矩阵;元素 [i][j] = P(i 与 j 配对),索引从 1 开始
bpp_matrix.pngPNG碱基配对概率热图
rna_accessibility.pngPNG各位置未配对的概率
mountain_plot.pngPNG展示沿序列变化的茎高度的山形图

故障排除

问题原因解决方案
ImportError: No module named 'RNA'未安装 ViennaRNA Python 绑定使用 conda install -c conda-forge viennarna 安装;单独使用 pip install 可能无法链接 C 库
RuntimeError: RNAfold not foundViennaRNA CLI 不在 PATH 中使用 which RNAfold 确认;如果使用 conda 环境,请在运行脚本前先激活环境
MFE 意外为正值(> 0)序列过短或重复,且没有有利配对短序列(< 10 nt)的 MFE 通常为正值;请检查序列长度和组成
调用 fc.pf() 后 bpp 矩阵全为零必须先对同一折叠复合体调用 fc.mfe()始终先调用 fc.mfe(),再调用 fc.exp_params_rescale(mfe),最后调用 fc.pf()
次优结构枚举返回数千个结构对长序列而言窗口过大对长序列,将 delta 降至 2–3 kcal/mol;非常稳定的序列会产生密集的次优结构集合
共折叠(RNA.cofold)出现意外配对一条链中的分子内折叠占主导先分别检查每条链;单链 MFE 较低表示其自身结构较强,可能干扰双链形成

参考资料

评论

支持 Markdown