diff --git a/README.md b/README.md index 35e48c3..a38f13a 100644 --- a/README.md +++ b/README.md @@ -1 +1,289 @@ -# gene_code_tools \ No newline at end of file +![image](https://github.com/CaptnClementine/gene_code_tools/assets/131146976/68f2999b-5b6e-4668-9865-fae0d4e0b778) +# gene_code_tools + + +`gene_code_tools` is a collection of Python functions for working with DNA, RNA, and protein sequences. It provides utility functions to check and manipulate sequences based on various criteria. + +In the main file **gene_code_main_operations** you can find 3 most important functions**:** + +- [ ] filter_dna + - Filter a dictionary of FASTQ sequences based on various criteria. +- [ ] run_amino_analyzer + - Perform basic protein analytics. +- [ ] run_dna_rna_tools + - Conduct fundamental analytics on RNA and DNA sequences. + +| Function | Description | Returns | Arguments | +| --- | --- | --- | --- | +| filter_dna | Filter FASTQ sequences based on criteria like GC content, length, and quality. | Filtered sequences (dict) | seqs (dict), gc_bounds (tuple or int), length_bounds (tuple or int, optional), quality_threshold (int, optional) | +| run_amino_analyzer | Perform various protein sequence operations. | Result of specified operation(s) | seq (str), args (Union[str, Tuple[str, ...]]) | +| run_dna_rna_tools | Perform DNA and RNA sequence operations. | Result of specified operation(s) | seq (str), args (Union[str, Tuple[str, ...]]) | + +This README is a long one! If you want just try one function -> Ctrl+F and search for Usage paragraph 💜 + +Here's more detailed information and examples for each function: + +## ⭐ function filter_dna + +### Features + +- [ ] Check if a sequence consists only of DNA characters. +- [ ] Calculate the GC content percentage of a DNA sequence. +- [ ] Check if a DNA sequence falls within specified GC content bounds. +- [ ] Check if a DNA sequence falls within specified length bounds. +- [ ] Check if the average quality score of a sequence exceeds a threshold. + +### Usage + +Here's an example of how to use the functions provided by `gene_code_tools`: + +```python +# Create a dictionary of FASTQ sequences +seqs = { + 'sequence1': ('AGCTAGCTAGCT', '!@#$!@#$!@#$'), + 'sequence2': ('TATATATATATA', 'abcdefghi'), + # Add more sequences as needed +} + +# Specify your filtering criteria +gc_bounds = (0, 80) # GC content bounds +length_bounds = 100 # Sequence length bounds +quality_threshold = 30 # Quality threshold + +# Filter the sequences based on the criteria +filtered_seqs = filter_dna(seqs, gc_bounds, length_bounds, quality_threshold) + +# Use the filtered sequences as needed +print(filtered_seqs) +``` + +### Common Errors + +When using Gene Code Tools, you might encounter common errors such as invalid input values or incorrect sequence formats. Here are some typical errors and how to handle them: + +1. **Invalid gc_bounds or length_bounds**: Ensure that the bounds provided are valid tuples with two non-negative values or a single non-negative integer. For example, `gc_bounds=(20, 80)` is valid, and `gc_bounds=44.4` sets an upper GC content limit of 44.4%. All bounds inclusive +2. **Invalid quality_threshold**: The `quality_threshold` should be an integer between 0 and 42 (inclusive). +3. **Invalid sequence characters**: When working with DNA sequences, make sure that the input sequences contain only valid DNA characters (A, T, G, C, a, t, g, c). + +### Specified Variables and Parameters + +Gene Code Tools provides the following specified variables and parameters: + +- `seqs` (dict): A dictionary containing FASTQ sequences. +- `gc_bounds` (tuple or int): GC content filtering bounds. +- `length_bounds` (tuple or int, optional): Length filtering bounds. +- `quality_threshold` (int, optional): Quality threshold for filtering sequences. + +### Examples + +Here are some examples of how to use Gene Code Tools: + +```python +# Example 1: Filtering DNA sequences +filtered_seqs = filter_dna(seqs, gc_bounds=(20, 80), length_bounds=50, quality_threshold=30) + +# Example 2: Using a single upper bound for GC content +filtered_seqs = filter_dna(seqs, gc_bounds=44.4, length_bounds=(10, 100)) + +# Example 3: Using a single upper bound for sequence length +filtered_seqs = filter_dna(seqs, gc_bounds=(20, 80), length_bounds=1000) + +# Example 4: Filtering without specifying bounds +filtered_seqs = filter_dna(seqs) +``` + +## ⭐ function run_amino_analyzer + +### **Features** + +- [ ] Transcribe DNA sequences into RNA. +- [ ] Reverse transcribe RNA sequences into DNA. +- [ ] Check for the presence of a start codon in RNA sequences. +- [ ] Reverse sequences. +- [ ] Find the complement of DNA or RNA sequences. +- [ ] Find the reverse complement of DNA or RNA sequences. +- [ ] Check if a sequence is a palindrome. +- [ ] Determine the type of RNA or DNA sequences (DNA, RNA, or mixed). + +## Usage + +To run amino_analyzer tool you need to use the function ***run_amino_analyzer*** with the following arguments: + +```python +from amino_analyzer import run_amino_analyzer +run_amino_analyzer(sequence, procedure, *, weight_type = 'average', enzyme: str = 'trypsine')` +``` + +- `sequence (str):` The input protein sequence in one-letter code. +- `procedure (str):` The procedure to perform over your protein sequence. +- `weight_type: str = 'average':` default argument for `aa_weight` function. `weight_type = 'monoisotopic'` can be used as another option. +- `enzyme: str = 'trypsine':` default argument for `peptide_cutter` function. `enzyme = 'chymotrypsin'` can be used as another option + + +**Available procedures list** +- `aa_weight` — calculates the amino acids weight in a protein sequence. +- `count_hydroaffinity` — counts the quantity of hydrophobic and hydrophilic amino acids in a protein sequence. +- `peptide_cutter` — identifies cleavage sites in a given peptide sequence using a specified enzyme (trypsine or chymotripsine). +- `one_to_three_letter_code` — converts a protein sequence from one-letter amino acid code to three-letter code. +- `sulphur_containing_aa_counter` - counts sulphur-containing amino acids in a protein sequence. + +You can also use each function separately by importing them in advance. + +## Examples +To calculate protein molecular weight: +```python +run_amino_analyzer("VLSPADKTNVKAAW", "aa_weight") # Output: 1481.715 + +run_amino_analyzer("VLSPADKTNVKAAW", "aa_weight", weight_type = 'monoisotopic') # Output: 1480.804 +``` + +To count hydroaffinity: +```python +run_amino_analyzer("VLSPADKTNVKAAW", "count_hydroaffinity") # Output: (8, 6) +``` + +To find trypsin/chymotripsine clivage sites: +```python +run_amino_analyzer("VLSPADKTNVKAAW", "peptide_cutter") # Output: 'Found 2 trypsin cleavage sites at positions 7, 11' + +run_amino_analyzer("VLSPADKTNVKAAWW", "peptide_cutter", enzyme = 'chymotrypsin') # Output: 'Found 1 chymotrypsin cleavage sites at positions 14' +``` + +To change to 3-letter code and count sulphur-containing amino acids. +```python +run_amino_analyzer("VLSPADKTNVKAAW", "one_to_three_letter_code") # Output: 'ValLeuSerProAlaAspLysThrAsnValLysAlaAlaTrp' + +run_amino_analyzer("VLSPADKTNVKAAWM", "sulphur_containing_aa_counter") # Output: The number of sulphur-containing amino acids in the sequence is equal to 1 +``` + +## Common Errors +Here are some common issues you can come ascross while using the amino-analyzer tool and their possible solutions: + +1. **ValueError: Incorrect procedure** + If you receive this error, it means that you provided an incorrect procedure when calling `run_amino_analyzer`. Make sure you choose one of the following procedures: `aa_weight`, `count_hydroaffinity`, `peptide_cutter`, `one_to_three_letter_code`, or `sulphur_containing_aa_counter`. + + Example: + ```python + run_amino_analyzer("VLSPADKTNVKAAW", "incorrect_procedure") + # Output: ValueError: Incorrect procedure. Acceptable procedures: aa_weight, count_hydroaffinity, peptide_cutter, one_to_three_letter_code, sulphur_containing_aa_counter + ``` + +2. **ValueError: Incorrect sequence** +This error occurs if the input sequence provided to run_amino_analyzer contains characters that are not valid amino acids. Make sure your sequence only contains valid amino acid characters (V, I, L, E, Q, D, N, H, W, F, Y, R, K, S, T, M, A, G, P, C, v, i, l, e, q, d, n, h, w, f, y, r, k, s, t, m, a, g, p, c). + + Example: + ```python + run_amino_analyzer("VLSPADKTNVKAAW!", "aa_weight") + # Output: ValueError: Incorrect sequence. Only amino acids are allowed (V, I, L, E, Q, D, N, H, W, F, Y, R, K, S, T, M, A, G, P, C, v, i, l, e, q, d, n, h, w, f, y, r, k, s, t, m, a, g, p, c). + ``` + +3. **ValueError: You have chosen an enzyme that is not provided** +This error occurs if you provide an enzyme other than "trypsin" or "chymotrypsin" when calling peptide_cutter. Make sure to use one of the specified enzymes. + + Example: + ```python + peptide_cutter("VLSPADKTNVKAAW", "unknown_enzyme") + # Output: You have chosen an enzyme that is not provided. Please choose between trypsin and chymotrypsin. + ``` +4. **ValueError: You have chosen an enzyme that is not provided.** +If you encounter this error, it means that you're trying to iterate over a float value. Ensure that you're using the correct function and passing the correct arguments. + + Example: + ```python + result = count_hydroaffinity(123) + # Output: TypeError: 'int' object is not iterable + ``` + + + +## ⭐ function run_dna_rna_tools + +**Features** + +- [ ] Transcribe DNA sequences into RNA. +- [ ] Reverse transcribe RNA sequences into DNA. +- [ ] Check for the presence of a start codon in RNA sequences. +- [ ] Reverse sequences. +- [ ] Find the complement of DNA or RNA sequences. +- [ ] Find the reverse complement of DNA or RNA sequences. +- [ ] Check if a sequence is a palindrome. +- [ ] Determine the type of RNA or DNA sequences (DNA, RNA, or mixed). + +## **Usage** + +### **Main Function: `run_dna_rna_tools`** + +The **`run_dna_rna_tools`** function is the main entry point for performing various DNA and RNA sequence operations. It takes a sequence and an optional set of additional arguments to specify the operation to perform. + +```python +from dna_rna_tools_utils import run_dna_rna_tools + +# Example 1: Transcribe DNA to RNA +result = run_dna_rna_tools("ATGC", "transcribe") +print(result) # Output: "AUGC" + +# Example 2: Reverse RNA sequence +result = run_dna_rna_tools("AUGC", "reverse") +print(result) # Output: "CGUA" + +``` + +### Arguments + +- **`seq (str)`**: The input DNA or RNA sequence. +- **`args (Union[str, Tuple[str, ...]])`**: Additional sequences or options. If the last argument is a string, it specifies the operation to perform. + +### Supported Operations + +- **`transcribe`**: Transcribe a DNA sequence into RNA. +- **`reverse_transcription`**: Reverse transcribe an RNA sequence into DNA. +- **`has_start_codon`**: Check if an RNA sequence has a start codon. +- **`reverse`**: Reverse a sequence. +- **`complement`**: Find the complement of a DNA or RNA sequence. +- **`reverse_complement`**: Find the reverse complement of a DNA or RNA sequence. +- **`is_palindrome`**: Check if a sequence is a palindrome. + +### **Gene Code Utilities (`dna_rna_tools_utils.py`)** + +The **`dna_rna_tools_utils.py`** module contains the core functions used by the main function. It includes the following functions: + +- **`is_dna(seq: str) -> bool`**: Check if a sequence is DNA. +- **`is_rna(seq: str) -> bool`**: Check if a sequence is RNA. +- **`transcribe(seq: str) -> str`**: Transcribe a DNA sequence into RNA. +- **`reverse(seq: str) -> str`**: Reverse a sequence. +- **`complement(seq: str) -> str`**: Find the complement of a DNA or RNA sequence. +- **`reverse_complement(seq: str) -> str`**: Find the reverse complement of a DNA or RNA sequence. +- **`reverse_transcription(seq: str) -> str`**: Perform reverse transcription on an RNA sequence. +- **`is_palindrome(seq: str) -> bool`**: Check if a sequence is a palindrome. +- **`has_start_codon(seq: str) -> Union[bool, str]`**: Check if an RNA sequence has a start codon. +- **`type_rna_or_dna(seqs: List[str]) -> str`**: Determine the type of RNA or DNA from a list of sequences. + +You can use these functions directly if needed. + +## **Common Errors** + +- **Invalid Procedure**: If you specify an invalid operation, you will receive an "Invalid procedure. Check your sequences and try again." error. +- **Sequence Type Mismatch**: If you try to perform an operation on the wrong sequence type (e.g., transcribing a DNA sequence), you will receive a type-specific error message. +- **Unsupported Sequences**: If your sequences contain characters other than A, T, G, C, U, a, t, g, c, u, you will receive an "Input sequence must be DNA or RNA." error. + +## **Specified Variables and Parameters** + +- **`seq (str)`**: The input DNA or RNA sequence. +- **`args (Union[str, Tuple[str, ...]])`**: Additional sequences or options. +- **`procedure (str)`**: The specified operation to perform. +- **`seqs (List[str])`**: List of sequences to operate on. +- **`dna_or_rna (str)`**: Indicates whether the sequences are DNA, RNA, or mixed. +- **`new_seq (List[str])`**: List to store the results of operations. + +## **Example** + +```python +from dna_rna_tools_utils import run_dna_rna_tools + +# Transcribe DNA to RNA and find the reverse complement +result = run_dna_rna_tools("ATGC", "transcribe", "reverse_complement") +print(result) # Output: "GCAT" + +``` + +If you have any questions, suggestions, or encounter any issues while using the amino-analyzer tool, feel free to reach out [CaptnClementine](https://github.com/YourGitHubUsername) 💛 diff --git a/gene_code_main_operations b/gene_code_main_operations new file mode 100644 index 0000000..cb92b8e --- /dev/null +++ b/gene_code_main_operations @@ -0,0 +1,145 @@ +from typing import Dict, Tuple, Union +from gene_code_utils.filter_dna_utils import is_dna, is_in_gc_bounds, is_in_length_bounds, check_quality +from gene_code_utils.amino_analyzer_utils import aa_weight, count_hydroaffinity, peptide_cutter, one_to_three_letter_code, sulphur_containing_aa_counter +from gene_code_utils.dna_rna_tools_utils import type_rna_or_dna, transcribe, reverse_transcription, has_start_codon, reverse, complement, reverse_complement, is_palindrome + + + +def filter_dna(seqs: Dict[str, Tuple[str, str]], gc_bounds: Union[Tuple[int, int], int] = (0, 100), + length_bounds: Union[Tuple[int, int], int] = (0, 2**32), quality_threshold: int = 0) -> Dict[str, Tuple[str, str]]: + """ + Filter and process a dictionary of FASTQ sequences based on specified criteria. + + Args: + seqs (dict): A dictionary containing FASTQ sequences. + Key: Sequence name (string). + Value: Tuple of two strings (sequence, quality). + gc_bounds (tuple or int): GC content filtering bounds. + If a tuple, it represents the lower and upper bounds (inclusive) for GC content as percentages. + If an int, it represents the upper bound for GC content as a percentage. + length_bounds (tuple or int, optional): Length filtering bounds. + If a tuple, it represents the lower and upper bounds (inclusive) for sequence length. + If an int, it represents the upper bound for sequence length. + Default is (0, 2**32). + quality_threshold (int, optional): Quality threshold for filtering sequences based on average quality. + Sequences with an average quality below this threshold will be discarded. + Default is 0 (phred33 scale). + + Returns: + filtered_seqs (dict): A dictionary containing filtered FASTQ sequences. + Key: Sequence name (string). + Value: Tuple of two strings (sequence, quality). + + Example: + filtered_seqs = filtr_dna(seqs, gc_bounds=(20, 80), length_bounds=50, quality_threshold=30) + - here gc_bounds interval will be [20, 80] and length_bounds will be [0, 50]. + quality_threshold will be 30 <= sequence score + + Warnings: If you prefer + """ + + filtered_seqs = dict() + for fastq_name, (seq, quality) in seqs.items(): + if is_dna(seq): + if type(gc_bounds) == int: + gc_bounds = (0, gc_bounds) + if type(length_bounds) == int: + length_bounds = (0, length_bounds) + if is_in_gc_bounds(gc_bounds, seq) and is_in_length_bounds(length_bounds, seq) and check_quality(quality_threshold, quality): + filtered_seqs[fastq_name] = (seq, quality) + + return filtered_seqs + + +from amino_analyzer_utils import aa_weight, count_hydroaffinity, peptide_cutter, one_to_three_letter_code, sulphur_containing_aa_counter + + +def run_amino_analyzer(sequence: str, procedure: str, *, weight_type: str = 'average', enzyme: str = 'trypsin'): + """ + This is the main function to run the amino-analyzer.py tool. + + Args: + sequence (str): The input protein sequence in one-letter code. + procedure (str): amino-analyzer.py tool has 5 functions at all: + 1. aa_weight - Calculate the amino acids weight in a protein sequence. Return float weight + weight_type = 'average': default argument for 'aa_weight' function. weight_type = 'monoisotopic' can be used as a second option. + 2. count_hydroaffinity - Count the quantity of hydrophobic and hydrophilic amino acids in a protein sequence. Return list in order: hydrophobic, hydrophilic + 3. peptide_cutter - This function identifies cleavage sites in a given peptide sequence using a specified enzyme. Return list of cleavage sites + enzyme = 'trypsin': default argument for 'peptide_cutter' function. enzyme = 'chymotrypsin' can be used as a second option. + 4. one_to_three_letter_code - This function converts a protein sequence from one-letter amino acid code to three-letter code. Return string of amino acids in three-letter code + 5. sulphur_containing_aa_counter - This function counts sulphur-containing amino acids in a protein sequence. Return quantaty of sulphur-containing amino acids + + Returns: + The result of the specified procedure. + + Raises: + ValueError: If the procedure is not recognized or if the input sequence contains non-amino acid characters. + + Note: + - Supported amino acid characters: V, I, L, E, Q, D, N, H, W, F, Y, R, K, S, T, M, A, G, P, C, v, i, l, e, q, d, n, h, w, f, y, r, k, s, t, m, a, g, p, c. + - Make sure to provide a valid procedure name and sequence for analysis. + """ + + procedures = ['aa_weight', 'count_hydroaffinity', 'peptide_cutter', 'one_to_three_letter_code', 'sulphur_containing_aa_counter'] + if procedure not in procedures: + raise ValueError(f"Incorrect procedure. Acceptable procedures: {', '.join(procedures)}") + + if not is_aa(sequence): + raise ValueError("Incorrect sequence. Only amino acids are allowed (V, I, L, E, Q, D, N, H, W, F, Y, R, K, S, T, M, A, G, P, C, v, i, l, e, q, d, n, h, w, f, y, r, k, s, t, m, a, g, p, c).") + + if procedure == 'aa_weight': + result = aa_weight(sequence, weight_type) + elif procedure == 'count_hydroaffinity': + result = count_hydroaffinity(sequence) + elif procedure == 'peptide_cutter': + result = peptide_cutter(sequence, enzyme) + elif procedure == 'one_to_three_letter_code': + result = one_to_three_letter_code(sequence) + elif procedure == 'sulphur_containing_aa_counter': + result = sulphur_containing_aa_counter(sequence) + return result + + +def run_dna_rna_tools(seq: str, *args: Union[str, Tuple[str, ...]]) -> Union[str, List[str]]: + """ + Run various DNA and RNA sequence operations. + + Args: + seq (str): The input DNA or RNA sequence. + *args (Union[str, Tuple[str, ...]]): Additional sequences or options. + If the last argument is a string, it specifies the operation to perform. + If the last argument is 'transcribe', 'reverse_transcription', or 'has_start_codon', the input sequence(s) must be RNA. + Otherwise, the input sequence(s) must be DNA. + + Returns: + Union[str, List[str]]: The result of the specified operation(s). + If a single operation is performed, the result is a string. + If multiple operations are performed, the result is a list of strings. + """ + procedure = args[len(args)-1] + seqs = list((seq,)+args[:-1]) + dna_or_rna = type_rna_or_dna(seqs) + new_seq = [] + + operation_map = { + 'transcribe': transcribe, + 'reverse_transcription': reverse_transcription, + 'has_start_codon': has_start_codon, + 'reverse': reverse, + 'complement': complement, + 'reverse_complement': reverse_complement, + 'is_palindrome': is_palindrome, + } + + if procedure not in operation_map: + raise ValueError("Invalid procedure. Check your sequences and try again.") + if (procedure in ('transcribe')) and (dna_or_rna == 'RNA'): + raise ValueError("This procedure is only for DNA sequences. Check your sequences and try again.") + elif (procedure in ('reverse_transcription', 'has_start_codon')) and (dna_or_rna == 'DNA'): + raise ValueError("This procedure is only for RNA sequences. Check your sequences and try again.") + + print(f"Hi! Here is your {procedure} for all your {dna_or_rna} sequences") + + operation_func = operation_map[procedure] + new_seq = [operation_func(i) for i in seqs] + return new_seq[0] if len(new_seq) == 1 else new_seq diff --git a/gene_code_utils/amino_analyzer_utils.py b/gene_code_utils/amino_analyzer_utils.py new file mode 100644 index 0000000..f01958e --- /dev/null +++ b/gene_code_utils/amino_analyzer_utils.py @@ -0,0 +1,157 @@ +AA_SET = set(['V', 'I', 'L', 'E', 'Q', 'D', 'N', 'H', 'W', 'F', 'Y', 'R', 'K', 'S', 'T', 'M', 'A', 'G', 'P', 'C', + 'v', 'i', 'l', 'e', 'q', 'd', 'n', 'h', 'w', 'f', 'y', 'r', 'k', 's', 't', 'm', 'a', 'g', 'p', 'c']) +HYDROPHOBIC_AA = ['A', 'V', 'L', 'I', 'P', 'F', 'W', 'M'] +HYDROPHILIC_AA = ['R', 'N', 'D', 'C', 'Q', 'E', 'G', 'H', 'K', 'S', 'T', 'Y'] +AMINO_ACIDS = {'A': 'Ala', 'C': 'Cys', 'D': 'Asp', 'E': 'Glu', 'F': 'Phe', 'G': 'Gly', 'H': 'His', 'I': 'Ile', 'K': 'Lys', 'L': 'Leu', + 'M': 'Met', 'N': 'Asn', 'P': 'Pro', 'Q': 'Gln', 'R': 'Arg', 'S': 'Ser', 'T': 'Thr', 'V': 'Val', 'W': 'Trp', 'Y': 'Tyr'} + + +def is_aa(seq: str) -> bool: + """ + Check if a sequence contains only amino acids. + + Args: + seq (str): The input sequence to be checked. + + Returns: + bool: True if the sequence contains only amino acids, False otherwise. + """ + unique_chars = set(seq) + return unique_chars <= AA_SET + + +def choose_weight(weight: str) -> dict: + """ + Choose the weight type of amino acids - average or monoisotopic. + + Args: + weight (str): The type of weight to choose, either 'average' or 'monoisotopic'. + + Returns: + dict: A dictionary mapping amino acids to their weights based on the chosen type. + """ + if weight == 'average': + average_weights = { + 'A': 71.0788, 'R': 156.1875, 'N': 114.1038, 'D': 115.0886, 'C': 103.1388, + 'E': 129.1155, 'Q': 128.1307, 'G': 57.0519, 'H': 137.1411, 'I': 113.1594, + 'L': 113.1594, 'K': 128.1741, 'M': 131.1926, 'F': 147.1766, 'P': 97.1167, + 'S': 87.0782, 'T': 101.1051, 'W': 186.2132, 'Y': 163.1760, 'V': 99.1326 + } + elif weight == 'monoisotopic': + monoisotopic_weights = { + 'A': 71.03711, 'R': 156.10111, 'N': 114.04293, 'D': 115.02694, 'C': 103.00919, + 'E': 129.04259, 'Q': 128.05858, 'G': 57.02146, 'H': 137.05891, 'I': 113.08406, + 'L': 113.08406, 'K': 128.09496, 'M': 131.04049, 'F': 147.06841, 'P': 97.05276, + 'S': 87.03203, 'T': 101.04768, 'W': 186.07931, 'Y': 163.06333, 'V': 99.06841 + } + else: + raise ValueError(f"I do not know what '{weight}' is :( \n Read help or just do not write anything except your sequence") + + return average_weights if weight == 'average' else monoisotopic_weights + + +def aa_weight(seq: str, weight: str = 'average') -> float: + """ + Calculate the amino acids weight in a protein sequence. + + Args: + seq (str): The amino acid sequence to calculate the weight for. + weight (str, optional): The type of weight to use, either 'average' or 'monoisotopic'. Default is 'average'. + + Returns: + float: The calculated weight of the amino acid sequence. + """ + weights_aa = choose_weight(weight) + final_weight = 0 + for aa in seq.upper(): + final_weight += weights_aa[aa] + return round(final_weight, 3) + + +def count_hydroaffinity(seq: str) -> list: + """ + Count the quantity of hydrophobic and hydrophilic amino acids in a protein sequence. + + Args: + seq (str): The protein sequence for which to count hydrophobic and hydrophilic amino acids. + + Returns: + tuple: A tuple containing the count of hydrophobic and hydrophilic amino acids, respectively. + """ + hydrophobic_count = 0 + hydrophilic_count = 0 + seq = seq.upper() + + for aa in seq: + if aa in HYDROPHOBIC_AA: + hydrophobic_count += 1 + elif aa in HYDROPHILIC_AA: + hydrophilic_count += 1 + + return [hydrophobic_count, hydrophilic_count] + + +def peptide_cutter(sequence: str, enzyme: str = "trypsin") -> list: + """ + This function identifies cleavage sites in a given peptide sequence using a specified enzyme. + + Args: + sequence (str): The input peptide sequence. + enzyme (str): The enzyme to be used for cleavage. Choose between "trypsin" and "chymotrypsin". Default is "trypsin". + + Returns: + str: A message indicating the number and positions of cleavage sites, or an error message if an invalid enzyme is provided. + """ + cleavage_sites = [] + if enzyme not in ("trypsin", "chymotrypsin"): + return "You have chosen an enzyme that is not provided. Please choose between trypsin and chymotrypsin." + + if enzyme == "trypsin": # Trypsin cuts peptide chains mainly at the carboxyl side of the amino acids lysine or arginine. + for aa in range(len(sequence)-1): + if sequence[aa] in ['K', 'R', 'k', 'r'] and sequence[aa+1] not in ['P','p']: + cleavage_sites.append(aa + 1) + + if enzyme == "chymotrypsin": # Chymotrypsin preferentially cleaves at Trp, Tyr and Phe in position P1(high specificity) + for aa in range(len(sequence) - 1): + if sequence[aa] in ['W', 'Y', 'F', 'w', 'y', 'f'] and sequence[aa+1] not in ['P','p']: + cleavage_sites.append(aa + 1) + + if cleavage_sites: + print( f"Found {len(cleavage_sites)} {enzyme} cleavage sites at positions {', '.join(map(str, cleavage_sites))}") + return cleavage_sites# list(', '.join(map(str, cleavage_sites))) + else: + print(f"No {enzyme} cleavage sites were found.") + return 0 + + +def one_to_three_letter_code(sequence: str) -> str: + """ + This function converts a protein sequence from one-letter amino acid code to three-letter code. + + Args: + sequence (str): The input protein sequence in one-letter code. + + Returns: + str: The converted protein sequence in three-letter code. + """ + three_letter_code = [AMINO_ACIDS.get(aa.upper()) for aa in sequence] + return '-'.join(three_letter_code) + + +def sulphur_containing_aa_counter(sequence: str) -> int: + """ + This function counts sulphur-containing amino acids (Cysteine and Methionine) in a protein sequence. + + Args: + sequence (str): The input protein sequence in one-letter code. + + Returns: + str: The number of sulphur-containing amino acids in a protein sequence. + """ + counter = 0 + sequence = sequence.upper() + for aa in sequence: + if aa == 'C' or aa == 'M': + counter += 1 + answer = str(counter) + return answer diff --git a/gene_code_utils/dna_rna_tools_utils.py b/gene_code_utils/dna_rna_tools_utils.py new file mode 100644 index 0000000..06707cd --- /dev/null +++ b/gene_code_utils/dna_rna_tools_utils.py @@ -0,0 +1,176 @@ +DNA = set ('ATGCatgc') +RNA = set ('AUGCaugc') +TRANSCRIPTION_TABLE = { + "a": "a", "A": "A", + "t": "u", "T": "U", + "u": "t", "U": "T", + "g": "g", "G": "G", + "c": "c", "C": "C" +} + + +def is_dna(seq: str) -> bool: + """ + Check if a sequence is DNA. + + Args: + seq (str): The input sequence to check. + + Returns: + bool: True if the sequence is DNA, False otherwise. + """ + unique_chars = set(seq) + return unique_chars <= DNA + + +def is_rna(seq: str) -> bool: + """ + Check if a sequence is RNA. + + Args: + seq (str): The input sequence to check. + + Returns: + bool: True if the sequence is RNA, False otherwise. + """ + unique_chars = set(seq) + return unique_chars <= RNA + + +def type_rna_or_dna(seqs: List[str]) -> str: + """ + Determine the type of RNA or DNA from a list of sequences. + + Args: + seqs (List[str]): List of sequences to determine their type. + + Returns: + str: 'DNA' if all sequences are DNA, 'RNA' if all sequences are RNA, + 'MIXED' if there are both DNA and RNA sequences, or raises a ValueError for unsupported sequences. + """ + counter_dna = 0 + counter_rna = 0 + ambigiuos = 0 + for i in seqs: + if is_dna(i) and is_rna(i): + ambigiuos = ambigiuos+1 + else: + if is_dna(i): + counter_dna=counter_dna+1 + elif is_rna(i): + counter_rna=counter_rna+1 + if (counter_dna + ambigiuos) == len(seqs): + print('You have ', ambigiuos, ' ambigious NA. I suppose they are DNA') + return "DNA" + elif (counter_rna + ambigiuos) == len(seqs): + print('You have ', ambigiuos, ' ambigious NA. I suppose they are RNA') + return "RNA" + elif (counter_dna + counter_rna + ambigiuos) == len(seqs): + return "MIXED" + else: + raise ValueError("I can work only with RNA and DNA. \n Check your sequences and try one more time!") + + +def reverse(seq: str) -> str: + """ + Reverse a sequence. + + Args: + seq (str): The input sequence. + + Returns: + str: The reversed sequence. + """ + return seq[::-1] + + +def complement(seq: str) -> str: + """ + Find the complement of a DNA or RNA sequence. + + Args: + seq (str): The input DNA or RNA sequence. + + Returns: + str: The complemented sequence. + """ + new_seq = [] + if is_dna(seq): + complement_dict = {'A': 'T', 'a': 't', 'C': 'G', 'c': 'g', 'G': 'C', 'g': 'c', 'T': 'A', 't': 'a'} + elif is_rna(seq): + complement_dict = {'A': 'U', 'a': 'u', 'C': 'G', 'c': 'g', 'G': 'C', 'g': 'c', 'U': 'A', 'u': 'a'} + else: + raise ValueError("Input sequence must be DNA or RNA.") + for i in seq: + new_seq.append(complement_dict.get(i)) + return ''.join(new_seq) + + +def reverse_complement(seq: str) -> str: + """ + Find the reverse complement of a DNA or RNA sequence. + + Args: + seq (str): The input DNA or RNA sequence. + + Returns: + str: The reverse complemented sequence. + """ + return reverse(complement(seq)) + + +def reverse_transcription(seq: str) -> str: + """ + Perform reverse transcription on an RNA sequence. + + Args: + seq (str): The input RNA sequence. + + Returns: + str: The reverse transcribed DNA sequence. + """ + c_dna = [] + u_to_t = {'U': 'T', 'u': 't'} + for i in seq: + if i in u_to_t: + c_dna.append(u_to_t.get(i)) + else: + c_dna.append(i) + return ''.join(c_dna) + + +def has_start_codon(seq: str) -> Union[bool, str]: + """ + Check if an RNA sequence has a start codon. + + Args: + seq (str): The input RNA sequence. + + Returns: + Union[bool, str]: True if a start codon is found, False if not found, + '?' if the input is DNA and needs to be transcribed first. + """ + if is_rna(seq): + return "AUG" in seq.upper() + if is_dna(seq): + print("First, you should transcribe your DNA.") + return '?' + raise ValueError("Input sequence must be DNA or RNA.") + + +def is_palindrome(seq: str) -> bool: + """ + Check if a sequence is a palindrome. + + Args: + seq (str): The input sequence to check. + + Returns: + bool: True if the sequence is a palindrome, False otherwise. + """ + new_seq = reverse_complement(seq) + return new_seq.upper() == seq.upper() + + +def transcribe(seq): + return ''.join([TRANSCRIPTION_TABLE[i] for i in seq]) diff --git a/gene_code_utils/filter_dna_utils.py b/gene_code_utils/filter_dna_utils.py new file mode 100644 index 0000000..05d19af --- /dev/null +++ b/gene_code_utils/filter_dna_utils.py @@ -0,0 +1,94 @@ +def is_dna(seq: str) -> bool: + """ + Check if the input sequence consists only of DNA characters. + + Args: + seq (str): The input DNA sequence. + + Returns: + bool: True if the sequence contains only DNA characters, False otherwise. + """ + unique_chars = set(seq) + nucleotides = set('ATGCatgc') + return unique_chars <= nucleotides + + +def count_gc_content(dna: str) -> float: + """ + Calculate the GC content percentage of a DNA sequence. + + Args: + dna (str): The input DNA sequence. + + Returns: + float: The GC content percentage. + """ + dna = dna.upper() + gc = dna.count('G') + dna.count('C') + at = dna.count('A') + dna.count('T') + gc_content = gc / (at + gc) * 100 + return gc_content + + +def is_in_gc_bounds(bounds: tuple, dna: str) -> bool: + """ + Check if the GC content of a DNA sequence falls within the specified bounds. + + Args: + bounds (tuple): A tuple specifying the lower and upper bounds for GC content. + dna (str): The input DNA sequence. + + Returns: + bool: True if the GC content is within the bounds, False otherwise. + """ + gc_content = count_gc_content(dna) + upper_bound = max(bounds[0], bounds[1]) + lower_bound = min(bounds[0], bounds[1]) + if upper_bound < 0 or lower_bound < 0: + raise ValueError("Invalid gc_bounds. Each value must be greater than zero.") + if upper_bound == lower_bound: + upper_bound += 1 + return lower_bound <= gc_content <= upper_bound + + +def is_in_length_bounds(bounds: tuple, dna: str) -> bool: + """ + Check if the length of a DNA sequence falls within the specified bounds. + + Args: + bounds (tuple): A tuple specifying the lower and upper bounds for sequence length. + dna (str): The input DNA sequence. + + Returns: + bool: True if the sequence length is within the bounds, False otherwise. + """ + dna_length = len(dna) + lower_bound = min(bounds[0], bounds[1]) + upper_bound = max(bounds[0], bounds[1]) + if upper_bound < 0 or lower_bound < 0: + raise ValueError("Invalid length_bounds. Each value must be greater than zero.") + if upper_bound == lower_bound: + upper_bound += 1 + return lower_bound <= dna_length <= upper_bound + + +def check_quality(quality_threshold: int, quality: str) -> bool: + """ + Check if the average quality score of a sequence exceeds a threshold. + + Args: + quality_threshold (int): The quality threshold for filtering sequences. + quality (str): The quality string (in Phred+33 format). + + Returns: + bool: True if the average quality score is above the threshold, False otherwise. + """ + if quality_threshold < 0 or quality_threshold > 42: + raise ValueError("Invalid quality_threshold. Must be an integer in the range [0, 42]") + score = 0 + for char in quality: + score += ord(char) - 33 + if ord(char) < 33 or ord(char) > 126: + return False + avg_quality = score / len(quality) + return quality_threshold <= avg_quality