{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":116062,"databundleVersionId":14084779,"sourceType":"competition"},{"sourceId":5499219,"sourceType":"datasetVersion","datasetId":3167603}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"204c3231-00bb-4a6d-b9e0-9c0ff2bf4b5e","cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport networkx as nx\nimport joblib\nfrom sklearn.feature_extraction.text import TfidfVectorizer\nfrom sklearn.preprocessing import MultiLabelBinarizer\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.multiclass import OneVsRestClassifier\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import f1_score\n#import obonet\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport tensorflow as tf\n\n# Reference Works:\n# [Starter + EDA] CAFA 6 Protein Function Starter NB by parthenos.\n\n\n# Interface KEYS references\nPROTEIN = 'protein'\nTERM = 'term'\nONTOLOGY = 'ontology'\nTAXON = 'taxon'\n\n# Upper limit for label Quantity\nMAXLABEL_Q = 1500\n\n# This class loads the necessary Data       \nclass DataLoader:\n    def __init__(self, input_path, in_t5_path):\n        self.input_path = input_path\n        self.in_t5_path = in_t5_path\n\n    def loadData(self):\n        # train_terms.tsv : contains a list of annotated terms(functions) for the proteins.\n        # Labels aka GO term ID are extracted to create a label df for the protein embeddings.\n        \n        # File Format\" pandas.read_csv, which expects a plain text file encoded in UTF-8.\n        self.train = pd.read_csv(f\"{self.input_path}/Train/train_terms.tsv\",sep=\"\\t\", \n                                 names=[PROTEIN,TERM, ONTOLOGY] )\n        print(\"Train terms dim:\", self.train.shape)\n\n        # File Format\" pandas.read_csv, which expects a plain text file encoded in UTF-8.\n        self.test = pd.read_csv(f\"{self.input_path}/Test/testsuperset-taxon-list.tsv\",sep=\"\\t\",\n                                 names=[PROTEIN,TAXON] )\n        print(\"Test terms dim:\", self.test.shape)\n        \n        # T5_embeds: ML training its only possible using numerical models, therefore\n        # aminoacid names sequences in the file train_sequences.fasta must be converted to\n        # numerical and vector format. \n        # EMBEDDINGS OF PROTEIN SEQUENCES: will be used in this notebook, embeddings are similar\n        # to word embeddings used to train NLP models. These P.Embeddings are ML methods to capture\n        # protein STRUCTURAL and FUNCTIONAL characteristics from it's sequence.\n        # There are publicly available pre-trained protein embedding models, given that PROTEINS\n        # are represented through its aminoacids,sequences,so the language is well known, also \n        # is possible to train a model to learn those protein embeddings, but has no sense redoing \n        # the wheel in something that is quite standard.\n\n        # TRAIN T5_embeds:\n        # File Format: numpy binary data\n        self.trainProtIds = np.load(f\"{self.in_t5_path}/t5embeds/train_ids.npy\")\n        print(\"Train Protein IDs dim:\", self.trainProtIds.shape)        \n        \n        # T5_embeds\n        # File Format: numpy binary data\n        self.trainEmbeddings = np.load(f\"{self.in_t5_path}/t5embeds/train_embeds.npy\")\n        print(\"T5Embeds dim:\", self.trainProtIds.shape)        \n\n        # Convert self.trainEmbeddings (T5_embeds) into a pandas dataframe:\n        colNum = self.trainEmbeddings.shape[1]\n        self.trainEmbeddingsDF = pd.DataFrame(self.trainEmbeddings, \n                                         columns=[\"Column_\"+str(i) for i in range(1,colNum+1)])\n        print(\"T5Embeds as DF dim:\", self.trainEmbeddingsDF.shape)\n        print(\"T5Embeds as DF HEAD:\")\n        #print(self.trainEmbeddingsDF.head())\n\n        # TEST T5_embeds:\n        # File Format: numpy binary data\n        self.testEmbeddings = np.load(f\"{self.in_t5_path}/t5embeds/test_embeds.npy\")\n        print(\"TEST: T5Embeds dim:\", self.testEmbeddings.shape)        \n\n        # File Format: numpy binary data\n        self.testProtIds = np.load(f\"{self.in_t5_path}/t5embeds/test_ids.npy\")\n        print(\"TEST: T5Embeds Protein IDs dim:\", self.testProtIds.shape)        \n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-19T07:37:36.330545Z","iopub.execute_input":"2025-11-19T07:37:36.331274Z","iopub.status.idle":"2025-11-19T07:37:36.342294Z","shell.execute_reply.started":"2025-11-19T07:37:36.331245Z","shell.execute_reply":"2025-11-19T07:37:36.341296Z"}},"outputs":[],"execution_count":null},{"id":"173322f7-e171-473e-b213-2d93fd6030d2","cell_type":"code","source":"class PostProcessor:\n    labels=[] \n    labelsDF=[]\n    \n    def __init__(self):\n        print(\"Initializing PostProcessor\")\n\n    # Most frequent 100 GO term IDs in train_terms.tsv\n    def plotMostFreq100GOTermIDs(self, train):\n        # Select first 1500 for plotting\n        plotDF=train['term'].value_counts().iloc[:100]\n        figure, axis = plt.subplots(1,1, figsize=(12,6))\n        bp=sns.barplot(ax=axis,x=np.array(plotDF.index),y=plotDF.values)\n        bp.set_xticklabels(bp.get_xticklabels(),rotation=90,size=6)\n        axis.set_title('Top 100 frequent GO term IDs')\n        bp.set_xlabel(\"GO term IDs\", fontsize=12)\n        bp.set_ylabel(\"Count\", fontsize=12)\n        plt.show()\n    \n    def extractLabels(self, train):\n        self.labels = train['term'].value_counts().index[:MAXLABEL_Q].tolist()\n        # Fetch train data for those filtered in the previous step\n        self.trainUpdated = train.loc[train['term'].isin(self.labels)]\n        print(\" trainUpdated HEAD:\")\n        print(self.trainUpdated.head())\n    \n    def plotAspectValues(self):\n        pieDF = self.trainUpdated[ONTOLOGY].value_counts()\n        paletteColor = sns.color_palette('bright')\n        plt.pie(pieDF.values, labels=np.array(pieDF.index),colors=paletteColor,autopct='%.0f%%')\n        plt.show()\n\n    def buildTrainLabelsDF(self, trainProtIds):\n        trainSize = trainProtIds.shape[0] # this returns the size of X\n        trainLabels = np.zeros((trainSize, MAXLABEL_Q))\n\n        # Convert from numpy to pandas\n        seriesTrainProtIds = pd.Series(trainProtIds)\n        \n        # Loop through each label\n        print(\"GENERATING TRAIN LABELS...\",end=\"\\r\")\n        for i in range(MAXLABEL_Q):\n            # For each label fetch the correspondingTrainTerms data\n            nTrainTerms = self.trainUpdated[self.trainUpdated['term'] == self.labels[i]]\n            \n            # Fetch all the unique EntryID aka proteins related to the current label(GO term ID)\n            labelRelatedProteins = nTrainTerms[PROTEIN].unique()\n\n            # In the variable seriesTrainProtIds, if a protein is related to the current label\n            # then flag it as 1, else 0. Replace the ith column of train_Y with that pandas series\n            trainLabels[:,i]=seriesTrainProtIds.isin(labelRelatedProteins).astype(float)\n\n\n        # convert train_Y numpy into pandas DF\n        self.labelsDF = pd.DataFrame(data=trainLabels,columns=self.labels)\n        print(\"Labels Data Frame dim:\", self.labelsDF.shape)\n\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-19T07:37:49.906728Z","iopub.execute_input":"2025-11-19T07:37:49.907486Z","iopub.status.idle":"2025-11-19T07:37:49.917633Z","shell.execute_reply.started":"2025-11-19T07:37:49.90746Z","shell.execute_reply":"2025-11-19T07:37:49.916809Z"}},"outputs":[],"execution_count":null},{"id":"285c6b67-753e-4481-b674-ab8a173f1f69","cell_type":"code","source":"class ModelTrainer:\n    INPUT_SHAPE = []\n    BATCH_SIZE=5120 \n    \n    def __init__(self):\n        print(\"Initializing Model Trainer...\")\n\n    def buildModel(self, labelsDF, trainDF):\n        INPUT_SHAPE = [trainDF.shape[1]]\n        self.model = tf.keras.Sequential([\n            tf.keras.layers.BatchNormalization(input_shape=INPUT_SHAPE),\n            tf.keras.layers.Dense(units=512,activation='relu'),\n            tf.keras.layers.Dense(units=512,activation='relu'),\n            tf.keras.layers.Dense(units=512,activation='relu'),\n            tf.keras.layers.Dense(units=MAXLABEL_Q,activation='sigmoid')]\n                                   )\n\n        # Model Compilation\n        self.model.compile(\n            optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),\n            loss='binary_crossentropy',\n            metrics=['binary_accuracy',tf.keras.metrics.AUC()],)\n\n        self.history=self.model.fit(trainDF,labelsDF,\n                          batch_size=self.BATCH_SIZE,\n                          epochs=5\n                         )\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-19T07:38:02.692841Z","iopub.execute_input":"2025-11-19T07:38:02.693184Z","iopub.status.idle":"2025-11-19T07:38:02.700576Z","shell.execute_reply.started":"2025-11-19T07:38:02.693153Z","shell.execute_reply":"2025-11-19T07:38:02.69967Z"}},"outputs":[],"execution_count":null},{"id":"9ced51f8-aae5-498d-a989-26c790e5f0a0","cell_type":"code","source":"# Download and parse Gene Ontology (the GO file \"go.obo\"), \n# GO (Gene Ontology), describes the understanding of the biological domain using the following \n# aspects:\n# 1- MF: Molecular Function\n# 2- BP: Biological Process\n# 3- CC: Cellular Component\nclass OntologyManager:\n    #def __init__(self, url=\"https://purl.obolibrary.org/obo/go.obo\"):\n    def __init__(self, url=\"https://current.geneontology.org/ontology/go.obo\"):\n        self.url = url\n\n    def loadGraph(self):\n        # Obonet.read_obo() builds a NetworkX graph representation\n        # Nodes = Go terms (e.g.: \"GO:0008150\n        # Edges = relationships like \"is_a\", or, \"part of\"\n        self.graph = obonet.read_obo(self.url)\n\n    def getRoots(self):\n        return [n for n, d in self.graph.in_degree() if d==0]","metadata":{},"outputs":[],"execution_count":null},{"id":"556a9c74-f5d5-40d2-8969-aa06d2614b1b","cell_type":"code","source":"class Analyzer:\n    def __init__(self):\n        print(\"Initializing Analyzer...\")\n\n    def plotData(df):     \n        sns.histplot(df, bins=40)\n        plt.title(\"Distribution of GO terms per protein\")\n        plt.show()\n\n    def lossAndAcc(self, history):\n        histDF = pd.DataFrame(history.history)\n        print(\"History Header\")\n        print(histDF.head())\n        histDF.loc[:,['loss']].plot(title=\"Cross-entropy\")\n        histDF.loc[:,['binary_accuracy']].plot(title=\"Accuracy\")\n                       \n        \n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-19T07:38:19.01791Z","iopub.execute_input":"2025-11-19T07:38:19.018575Z","iopub.status.idle":"2025-11-19T07:38:19.024009Z","shell.execute_reply.started":"2025-11-19T07:38:19.018547Z","shell.execute_reply":"2025-11-19T07:38:19.023056Z"}},"outputs":[],"execution_count":null},{"id":"b467bc5a-e1f5-457c-9872-6e1b7ad8cf35","cell_type":"code","source":"class SubmissionBuilder:\n    PROTEIN_ID = 'Protein ID'\n    GO_TERM_ID ='GO Term ID'\n    PREDICTION = 'Prediction'\n\n    def __init__(self):\n        print(\"Initializing Submission Builder...\")\n\n    def buildData(self, testEmbeddings):\n        #Convert Test embedding to DataFrame\n        colmnCount = testEmbeddings.shape[1]\n        self.testEmbeddingsDF = pd.DataFrame(testEmbeddings,\n                                        columns=[\"Column_\"+str(i) for i in range(1,colmnCount+1)])\n        print(\"Test Embeddings dim:\", self.testEmbeddingsDF.shape)\n\n    # TODO put in a predictions object?\n    def predict(self,testEmbeddingsDF,model): #use: self.testEmbeddingsDF\n        self.prediction=model.predict(testEmbeddingsDF)\n\n    # Reference: hrrps://www.kaggle.com/code/alexandervc/baseline-multilabel-to-multitarget-binary\n    def buildSubmission(self, testProtIds, \n                        predictions, labels): # testProtIds: see DataLoader class\n        submissionDF = pd.DataFrame(columns=[self.PROTEIN_ID,self.GO_TERM_ID,self.PREDICTION])\n        l=[]\n        for k in list(testProtIds):\n            l+=[k]* predictions.shape[1]\n        \n        submissionDF[self.PROTEIN_ID]=l\n        submissionDF[self.GO_TERM_ID]=labels*predictions.shape[0]\n        submissionDF[self.PREDICTION]=predictions.ravel()\n        submissionDF.to_csv(\"submission.tsv\",header=False,index=False,sep=\"\\t\")\n        print(\"File Generation Complete!\")\n        \n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-19T07:38:24.805794Z","iopub.execute_input":"2025-11-19T07:38:24.806089Z","iopub.status.idle":"2025-11-19T07:38:24.813775Z","shell.execute_reply.started":"2025-11-19T07:38:24.806067Z","shell.execute_reply":"2025-11-19T07:38:24.812849Z"}},"outputs":[],"execution_count":null},{"id":"22bdd8d7-3f63-4724-b7c1-3f281bb91580","cell_type":"code","source":"class ProteinFunctionPipeline:\n    def __init__(self, inputPath, in_t5_path):\n        self.dataLoader = DataLoader(inputPath, in_t5_path)\n        self.postProcessor = PostProcessor()\n        self.modelTrainer = ModelTrainer()    \n        #self.ontologyManager = OntologyManager()\n        self.analyzer = Analyzer()\n        self.submissionBuilder = SubmissionBuilder()\n    \n    def runPipeline(self):\n\n        # DATA LOADING\n        print(\"RUNNING PIPELINE ACTIVATED...\")\n        print(\"Loading Data...\")\n        self.dataLoader.loadData()\n        print(\"Data Loaded...\")\n\n        # DATA POST PROCESSING\n        #print(self.postProcessor.plotMostFreq100GOTermIDs(self.dataLoader.train))\n        print(self.postProcessor.extractLabels(self.dataLoader.train))\n        #print(self.postProcessor.plotAspectValues())\n        print(self.postProcessor.buildTrainLabelsDF(self.dataLoader.trainProtIds))\n        print(\"PostProcessor job done...\")\n\n        # TRAIN:\n        print(self.modelTrainer.buildModel(self.postProcessor.labelsDF,\n                                      self.dataLoader.trainEmbeddingsDF))\n\n        # ANALYZE RESULTS:\n        #print(self.analyzer.lossAndAcc(self.modelTrainer.history))\n\n        # SUBMISSION:\n        print(self.submissionBuilder)\n        self.submissionBuilder.buildData(self.dataLoader.testEmbeddings)\n        self.submissionBuilder.predict(self.submissionBuilder.testEmbeddingsDF,\n                                      self.modelTrainer.model) \n        self.submissionBuilder.buildSubmission(self.dataLoader.testProtIds,\n                                              self.submissionBuilder.prediction,\n                                              self.postProcessor.labels) \n        \n\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-19T07:38:33.0193Z","iopub.execute_input":"2025-11-19T07:38:33.019895Z","iopub.status.idle":"2025-11-19T07:38:33.026644Z","shell.execute_reply.started":"2025-11-19T07:38:33.019868Z","shell.execute_reply":"2025-11-19T07:38:33.025951Z"}},"outputs":[],"execution_count":null},{"id":"66275132-8aeb-4c3d-822e-212090c8dcd1","cell_type":"code","source":"if __name__ == \"__main__\":\n    pth = \"/kaggle/input/cafa-6-protein-function-prediction\"\n    t5embPth = \"/kaggle/input\"    \n    pipeline = ProteinFunctionPipeline(pth, t5embPth)\n    pipeline.runPipeline()","metadata":{"scrolled":true,"trusted":true,"execution":{"iopub.status.busy":"2025-11-19T07:38:51.657044Z","iopub.execute_input":"2025-11-19T07:38:51.65741Z","iopub.status.idle":"2025-11-19T07:50:05.343907Z","shell.execute_reply.started":"2025-11-19T07:38:51.657384Z","shell.execute_reply":"2025-11-19T07:50:05.342424Z"}},"outputs":[],"execution_count":null}]}