import pandas as pd
import natsort as ns #3rd party package for natural sorting 
import re

data = pd.read_csv("5G_counts.tsv", sep = "\t")

columns_list = list(range(0,9)) + list(range(20,42)) #creating a list of columns that I care about (see below)
data_1 = data.iloc[:, columns_list] #taking only 0-8 and 20-42 columns removing old FM runs 

#Want to sort the data columns (20 - 42) in their timely order. Will split into 2 dataframes, sort, then put together.

first_8 = data_1.iloc[:, 0:9] #new data frame with first 9 columns
remaining_data = data_1.iloc[:,9:] # new data frame with remaining columns (to be sorted)


cols = list(ns.natsorted(remaining_data.columns)) #using natural sort package
newdf=remaining_data[cols]


data_2 = pd.concat([first_8, newdf], axis = 1) #ok so now combined first 8 columns with FM34 and FM40 columns 

#the columns still contain many QC runs lets get rid of them (see aside for removing columins with "QC" in them)
list(data_2.columns)

data_3 = data_2.select(lambda x: not re.search("QC", x), axis = 1)

data_3

#OK now need to create a TPM counts for all the columns 
data_3["gene_length"] = (data_3["end_coord"]-data_3["start_coord"] + 1)/1000 #gene length in kilo base pair  

data_3

#before moving on, I want to see the stats for the gene length column (min, max, mean, etc.)
data_3.gene_length.describe()

#lets find the loc range of the columns I want to divide and my gene length column
print(data_3.columns.get_loc("gene_length")) # need to devide all FM40 columns by this column 
print(data_3.columns.get_loc("5GB1_FM40_T0m_TR2")) # this is where my range starts. so columns [9-16]/[17]

RPK = data_3.iloc[:,9:17].div(data_3.gene_length, axis=0) #it is 9-17 because the last value is not inclusive. 

data_4 = pd.concat([first_8, RPK], axis = 1)

data_4

data_4.iloc[:,9:17].sum() #the sum of reads normalized to gene length is different between samples. 

norm_sum = data_4.iloc[:,9:17].sum(axis=0)/1000000 #creating a series with the sums of each FM40 column / 1,000,000
norm_sum = pd.Series.to_frame(norm_sum) #converting this series into a dataframe 
norm_sum = norm_sum.T #transposing the dataframe so that there is one value per column

norm_sum

TPM = data_4.iloc[:,9:17].div(norm_sum.ix[0]) #dividing FM40 columns by the the total transcript counts in each repicate

data_5 = pd.concat([first_8, TPM], axis = 1) #this is the TPM!

data_5.iloc[:,9:17].sum() # can check that the sum total of each colum is identical. Now can do stats! 

data_5

"""
Explanation: Time Course Transcriptomics for Cu-Induced transition in 5GB1
End of explanation
"""


#can individualy identify 1 column into a new dataframe
df = data["locus_tag"]
df

# for multiple column selection must use the __getitem__ syntax [] 
df1 = data[["locus_tag","type"]]  
df1
#Dont want to manually enter all the column names 

"""
Explanation: Aside, how to keep only columns with FM40, and FM34
End of explanation
"""


list(data.columns)

print(data.columns.get_loc("translation"))
print(data.columns.get_loc("5GB1_FM40_T0m_TR2"))
print(data.columns.get_loc("5GB1_FM40_T90m_TR2_QC"))

columns_list = list(range(0,9)) + list(range(20,42))

columns_list

data_1 = data.iloc[:, columns_list] #slicing the column index the way I want. 

"""
Explanation: identifying the column index in order to remove unnecessary columns from data
End of explanation
"""


first_8 = data_1.iloc[:, 0:9] #new data frame with first 9 columns
remaining_data = data_1.iloc[:,9:] # new data frame with remaining columns (to be sorted)
remaining_data

sorted(remaining_data.columns)  #sorted is a python function that sorts your input (dont know by what criteria)

list(sorted(remaining_data.columns,key=str)) # will list sorted columns, but doesnt sort this naturally (150 before 40)

import natsort as ns #3rd party package for natural sorting 
list(ns.natsorted(remaining_data.columns)) #this works!

cols=list(ns.natsorted(remaining_data.columns)) #this works!

remaining_data[cols].head()

newdf=remaining_data[cols]

newdf.head()

ns.natsorted(remaining_data)#the problem with this package is that passing the object as an argument 
                                            #returns a list, and I cant use that list for the dataframe, I need index. 

remaining_data.loc("5GB1_Cu_transition_tim")

remaining_data.columns = ns.natsorted(remaining_data.columns) 

list(remaining_data.columns)

remaining_data["5GB1_FM40_T150m_TR1_remake"]

data["5GB1_FM40_T150m_TR1_remake"]

"""
Explanation: Reordering certain columns alphabetically, only after the initial 0-8 columns.
End of explanation
"""


#many columns with QC runs. Gotta filter those out. 
list(data_2.columns) 

data_2.select(lambda x: not re.search("QC", x), axis = 1) #ok this is what I need, not lets break it down. 

# re is a regex (regular expression) module. It is useful in selecting strings or parts of strings. Here is an example. 

str_1 = 'an example word:cat12, word:cat!!, word:cattt165'
match = re.search(r'word:cat\d+', str_1) #the r ignores slashes (google education has a nice tutorial with re module)

if match:
    print("found", match.group())
    
else:
    print("did not find")


#lets find multiple words, the precious example on found 1

#say you have a text with many email addresses

str_2 = "purple alice@gmail.com, and many other like bob@yahoo.com and also a dishwasher"
emails = re.findall(r"\w+@\w+\.\w+", str_2)
for email in emails:
    print (email)


print(emails)

"""
Explanation: Aside to remove all columns containing the string "QC"
End of explanation
"""


data.ix[1:3] #supports mixed integer and label based access. It is
            #primarily label based, but will fall back to integer positional
            #access unless the corresponding axis is of integer type.

data.loc[1] #returns the first row, Note: "1" is
            #interpreted as a *label* of the index, and **never** as an
            # integer position along the index).
data.loc[0:2,"5GB1_FM34_T0_TR1_QC"] #the integers are interpreted as names, not positions. 

data.iloc[1:3] #returns the data for axis 0 = 1,2,3 (or the first three rows of data)
data.iloc[3] #returns the data for the third row 
data.iloc[[1,2,5]] #returns data for rows 1,2,5


type(data.iloc[4])

"""
Explanation: Taking an aside to understanding iloc, loc, ix
End of explanation
"""


