关于 viennarna-structure-prediction
viennarna-structure-prediction 是面向 RNA 研究者的 ViennaRNA 结构预测技能,涵盖 Python 和命令行操作,可用于热力学折叠、概率分析及 RNA 相互作用预测。
主要功能
- 01预测最小自由能结构
- 02分析碱基配对概率
- 03预测 RNA 共折叠与双链
- 04进行约束及次优折叠
- 05使用 RNAfold 命令行
适用场景
- 01分析 RNA 序列二级结构
- 02比较不同序列的结构不确定性
- 03评估 siRNA 靶区可及性
- 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 °C | 0–100 °C | 折叠温度;温度越低,结构越稳定 |
dangles (模型详情) | 2 | 0、1、2、3 | 悬垂末端处理方式;2 = 平均值,0 = 不处理,大多数应用建议使用 2 |
noGU (模型详情) | False | True/False | 为 True 时禁止 G-U 摇摆配对 |
noLP (模型详情) | False | True/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.png | PNG | 碱基配对概率热图 |
rna_accessibility.png | PNG | 各位置未配对的概率 |
mountain_plot.png | PNG | 展示沿序列变化的茎高度的山形图 |
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
ImportError: No module named 'RNA' | 未安装 ViennaRNA Python 绑定 | 使用 conda install -c conda-forge viennarna 安装;单独使用 pip install 可能无法链接 C 库 |
RuntimeError: RNAfold not found | ViennaRNA 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 较低表示其自身结构较强,可能干扰双链形成 |
参考资料
- ViennaRNA 文档 — 官方文档、参数文件和 Python API 参考
- Lorenz et al., Algorithms Mol Biol 2011 — ViennaRNA Package 2.0 论文(结构预测的主要引用文献)
- Turner & Mathews, Nucleic Acids Res 2010 — ViennaRNA 使用的最近邻热力学参数
- ViennaRNA GitHub:ViennaRNA/ViennaRNA — 源代码、问题跟踪器和 Python 教程笔记本