Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Process multiple sequence files in batch using Biopython. Use when working with many files, merging/splitting sequences, or automating file operations across directories.
.claude/skills/bio-batch-processing/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-15 | ✗→✓ | ▲ Improved | — | — |
| case-20 | ✗→✓ | ▲ Improved | — | — |
| case-21 | ✗→✓ | ▲ Improved | — | — |
| case-03 | ✗→✓ | ▲ Improved | — | — |
| case-12 | ✗→✓ | ▲ Improved | — | — |
Reference examples tested with: BioPython 1.83+
Before using code patterns, verify installed versions match. If versions differ:
pip show <package> then help(module.function) to check signaturesIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
"Process all my sequence files in a directory" → Iterate, merge, split, convert, and generate summary statistics across multiple sequence files.
SeqIO.parse(), Path.glob() (BioPython, pathlib)Process multiple sequence files efficiently using Biopython.
pythonfrom pathlib import Path from Bio import SeqIO
pythonfrom pathlib import Path for fasta_file in Path('data/').glob('*.fasta'): records = list(SeqIO.parse(fasta_file, 'fasta')) print(f'{fasta_file.name}: {len(records)} sequences')
pythonfor fq_file in Path('.').glob('*.fastq'): count = sum(1 for _ in SeqIO.parse(fq_file, 'fastq')) print(f'{fq_file.name}: {count} reads')
pythonfor gb_file in Path('data/').rglob('*.gb'): print(f'Found: {gb_file}')
pythonfrom pathlib import Path def all_records(directory, pattern, format): for filepath in Path(directory).glob(pattern): yield from SeqIO.parse(filepath, format) records = all_records('data/', '*.fasta', 'fasta') count = SeqIO.write(records, 'merged.fasta', 'fasta') print(f'Merged {count} records')
Goal: Combine sequences from multiple files into one, tagging each record with its source filename.
Approach: Stream records from each file through a generator that appends source metadata to the description.
Reference (BioPython 1.83+):
pythondef records_with_source(directory, pattern, format): for filepath in Path(directory).glob(pattern): for record in SeqIO.parse(filepath, format): record.description = f'{record.description} [source={filepath.name}]' yield record records = records_with_source('data/', '*.fasta', 'fasta') SeqIO.write(records, 'merged_tracked.fasta', 'fasta')
pythonfiles = ['sample1.fasta', 'sample2.fasta', 'sample3.fasta'] def merge_files(file_list, format): for filepath in file_list: yield from SeqIO.parse(filepath, format) SeqIO.write(merge_files(files, 'fasta'), 'combined.fasta', 'fasta')
Goal: Divide a large sequence file into smaller chunks of N records each.
Approach: Consume the iterator in fixed-size batches using islice, writing each batch to a numbered output file.
Reference (BioPython 1.83+):
pythonfrom itertools import islice def split_file(input_file, format, records_per_file, output_prefix): records = SeqIO.parse(input_file, format) file_num = 1 while True: batch = list(islice(records, records_per_file)) if not batch: break output_file = f'{output_prefix}_{file_num}.{format}' SeqIO.write(batch, output_file, format) print(f'Wrote {len(batch)} records to {output_file}') file_num += 1 split_file('large.fasta', 'fasta', 1000, 'split')
Goal: Group sequences into separate files based on a shared ID prefix (e.g., sample or chromosome).
Approach: Parse all records into a prefix-keyed dictionary, then write each group to its own file.
Reference (BioPython 1.83+):
pythonfrom collections import defaultdict records_by_prefix = defaultdict(list) for record in SeqIO.parse('input.fasta', 'fasta'): prefix = record.id.split('_')[0] records_by_prefix[prefix].append(record) for prefix, records in records_by_prefix.items(): SeqIO.write(records, f'{prefix}.fasta', 'fasta')
pythonfor record in SeqIO.parse('multi.fasta', 'fasta'): SeqIO.write(record, f'{record.id}.fasta', 'fasta')
pythonfrom pathlib import Path for gb_file in Path('genbank/').glob('*.gb'): fasta_file = Path('fasta/') / gb_file.with_suffix('.fasta').name count = SeqIO.convert(str(gb_file), 'genbank', str(fasta_file), 'fasta') print(f'{gb_file.name} -> {fasta_file.name}: {count} records')
pythonfrom pathlib import Path results = [] for input_file in Path('input/').glob('*.gb'): output_file = Path('output/') / input_file.with_suffix('.fasta').name count = SeqIO.convert(str(input_file), 'genbank', str(output_file), 'fasta') results.append({'file': input_file.name, 'records': count}) print(f'Converted {len(results)} files, {sum(r["records"] for r in results)} total records')
pythonfrom multiprocessing import Pool from pathlib import Path def process_file(filepath): records = list(SeqIO.parse(filepath, 'fasta')) return {'file': filepath.name, 'count': len(records), 'total_bp': sum(len(r.seq) for r in records)} files = list(Path('data/').glob('*.fasta')) with Pool(4) as pool: results = pool.map(process_file, files) for r in results: print(f'{r["file"]}: {r["count"]} seqs, {r["total_bp"]} bp')
pythonfrom concurrent.futures import ThreadPoolExecutor from pathlib import Path def count_records(filepath): return filepath.name, sum(1 for _ in SeqIO.parse(filepath, 'fasta')) files = list(Path('data/').glob('*.fasta')) with ThreadPoolExecutor(max_workers=4) as executor: results = executor.map(count_records, files) for name, count in results: print(f'{name}: {count}')
pythonfrom pathlib import Path total_seqs = 0 total_bp = 0 file_count = 0 for fasta_file in Path('data/').glob('*.fasta'): for record in SeqIO.parse(fasta_file, 'fasta'): total_seqs += 1 total_bp += len(record.seq) file_count += 1 print(f'Files: {file_count}') print(f'Sequences: {total_seqs}') print(f'Total bp: {total_bp}') print(f'Average length: {total_bp / total_seqs:.0f}')
Goal: Generate a CSV summary of sequence counts and length statistics for every file in a directory.
Approach: Iterate files, compute per-file stats, collect into a list of dicts, and write as CSV.
Reference (BioPython 1.83+):
pythonfrom pathlib import Path import csv summaries = [] for fasta_file in Path('data/').glob('*.fasta'): records = list(SeqIO.parse(fasta_file, 'fasta')) lengths = [len(r.seq) for r in records] summaries.append({ 'file': fasta_file.name, 'sequences': len(records), 'total_bp': sum(lengths), 'min_len': min(lengths) if lengths else 0, 'max_len': max(lengths) if lengths else 0, 'avg_len': sum(lengths) / len(lengths) if lengths else 0 }) with open('summary.csv', 'w', newline='') as f: writer = csv.DictWriter(f, fieldnames=summaries[0].keys()) writer.writeheader() writer.writerows(summaries)
pythonfrom pathlib import Path from Bio.SeqUtils import gc_fraction Path('high_gc').mkdir(exist_ok=True) Path('low_gc').mkdir(exist_ok=True) for fasta_file in Path('input/').glob('*.fasta'): records = list(SeqIO.parse(fasta_file, 'fasta')) avg_gc = sum(gc_fraction(r.seq) for r in records) / len(records) if avg_gc >= 0.5: dest = Path('high_gc') / fasta_file.name else: dest = Path('low_gc') / fasta_file.name SeqIO.write(records, dest, 'fasta')
| Task | Approach | |------|----------| | Merge files | Generator yielding from each file | | Split file | islice with batch size | | Convert all | Loop with SeqIO.convert | | Parallel processing | multiprocessing.Pool or ThreadPoolExecutor | | Summary stats | Accumulate while iterating |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +50 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.