From fbd7d89a837873e50cd1199a07c056c397b4074c Mon Sep 17 00:00:00 2001 From: sandho0000 <michael.sandholzer@unibas.ch> Date: Mon, 14 Nov 2022 16:06:29 +0100 Subject: [PATCH] modified: modules.py --- build/lib/read_sequencer_package/__init__.py | 0 build/lib/read_sequencer_package/cli.py | 23 +++ build/lib/read_sequencer_package/modules.py | 148 +++++++++++++++++++ dist/read_sequencer-0.1.1-py3-none-any.whl | Bin 0 -> 4386 bytes dist/read_sequencer-0.1.1.tar.gz | Bin 0 -> 3593 bytes 5 files changed, 171 insertions(+) create mode 100644 build/lib/read_sequencer_package/__init__.py create mode 100644 build/lib/read_sequencer_package/cli.py create mode 100644 build/lib/read_sequencer_package/modules.py create mode 100644 dist/read_sequencer-0.1.1-py3-none-any.whl create mode 100644 dist/read_sequencer-0.1.1.tar.gz diff --git a/build/lib/read_sequencer_package/__init__.py b/build/lib/read_sequencer_package/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/read_sequencer_package/cli.py b/build/lib/read_sequencer_package/cli.py new file mode 100644 index 0000000..e786d78 --- /dev/null +++ b/build/lib/read_sequencer_package/cli.py @@ -0,0 +1,23 @@ +import argparse +from modules import read_sequencer as rs + +parser = argparse.ArgumentParser(prog='read_sequencer', + description='Simulates sequencing of DNA sequences specified by an FASTA file.') +parser.add_argument('--input_file_path', + help='path to FASTA file') +parser.add_argument('--output_file_path', + help='path to FASTA file') +parser.add_argument('--read_length', + help='read length for sequencing', + type=int) + +args = parser.parse_args() + +def main(): + read_sequencer = rs() + read_sequencer.read_fasta(args.input_file_path) + read_sequencer.run_sequencing(args.read_length) + read_sequencer.write_fasta(args.output_file_path) + +if __name__ == '__main__': + main() diff --git a/build/lib/read_sequencer_package/modules.py b/build/lib/read_sequencer_package/modules.py new file mode 100644 index 0000000..39a6866 --- /dev/null +++ b/build/lib/read_sequencer_package/modules.py @@ -0,0 +1,148 @@ +def generate_sequences(n, mean, sd): + """ + Generates random sequences. + + Args: + n (int): Amount of sequences to generate. + mean (int): mean length of sequence (gaussian distribution). + sd (float): standard deviation of length of sequence (gaussian distribution). + + Returns: + list: of n sequences + """ + from random import gauss, choice + dict = {} + for i in range(n): + keys = range(n) + seq = "" + nt = ["A", "T", "C", "G"] + for value in range(abs(round(gauss(mean, sd)))): + seq = seq + choice(nt) + dict[keys[i]] = seq + return dict + + +def read_in_fasta(file_path): + ''' + This function reads in FASTA files. + + Args: + file_path (str): A file path directing to the fasta file. + + Returns: + Dict: It returns a dictionary with sequences. + + ''' + sequences = {} + f = open(file_path) + for line in f: + if line[0] == '>': + defline = line.strip() + defline = defline.replace('>', '') + else: + if defline not in sequences: + sequences[defline] = '' + sequences[defline] += line.strip() + f.close() + return sequences + +def read_sequence(seq, read_length): + ''' + This function reads a sequence of a specific read length and adds additional nucleotides if the sequence is + smaller then the requested length or cuts the sequence if its longer. + + Args: + seq (str): the sequence to read + read_length (int): length of reads + + Returns: + str: returns sequenced element + + ''' + from random import choice + bases = ["A", "T", "C", "G"] + sequenced = '' + if read_length >= len(seq): + for nt in range(len(seq)): + sequenced += seq[nt] + for nt in range(len(seq), read_length): + sequenced += choice(bases) + else: + for nt in range(read_length): + sequenced += seq[nt] + + return sequenced + +def simulate_sequencing(sequences, read_length): + """ + Simulates sequencing. + + Args: + sequences (dict): Dictionary of sequences to sequence. + read_length (int): length of reads + + Returns: + dict: of n sequences as values + """ + results = {} + for index, key in enumerate(sequences): + results[key] = read_sequence(sequences[key], read_length=read_length) + + return results + +import random +def generate_sequences(n, mean, sd): + """ + Generates random sequences. + + Args: + n (int): Amount of sequences to generate. + mean (int): mean length of sequence (gaussian distribution). + sd (float): standart deviation of length of sequence (gaussian distribution). + + Returns: + dict: of n sequences + """ + dict1 = {} + for i in range(n): + keys = range(n) + seq = "" + nt = ["A", "T", "C", "G"] + for value in range(round(random.gauss(mean, sd))): + seq = seq + random.choice(nt) + dict1[keys[i]] = seq + return dict1 + +def write_fasta(sequences, file_path): + """ + Takes a dictionary and writes it to a fasta file. + Must specify the filename when calling the function. + + Args: + sequences (dict): Dictionary of sequence. + file_path (str): A file path directing to the output folder. + + """ + from textwrap import wrap + with open(file_path, "w") as outfile: + for key, value in sequences.items(): + outfile.write(key + "\n") + outfile.write("\n".join(wrap(value, 60))) + outfile.write("\n") + +class read_sequencer: + def __init__(self): + self.sequences = {} + self.reads = {} + + def add_random_sequences(self, n, mean, sd): + self.sequences = generate_sequences(n, mean, sd) + + def read_fasta(self, input_file): + self.sequences = read_in_fasta(input_file) + + def run_sequencing(self, read_length): + self.reads = simulate_sequencing(self.sequences, read_length) + + def write_fasta(self, output_file_path): + write_fasta(self.reads, output_file_path) diff --git a/dist/read_sequencer-0.1.1-py3-none-any.whl b/dist/read_sequencer-0.1.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..234b5227d12d9f5e410d26b2e131fed34c2596fc GIT binary patch literal 4386 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 literal 0 HcmV?d00001 diff --git a/dist/read_sequencer-0.1.1.tar.gz b/dist/read_sequencer-0.1.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..7eb03d4a5f331e1050f6b5f6c8cb5ee356fc2595 GIT binary patch literal 3593 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 P{J7zN`E!`=0H6Q>xO;Ew literal 0 HcmV?d00001 -- GitLab