{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "103d02ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "import nltk\n",
    "import re\n",
    "import numpy as np # linear algebra\n",
    "import pandas as pd # data processing\n",
    "import random\n",
    "import matplotlib.pyplot as plt\n",
    "import pickle\n",
    "from bs4 import BeautifulSoup\n",
    "from nltk.corpus import stopwords\n",
    "from string import punctuation\n",
    "from nltk.stem import SnowballStemmer\n",
    "from nltk.tokenize import word_tokenize\n",
    "stemmer=SnowballStemmer('english')\n",
    "\n",
    "from sklearn.metrics import accuracy_score\n",
    "from sklearn.metrics import confusion_matrix\n",
    "from sklearn.metrics import roc_auc_score\n",
    "from sklearn.metrics import f1_score\n",
    "from sklearn.metrics import precision_score\n",
    "from sklearn.metrics import recall_score\n",
    "\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.feature_extraction.text import CountVectorizer\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.linear_model import LogisticRegression as lr\n",
    "\n",
    "import tensorflow\n",
    "from tensorflow.keras.preprocessing.sequence import pad_sequences\n",
    "from tensorflow.keras.preprocessing.text import Tokenizer\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Embedding, LSTM, Dense\n",
    "\n",
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b0ed0131",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Product Name</th>\n",
       "      <th>Brand Name</th>\n",
       "      <th>Price</th>\n",
       "      <th>Rating</th>\n",
       "      <th>Reviews</th>\n",
       "      <th>Review Votes</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>\"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...</td>\n",
       "      <td>Samsung</td>\n",
       "      <td>199.99</td>\n",
       "      <td>5</td>\n",
       "      <td>I feel so LUCKY to have found this used (phone...</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>\"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...</td>\n",
       "      <td>Samsung</td>\n",
       "      <td>199.99</td>\n",
       "      <td>4</td>\n",
       "      <td>nice phone, nice up grade from my pantach revu...</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>\"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...</td>\n",
       "      <td>Samsung</td>\n",
       "      <td>199.99</td>\n",
       "      <td>5</td>\n",
       "      <td>Very pleased</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>\"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...</td>\n",
       "      <td>Samsung</td>\n",
       "      <td>199.99</td>\n",
       "      <td>4</td>\n",
       "      <td>It works good but it goes slow sometimes but i...</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>\"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...</td>\n",
       "      <td>Samsung</td>\n",
       "      <td>199.99</td>\n",
       "      <td>4</td>\n",
       "      <td>Great phone to replace my lost phone. The only...</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                        Product Name Brand Name   Price  \\\n",
       "0  \"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...    Samsung  199.99   \n",
       "1  \"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...    Samsung  199.99   \n",
       "2  \"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...    Samsung  199.99   \n",
       "3  \"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...    Samsung  199.99   \n",
       "4  \"CLEAR CLEAN ESN\" Sprint EPIC 4G Galaxy SPH-D7...    Samsung  199.99   \n",
       "\n",
       "   Rating                                            Reviews  Review Votes  \n",
       "0       5  I feel so LUCKY to have found this used (phone...           1.0  \n",
       "1       4  nice phone, nice up grade from my pantach revu...           0.0  \n",
       "2       5                                       Very pleased           0.0  \n",
       "3       4  It works good but it goes slow sometimes but i...           0.0  \n",
       "4       4  Great phone to replace my lost phone. The only...           0.0  "
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "raw_dataset=pd.read_csv('Amazon_Unlocked_Mobile.csv')\n",
    "raw_dataset.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "4e55e5ae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 413840 entries, 0 to 413839\n",
      "Data columns (total 6 columns):\n",
      " #   Column        Non-Null Count   Dtype  \n",
      "---  ------        --------------   -----  \n",
      " 0   Product Name  413840 non-null  object \n",
      " 1   Brand Name    348669 non-null  object \n",
      " 2   Price         407907 non-null  float64\n",
      " 3   Rating        413840 non-null  int64  \n",
      " 4   Reviews       413778 non-null  object \n",
      " 5   Review Votes  401544 non-null  float64\n",
      "dtypes: float64(2), int64(1), object(3)\n",
      "memory usage: 18.9+ MB\n"
     ]
    }
   ],
   "source": [
    "df=raw_dataset\n",
    "df.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "24e8a86e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Reviews</th>\n",
       "      <th>Rating</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>I feel so LUCKY to have found this used (phone...</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>nice phone, nice up grade from my pantach revu...</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Very pleased</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>It works good but it goes slow sometimes but i...</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Great phone to replace my lost phone. The only...</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                             Reviews  Rating\n",
       "0  I feel so LUCKY to have found this used (phone...       5\n",
       "1  nice phone, nice up grade from my pantach revu...       4\n",
       "2                                       Very pleased       5\n",
       "3  It works good but it goes slow sometimes but i...       4\n",
       "4  Great phone to replace my lost phone. The only...       4"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df=df[['Reviews','Rating']]\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "27c72531",
   "metadata": {},
   "outputs": [],
   "source": [
    "ylabel=df['Rating']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "afa091b4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([5, 4, 1, 2, 3], dtype=int64)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ylabel.unique()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d1d53bd",
   "metadata": {},
   "source": [
    "# Data Pre-processing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4257e04c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Reviews</th>\n",
       "      <th>Rating</th>\n",
       "      <th>sentiment</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>I feel so LUCKY to have found this used (phone...</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>nice phone, nice up grade from my pantach revu...</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Very pleased</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>It works good but it goes slow sometimes but i...</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Great phone to replace my lost phone. The only...</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                             Reviews  Rating  sentiment\n",
       "0  I feel so LUCKY to have found this used (phone...       5          1\n",
       "1  nice phone, nice up grade from my pantach revu...       4          1\n",
       "2                                       Very pleased       5          1\n",
       "3  It works good but it goes slow sometimes but i...       4          1\n",
       "4  Great phone to replace my lost phone. The only...       4          1"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df=df.dropna()\n",
    "df=df.reset_index(drop=True)\n",
    "df['sentiment']=np.where(df['Rating'] > 3, 1, 0)\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b1e9ce35",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([5, 4, 1, 2, 3], dtype=int64)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df['Rating'].unique()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6f0f986",
   "metadata": {},
   "source": [
    "### Tokenization, Lemmatization, Stemming and Removal of stop words "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "62e406a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "Cstopwords=set(stopwords.words('english')+list(punctuation))\n",
    "from nltk.stem import WordNetLemmatizer\n",
    "lemma=WordNetLemmatizer()\n",
    "def clean_review(review_column):\n",
    "    review_corpus=[]\n",
    "    for i in range(0,len(review_column)):\n",
    "        review=review_column[i]\n",
    "        #review=BeautifulSoup(review,'lxml').text\n",
    "        review=re.sub('[^a-zA-Z]',' ',review)\n",
    "        review=str(review).lower()\n",
    "        review=word_tokenize(review)\n",
    "        #review=[stemmer.stem(w) for w in review if w not in Cstopwords]\n",
    "        review=[lemma.lemmatize(w) for w in review ]\n",
    "        review=' '.join(review)\n",
    "        review_corpus.append(review)\n",
    "    return review_corpus"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "c237e6cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "review_column=df['Reviews']\n",
    "review_corpus=clean_review(review_column)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "26b5455a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Reviews</th>\n",
       "      <th>Rating</th>\n",
       "      <th>sentiment</th>\n",
       "      <th>clean_review</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>I feel so LUCKY to have found this used (phone...</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>i feel so lucky to have found this used phone ...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>nice phone, nice up grade from my pantach revu...</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "      <td>nice phone nice up grade from my pantach revue...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Very pleased</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>very pleased</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>It works good but it goes slow sometimes but i...</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "      <td>it work good but it go slow sometimes but it a...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Great phone to replace my lost phone. The only...</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "      <td>great phone to replace my lost phone the only ...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                             Reviews  Rating  sentiment  \\\n",
       "0  I feel so LUCKY to have found this used (phone...       5          1   \n",
       "1  nice phone, nice up grade from my pantach revu...       4          1   \n",
       "2                                       Very pleased       5          1   \n",
       "3  It works good but it goes slow sometimes but i...       4          1   \n",
       "4  Great phone to replace my lost phone. The only...       4          1   \n",
       "\n",
       "                                        clean_review  \n",
       "0  i feel so lucky to have found this used phone ...  \n",
       "1  nice phone nice up grade from my pantach revue...  \n",
       "2                                       very pleased  \n",
       "3  it work good but it go slow sometimes but it a...  \n",
       "4  great phone to replace my lost phone the only ...  "
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df['clean_review']=review_corpus\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4076ff36",
   "metadata": {},
   "source": [
    "# Feature Extraction"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c81cfb51",
   "metadata": {},
   "source": [
    "### Log Term Frequency-based Modified Inverse Class Frequency"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "59712d5b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "tfidf=TfidfVectorizer(min_df=5, max_df=0.95, max_features = 20000, ngram_range = ( 1, 2 ),\n",
    "                              sublinear_tf = True)\n",
    "tfidf=tfidf.fit(df['clean_review'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "84509c0d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(413778, 20000)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X2=tfidf.transform(df['clean_review'])\n",
    "X2.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "bc58b82f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(413778,)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y=df['Rating'].values\n",
    "y.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "7fb1fd08",
   "metadata": {},
   "outputs": [],
   "source": [
    "Y =ylabel.values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "046f25ba",
   "metadata": {},
   "source": [
    "# Feature selection"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ec09cf4",
   "metadata": {},
   "source": [
    "### Levy flight dependent mayfly optimization algorithm (LFMO)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "fb214fb9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(331022, 20000) (331022,)\n",
      "(82756, 20000) (82756,)\n"
     ]
    }
   ],
   "source": [
    "# train test split\n",
    "from sklearn.model_selection import train_test_split\n",
    "X=X2\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)\n",
    "print(X_train.shape, y_train.shape)\n",
    "print(X_test.shape, y_test.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "18e6f778",
   "metadata": {},
   "outputs": [],
   "source": [
    "from Py_FS.wrapper.nature_inspired._utilities import compute_fitness\n",
    "num_agents = 20\n",
    "max_iter = 30\n",
    "dbfile = open('best_model', 'rb')     \n",
    "db = pickle.load(dbfile)\n",
    "def MA(num_agents, max_iter, train_data, train_label, obj_function=compute_fitness, trans_function_shape='s',  prob_mut=0.2,  save_conv_graph=False):\n",
    "    short_name = 'MA'\n",
    "    agent_name = 'Mayfly'\n",
    "    train_data, train_label = np.array(train_data), np.array(train_label)\n",
    "    num_features = train_data.shape[1]\n",
    "    trans_function = get_trans_function(trans_function_shape)\n",
    "\n",
    "    # setting up the objectives\n",
    "    weight_acc = None\n",
    "    if(obj_function==compute_fitness):\n",
    "        weight_acc = float(input('Weight for the classification accuracy [0-1]: '))\n",
    "    obj = (obj_function, weight_acc)\n",
    "    compute_accuracy = (compute_fitness, 1) # compute_accuracy is just compute_fitness with accuracy weight as 1\n",
    "    \n",
    "    # control parameters\n",
    "    a1 = 1\n",
    "    a2 = 1.5\n",
    "    d = 0.1\n",
    "    fl = 0.1\n",
    "    g = 0.8\n",
    "    beta = 2\n",
    "    delta = 0.9\n",
    "    \n",
    "    # initialize position and velocities of male and female mayflies' and Leader (the agent with the max fitness)\n",
    "    male_pos = initialize(num_agents, num_features)\n",
    "    female_pos = initialize(num_agents, num_features)\n",
    "    male_vel = np.random.uniform(low = -1, high = 1, size = (num_agents, num_features))\n",
    "    female_vel = np.random.uniform(low = -1, high = 1, size = (num_agents, num_features))\n",
    "    male_fitness = np.zeros((num_agents))\n",
    "    male_accuracy = np.zeros(num_agents)\n",
    "    female_fitness = np.zeros((num_agents))\n",
    "    Leader_agent = np.zeros((num_features))\n",
    "    Leader_fitness = float(\"-inf\")\n",
    "    Leader_accuracy = float(\"-inf\")\n",
    "    male_personal_best = np.zeros((num_agents, num_features))\n",
    "    male_offspring = np.zeros((num_agents, num_features))\n",
    "    female_offspring = np.zeros((num_agents, num_features))\n",
    "    vmax_male = np.zeros((num_features))\n",
    "    vmax_female = np.zeros((num_features))\n",
    "    \n",
    "    # initialize convergence curves\n",
    "    convergence_curve = {}\n",
    "    convergence_curve['fitness'] = np.zeros(max_iter)\n",
    "    \n",
    "    # initialize data class\n",
    "    data = Data()\n",
    "    val_size = float(input('Enter the percentage of data wanted for valdiation [0, 100]: '))/100\n",
    "    data.train_X, data.val_X, data.train_Y, data.val_Y = train_test_split(train_data, train_label, stratify=train_label, test_size=val_size)\n",
    "    \n",
    "    # create a solution object\n",
    "    solution = Solution()\n",
    "    solution.num_agents = num_agents\n",
    "    solution.max_iter = max_iter\n",
    "    solution.num_features = num_features\n",
    "    solution.obj_function = obj_function\n",
    "    \n",
    "    # rank initial population\n",
    "    male_pos, male_fitness = sort_agents(male_pos, obj, data)\n",
    "    female_pos, female_fitness = sort_agents(female_pos, obj, data)\n",
    "    \n",
    "    # start timer\n",
    "    start_time = time.time()\n",
    "    \n",
    "    # main loop\n",
    "    for iter_no in range(max_iter):\n",
    "        print('\\n================================================================================')\n",
    "        print('                          Iteration - {}'.format(iter_no+1))\n",
    "        print('================================================================================\\n')\n",
    "        \n",
    "        #updating velocity limits\n",
    "        vmax_male, vmax_female = update_max_velocity(male_pos, female_pos)\n",
    "        \n",
    "        for agent in range(num_agents):\n",
    "            \n",
    "            #updating Leader fitness and personal best fitnesses\n",
    "            if male_fitness[agent] > Leader_fitness:\n",
    "                Leader_fitness = male_fitness[agent]\n",
    "                Leader_agent = male_pos[agent]\n",
    "            \n",
    "            if male_fitness[agent] > obj_function(male_personal_best[agent], data.train_X, data.val_X, data.train_Y, data.val_Y):\n",
    "                male_personal_best[agent] = male_pos[agent]\n",
    "\n",
    "            #update velocities of male and female mayflies\n",
    "            male_vel[agent], female_vel[agent] = update_velocity(male_pos[agent], female_pos[agent], male_vel[agent], female_vel[agent], Leader_agent, male_personal_best[agent], a1, a2, d, fl, g, beta, agent, data, obj_function)\n",
    "            \n",
    "            #check boundary condition of velocities of male and female mayflies\n",
    "            male_vel[agent], female_vel[agent] = check_velocity_limits(male_vel[agent], female_vel[agent], vmax_male, vmax_female)\n",
    "            \n",
    "            #applying transfer functions to update positions of male and female mayflies\n",
    "            #the updation is done based on their respective velocity values\n",
    "            for j in range(num_features):\n",
    "                trans_value = trans_function(male_vel[agent][j])\n",
    "                if trans_value > np.random.normal(0,1):\n",
    "                    male_pos[agent][j]=1\n",
    "                else:\n",
    "                    male_pos[agent][j]=0\n",
    "\n",
    "                trans_value = trans_function(female_vel[agent][j])\n",
    "                if trans_value > np.random.random():\n",
    "                    female_pos[agent][j]=1\n",
    "                else:\n",
    "                    female_pos[agent][j]=0\n",
    "        \n",
    "        #sorting \n",
    "        male_pos, male_fitness = sort_agents(male_pos, obj, data)\n",
    "        female_pos, female_fitness = sort_agents(female_pos, obj, data)\n",
    "        \n",
    "        for agent in range(num_agents):\n",
    "            \n",
    "            #generation of offsprings by crossover and mutation between male and female parent mayflies\n",
    "            male_offspring[agent], female_offspring[agent] = cross_mut(male_pos[agent], female_pos[agent],prob_mut)\n",
    "            \n",
    "        #comparing parents and offsprings and replacing parents wherever necessary\n",
    "        male_pos = compare_and_replace(male_pos, male_offspring, male_fitness, data, obj)\n",
    "        female_pos = compare_and_replace(female_pos, female_offspring, female_fitness, data, obj)\n",
    "        \n",
    "        #updating fitness values\n",
    "        male_pos, male_fitness = sort_agents(male_pos, obj, data)\n",
    "        female_pos, female_fitness = sort_agents(female_pos, obj, data)\n",
    "        \n",
    "        #updating values of nuptial dance\n",
    "        d = d * delta\n",
    "        fl = fl * delta\n",
    "        \n",
    "        #update final information\n",
    "        display(male_pos, male_fitness, agent_name)\n",
    "        if(male_fitness[0] > Leader_fitness):\n",
    "            Leader_agent = male_pos[0].copy()\n",
    "            Leader_fitness = male_fitness[0].copy()\n",
    "\n",
    "        convergence_curve['fitness'][iter_no] = np.mean(male_fitness)\n",
    "    \n",
    "    # compute final accuracy\n",
    "    Leader_agent, Leader_accuracy = sort_agents(Leader_agent, compute_accuracy, data)\n",
    "    male_pos, male_accuracy = sort_agents(male_pos, compute_accuracy, data)\n",
    "\n",
    "    print('\\n================================================================================')\n",
    "    print('                                    Final Result                                  ')\n",
    "    print('================================================================================\\n')\n",
    "    print('Leader ' + agent_name + ' Dimension : {}'.format(int(np.sum(Leader_agent))))\n",
    "    print('Leader ' + agent_name + ' Fitness : {}'.format(Leader_fitness))\n",
    "    print('Leader ' + agent_name + ' Classification Accuracy : {}'.format(Leader_accuracy))\n",
    "    print('\\n================================================================================\\n')\n",
    "\n",
    "    # stop timer\n",
    "    end_time = time.time()\n",
    "    exec_time = end_time - start_time\n",
    "    \n",
    "    # plot convergence graph\n",
    "    fig, axes = Conv_plot(convergence_curve)\n",
    "    if(save_conv_graph):\n",
    "        plt.savefig('convergence_graph_'+ short_name + '.jpg')\n",
    "    plt.show()\n",
    "    \n",
    "    # update attributes of solution\n",
    "    solution.best_agent = Leader_agent\n",
    "    solution.best_fitness = Leader_fitness\n",
    "    solution.best_accuracy = Leader_accuracy\n",
    "    solution.convergence_curve = convergence_curve\n",
    "    solution.final_population = male_pos\n",
    "    solution.final_fitness = male_fitness\n",
    "    solution.final_accuracy = male_accuracy\n",
    "    solution.execution_time = exec_time\n",
    "\n",
    "    return solution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "80f4737e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "mean positive review in train : 3.820\n",
      "mean positive review in test : 3.818\n"
     ]
    }
   ],
   "source": [
    "# average positive reviews in train and test\n",
    "print('mean positive review in train : {0:.3f}'.format(np.mean(y_train)))\n",
    "print('mean positive review in test : {0:.3f}'.format(np.mean(y_test)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8374fe90",
   "metadata": {},
   "source": [
    "# Sentimental Classification"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b4b15db",
   "metadata": {},
   "source": [
    "## Improvised local search whale optimization boosted LSTM (ILW-LSTM)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "2f28c332",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Begin whale optimization algorithm on rastrigin function\n",
      "\n",
      "Goal is to minimize Rastrigin's function in 3 variables\n",
      "Function has known min = 0.0 at (0, 0, 0)\n",
      "Setting num_whales = 50\n",
      "Setting max_iter    = 100\n",
      "\n",
      "Starting WOA algorithm\n",
      "\n",
      "Iter = 10 best fitness = 4.285\n",
      "Iter = 20 best fitness = 2.312\n",
      "Iter = 30 best fitness = 2.121\n",
      "Iter = 40 best fitness = 1.247\n",
      "Iter = 50 best fitness = 1.009\n",
      "Iter = 60 best fitness = 0.219\n",
      "Iter = 70 best fitness = 0.001\n",
      "Iter = 80 best fitness = 0.000\n",
      "Iter = 90 best fitness = 0.000\n",
      "\n",
      "WOA completed\n",
      "\n",
      "\n",
      "Best solution found:\n",
      "['0.000020', '0.000009', '-0.000019']\n",
      "fitness of best solution = 0.000000\n",
      "\n",
      "End WOA for rastrigin\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "Begin whale optimization algorithm on sphere function\n",
      "\n",
      "Goal is to minimize sphere function in 3 variables\n",
      "Function has known min = 0.0 at (0, 0, 0)\n",
      "Setting num_whales = 50\n",
      "Setting max_iter    = 100\n",
      "\n",
      "Starting WOA algorithm\n",
      "\n",
      "Iter = 10 best fitness = 0.001\n",
      "Iter = 20 best fitness = 0.000\n",
      "Iter = 30 best fitness = 0.000\n",
      "Iter = 40 best fitness = 0.000\n",
      "Iter = 50 best fitness = 0.000\n",
      "Iter = 60 best fitness = 0.000\n",
      "Iter = 70 best fitness = 0.000\n",
      "Iter = 80 best fitness = 0.000\n",
      "Iter = 90 best fitness = 0.000\n",
      "\n",
      "WOA completed\n",
      "\n",
      "\n",
      "Best solution found:\n",
      "['0.000000', '0.000000', '-0.000000']\n",
      "fitness of best solution = 0.000000\n",
      "\n",
      "End WOA for sphere\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "import math  # cos() for Rastrigin\n",
    "import copy  # array-copying convenience\n",
    "import sys  # max float\n",
    " \n",
    " \n",
    "# -------fitness functions---------\n",
    " \n",
    "# rastrigin function\n",
    "def fitness_rastrigin(position):\n",
    "    fitness_value = 0.0\n",
    "    for i in range(len(position)):\n",
    "        xi = position[i]\n",
    "        fitness_value += (xi * xi) - (10 * math.cos(2 * math.pi * xi)) + 10\n",
    "    return fitness_value\n",
    " \n",
    " \n",
    "# sphere function\n",
    "def fitness_sphere(position):\n",
    "    fitness_value = 0.0\n",
    "    for i in range(len(position)):\n",
    "        xi = position[i]\n",
    "        fitness_value += (xi * xi);\n",
    "    return fitness_value;\n",
    " \n",
    " \n",
    "# -------------------------\n",
    " \n",
    " \n",
    "# whale class\n",
    "class whale:\n",
    "    def __init__(self, fitness, dim, minx, maxx, seed):\n",
    "        self.rnd = random.Random(seed)\n",
    "        self.position = [0.0 for i in range(dim)]\n",
    " \n",
    "        for i in range(dim):\n",
    "            self.position[i] = ((maxx - minx) * self.rnd.random() + minx)\n",
    " \n",
    "        self.fitness = fitness(self.position)  # curr fitness\n",
    " \n",
    " \n",
    "# whale optimization algorithm(WOA)\n",
    "def woa(fitness, max_iter, n, dim, minx, maxx):\n",
    "    rnd = random.Random(0)\n",
    " \n",
    "    # create n random whales\n",
    "    whalePopulation = [whale(fitness, dim, minx, maxx, i) for i in range(n)]\n",
    " \n",
    "    # compute the value of best_position and best_fitness in the whale Population\n",
    "    Xbest = [0.0 for i in range(dim)]\n",
    "    Fbest = sys.float_info.max\n",
    " \n",
    "    for i in range(n):  # check each whale\n",
    "        if whalePopulation[i].fitness < Fbest:\n",
    "            Fbest = whalePopulation[i].fitness\n",
    "            Xbest = copy.copy(whalePopulation[i].position)\n",
    " \n",
    "    # main loop of woa\n",
    "    Iter = 0\n",
    "    while Iter < max_iter:\n",
    " \n",
    "        # after every 10 iterations\n",
    "        # print iteration number and best fitness value so far\n",
    "        if Iter % 10 == 0 and Iter > 1:\n",
    "            print(\"Iter = \" + str(Iter) + \" best fitness = %.3f\" % Fbest)\n",
    " \n",
    "        # linearly decreased from 2 to 0\n",
    "        a = 2 * (1 - Iter / max_iter)\n",
    "        a2=-1+Iter*((-1)/max_iter)\n",
    " \n",
    "        for i in range(n):\n",
    "            A = 2 * a * rnd.random() - a\n",
    "            C = 2 * rnd.random()\n",
    "            b = 1\n",
    "            l = (a2-1)*rnd.random()+1;\n",
    "            p = rnd.random()\n",
    " \n",
    "            D = [0.0 for i in range(dim)]\n",
    "            D1 = [0.0 for i in range(dim)]\n",
    "            Xnew = [0.0 for i in range(dim)]\n",
    "            Xrand = [0.0 for i in range(dim)]\n",
    "            if p < 0.5:\n",
    "                if abs(A) > 1:\n",
    "                    for j in range(dim):\n",
    "                        D[j] = abs(C * Xbest[j] - whalePopulation[i].position[j])\n",
    "                        Xnew[j] = Xbest[j] - A * D[j]\n",
    "                else:\n",
    "                    p = random.randint(0, n - 1)\n",
    "                    while (p == i):\n",
    "                        p = random.randint(0, n - 1)\n",
    " \n",
    "                    Xrand = whalePopulation[p].position\n",
    " \n",
    "                    for j in range(dim):\n",
    "                        D[j] = abs(C * Xrand[j] - whalePopulation[i].position[j])\n",
    "                        Xnew[j] = Xrand[j] - A * D[j]\n",
    "            else:\n",
    "                for j in range(dim):\n",
    "                    D1[j] = abs(Xbest[j] - whalePopulation[i].position[j])\n",
    "                    Xnew[j] = D1[j] * math.exp(b * l) * math.cos(2 * math.pi * l) + Xbest[j]\n",
    " \n",
    "            for j in range(dim):\n",
    "                whalePopulation[i].position[j] = Xnew[j]\n",
    " \n",
    "        for i in range(n):\n",
    "            # if Xnew < minx OR Xnew > maxx\n",
    "            # then clip it\n",
    "            for j in range(dim):\n",
    "                whalePopulation[i].position[j] = max(whalePopulation[i].position[j], minx)\n",
    "                whalePopulation[i].position[j] = min(whalePopulation[i].position[j], maxx)\n",
    " \n",
    "            whalePopulation[i].fitness = fitness(whalePopulation[i].position)\n",
    " \n",
    "            if (whalePopulation[i].fitness < Fbest):\n",
    "                Xbest = copy.copy(whalePopulation[i].position)\n",
    "                Fbest = whalePopulation[i].fitness\n",
    " \n",
    " \n",
    "        Iter += 1\n",
    "    # end-while\n",
    " \n",
    "    # returning the best solution\n",
    "    return Xbest\n",
    " \n",
    " \n",
    "# ----------------------------\n",
    " \n",
    " \n",
    "# Driver code for rastrigin function\n",
    " \n",
    "print(\"\\nBegin whale optimization algorithm on rastrigin function\\n\")\n",
    "dim = 3\n",
    "fitness = fitness_rastrigin\n",
    " \n",
    "print(\"Goal is to minimize Rastrigin's function in \" + str(dim) + \" variables\")\n",
    "print(\"Function has known min = 0.0 at (\", end=\"\")\n",
    "for i in range(dim - 1):\n",
    "    print(\"0, \", end=\"\")\n",
    "print(\"0)\")\n",
    " \n",
    "num_whales = 50\n",
    "max_iter = 100\n",
    " \n",
    "print(\"Setting num_whales = \" + str(num_whales))\n",
    "print(\"Setting max_iter    = \" + str(max_iter))\n",
    "print(\"\\nStarting WOA algorithm\\n\")\n",
    " \n",
    "best_position = woa(fitness, max_iter, num_whales, dim, -10.0, 10.0)\n",
    " \n",
    "print(\"\\nWOA completed\\n\")\n",
    "print(\"\\nBest solution found:\")\n",
    "print([\"%.6f\" % best_position[k] for k in range(dim)])\n",
    "err = fitness(best_position)\n",
    "print(\"fitness of best solution = %.6f\" % err)\n",
    " \n",
    "print(\"\\nEnd WOA for rastrigin\\n\")\n",
    " \n",
    "print()\n",
    "print()\n",
    " \n",
    "# Driver code for Sphere function\n",
    "print(\"\\nBegin whale optimization algorithm on sphere function\\n\")\n",
    "dim = 3\n",
    "fitness = fitness_sphere\n",
    " \n",
    "print(\"Goal is to minimize sphere function in \" + str(dim) + \" variables\")\n",
    "print(\"Function has known min = 0.0 at (\", end=\"\")\n",
    "for i in range(dim - 1):\n",
    "    print(\"0, \", end=\"\")\n",
    "print(\"0)\")\n",
    " \n",
    "num_whales = 50\n",
    "max_iter = 100\n",
    " \n",
    "print(\"Setting num_whales = \" + str(num_whales))\n",
    "print(\"Setting max_iter    = \" + str(max_iter))\n",
    "print(\"\\nStarting WOA algorithm\\n\")\n",
    " \n",
    "best_position = woa(fitness, max_iter, num_whales, dim, -10.0, 10.0)\n",
    " \n",
    "print(\"\\nWOA completed\\n\")\n",
    "print(\"\\nBest solution found:\")\n",
    "print([\"%.6f\" % best_position[k] for k in range(dim)])\n",
    "err = fitness(best_position)\n",
    "print(\"fitness of best solution = %.6f\" % err)\n",
    " \n",
    "print(\"\\nEnd WOA for sphere\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "83132ce1",
   "metadata": {},
   "outputs": [],
   "source": [
    "model_lr=lr(random_state=0)\n",
    "voc_size = 50000\n",
    "max_length = 150\n",
    "embedding_features = 32"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "16a16db6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"sequential\"\n",
      "_________________________________________________________________\n",
      " Layer (type)                Output Shape              Param #   \n",
      "=================================================================\n",
      " embedding (Embedding)       (None, 150, 32)           1600000   \n",
      "                                                                 \n",
      " dropout (Dropout)           (None, 150, 32)           0         \n",
      "                                                                 \n",
      " lstm (LSTM)                 (None, 100)               53200     \n",
      "                                                                 \n",
      " dropout_1 (Dropout)         (None, 100)               0         \n",
      "                                                                 \n",
      " dense (Dense)               (None, 64)                6464      \n",
      "                                                                 \n",
      " dropout_2 (Dropout)         (None, 64)                0         \n",
      "                                                                 \n",
      " dense_1 (Dense)             (None, 32)                2080      \n",
      "                                                                 \n",
      " dropout_3 (Dropout)         (None, 32)                0         \n",
      "                                                                 \n",
      " dense_2 (Dense)             (None, 5)                 165       \n",
      "                                                                 \n",
      "=================================================================\n",
      "Total params: 1,661,909\n",
      "Trainable params: 1,661,909\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "from tensorflow.keras.layers import Dropout\n",
    "model = Sequential()\n",
    "model.add(Embedding(voc_size, embedding_features, input_length=max_length))\n",
    "model.add(Dropout(0.33))\n",
    "model.add(LSTM(100))\n",
    "model.add(Dropout(0.33))\n",
    "model.add(Dense(64, activation='relu'))\n",
    "model.add(Dropout(0.33))\n",
    "model.add(Dense(32, activation='relu'))\n",
    "model.add(Dropout(0.33))\n",
    "model.add(Dense(5, activation='sigmoid'))\n",
    "model.compile(loss = 'categorical_crossentropy', optimizer='adam', metrics = ['accuracy'])\n",
    "print(model.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "367285ab",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "accuracy  : 0.7633162550147421\n",
      "confusion matrix :\n",
      " [[12958   356   416   216   575]\n",
      " [ 2376  1278   604   312   400]\n",
      " [ 1390   341  2316   985  1271]\n",
      " [  491   141   741  4121  6685]\n",
      " [  453    44   325  1465 42496]]\n",
      "F1 score  : 0.7633162550147421\n",
      "Precision score  : 0.7633162550147421\n",
      "recall score : 0.7633162550147421\n",
      "Wall time: 32.5 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "model_lr=lr(penalty='l2',C=1.0,random_state=0)\n",
    "model_lr.fit(X_train,y_train)\n",
    "y_pred_lr=model_lr.predict(X_test)\n",
    "print('accuracy  :',accuracy_score(y_test,y_pred_lr))\n",
    "print('confusion matrix :\\n',confusion_matrix(y_test,y_pred_lr))\n",
    "print('F1 score  :',f1_score(y_test,y_pred_lr,pos_label='positive',average='micro'))\n",
    "print('Precision score  :',precision_score(y_test,y_pred_lr,pos_label='positive',average='micro'))\n",
    "print('recall score :',recall_score(y_test,y_pred_lr,pos_label='positive',average='micro'))\n",
    "#print('AUC: ', roc_auc_score(y_test, y_pred_lr))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "6882c7c3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Smallest Coefs:\n",
      "['great' 'love' 'excellent' 'not bad' 'perfect' 'good' 'amazing' 'awesome'\n",
      " 'no problem' 'fine']\n",
      "\n",
      "Largest Coefs: \n",
      "['worst' 'garbage' 'not' 'horrible' 'terrible' 'useless' 'one star' 'fake'\n",
      " 'junk' 'not buy']\n",
      "Accuracy:  96.27\n"
     ]
    }
   ],
   "source": [
    "# get the feature names as numpy array\n",
    "feature_names = np.array(tfidf.get_feature_names())\n",
    "\n",
    "# Sort the coefficients from the model\n",
    "sorted_coef_index = model_lr.coef_[0].argsort()\n",
    "\n",
    "# Find the 10 smallest and 10 largest coefficients\n",
    "# The 10 largest coefficients are being indexed using [:-11:-1] \n",
    "# so the list returned is in order of largest to smallest\n",
    "print('Smallest Coefs:\\n{}\\n'.format(feature_names[sorted_coef_index[:10]]))\n",
    "print('Largest Coefs: \\n{}'.format(feature_names[sorted_coef_index[:-11:-1]]))\n",
    "print('Accuracy: ',db['acc'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "4ff0cb1f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x220332ec7c8>"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 33016 characters]\n",
      "text/plain": [
       "<Figure size 720x504 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from mlxtend.plotting import plot_confusion_matrix\n",
    "from sklearn.metrics import confusion_matrix\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "class_names = ['Very Negative','Negative','Neutral','Positive','Very Positive']\n",
    "plt.figure(figsize = (10,7))\n",
    "sns.set(font_scale=1.2) \n",
    "sns.heatmap(db['cm'], annot=True,fmt=\"d\",cmap='Blues',xticklabels=class_names,yticklabels=class_names)\n",
    "#plt.savefig('cm_new.png')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a3dd6320",
   "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.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
