Source code for mutcleaner.cleaners.antitoxin_pard3_custom_cleaners
import re
import pandas as pd
import numpy as np
from typing import List
from tqdm import tqdm
from ..core.pipeline import pipeline_step
from ..core.sequence import ProteinSequence
__all__ = ["infer_wt_sequence", "add_wild_type_sequence", "simplify_mutations"]
[docs]
@pipeline_step
def infer_wt_sequence(
dataset: pd.DataFrame,
mutation_col: str,
sequence_col: str = "mut_seq",
wt_column: str = "wt_seq",
) -> pd.DataFrame:
"""
infer wild type seuquence from mutation_sequence and mutation information
Argument
--------
dataset : pd.DataFrame
the dataset which can be inferred wild-type sequence with mutation sequence and mutation information
mutation_col : str
the column contatins mutation information
sequence_col : str, default="mut_seq"
the column contains mutation sequence
wt_column : str, default="wt_seq"
the column that can be inferred by mutation sequence and mutation information
Returns
-------
dataset: pd.DataFrame
the dataset contatins added wild-type sequence
"""
if dataset.empty:
raise ValueError("Dataset is empty. Cannot infer WT sequence.")
dataset = dataset.copy()
seq_len = len(dataset[sequence_col].iat[0])
sequences: List[str] = dataset[sequence_col].tolist()
mutation_seqs: List[str] = dataset[mutation_col].tolist()
wt_sequence_list = []
try:
mut_matrix = (
np.array(mutation_seqs, dtype="U").view("U1").reshape(len(dataset), -1)
)
seq_matrix = np.array(sequences, dtype="U").view("U1").reshape(len(dataset), -1)
except ValueError:
raise ValueError("Sequences in the DataFrame are not of equal length.")
for i in range(seq_len):
match_indices = np.where(mut_matrix[:, i] == "_")[0]
if match_indices.size > 0:
idx = match_indices[0]
original_char = seq_matrix[idx, i]
wt_sequence_list.append(original_char)
else:
print(
f"Warning: Position {i} is mutated in All sequences. Cannot recover wild-type sequence."
)
wt_sequence_list.append("X")
wt_seq = "".join(wt_sequence_list)
dataset[wt_column] = wt_seq
return dataset
[docs]
@pipeline_step
def simplify_mutations(
dataset: pd.DataFrame,
mutation_column: str = "mut_info",
mutation_sep: str = ":",
) -> pd.DataFrame:
"""
Simplify mutation strings by removing no-change tokens like L47L.
Keeps unknown tokens unchanged (does not filter rows).
Examples
--------
>>> import pandas as pd
>>> df = pd.DataFrame({
>>> 'name':["protein1", "protein1", "protein1"]
>>> 'mut_info':["L47L:D51D:I52I","L47A:D51D:I52A","L47_:D51D:I52I"]
>>> })
>>> df = simplify_mutations(df)
>>> df[mut_info]
pd.Series("WT","L47A,I52A","L47_") # only simplify the mutations but not fliter
"""
if mutation_column not in dataset.columns:
raise ValueError(f"the {mutation_column} is not in the dataset")
dataset = dataset.copy()
# only for simplifying
# validate the legality of mutations
mutation_regex = re.compile(r"^([A-Z\*_])(\d+)([A-Z\*_])$")
def simplify_single_mutations(mutations):
if pd.isna(mutations):
return mutations
mutations = str(mutations).strip()
if not mutations:
return mutations
keep = []
for single_mutation_tok in mutations.split(mutation_sep):
single_mutation_tok = single_mutation_tok.strip()
if not single_mutation_tok:
continue
single_mutation = mutation_regex.match(single_mutation_tok)
if single_mutation is None:
keep.append(single_mutation_tok)
continue
wt, pos, mut = (
single_mutation.group(1),
single_mutation.group(2),
single_mutation.group(3),
)
if wt != mut:
keep.append(f"{wt}{pos}{mut}")
return "WT" if len(keep) == 0 else ",".join(keep)
dataset[mutation_column] = dataset[mutation_column].apply(simplify_single_mutations)
return dataset