bio-genome-annotation-ncrna-annotation — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited bio-genome-annotation-ncrna-annotation (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Reference examples tested with: pandas 2.2+
Before using code patterns, verify installed versions match. If versions differ:
pip show <package> then help(module.function) to check signatures<tool> --version then <tool> --help to confirm flagsIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
"Find non-coding RNAs in my genome" → Scan a genome assembly for rRNAs, tRNAs, snoRNAs, and other ncRNA families using covariance model search and specialized detectors.
cmscan --rfam --tblout hits.tbl Rfam.cm assembly.fa (Infernal), tRNAscan-SE -o trnas.txt assembly.faIdentify and annotate non-coding RNAs in genome assemblies using Infernal (general ncRNAs via Rfam covariance models) and tRNAscan-SE (specialized tRNA detection).
Infernal uses covariance models (CMs) from Rfam to identify ncRNA families by both sequence and secondary structure similarity.
# Download Rfam covariance models (~600 MB compressed)
wget https://ftp.ebi.ac.uk/pub/databases/Rfam/CURRENT/Rfam.cm.gz
gunzip Rfam.cm.gz
# Press the CM database (required for cmscan)
cmpress Rfam.cm
# Download clan information (for overlap resolution)
wget https://ftp.ebi.ac.uk/pub/databases/Rfam/CURRENT/Rfam.clanin# Search genome against all Rfam families
# --cut_ga: Use Rfam gathering threshold (recommended, family-specific)
# --rfam: Run in Rfam mode (skip slow alignment for large models)
# --nohmmonly: Use CM scoring only (more accurate, slower)
# --fmt 2: Output in table format
cmscan \
--cut_ga \
--rfam \
--nohmmonly \
--tblout ncrna_hits.tbl \
--fmt 2 \
--cpu 16 \
--clanin Rfam.clanin \
Rfam.cm \
genome.fasta > ncrna_hits.out| Option | Description |
|---|---|
--cut_ga | Use Rfam gathering threshold (recommended) |
--rfam | Speed optimization for Rfam-scale searches |
--nohmmonly | Force CM-only scoring (more sensitive for structured RNAs) |
--clanin | Rfam clan file for resolving overlapping hits |
--tblout | Tabular output file |
--fmt 2 | Extended table format with accessions |
--cpu | CPU threads |
-E | E-value threshold (default: 10; --cut_ga is preferred) |
# Using Infernal's built-in conversion
grep -v '^#' ncrna_hits.tbl | \
awk 'BEGIN{OFS="\t"} {
if ($10 == "+") strand = "+"; else strand = "-";
if ($10 == "+") {start=$8; end=$9} else {start=$9; end=$8};
print $4, "Infernal", "ncRNA", start, end, $17, strand, ".", "ID="$2";Name="$3";rfam_acc="$2";evalue="$17
}' > ncrna_infernal.gff3| Category | Typical E-value | Notes |
|---|---|---|
| High confidence | < 1e-10 | Unambiguous family assignment |
| Moderate | 1e-10 to 1e-5 | Likely real, verify context |
| Rfam GA threshold | family-specific | Recommended cutoff with --cut_ga |
| Marginal | > 1e-3 | May be pseudogenes or degraded copies |
Specialized tRNA detector using covariance models. More sensitive and specific for tRNAs than Infernal alone.
# Bacterial/archaeal mode
tRNAscan-SE -B -o trna_results.txt --gff trna.gff3 genome.fasta
# Eukaryotic mode
tRNAscan-SE -E -o trna_results.txt --gff trna.gff3 genome.fasta
# General mode (auto-detect domain)
tRNAscan-SE -G -o trna_results.txt --gff trna.gff3 genome.fasta| Option | Description |
|---|---|
-B | Bacterial mode |
-A | Archaeal mode |
-E | Eukaryotic mode |
-G | General (mixed/unknown) mode |
-o | Tabular output |
--gff | GFF3 output |
--detail | Detailed output with isotype info |
--thread | CPU threads |
-Q | Covariance model scoring only (slower, more accurate) |
| Domain | Flag | Notes |
|---|---|---|
| Bacteria | -B | Shorter introns, smaller genomes |
| Archaea | -A | Split tRNAs, bulge-helix-bulge introns |
| Eukaryota | -E | Longer introns, pseudogenes common |
| Organelle | -O | Mitochondrial/chloroplast tRNAs |
| Mitochondrial | -M | Mammalian mitochondrial-specific |
| Organism Type | Expected tRNAs | Notes |
|---|---|---|
| Bacteria | 30-90 | Fewer isotypes, some shared |
| Archaea | 30-60 | Similar to bacteria |
| Yeast | 275-400 | Many isodecoders |
| Nematode | 600-900 | Gene family expansion |
| Mammal | 400-600 | Many pseudogenes |
| Plant | 500-1,000+ | Large gene families |
barrnap predicts rRNA genes using HMM models. Note: barrnap has been unmaintained since 2018. Bakta handles rRNA detection internally for prokaryotes.
# Detect rRNAs
barrnap --kingdom bac genome.fasta > rrna.gff3
# Kingdoms: bac (bacteria), arc (archaea), euk (eukaryote), mito (mitochondria)
barrnap --kingdom euk --threads 4 genome.fasta > rrna.gff3Goal: Merge Infernal and tRNAscan-SE results into a unified ncRNA annotation, using the best tool for each RNA type.
Approach: Parse Infernal tabular output and classify hits by Rfam family name into broad ncRNA categories, parse tRNAscan-SE GFF for tRNA calls, then combine by dropping Infernal tRNA hits (tRNAscan-SE is more accurate for tRNAs) and keeping Infernal for all other ncRNA types.
import pandas as pd
from collections import defaultdict
def parse_infernal_tbl(tbl_file):
'''Parse Infernal cmscan tabular output.'''
hits = []
with open(tbl_file) as f:
for line in f:
if line.startswith('#'):
continue
parts = line.split()
if len(parts) < 18:
continue
strand = '+' if parts[9] == '+' else '-'
start, end = (int(parts[7]), int(parts[8])) if strand == '+' else (int(parts[8]), int(parts[7]))
hits.append({
'target': parts[0],
'rfam_acc': parts[1],
'rfam_name': parts[2],
'seqid': parts[3],
'start': start,
'end': end,
'strand': strand,
'evalue': float(parts[16]),
'score': float(parts[15]),
'rna_type': classify_rfam(parts[2]),
})
return pd.DataFrame(hits)
def classify_rfam(rfam_name):
'''Classify Rfam family into broad ncRNA categories.'''
name_lower = rfam_name.lower()
if 'rrna' in name_lower or 'ssu' in name_lower or 'lsu' in name_lower:
return 'rRNA'
if 'trna' in name_lower:
return 'tRNA'
if 'snorna' in name_lower or 'snord' in name_lower or 'snora' in name_lower:
return 'snoRNA'
if 'mir' in name_lower:
return 'miRNA'
if 'riboswitch' in name_lower or 'thermoregulator' in name_lower:
return 'riboswitch'
if 'ires' in name_lower or 'leader' in name_lower:
return 'cis-reg'
return 'other_ncRNA'
def parse_trnascan_gff(gff_file):
'''Parse tRNAscan-SE GFF3 output.'''
trnas = []
with open(gff_file) as f:
for line in f:
if line.startswith('#'):
continue
parts = line.strip().split('\t')
if len(parts) < 9 or parts[2] != 'tRNA':
continue
attrs = dict(item.split('=') for item in parts[8].split(';') if '=' in item)
trnas.append({
'seqid': parts[0],
'start': int(parts[3]),
'end': int(parts[4]),
'strand': parts[6],
'isotype': attrs.get('isotype', 'unknown'),
'anticodon': attrs.get('anticodon', 'unknown'),
'score': float(parts[5]) if parts[5] != '.' else 0,
'rna_type': 'tRNA',
})
return pd.DataFrame(trnas)
def combine_ncrna_annotations(infernal_tbl, trnascan_gff, output_gff):
'''Combine Infernal and tRNAscan-SE results, preferring tRNAscan for tRNAs.'''
infernal_df = parse_infernal_tbl(infernal_tbl)
trna_df = parse_trnascan_gff(trnascan_gff)
# Remove Infernal tRNA hits (tRNAscan-SE is more accurate for tRNAs)
infernal_no_trna = infernal_df[infernal_df['rna_type'] != 'tRNA']
summary = defaultdict(int)
for rna_type in infernal_no_trna['rna_type'].unique():
summary[rna_type] = len(infernal_no_trna[infernal_no_trna['rna_type'] == rna_type])
summary['tRNA'] = len(trna_df)
print('=== ncRNA Summary ===')
for rna_type, count in sorted(summary.items()):
print(f' {rna_type}: {count}')
print(f' Total: {sum(summary.values())}')
return infernal_no_trna, trna_df, summary--rfam flag for Rfam-mode optimizations--FZ 2 to further speed up (less sensitive)--detail flag to identify pseudogenes in output~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.