Task: Bioinformatics_Sequence mutation


C++: #include <array>
#include <iomanip>
#include <iostream>
#include <random>
#include <string>

class sequence_generator {
public:
    sequence_generator();
    std::string generate_sequence(size_t length);
    void mutate_sequence(std::string&);
    static void print_sequence(std::ostream&, const std::string&);
    enum class operation { change, erase, insert };
    void set_weight(operation, unsigned int);
private:
    char get_random_base() {
        return bases_[base_dist_(engine_)];
    }
    operation get_random_operation();
    static const std::array<char, 4> bases_;
    std::mt19937 engine_;
    std::uniform_int_distribution<size_t> base_dist_;
    std::array<unsigned int, 3> operation_weight_;
    unsigned int total_weight_;
};

const std::array<char, 4> sequence_generator::bases_{ 'A', 'C', 'G', 'T' };

sequence_generator::sequence_generator() : engine_(std::random_device()()),
    base_dist_(0, bases_.size() - 1),
    total_weight_(operation_weight_.size()) {
    operation_weight_.fill(1);
}

sequence_generator::operation sequence_generator::get_random_operation() {
    std::uniform_int_distribution<unsigned int> op_dist(0, total_weight_ - 1);
    unsigned int n = op_dist(engine_), op = 0, weight = 0;
    for (; op < operation_weight_.size(); ++op) {
        weight += operation_weight_[op];
        if (n < weight)
            break;
    }
    return static_cast<operation>(op);
}

void sequence_generator::set_weight(operation op, unsigned int weight) {
    total_weight_ -= operation_weight_[static_cast<size_t>(op)];
    operation_weight_[static_cast<size_t>(op)] = weight;
    total_weight_ += weight;
}

std::string sequence_generator::generate_sequence(size_t length) {
    std::string sequence;
    sequence.reserve(length);
    for (size_t i = 0; i < length; ++i)
        sequence += get_random_base();
    return sequence;
}

void sequence_generator::mutate_sequence(std::string& sequence) {
    std::uniform_int_distribution<size_t> dist(0, sequence.length() - 1);
    size_t pos = dist(engine_);
    char b;
    switch (get_random_operation()) {
    case operation::change:
        b = get_random_base();
        std::cout << "Change base at position " << pos << " from "
            << sequence[pos] << " to " << b << '\n';
        sequence[pos] = b;
        break;
    case operation::erase:
        std::cout << "Erase base " << sequence[pos] << " at position "
            << pos << '\n';
        sequence.erase(pos, 1);
        break;
    case operation::insert:
        b = get_random_base();
        std::cout << "Insert base " << b << " at position "
            << pos << '\n';
        sequence.insert(pos, 1, b);
        break;
    }
}

void sequence_generator::print_sequence(std::ostream& out, const std::string& sequence) {
    constexpr size_t base_count = bases_.size();
    std::array<size_t, base_count> count = { 0 };
    for (size_t i = 0, n = sequence.length(); i < n; ++i) {
        if (i % 50 == 0) {
            if (i != 0)
                out << '\n';
            out << std::setw(3) << i << ": ";
        }
        out << sequence[i];
        for (size_t j = 0; j < base_count; ++j) {
            if (bases_[j] == sequence[i]) {
                ++count[j];
                break;
            }
        }
    }
    out << '\n';
    out << "Base counts:\n";
    size_t total = 0;
    for (size_t j = 0; j < base_count; ++j) {
        total += count[j];
        out << bases_[j] << ": " << count[j] << ", ";
    }
    out << "Total: " << total << '\n';
}

int main() {
    sequence_generator gen;
    gen.set_weight(sequence_generator::operation::change, 2);
    std::string sequence = gen.generate_sequence(250);
    std::cout << "Initial sequence:\n";
    sequence_generator::print_sequence(std::cout, sequence);
    constexpr int count = 10;
    for (int i = 0; i < count; ++i)
        gen.mutate_sequence(sequence);
    std::cout << "After " << count << " mutations:\n";
    sequence_generator::print_sequence(std::cout, sequence);
    return 0;
}






Python: import random
from collections import Counter

def basecount(dna):
    return sorted(Counter(dna).items())

def seq_split(dna, n=50):
    return [dna[i: i+n] for i in range(0, len(dna), n)]

def seq_pp(dna, n=50):
    for i, part in enumerate(seq_split(dna, n)):
        print(f"{i*n:>5}: {part}")
    print("\n  BASECOUNT:")
    tot = 0
    for base, count in basecount(dna):
        print(f"    {base:>3}: {count}")
        tot += count
    base, count = 'TOT', tot
    print(f"    {base:>3}= {count}")

def seq_mutate(dna, count=1, kinds="IDSSSS", choice="ATCG" ):
    mutation = []
    k2txt = dict(I='Insert', D='Delete', S='Substitute')
    for _ in range(count):
        kind = random.choice(kinds)
        index = random.randint(0, len(dna))
        if kind == 'I':    
            dna = dna[:index] + random.choice(choice) + dna[index:]
        elif kind == 'D' and dna:  
            dna = dna[:index] + dna[index+1:]
        elif kind == 'S' and dna:  
            dna = dna[:index] + random.choice(choice) + dna[index+1:]
        mutation.append((k2txt[kind], index))
    return dna, mutation

if __name__ == '__main__':
    length = 250
    print("SEQUENCE:")
    sequence = ''.join(random.choices('ACGT', weights=(1, 0.8, .9, 1.1), k=length))
    seq_pp(sequence)
    print("\n\nMUTATIONS:")
    mseq, m = seq_mutate(sequence, 10)
    for kind, index in m:
        print(f" {kind:>10} @{index}")
    print()
    seq_pp(mseq)