{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "from joblib import  load\n",
    "import numpy as np\n",
    "import numpy.ma as ma  \n",
    "\n",
    "import pandas as pd\n",
    "\n",
    "import modlamp.descriptors\n",
    "import modlamp.analysis\n",
    "import modlamp.sequences\n",
    "\n",
    "import random\n",
    "from collections import defaultdict\n",
    "from typing import Tuple, Dict, List\n",
    "import torch\n",
    "# PLOTTING \n",
    "\n",
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.patches as mpatches\n",
    "\n",
    "import os\n",
    "from torch import optim, nn, utils, transpose, device, cuda, save, isinf, backends, manual_seed, LongTensor, tensor, long\n",
    "DEVICE = device(f'cuda:{cuda.current_device()}' if cuda.is_available() else 'cpu')\n",
    "from typing import Optional, Literal, List, Tuple, Union\n",
    "from torch.optim import Adam\n",
    "from pathlib import Path\n",
    "from model.constants import MIN_LENGTH, MAX_LENGTH, VOCAB_SIZE\n",
    "\n",
    "sns.set_style(\n",
    "    'whitegrid', \n",
    "    {\n",
    "        'grid.color': '.95',\n",
    "        'axes.spines.right': False,\n",
    "        'axes.spines.top': False,\n",
    "    }\n",
    ")\n",
    "\n",
    "params = {\n",
    "    'axes.labelsize': 6,\n",
    "    'axes.titlesize':6,\n",
    "    'font.size': 6, \n",
    "    'legend.fontsize': 5,\n",
    "    'xtick.labelsize': 6, \n",
    "    'ytick.labelsize': 6,\n",
    "}\n",
    "\n",
    "plt.rcParams.update(params)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "boxprops = dict(linewidth=0.0, color='k')\n",
    "flierprops = dict(linewidth=0.5)\n",
    "medianprops = dict(linewidth=0.5, color='k')\n",
    "whiskerprops = dict(linewidth=0.5)\n",
    "capprops = dict(linewidth=0.5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculate_length(data:list):\n",
    "    lengths = [len(x) for x in data]\n",
    "    return lengths\n",
    "\n",
    "def calculate_charge(data:list):\n",
    "    h = modlamp.analysis.GlobalAnalysis(data)\n",
    "    h.calc_charge()\n",
    "    return h.charge\n",
    "\n",
    "def calculate_isoelectricpoint(data:list):\n",
    "    h = modlamp.analysis.GlobalDescriptor(data)\n",
    "    h.isoelectric_point()\n",
    "    return list(h.descriptor.flatten())\n",
    "\n",
    "def calculate_aromaticity(data:list):\n",
    "    h = modlamp.analysis.GlobalDescriptor(data)\n",
    "    h.aromaticity()\n",
    "    return list(h.descriptor.flatten())\n",
    "\n",
    "def calculate_hydrophobicity(data:list):\n",
    "    h = modlamp.analysis.GlobalAnalysis(data)\n",
    "    h.calc_H(scale='eisenberg')\n",
    "    return list(h.H)\n",
    "\n",
    "def calculate_hydrophobicmoment(data:list):\n",
    "    h = modlamp.descriptors.PeptideDescriptor(data, 'eisenberg')\n",
    "    h.calculate_moment()\n",
    "    return list(h.descriptor.flatten())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def select_peptides(results, mode):\n",
    "    if mode=='pos':\n",
    "        peptides = np.array(results[f'pos_peptides']).reshape(64, -1).T\n",
    "        amp = (results['pos_class_prediction'] < 0.8).reshape(64, -1)\n",
    "        mic = results['pos_mic_prediction'].reshape(64, -1)\n",
    "        combined = ma.masked_where(amp, mic)\n",
    "        good = combined.argmax(axis=0)\n",
    "        good_peptides = peptides[list(range(peptides.shape[0])), good]\n",
    "        good_amp = np.array(results['pos_class_prediction']).reshape(64, -1).T[list(range(peptides.shape[0])), good]\n",
    "        good_mic = np.array(results['pos_mic_prediction']).reshape(64, -1).T[list(range(peptides.shape[0])), good]\n",
    "        return pd.DataFrame.from_dict({\n",
    "            'sequence': good_peptides.tolist(), \n",
    "            'amp': good_amp.tolist(),\n",
    "            'mic': good_mic.tolist(),\n",
    "        }\n",
    "        )\n",
    "    else:\n",
    "        peptides = np.array(results['neg_peptides']).reshape(64, -1).T\n",
    "        amp = (results['neg_class_prediction'] > 0.2).reshape(64, -1)\n",
    "        mic = results['neg_mic_prediction'].reshape(64, -1)\n",
    "        combined = ma.masked_where(amp, mic)\n",
    "        good = combined.argmin(axis=0)\n",
    "        good_peptides = peptides[list(range(peptides.shape[0])), good]\n",
    "        good_amp = np.array(results['neg_class_prediction']).reshape(64, -1).T[list(range(peptides.shape[0])), good]\n",
    "        good_mic = np.array(results['neg_mic_prediction']).reshape(64, -1).T[list(range(peptides.shape[0])), good]\n",
    "        return pd.DataFrame.from_dict({\n",
    "            'sequence': good_peptides.tolist(), \n",
    "            'amp': good_amp.tolist(),\n",
    "            'mic': good_mic.tolist(),\n",
    "        }\n",
    "        )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Unconstrained"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "sequences = pd.read_csv('./sequences.csv', header=None)\n",
    "sequences[0] = sequences[0].str.strip().str.rstrip(\"0\")\n",
    "df = sequences[~sequences[0].str.contains(\"0\")]\n",
    "generated = df[0].tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Get random peptides\n",
    "random_gen = modlamp.sequences.Random(50000, 1, 25)\n",
    "random_gen.generate_sequences(proba='random')\n",
    "random_peptides = random_gen.sequences\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "class AMPDataManager:\n",
    "\n",
    "    def __init__(\n",
    "            self,\n",
    "            positive_filepath: str,\n",
    "            negative_filepath: str,\n",
    "            min_len: int,\n",
    "            max_len: int,\n",
    "    ):\n",
    "        self.positive_data = pd.read_csv(positive_filepath)\n",
    "        self.negative_data = pd.read_csv(negative_filepath)\n",
    "\n",
    "        self.min_len = min_len\n",
    "        self.max_len = max_len\n",
    "\n",
    "    def _filter_by_length(self, df: pd.DataFrame) -> pd.DataFrame:\n",
    "        mask = (df['Sequence'].str.len() >= self.min_len) & (df['Sequence'].str.len() <= self.max_len)\n",
    "        return df.loc[mask]\n",
    "\n",
    "    def _filter_data(self):\n",
    "        return self._filter_by_length(self.positive_data), self.negative_data\n",
    "\n",
    "    @staticmethod\n",
    "    def _get_probs(peptide_lengths: List[int]) -> Dict[int, float]:\n",
    "        probs = defaultdict(lambda: 1)\n",
    "        for length in peptide_lengths:\n",
    "            probs[length] += 1\n",
    "        return {k: round(v / len(peptide_lengths), 4) for k, v in probs.items()}\n",
    "\n",
    "    @staticmethod\n",
    "    def _draw_subsequences(df, new_lengths):\n",
    "        # random.seed(44)\n",
    "        new_lengths.sort(reverse=True)\n",
    "        df = df.sort_values(by=\"Sequence length\", ascending=False)\n",
    "\n",
    "        d = []\n",
    "        for row, new_length in zip(df.itertuples(), new_lengths):\n",
    "            seq = row[2]\n",
    "            curr_length = row[3]\n",
    "            if new_length > curr_length:\n",
    "                new_seq = seq\n",
    "            elif new_length == curr_length:\n",
    "                new_seq = seq\n",
    "            else:\n",
    "                begin = random.randrange(0, int(curr_length) - new_length)\n",
    "                new_seq = seq[begin:begin + new_length]\n",
    "            d.append(\n",
    "                {\n",
    "                    'Name': row[1],\n",
    "                    'Sequence': new_seq,\n",
    "                }\n",
    "            )\n",
    "        new_df = pd.DataFrame(d)\n",
    "        return new_df\n",
    "\n",
    "    def _equalize_data(self, positive_data: pd.DataFrame, negative_data: pd.DataFrame, balanced_classes: bool = True):\n",
    "        positive_seq = positive_data['Sequence'].tolist()\n",
    "        positive_lengths = [len(seq) for seq in positive_seq]\n",
    "\n",
    "        negative_seq = negative_data['Sequence'].tolist()\n",
    "        negative_lengths = [len(seq) for seq in negative_seq]\n",
    "        negative_data.loc[:, \"Sequence length\"] = negative_lengths\n",
    "\n",
    "        probs = self._get_probs(positive_lengths)\n",
    "        k = len(positive_lengths) if balanced_classes else len(negative_lengths)\n",
    "\n",
    "        new_negative_lengths = random.choices(list(probs.keys()), probs.values(), k=k)\n",
    "        negative_data_distributed = self._draw_subsequences(self.negative_data, new_negative_lengths)\n",
    "        return positive_data, negative_data_distributed\n",
    "\n",
    "    def plot_distributions(self, equalize: bool = True):\n",
    "        if equalize:\n",
    "            pos_dataset, neg_dataset = self.get_data()\n",
    "        else:\n",
    "            pos_dataset, neg_dataset = self.positive_data, self.negative_data\n",
    "        sns.set(color_codes=True)\n",
    "        # TODO: figure out where this functionality should be. Goal is to plot distribution before and after baladancing\n",
    "        positive_seq = pos_dataset['Sequence'].tolist()\n",
    "        positive_lengths = [len(seq) for seq in positive_seq]\n",
    "\n",
    "        negative_seq = neg_dataset['Sequence'].tolist()\n",
    "        negative_lengths = [len(seq) for seq in negative_seq]\n",
    "\n",
    "        fig, (ax2, ax3) = plt.subplots(figsize=(12, 6), ncols=2)\n",
    "        sns.distplot(positive_lengths, ax=ax2)\n",
    "        sns.distplot(negative_lengths, ax=ax3)\n",
    "        ax2.set_title(\"Positive\")\n",
    "        ax3.set_title(\"Negative\")\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "    def _join_datasets(self, pos_dataset: pd.DataFrame, neg_dataset: pd.DataFrame) -> Tuple[np.ndarray, np.ndarray]:\n",
    "        pos_dataset.loc[:, 'Label'] = 1\n",
    "        neg_dataset.loc[:, 'Label'] = 0\n",
    "        merged = pd.concat([pos_dataset, neg_dataset])\n",
    "        x = np.asarray(merged['Sequence'].tolist())\n",
    "        y = np.asarray(merged['Label'].tolist())\n",
    "        return x, y\n",
    "\n",
    "    def get_data(self, balanced: bool = True):\n",
    "        pos_dataset, neg_dataset = self._filter_data()\n",
    "        return self._equalize_data(pos_dataset, neg_dataset, balanced_classes=balanced)\n",
    "\n",
    "    def get_merged_data(self, balanced: bool = True):\n",
    "        pos_dataset, neg_dataset = self.get_data(balanced=balanced)\n",
    "        return self._join_datasets(pos_dataset, neg_dataset)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\olagw\\AppData\\Local\\Temp\\ipykernel_20236\\825684258.py:153: SettingWithCopyWarning: \n",
      "A value is trying to be set on a copy of a slice from a DataFrame.\n",
      "Try using .loc[row_indexer,col_indexer] = value instead\n",
      "\n",
      "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
      "  pos_dataset.loc[:, 'Label'] = 1\n"
     ]
    }
   ],
   "source": [
    "def set_seed(seed: int = 42) -> None:\n",
    "    \"\"\"\n",
    "    Source:\n",
    "    https://wandb.ai/sauravmaheshkar/RSNA-MICCAI/reports/How-to-Set-Random-Seeds-in-PyTorch-and-Tensorflow--VmlldzoxMDA2MDQy\n",
    "    \"\"\"\n",
    "    np.random.seed(seed)\n",
    "    random.seed(seed)\n",
    "    manual_seed(seed)\n",
    "    cuda.manual_seed(seed)\n",
    "    # When running on the CuDNN backend, two further options must be set\n",
    "    backends.cudnn.deterministic = True\n",
    "    backends.cudnn.benchmark = False\n",
    "    # Set a fixed value for the hash seed\n",
    "    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n",
    "    # logger.info(f\"Random seed set to {seed}\")\n",
    "    return None\n",
    "set_seed()\n",
    "\n",
    "data_manager = AMPDataManager(\n",
    "    './data/unlabelled_positive.csv',\n",
    "    './data/unlabelled_negative.csv',\n",
    "    min_len=MIN_LENGTH,\n",
    "    max_len=MAX_LENGTH)\n",
    "\n",
    "amp_x, amp_y = data_manager.get_merged_data()\n",
    "df = amp_x.tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculate_physchem(peptides, datasets):\n",
    "    physchem = {}\n",
    "    physchem['dataset'] = []\n",
    "    physchem['length'] = []\n",
    "    physchem['charge'] = []\n",
    "    physchem['pi'] = []\n",
    "    physchem['aromacity'] = []\n",
    "    physchem['hydrophobicity'] = []\n",
    "    physchem['hm'] = []\n",
    "\n",
    "\n",
    "    for dataset, name in zip(peptides, datasets):\n",
    "        physchem['dataset'] += (len(dataset) * [name])\n",
    "        physchem['length'] += calculate_length(dataset)\n",
    "        physchem['charge'] += calculate_charge(dataset)[0].tolist()\n",
    "        physchem['pi'] += calculate_isoelectricpoint(dataset)\n",
    "        physchem['aromacity'] += calculate_aromaticity(dataset) \n",
    "        physchem['hydrophobicity'] += calculate_hydrophobicity(dataset)[0].tolist()\n",
    "        physchem['hm'] += calculate_hydrophobicmoment(dataset)\n",
    "\n",
    "    return pd.DataFrame(dict([ (k, pd.Series(v)) for k,v in physchem.items() ]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "datasets = [\n",
    "    'Random',\n",
    "    'Baseline',\n",
    "    'Generated'\n",
    "]\n",
    "\n",
    "peptides = [\n",
    "    random_peptides,\n",
    "    df,\n",
    "    generated\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [],
   "source": [
    "physchem = calculate_physchem(peptides, datasets)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "physchem_temp = physchem.loc[:, ['pi', 'charge', 'hm', 'aromacity', 'dataset']]\n",
    "physchem_quantiles = physchem_temp.groupby('dataset').quantile([0.25, 0.5, 0.75])\n",
    "physchem_quantiles.to_csv('Supp_SourceData_FigureS1_quantiles.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [],
   "source": [
    "datasets = [\n",
    "    ['Random','Baseline','Generated']\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 131600 characters]",
      "text/plain": [
       "<Figure size 1800x1500 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(\n",
    "    ncols=2, \n",
    "    nrows=2,\n",
    "    figsize=(6, 5),\n",
    "    dpi=300,\n",
    ")\n",
    "\n",
    "# Define properties and labels\n",
    "properties = ['pi', 'charge', 'hm', 'aromacity']\n",
    "labels = ['Isoelectric point', 'Charge', 'Hydrophobic moment', 'Aromaticity']\n",
    "\n",
    "# Only use \"Random\" and \"Generated\"\n",
    "selected_datasets = ['Random', 'Baseline', 'Generated']\n",
    "\n",
    "for prop, label, ax in zip(properties, labels, axes.flatten()):\n",
    "    # Extract only \"Random\" and \"Generated\" data\n",
    "    filtered_data = [\n",
    "        physchem.loc[physchem['dataset'] == dataset, prop].tolist()\n",
    "        for dataset in selected_datasets\n",
    "    ]\n",
    "    \n",
    "    # Create boxplot\n",
    "    parts = ax.boxplot(\n",
    "        filtered_data,\n",
    "        showfliers=False,\n",
    "        patch_artist=True,\n",
    "        widths=0.4\n",
    "    )\n",
    "    \n",
    "    # Apply colors\n",
    "    for patch, color in zip(parts['boxes'], ['grey', 'red', 'green']):\n",
    "        patch.set_facecolor(color)\n",
    "    \n",
    "    # Set labels\n",
    "    ax.set_xticks([1, 2, 3])  \n",
    "    ax.set_xticklabels(['Random', 'Baseline', 'Generated'], rotation=45)\n",
    "    ax.set_ylabel(label)\n",
    "\n",
    "# Adjust layout\n",
    "plt.tight_layout()\n",
    "\n",
    "# Add legend\n",
    "plt.legend(\n",
    "    handles=[\n",
    "        mpatches.Patch(color='grey', label='Random'),    \n",
    "        mpatches.Patch(color='red', label='Baseline'),\n",
    "        mpatches.Patch(color='green', label='Generated'),\n",
    "    ], \n",
    "    loc='upper right'\n",
    ")\n",
    "\n",
    "# Save & Show Plot\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.svg\")\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.pdf\", bbox_inches=\"tight\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 125040 characters]",
      "text/plain": [
       "<Figure size 1800x1500 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(\n",
    "    ncols=2, \n",
    "    nrows=2,\n",
    "    figsize=(6, 5),\n",
    "    dpi=300,\n",
    ")\n",
    "\n",
    "# Define properties and labels\n",
    "properties = ['pi', 'charge', 'hm', 'aromacity']\n",
    "labels = ['Isoelectric point', 'Charge', 'Hydrophobic moment', 'Aromaticity']\n",
    "\n",
    "# Only use \"Random\" and \"Generated\"\n",
    "selected_datasets = ['Random']\n",
    "\n",
    "for prop, label, ax in zip(properties, labels, axes.flatten()):\n",
    "    # Extract only \"Random\" and \"Generated\" data\n",
    "    filtered_data = [\n",
    "        physchem.loc[physchem['dataset'] == dataset, prop].tolist()\n",
    "        for dataset in selected_datasets\n",
    "    ]\n",
    "    \n",
    "    # Create boxplot\n",
    "    parts = ax.hist(\n",
    "        filtered_data,\n",
    "        color= ['grey'], label=['Random']\n",
    "    )\n",
    "    ax.set_ylabel(label)\n",
    "\n",
    "# Adjust layout\n",
    "plt.tight_layout()\n",
    "\n",
    "# Add legend\n",
    "plt.legend(\n",
    "    handles=[\n",
    "        mpatches.Patch(color='grey', label='Random'),    \n",
    "\n",
    "    ], \n",
    "    loc='upper right'\n",
    ")\n",
    "\n",
    "# Save & Show Plot\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.svg\")\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.pdf\", bbox_inches=\"tight\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 140080 characters]",
      "text/plain": [
       "<Figure size 1800x1500 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(\n",
    "    ncols=2, \n",
    "    nrows=2,\n",
    "    figsize=(6, 5),\n",
    "    dpi=300,\n",
    ")\n",
    "\n",
    "# Define properties and labels\n",
    "properties = ['pi', 'charge', 'hm', 'aromacity']\n",
    "labels = ['Isoelectric point', 'Charge', 'Hydrophobic moment', 'Aromaticity']\n",
    "\n",
    "# Only use \"Random\" and \"Generated\"\n",
    "selected_datasets = ['Baseline']\n",
    "\n",
    "for prop, label, ax in zip(properties, labels, axes.flatten()):\n",
    "    # Extract only \"Random\" and \"Generated\" data\n",
    "    filtered_data = [\n",
    "        physchem.loc[physchem['dataset'] == dataset, prop].tolist()\n",
    "        for dataset in selected_datasets\n",
    "    ]\n",
    "    \n",
    "    # Create boxplot\n",
    "    parts = ax.hist(\n",
    "        filtered_data,\n",
    "        color= ['red'], label=['Baseline']\n",
    "    )\n",
    "    ax.set_ylabel(label)\n",
    "\n",
    "# Adjust layout\n",
    "plt.tight_layout()\n",
    "\n",
    "# Add legend\n",
    "plt.legend(\n",
    "    handles=[\n",
    "        mpatches.Patch(color='red', label='Baseline'),\n",
    "    ], \n",
    "    loc='upper right'\n",
    ")\n",
    "\n",
    "# Save & Show Plot\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.svg\")\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.pdf\", bbox_inches=\"tight\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 114564 characters]",
      "text/plain": [
       "<Figure size 1800x1500 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(\n",
    "    ncols=2, \n",
    "    nrows=2,\n",
    "    figsize=(6, 5),\n",
    "    dpi=300,\n",
    ")\n",
    "\n",
    "# Define properties and labels\n",
    "properties = ['pi', 'charge', 'hm', 'aromacity']\n",
    "labels = ['Isoelectric point', 'Charge', 'Hydrophobic moment', 'Aromaticity']\n",
    "\n",
    "# Only use \"Random\" and \"Generated\"\n",
    "selected_datasets = ['Generated']\n",
    "\n",
    "for prop, label, ax in zip(properties, labels, axes.flatten()):\n",
    "    # Extract only \"Random\" and \"Generated\" data\n",
    "    filtered_data = [\n",
    "        physchem.loc[physchem['dataset'] == dataset, prop].tolist()\n",
    "        for dataset in selected_datasets\n",
    "    ]\n",
    "    \n",
    "    # Create boxplot\n",
    "    parts = ax.hist(\n",
    "        filtered_data,\n",
    "        color= ['green'], label=['Generated']\n",
    "    )\n",
    "    \n",
    "    ax.set_ylabel(label)\n",
    "\n",
    "# Adjust layout\n",
    "plt.tight_layout()\n",
    "\n",
    "# Add legend\n",
    "plt.legend(\n",
    "    handles=[\n",
    "        mpatches.Patch(color='green', label='Generated'),\n",
    "    ], \n",
    "    loc='upper right'\n",
    ")\n",
    "\n",
    "# Save & Show Plot\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.svg\")\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.pdf\", bbox_inches=\"tight\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {},
   "outputs": [],
   "source": [
    "from decimal import getcontext\n",
    "STD_AA = list('ACDEFGHIKLMNPQRSTVWY')\n",
    "loop = [STD_AA[i:i+1][0] for i in range(0, len(STD_AA), 1)]\n",
    "getcontext().prec = 3\n",
    "\n",
    "freq_random = {}\n",
    "freq_baseline = {}\n",
    "freq_generated = {}\n",
    "\n",
    "all_amino_random = sum(len(s) for s in random_peptides)\n",
    "all_amino_baseline = sum(len(s) for s in df)\n",
    "all_amino_generated = sum(len(s) for s in generated)\n",
    "for amino in loop:\n",
    "    freq_random[amino] = sum(s.count(amino) for s in random_peptides)/all_amino_random\n",
    "    freq_baseline[amino] = sum(s.count(amino) for s in df)/all_amino_baseline\n",
    "    freq_generated[amino] = sum(s.count(amino) for s in generated)/all_amino_generated"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 186620 characters]",
      "text/plain": [
       "<Figure size 3600x3000 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from matplotlib.ticker import FormatStrFormatter\n",
    "\n",
    "fig, axes = plt.subplots(\n",
    "    ncols=1, \n",
    "    nrows=3,\n",
    "    figsize=(12, 10),\n",
    "    dpi=300,\n",
    ")\n",
    "\n",
    "# Only use \"Random\" and \"Generated\"\n",
    "selected_datasets = ['Random', 'Baseline', 'Generated']\n",
    "\n",
    "for data, name, ax in zip([freq_random, freq_baseline, freq_generated],['Random', 'Baseline', 'Generated'], axes.flatten()):\n",
    "    \n",
    "    ax.bar(data.keys(), data.values() \n",
    "    ax.yaxis.set_major_formatter(FormatStrFormatter('%.3f'))\n",
    "    ax.set_xlabel(name)\n",
    "    ax.set_ylabel('Frequencies')\n",
    "\n",
    "# Adjust layout\n",
    "plt.tight_layout()\n",
    "\n",
    "# Save & Show Plot\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.svg\")\n",
    "fig.savefig(\"./Filtered_Random_Uniprot.pdf\", bbox_inches=\"tight\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
