<jupyter_start><jupyter_text>Feature Engineering in GenomicsFeature Engineering is the process of transforming raw data into features/input variables that are easily digested by algorithms. People think that data scientists often spend most of their time testing out various algorithms; however, the majority of performance gains generally come from well-crafted features.While performing feature engineering, it is critical to keep in mind the question that you are trying to answer. For the purposes of this exercise, we will be using ...genomic data, with an aims to answer the following questions:In this notebook, we will introduce the following types of feature engineering:- Feature pruning- Time-based features (month, year, etc)- One-hot encoding to create dummy variables- Extracting features from strings- Feature scaling- Data imputation / cleaningHow to transform your genomics data to fit into machine learning models  Converting DNA Sequence String into NumPy Array  1. Ordinal / Label Encodingthis label (ordinary) encoding will encode each base nucleotide as a custom numerical value. Mostly use `'A':0.25, 'C':0.5,'G':0.75, 'T':1.0`, but sometimes may use `A':1, 'C':2,'G':3, 'T':4`, which is not recommended. Here is a custom code for label encoding. However, `sklearn` has an inbuilt encoder.<jupyter_code>dna_sequence_string = "ATATATCCCGGGAATTTTCGTAGTTAGGCTGATTTTATTGGCGCGAAAATTTTTT"
dna_dict_per = {'A':0.25, 'C':0.5,'G':0.75, 'T':1.0, 'N':0 }
dna_dict_int = {'A':1, 'C':2,'G':3, 'T':4, 'N':0 }
label_encode = [dna_dict_per[dna] for dna in list(dna_sequence_string)]
label_encode
import numpy as np
import re
def string_to_array(seq_string):
    seq_string = seq_string.lower()
    seq_string = re.sub('[^acgt]', 'n', seq_string)
    seq_string = np.array(list(seq_string))
    return seq_string
# create a label encoder with 'acgtn' alphabet
from sklearn.preprocessing import LabelEncoder
label_encoder = LabelEncoder()
label_encoder.fit(np.array(['a','c','g','t','z']))
def ordinal_encoder(my_array):
    integer_encoded = label_encoder.transform(my_array)
    float_encoded = integer_encoded.astype(float)
    float_encoded[float_encoded == 0] = 0.25 # A
    float_encoded[float_encoded == 1] = 0.50 # C
    float_encoded[float_encoded == 2] = 0.75 # G
    float_encoded[float_encoded == 3] = 1.00 # T
    float_encoded[float_encoded == 4] = 0.00 # anything else, lets say n
    return float_encoded
seq_test = 'TTCAGCCAGTG'
ordinal_encoder(string_to_array(seq_test))<jupyter_output><empty_output><jupyter_text>2. One hot EncoderFor the standard base of nucleotides, the “ACGT” sequence string will be one-hot encoded as [[1. 0. 0. 0.] [0. 1. 0. 0.] [0. 0. 1. 0.] [0. 0. 0. 1.]] using the NumPy array [‘a’ ‘c’ ‘g’ ‘t’].<jupyter_code>from numpy import array
from numpy import argmax
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
import re

class hot_dna:
    def __init__(self,fasta):
        #check for and grab sequence name
        if re.search(">",fasta):
            name = re.split("\n",fasta)[0]
            sequence = re.split("\n",fasta)[1]
        else :
            name = 'unknown_sequence'
            sequence = fasta
  
        #get sequence into an array
        seq_array = array(list(sequence))
    
        #integer encode the sequence
        label_encoder = LabelEncoder()
        integer_encoded_seq = label_encoder.fit_transform(seq_array)
    
        #one hot the sequence
        onehot_encoder = OneHotEncoder(sparse=False)
        #reshape because that's what OneHotEncoder likes
        integer_encoded_seq = integer_encoded_seq.reshape(len(integer_encoded_seq), 1)
        onehot_encoded_seq = onehot_encoder.fit_transform(integer_encoded_seq)

        #add the attributes to self 
        self.name = name
        self.sequence = fasta
        self.integer = integer_encoded_seq
        self.onehot = onehot_encoded_seq
fasta = ">fake_sequence\nATGTGTCGTAGTCGTACG"
my_hottie = hot_dna(fasta)
my_hottie.onehot
from sklearn.preprocessing import OneHotEncoder
def one_hot_encoder(seq_string):
    int_encoded = label_encoder.transform(seq_string)
    onehot_encoder = OneHotEncoder(sparse=False, dtype=int)
    int_encoded = int_encoded.reshape(len(int_encoded), 1)
    onehot_encoded = onehot_encoder.fit_transform(int_encoded)
    onehot_encoded = np.delete(onehot_encoded, -1, 1)
    return onehot_encoded
seq_test = 'GAATTCTCGAA'
one_hot_encoder(string_to_array(seq_test))<jupyter_output><empty_output><jupyter_text>K-mer countingAnother form of encoding, mostly used in Natural language processing is k-mer counting. Here we can decide on the size of the k-mer and then count them in various sequences, then see how if that can correlate.We first take the long biological sequence and break it down into k-mer length overlapping “words”. For example, if we use “words” of length 6 (hexamers), “ATGCATGCA” becomes: ‘ATGCAT’, ‘TGCATG’, ‘GCATGC’, ‘CATGCA’. Hence our example sequence is broken down into 4 hexamer words.In genomics, we refer to these types of manipulations as [“k-mer counting”](https://en.wikipedia.org/wiki/K-mer), or counting the occurrences of each possible k-mer sequence and Python natural language processing tools make it super easy.<jupyter_code>def Kmers_funct(seq, k):
    return [seq[x:x+k].lower() for x in range(len(seq) - k + 1)]
mySeq = 'GTGCCCAGGTTCAGTGAGTGACACAGGCAG'
Kmers_funct(mySeq, k=5)<jupyter_output><empty_output><jupyter_text>We can the convert the kmers to words, and then use the natural language processing methods.<jupyter_code>words = Kmers_funct(mySeq, k=6)
joined_sentence = ' '.join(words)
joined_sentence
mySeq1 = 'TCTCACACATGTGCCAATCACTGTCACCCTCTCACACA'
mySeq2 = 'GTGCCCAGGTTCAGTGAGTGACACAGGCAGTCTCACACA'
sentence1 = ' '.join(Kmers_funct(mySeq1, k=6))
sentence2 = ' '.join(Kmers_funct(mySeq2, k=6))
sentence1
sentence2
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer()
X = cv.fit_transform([joined_sentence, sentence1, sentence2]).toarray()
X<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09648688767936665, 'fraction_numerical': 0.015009071416790368, 'mean_word_length': 4.720754716981132, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 19, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '94281', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 1989, 'n_tokens_neox': 1890, 'n_words': 588, 'path': 'Notebooks/04_Feature_Engineering_genomics.ipynb', 'repo_name': 'karegapauline/MachineLearning4Genomics', 'size': '6063'}