import pandas as pd

"""
Explanation: Manipolazione di un file GTF (Gene Transfer Format) attraverso la libreria Pandas
1) Importare Pandas
End of explanation
"""


df = pd.read_csv('./input.gtf', sep='\t', header = None)

df

"""
Explanation: 2) Leggere il file GTF
df = pd.read_csv(gtf_file_name, sep='\t', header = None)
End of explanation
"""


replace_dict = {0 : 'reference', 1 : 'source', 2 : 'feature', 3 : 'start', 4 : 'end', 5 : 'score', 6 : 'strand', 7 : 'frame', 8 : 'attributes'}
df.rename(columns = replace_dict, inplace = True)

df

"""
Explanation: NB: read_csv() ha un attributo names che permette di specificare la lista dei nomi delle colonne del data frame restituito.
3) Cambiare i nomi delle colonne
I nomi delle colonne devono essere:
- reference
- source
- feature
- start
- end
- score
- strand
- frame
- attributes
End of explanation
"""


df.drop(['source', 'score'], axis=1, inplace = True)

"""
Explanation: 4) Eliminare le colonne source e score e sostituire l'identificatore ENm006 con l'identificatore ENCODE_REGION in tutti i campi della colonna reference
End of explanation
"""


df['reference'].replace('ENm006', 'ENCODE_REGION', inplace = True)

df['reference'] = df['reference'].apply(lambda x: 'ENCODE_REGION')

df['reference'] = 'ENCODE_REGION'

df

"""
Explanation: Tre alternative per sostituire ENm006 con ENCODE_REGION:
End of explanation
"""


import re

df['gene'] = ''
df['transcript'] = ''

for (index, record) in df.iterrows():
    transcript_id = re.search('transcript_id\s+(.+?);', record['attributes']).group(1).replace('"', '')
    gene_id = re.search('gene_id\s+(.+?);', record['attributes']).group(1).replace('"', '')
    df.loc[index, 'transcript'] =  transcript_id
    df.loc[index, 'gene'] =  gene_id

df.drop('attributes', axis=1, inplace=True)

df

"""
Explanation: 5) Sostituire la colonna degli attributi con le due colonne  transcript e gene
La colonne transcript e gene dovranno contenere solo l'ID del trascritto e del gene.
End of explanation
"""


df['length'] = df['end'] - df['start'] + 1

"""
Explanation: 6) Aggiungere la colonna length contenente la lunghezza della feature
End of explanation
"""


df = df.reindex(columns = ['reference', 'feature', 'start', 'end', 'length', 'strand', 'frame', 'gene', 'transcript'])

df

"""
Explanation: Reindicizzazione delle colonne:
End of explanation
"""


df = df[df['length'] > 6]

"""
Explanation: 7) Rimuovere tutte le features di lunghezza minore o uguale a 6 basi
End of explanation
"""


df.drop(df[df.length <= 6].index, axis=0, inplace = True)

df

"""
Explanation: Alternativa con il metodo drop():
End of explanation
"""


df.sort_values('start', ascending = True)

"""
Explanation: 8) Ottenere un data frame ordinato per coordinate crescenti delle features
End of explanation
"""


df.sort_values('length', ascending = True)

"""
Explanation: 9) Ottenere un data frame ordinato per lunghezza crescente delle features
End of explanation
"""


list(df['gene'].unique())

"""
Explanation: 10) Determinare la lista dei geni annotati
End of explanation
"""


set(df['gene'])

"""
Explanation: In alternativa si può anche determinare il set:
End of explanation
"""


list(df['transcript'].unique())

"""
Explanation: 11) Determinare la lista degli identificatori dei trascritti annotati
End of explanation
"""


df['length'].mean()

"""
Explanation: 12) Calcolare la lunghezza media delle features
End of explanation
"""


min_length = df[df.feature == 'exon'].length.min()

"""
Explanation: 13) Determinare la lunghezza minima degli esoni e i trascritti che contengono un esone di lunghezza minima
a) Determinare la lunghezza minina degli esoni
End of explanation
"""


min_length = df[df['feature'] == 'exon']['length'].min()

"""
Explanation: In alternativa si può usare la notazione con il punto:
End of explanation
"""


mask = (df.feature == 'exon') & (df.length == min_length)
list(df[mask]['transcript'].unique())

"""
Explanation: b) Estrarre la lista dei trascritti che contengono un esone di lunghezza minima
End of explanation
"""


len(df[df['gene'] == 'ARHGAP4']['transcript'].unique())

"""
Explanation: 14) Contare quanti trascritti sono annotati per il gene ARHGAP4
End of explanation
"""


list(df[df['strand'] == '+']['gene'].unique())

"""
Explanation: 15) Estrarre la lista dei geni con strand +
End of explanation
"""


df_temp = df[(df['gene'] == 'ATP6AP1') & (df['feature'] == 'exon')]
set(zip(df_temp['start'], df_temp['end']))

"""
Explanation: 16) Estrarre il set degli esoni (distinti) del gene ATP6AP1
NB: il set deve essere composto da tuple (start, end).
End of explanation
"""


len(df[(df.gene == 'ARHGAP4') & (df.feature == 'CDS')]['transcript'].unique())

"""
Explanation: 17) Contare il numero di trascritti del gene ARHGAP4 che hanno una CDS annotata
End of explanation
"""


df[df['gene'] == 'ATP6AP1']['strand'].unique()[0]

"""
Explanation: 18) Estrarre lo strand del gene  ATP6AP1
End of explanation
"""


df_temp = df[(df.gene == 'ATP6AP1') & (df.feature == 'exon')][['start', 'end', 'transcript']]

"""
Explanation: 19) Estrarre per ogni trascritto del gene  ATP6AP1 la lista delle tuple (start, end) dei suoi esoni
a) Estrarre il data frame delle sole righe relative alle features exon dei trascritti del gene ATP6AP1 e delle sole colonne start, end e transcript.
End of explanation
"""


transcript_list = list(df_temp['transcript'].unique())

"""
Explanation: b) Estrarre la lista dei trascritti
End of explanation
"""


feature_list_for_transcript = []

for transcript in transcript_list:
    start_transcript = df_temp[df_temp['transcript'] == transcript]['start']
    end_transcript = df_temp[df_temp['transcript'] == transcript]['end']
    feature_list = list(zip(start_transcript, end_transcript))
    feature_list_for_transcript.append(feature_list)

"""
Explanation: c) Estrarre per ogni trascritto la lista delle sue features (start, end)
End of explanation
"""


for transcript in transcript_list:
    print(transcript)
    print(feature_list_for_transcript.pop(0))

"""
Explanation: d) Produrre in output per ogni trascritto la lista delle sue features (start, end)
End of explanation
"""


df.groupby(['gene', 'feature'])['transcript'].count()

"""
Explanation: 20) Contare per ogni gene quante sono le features annotate per ognuno dei tipi presenti nel GTF
End of explanation
"""


