{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LXzdcX5vg4w9"
      },
      "source": [
        "# Processing Complex Data\n",
        "\n",
        "So far we have assumed that the intput is in the form of numerical vectors to which we can apply directly the algorithms we have. Often the data will be more complex. For example what if we want to cluster categorical data, itemsets, or text? Python provides libraries for processing the data and transforming them to a format that we can use.\n",
        "\n",
        "Python offers a set of tools for extracting features:http://scikit-learn.org/stable/modules/feature_extraction.html"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 52,
      "metadata": {
        "id": "yseBxdXkg4xD"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import scipy as sp\n",
        "import scipy.sparse as sp_sparse\n",
        "import scipy.spatial.distance as sp_dist\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "import sklearn as sk\n",
        "import sklearn.datasets as sk_data\n",
        "import sklearn.metrics as metrics\n",
        "from sklearn import preprocessing\n",
        "import sklearn.cluster as sk_cluster\n",
        "import sklearn.feature_extraction.text as sk_text\n",
        "\n",
        "\n",
        "import scipy.cluster.hierarchy as hr\n",
        "\n",
        "import time\n",
        "import seaborn as sns\n",
        "\n",
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ybj3HL2tg4xG"
      },
      "source": [
        "We will now see some **Encoders**, which are libraries that take as **input** some *data records* and produce as **output** *numerical vectors* that represent them. All of these libraries have two methods:\n",
        "\n",
        "**fit**: \"learns\" the encoding from the input data\n",
        "\n",
        "**transform**: takes a set of data in the original representation and produces the new numerical representation\n",
        "\n",
        "**get_feature_names_out**: Gives us the names of the new attributes that are created"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4xoon21dg4xH"
      },
      "source": [
        "### Ordinal Encoder\n",
        "\n",
        "The Ordinal encoder enables us to encode categorical attributes as numerical by assigning an increasing numerical value to each attribute value.\n",
        "\n",
        "You can read more here:\n",
        "\n",
        "https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OrdinalEncoder.html#sklearn.preprocessing.OrdinalEncoder"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "id": "sZA1AB5Wg4xH",
        "outputId": "48182765-f842-49f8-f52b-042c7c79deed",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[array(['divorced', 'married', 'single'], dtype=object), array(['No', 'Yes'], dtype=object), array(['Athens', 'Ioannina', 'Thessaloniki'], dtype=object)]\n",
            "[[1. 1. 0.]\n",
            " [2. 0. 1.]\n",
            " [1. 0. 2.]\n",
            " [0. 1. 0.]]\n"
          ]
        }
      ],
      "source": [
        "from sklearn.preprocessing import OrdinalEncoder\n",
        "\n",
        "X = [['married','Yes','Athens'],\n",
        "     ['single','No', 'Ioannina'],\n",
        "     ['married','No', 'Thessaloniki'],\n",
        "     ['divorced', 'Yes', 'Athens']]\n",
        "enc = OrdinalEncoder(handle_unknown = 'use_encoded_value', unknown_value = np.nan)\n",
        "enc.fit(X)\n",
        "print(enc.categories_)\n",
        "print(enc.transform(X))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0PaS58Odg4xJ"
      },
      "source": [
        "We can now apply to new data. New values that are not previously endcoded will be represented with NaN"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Mdh4hauqg4xK",
        "outputId": "0976048d-eaf7-44c8-f30d-b6f614928fb8",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array([[ 1.,  0.,  0.],\n",
              "       [ 2.,  1.,  1.],\n",
              "       [ 2.,  1., nan]])"
            ]
          },
          "metadata": {},
          "execution_count": 4
        }
      ],
      "source": [
        "Y = [['married','No','Athens'],\n",
        "     ['single','Yes', 'Ioannina'],\n",
        "     ['single','Yes', 'Patras']\n",
        "    ]\n",
        "enc.transform(Y)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0oXBv170g4xL"
      },
      "source": [
        "Numeric values are also encoded with increasing numbers"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "eMl8OWJzg4xL",
        "outputId": "eddc304c-257a-4026-ac73-c1c8b02fd885",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[array(['divorced', 'married', 'single'], dtype=object), array(['No', 'Yes'], dtype=object), array([24000, 30000, 50000], dtype=object)]\n",
            "\n",
            "\n",
            "[[1. 1. 1.]\n",
            " [2. 0. 0.]\n",
            " [0. 1. 2.]]\n",
            "\n",
            "\n",
            "[[ 1.  0. -1.]\n",
            " [ 2.  1.  0.]]\n"
          ]
        }
      ],
      "source": [
        "X = [['married','Yes',30000],\n",
        "     ['single','No', 24000],\n",
        "     ['divorced', 'Yes', 50000]]\n",
        "enc = OrdinalEncoder(handle_unknown = 'use_encoded_value',unknown_value = -1)\n",
        "enc.fit(X)\n",
        "print(enc.categories_)\n",
        "print(\"\\n\")\n",
        "print(enc.transform(X))\n",
        "Y = [['married','No',10000],\n",
        "     ['single','Yes', 24000]]\n",
        "print(\"\\n\")\n",
        "print(enc.transform(Y))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nKY2HMrXg4xM"
      },
      "source": [
        "### DictVectorizer\n",
        "\n",
        "The DictVectorizer feature extraction:\n",
        "http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.DictVectorizer.html#sklearn.feature_extraction.DictVectorizer\n",
        "\n",
        "The DictVectorizer takes a dictionary of attribute-value pairs and transforms them into numerical vectors. Real values are preserved, while categorical attributes are transformed into binary. The vectorizer produces a *sparse representation*.\n",
        "\n",
        "Note that we can get a dictionary representation of the data from pandas with the [to_dict](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_dict.html) method"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "scrolled": true,
        "id": "hGG3nYC2g4xN",
        "outputId": "2f344602-05ac-43db-d255-adbd870e8472",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "<class 'scipy.sparse._csr.csr_matrix'>\n",
            "\n",
            "\n",
            "[[ 1.  0.  0. 45.]\n",
            " [ 0.  1.  0. 12.]\n",
            " [ 0.  0.  1. 23.]]\n",
            "\n",
            "\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array(['city=Dubai', 'city=London', 'city=San Fransisco', 'temperature'],\n",
              "      dtype=object)"
            ]
          },
          "metadata": {},
          "execution_count": 4
        }
      ],
      "source": [
        "from sklearn.feature_extraction import DictVectorizer\n",
        "\n",
        "measurements = [\n",
        "{'city': 'Dubai', 'temperature': 45},\n",
        "{'city': 'London', 'temperature': 12},\n",
        "{'city': 'San Fransisco', 'temperature': 23},\n",
        "]\n",
        "vec = DictVectorizer()\n",
        "print(type(vec.fit_transform(measurements)))\n",
        "print(\"\\n\")\n",
        "print(vec.fit_transform(measurements).toarray())\n",
        "print(\"\\n\")\n",
        "vec.get_feature_names_out()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VYieJLezg4xN"
      },
      "source": [
        "Missing attributes will be assigned zero value"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "i0E3tUmyg4xO",
        "outputId": "5ba4dbb5-04c6-4352-c812-0c920a456809",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "['city=Dubai' 'city=London' 'city=San Fransisco' 'dummy' 'temperature']\n",
            "[[ 1.  0.  0.  3. 45.]\n",
            " [ 0.  1.  0.  0. 12.]\n",
            " [ 0.  0.  1.  0. 23.]]\n"
          ]
        }
      ],
      "source": [
        "measurements = [\n",
        "{'city': 'Dubai', 'temperature': 45, 'dummy': 3},\n",
        "{'city': 'London', 'temperature': 12},\n",
        "{'city': 'San Fransisco', 'temperature': 23},\n",
        "]\n",
        "vec = DictVectorizer()\n",
        "vec.fit(measurements)\n",
        "print(vec.get_feature_names_out())\n",
        "print(vec.transform(measurements).toarray())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CUWXcws5g4xO"
      },
      "source": [
        "Unknown attributes will be ignored"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rRCYo3oog4xP",
        "outputId": "7692b545-b1aa-4a63-8641-13d3cfca1843",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[[ 0.  0.  0.  4. 32.]]\n"
          ]
        }
      ],
      "source": [
        "x = {'city': 'Athens', 'temperature': 32, 'dummy':4, 'dummy2': 2}\n",
        "print(vec.transform(x).toarray())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GpkhB_I0g4xP"
      },
      "source": [
        "The output of a transform is a sparse matrix"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "scrolled": true,
        "id": "62EDHl6dg4xP",
        "outputId": "59725a95-485a-459e-fdcd-173835a885a5",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "<Compressed Sparse Row sparse matrix of dtype 'float64'\n",
            "\twith 9 stored elements and shape (3, 6)>\n",
            "  Coords\tValues\n",
            "  (0, 0)\t100.0\n",
            "  (0, 2)\t1.0\n",
            "  (0, 4)\t1.0\n",
            "  (1, 0)\t120.0\n",
            "  (1, 3)\t1.0\n",
            "  (1, 5)\t1.0\n",
            "  (2, 0)\t80.0\n",
            "  (2, 1)\t1.0\n",
            "  (2, 4)\t1.0\n",
            "\n",
            "\n",
            "[[100.   0.   1.   0.   1.   0.]\n",
            " [120.   0.   0.   1.   0.   1.]\n",
            " [ 80.   1.   0.   0.   1.   0.]]\n",
            "\n",
            "\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array(['income', 'marital_status=divorced', 'marital_status=married',\n",
              "       'marital_status=single', 'refund=No', 'refund=Yes'], dtype=object)"
            ]
          },
          "metadata": {},
          "execution_count": 12
        }
      ],
      "source": [
        "measurements = [\n",
        "    {'refund' : 'No','marital_status': 'married', 'income' : 100},\n",
        "    {'refund' : 'Yes','marital_status': 'single', 'income' : 120},\n",
        "    {'refund' : 'No','marital_status':'divorced', 'income' : 80},\n",
        "]\n",
        "vec = DictVectorizer()\n",
        "print(vec.fit_transform(measurements))\n",
        "print(\"\\n\")\n",
        "print(vec.transform(measurements).toarray())\n",
        "print(\"\\n\")\n",
        "vec.get_feature_names_out()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UNR0KRoJg4xQ"
      },
      "source": [
        "### OneHotEncoder"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AB626DUjg4xQ"
      },
      "source": [
        "The **OneHotEncoder** can be used for categorical data to transform them into binary, where for each attribute value we have 0 or 1 depending on whether this value appears in the feature vector. It works with numerical categorical values.\n",
        "\n",
        "You can read more about it here: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "b76fhhiDg4xQ",
        "outputId": "545e5b98-b8ea-450c-ca6b-5fc7ad642b94",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array([[1., 0., 0., 1., 0., 0., 1.],\n",
              "       [1., 0., 1., 0., 1., 0., 0.]])"
            ]
          },
          "metadata": {},
          "execution_count": 13
        }
      ],
      "source": [
        "X = [[0,1,2],\n",
        "     [1,2,3],\n",
        "     [0,1,4]]\n",
        "enc = preprocessing.OneHotEncoder(handle_unknown='ignore')\n",
        "enc.fit(X)\n",
        "enc.transform([[0,2,4],[0,1,2]]).toarray()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Oh_VnLr4g4xR"
      },
      "source": [
        "In this example every number in every column defines a separate feature"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WXB83m5Sg4xR",
        "outputId": "a7b8c284-8514-4cc0-fdc3-c05d67d3c21d",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[array([0, 1]), array([1, 2]), array([2, 3, 4])]"
            ]
          },
          "metadata": {},
          "execution_count": 14
        }
      ],
      "source": [
        "enc.categories_"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JqYdFaT6g4xR"
      },
      "source": [
        "We can ask for unknown values to be ignored"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "c-VJ3RbJg4xS",
        "outputId": "5fd7bd36-98b2-4f52-f730-c29d7bd4d01d",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array([[0., 0., 0., 1., 0., 0., 1.],\n",
              "       [0., 1., 1., 0., 1., 0., 0.]])"
            ]
          },
          "metadata": {},
          "execution_count": 15
        }
      ],
      "source": [
        "enc.transform([[2,2,4],[1,1,2]]).toarray()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "M3x9uY5jg4xS",
        "outputId": "90761c1a-fcc5-490c-add6-954556668f60",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array([[0., 1., 0., 0., 1., 0., 1., 0.],\n",
              "       [0., 0., 1., 1., 0., 1., 0., 0.],\n",
              "       [1., 0., 0., 0., 1., 0., 0., 1.]])"
            ]
          },
          "metadata": {},
          "execution_count": 16
        }
      ],
      "source": [
        "X = [['married','Yes',30000],\n",
        "     ['single','No', 24000],\n",
        "     ['divorced', 'Yes', 50000]]\n",
        "enc = preprocessing.OneHotEncoder(handle_unknown='ignore')\n",
        "enc.fit_transform(X).toarray()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "85y-sOlbg4xS",
        "outputId": "74e3352a-2e34-472e-b6c9-dccca01f06fd",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[array(['divorced', 'married', 'single'], dtype=object),\n",
              " array(['No', 'Yes'], dtype=object),\n",
              " array([24000, 30000, 50000], dtype=object)]"
            ]
          },
          "metadata": {},
          "execution_count": 17
        }
      ],
      "source": [
        "enc.categories_"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yZ89YjXFg4xT"
      },
      "source": [
        "You can keep binary categories as binary. In the following example note that we used only a single column for the fist two attributes"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7rsqlldsg4xT",
        "outputId": "7e380ce0-0fd1-4b09-a471-361ef5f220a8",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[array([0, 1]), array([1, 2]), array([2, 3, 4])]\n",
            "[[1. 1. 0. 0. 1.]\n",
            " [0. 0. 1. 0. 0.]]\n"
          ]
        }
      ],
      "source": [
        "X = [[0,1,2],\n",
        "     [1,2,3],\n",
        "     [0,1,4]]\n",
        "enc = preprocessing.OneHotEncoder(drop = 'if_binary')  # First two features are binary. They take values {0,1} and {1,2}\n",
        "enc.fit(X)                                             # Keep them in ONE column\n",
        "print(enc.categories_)\n",
        "print(enc.transform([[1,2,4],[0,1,2]]).toarray())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KLvOiWUqg4xT"
      },
      "source": [
        "## Text processing\n",
        "\n",
        "Feature extraction from text:\n",
        "http://scikit-learn.org/stable/modules/classes.html#text-feature-extraction-ref"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "phvz63YKg4xT"
      },
      "source": [
        "### CountVectorizer\n",
        "\n",
        "The CountVectorizer can be used to extract features in the form of bag of words. It is typically used for text, but you could use it to represent also a collection of itemsets (where each item will become a word)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "id": "H7035275g4xU",
        "outputId": "99810c83-8ebd-4d81-b0c7-95782b3c2e25",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "['and' 'document' 'first' 'is' 'one' 'second' 'the' 'third' 'this']\n",
            "\n",
            "\n",
            "[[0 1 1 1 0 0 1 0 1]\n",
            " [0 1 0 1 0 2 1 0 1]\n",
            " [1 0 0 0 1 0 1 1 0]\n",
            " [0 1 1 1 0 0 1 0 1]]\n"
          ]
        }
      ],
      "source": [
        "import sklearn.feature_extraction.text as sk_text\n",
        "\n",
        "corpus = ['This is the first document.',\n",
        "           'this is the second, second document.',\n",
        "           'And the third one.',\n",
        "           'Is this the first document?',\n",
        "          ]\n",
        "\n",
        "vectorizer = sk_text.CountVectorizer()\n",
        "X = vectorizer.fit_transform(corpus)\n",
        "print(vectorizer.get_feature_names_out())\n",
        "print(\"\\n\")\n",
        "print(X.toarray())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NqkMOSz6g4xU"
      },
      "source": [
        "We can do some feature selection.\n",
        "\n",
        "Here we drop words that appear only in one of the documents (phrases)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "id": "YOh1ohxqg4xU",
        "outputId": "b5a8427c-9caf-47ac-ab1a-b9bcc0ed33d8",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "['document' 'first' 'is' 'the' 'this']\n",
            "[[1 1 1 1 1]\n",
            " [1 0 1 1 1]\n",
            " [0 0 0 1 0]\n",
            " [1 1 1 1 1]]\n"
          ]
        }
      ],
      "source": [
        "import sklearn.feature_extraction.text as sk_text\n",
        "\n",
        "corpus = ['This is the first document.',\n",
        "           'this is the second second document.',\n",
        "           'And the third one.',\n",
        "           'Is this the first document?',\n",
        "          ]\n",
        "\n",
        "vectorizer = sk_text.CountVectorizer(min_df=2)\n",
        "X = vectorizer.fit_transform(corpus)\n",
        "print(vectorizer.get_feature_names_out())\n",
        "print(X.toarray())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RLhrWHAhg4xV",
        "outputId": "3ed45d1e-644f-4557-8206-2a91fd152f2e",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array([[1, 1, 0, 1, 0]])"
            ]
          },
          "metadata": {},
          "execution_count": 22
        }
      ],
      "source": [
        "vectorizer.transform(['The first and    last,document.']).toarray()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qiyHQTFwg4xW"
      },
      "source": [
        "Here we drop stopwords = common words (there is a built-in stopword list in the library)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vPWp6hJYg4xW",
        "outputId": "2c5ea0a2-6a48-4682-c9ab-037e985def9d",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "['document' 'second']\n",
            "[[1 0]\n",
            " [1 2]\n",
            " [0 0]\n",
            " [1 0]]\n"
          ]
        }
      ],
      "source": [
        "corpus = ['This is the first document.',\n",
        "           'this is the second second document.',\n",
        "           'And the third one.',\n",
        "           'Is this the first document?',\n",
        "          ]\n",
        "\n",
        "vectorizer = sk_text.CountVectorizer(stop_words = 'english')\n",
        "\n",
        "X2 = vectorizer.fit_transform(corpus)\n",
        "print(vectorizer.get_feature_names_out())\n",
        "print(X2.toarray())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yJJZzrfqg4xW"
      },
      "source": [
        "### TfIdfVectorizer\n",
        "\n",
        "TfIdfVectorizer transforms text into a sparse matrix where rows are text and columns are words, and values are the tf-idf values. It performs tokenization, normalization, and removes stop-words. More here: http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html#sklearn.feature_extraction.text.TfidfVectorizer"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "![image.png](data:image/png;base64,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)\n",
        "\n",
        "![image.png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXcAAABCCAYAAACsAJZUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAB3mSURBVHhe7Z0HsBTF08DHnEUwYEBFxIAIBsyKAooIGFCkDIQnihRgiTmgIp9CCQKiYijJwYAREyiKIEFFMKJgVswRFQUxu9/8mp37z1v27vbupXtH/6oO3u3u7eSeme6embUCi1EURVGKirXD/xVFUZQiQoW7oihKEaLCXVEUpQhR4a4oilKEqHBXFEUpQlS4K4qiFCEq3BVFUYoQFe6KoihFiAp3RVGUIkSFu6IoShGiwl1RFKUIUeGuKIpShKhwV8qFf//91/zwww/mjz/+CK8UNr/++quZOXOmefPNNyXuSnHy999/mzfeeEPKeuXKleHV6oNrV7/99lt4JTkq3AucPn36mJ122slsvvnm8rniiiuMv5Hn4sWLTb169VL3+Vx66aXh3cqB8GrWrGl23XVX8/zzz4dXCxPybuTIkaZdu3Zm1qxZplmzZubee+8N7yrFxFtvvWWOPPJIM23aNNOzZ0/TrVu3Mg8+GBRQ3wcNGiQdR0Xx+++/my5duqTa1UsvvRTeyQG2/FUKnx49egRWcAc1atQIpk6dGl5dxX///Rc89NBDQadOnQI7OgmvVi6vvfZasM022wRPPfVUeKUwmT9/frDLLrvI/3PmzAls4wkmTJgQ3lWKhV9++SVo3rx5cMMNNwQ//vhjcOihhwZnnHFGYIVm+ERpvv/++6Bt27bBpEmTwivx2FmA1PMGDRoE33zzTXi14hgyZEhQv379YMmSJeGV5OjIvZqwySabmP79+5t1113XXHvttearr74K7xiz1lprGVsBTO3atc1GG20UXq1c1l67elSlF1980Wy44YYyG2ratKn56aefZISkFBeffPKJzGr33XdfU6tWLRn53nfffVL2cSxatMjMmzdP2lAmGjZsaG688UYzePDgrM+WlX/++ccsXLjQ7LbbbmbrrbcOryZHhXs1AmF04YUXmg8++EAqWEVOCxVlTcLOPKUTsLO68Eo86623njnrrLPM8ccfL4OqisTOJszrr78uHQqDu1xZ5/8s4d9KAfPss8+affbZxxxzzDFmzpw55plnnjF777232XPPPeX+t99+KzrGY489Vr4j+O101Lz33nvm448/NjvssIPom7nGs6+88orZcsstZaSPsQajzauvvmr++usvU6NGDXkXFX7jjTcWPT6/5T2ETaVGF+iP1nnnPffcI7psRjSMkAnbhRFl+fLl5rHHHjNTp04166yzjtl+++3lfdF4b7HFFjLqwrBEmJkaFL99+eWXzYMPPmjstFzSTGME4v/zzz/LuzCi0jgJj5FcplnHl19+aaZMmWJmz54tjT8uDsSN/Bo7dqzEe/311zd26l7qOWYITzzxhJk8ebLobUnvBhtskFM5kSbKiTRQLjR+0uJGkEnyjXczSh0/frz5/PPPZUS46aabyj3//QgT8o7R7DvvvJO2HNOly4HueMaMGVImf/75p9luu+1SZQIYObF9PPXUUzJSXbp0qdwnfZnIVC7U5w8//NA88sgjYlMhjcx4+fiQN+QZabjrrrvkHU5ox43w3fOfffaZ5DtpJa755Fs6yEPaDu/87rvvzJgxY8zFF18so/ecsYWtVAMuueQS0feBbfCi97NTzsA2ULnGPZ5x2AoYHHfcccFmm20WtG7dOlixYkWwbNmywI46AtsY5PfufU8++WSw++67y7Pnn39+0K1bt+CBBx4I7NRTnrOCQK7fdtttwejRo+VZwrKCSX4PThd5yCGHBHZ2ETz66KPB8OHDg5122ikYMWKE2AUctjEHtqMK7DQ5sMIoOO2004KTTz5Z9KTEu02bNmJbOOigg4IuXboEZ599tnyfOXNm+IbV4T1HHnmkhGUbfvDwww8HBxxwgIQF6FpvueWWwM5+Atvg5J3XXnut5EkctpML7OxI9LBWcAcfffRR0LVrV7F9WOERPhUEVgAHJ5xwQtC+fXt5jry0QkLSDqSbdNarVy8YN25c8OmnnwannHJK0KRJE4lnLuV0++23y3t4lrj07t1byqJDhw7B+++/nzXfiDfxR/dMfrl4Pf7443Kf99epU0fef91118mz2HIuvfTSYMcddwxs5y/PQbZ0AWEcdthhwdChQ4NPPvkkuOiii0T37eos91u2bBksWLBA9Ne8D122S28cScrFDoQkjdioTjzxRKm7CxculHs+X3zxRXDFFVeknqVu8KztpEvVVwfl0qtXLykXX+eeS76lw6WLOkzbO++88wIr0KX9kE/5oMK9muALdyoeDYbKREWiYnDPF+7AczRwJzQcw4YNKyU0gAa5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)\n",
        "\n",
        "![image.png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPIAAABDCAYAAACm/lh0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAABD1SURBVHhe7Z0JtFVTGMd3mYeKimR+ROahlFmmUCgSWWTKXBLVSpZhLSJDVuahlBaSEsvKEEKmisg8z6GFCJlnx/59b5/7zjvvDvu+8Z5zv99ap97ddzr33P3t/Q3/vW+zwGIURUk0zd3/iqIkGDVkRUkBasiKkgLUkBUlBaghK0oKUENWlBSghqwoKUANWVFSgBqyoqQANWRFSQFqyEoN/v77b3PnnXeab7/91rUoDcFff/1lpkyZYhYvXuxaao8aslINjPjcc881zZs3N23btnWtSkOw/PLLm6222sqcccYZ5osvvnCttUMNWcmAEY8YMcKsvfba5qijjjLNmjVz95Qezz77rKmoqDAtW7aU49BDDzW///67u9eYn3/+2fTu3TtzP8fRRx9tfvvtN/eI0mCbbbYxZ599tlz3n376ybXWAlY/KQpMmDAh6NWrV/Djjz+6ltLmv//+CwYPHhy0bt06aNOmTTB//nx3TxUzZswI+vfvH1gDdi2lB5/j6quvDoYMGRJYd9u1FofOyIrw8ssvmxtuuMFcfPHFMnslATvgmD///NNccsklEm/ec889TEzu3ko+//xzc/DBB5uVVlrJtZQeeD4DBgwwH3zwgbnllltca3GoISvm119/FQM+7LDDxNVLCsSVq6++urjVm266qXnooYfEcEP++ecf8/7775vNN9/ctZQuDJ7nn3++ufXWW827777rWv1RQ1YyBnD88ceXdFwchw6PAa+11loyCH355Zfmsccec/dWztjM1Outt55rKW26dOlitt9+e3P55ZdLvqIY1JDLHEpMY8eONUcccYRZd911XWsyePXVV02nTp3kbxJbrVq1MlOnTs0kjXBVW7RoYVZbbTW5Xeost9xy5tRTTzXPPfeceemll1yrH2rIZc7s2bPN119/LYaQJJYuXWq+//77zGzLzNy9e3eJ9V988UVpY8bu2rWr/J0UmJG33XZbM2HCBAkNfFFDLmOIjW+//Xazzz77iCEkiTA+bt26tdxedtllpWQGiCwoRTFjJyE+jkJSrkePHubxxx8Xj8IXNeQy5u2335YZrGfPnmIISSKMj6Mx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)\n",
        "\n",
        "![image.png](data:image/png;base64,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yjZr1iwIyS4shNNxMEXHT8xIk86ZcMH1+/IMnSbTezceHcjTTz8dxMgeLNhipOQ9dGrZdHGxWIoBJG0OZhPLli1LuJWQE9eTbA3moPPlL1XSJY6u40Anzy4sDLPkCdzFbIjzvrC0qI+sZaADdiVJudeqVcvWg2SDmnQIey+uVep6OrCuIfWDwRLtau3atVZW0iQcGdxFc0XJJUrkFxD8VhRFUZRziL1GoSiKovx/ooZCURRFiUQNhaIoihKJGgpFURQlEjUUiqIoSiRqKBRFUZRI1FAoiqIokaihUBRFUSJRQ6EoiqJEooZCURRFiUQNhaIoihKJGgpFURQlEjUUiqIoSiRqKBRFUZRI1FAoiqIokaihUBRFUSJRQ6EoiqJEooZCURRFiUQNhaIoihKJGgpFURQlkpw3FMePHzd//fVXcJWcuM8piqIo6ZHThuK7774zzz33nDl16lQQkpyDBw+avn37ml27dgUhiqIoSjaINBR//PGHefDBB811112XOMqVK2fq1q1rKlasaK/vuusu89FHH5l///03iFXIxo0bTeXKlYvETXZMnz49iHWW3377zbz00kvmmWeesc+kokaNGmbAgAHmhRdeMEeOHAlCFUVRlOISaShuuOEG8/nnn5utW7eaatWqmZo1a5rt27ebbdu2mQMHDpg///zTvPzyy2bIkCGmW7duRTroRo0amX379pkVK1aYUqVK2fuHDx+2z3Dwe/Xq1TZNZgMu+fn55o033jAtWrQweXl5QehZMGCdO3c277//fhBSyD333GNuvPFGM23atCDk0gMXWseOHc2ECROCkNxh0aJF1mgzAGAgEIdclkfJDBkEUh/ikkmcbPL/Wg/fffdd8/DDD5/Tx6ZLLNfT5Zdfbi677DJTvXp1U7p06SC0MPyhhx4yH374ofn666/N8OHDzT///BPcLYR4cMUVV5gSJUrY38DvO+64w4wfP97s3LkzCC0Ew7R06VIrYBjff/+9Wbt2ra14LqTZvXt3M3PmTPPLL78EofHo37+/7QTDKvLevXtNvXr1LlolzwVo6AwIVq5caQcADAQURcldWLNdsGCBufLKK80111wThGZGVtYomjRpYnr06GFmzZplDUYqmDEcO3bMnpkx/P3339biC4sXLza33HKLqVWrVhBSlPXr15sKFSrYZ3zq1KljjdlXX30VhKQHnSGG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)"
      ],
      "metadata": {
        "id": "gbTrHzSFDMt9"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "metadata": {
        "id": "A-JzeV_Ag4xX",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "c9269d33-24d9-4ac5-c415-ecea1972c503"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "['and' 'document' 'first' 'is' 'one' 'second' 'the' 'third' 'this']\n",
            "[[0.         0.43877674 0.54197657 0.43877674 0.         0.\n",
            "  0.35872874 0.         0.43877674]\n",
            " [0.         0.27230147 0.         0.27230147 0.         0.85322574\n",
            "  0.22262429 0.         0.27230147]\n",
            " [0.55280532 0.         0.         0.         0.55280532 0.\n",
            "  0.28847675 0.55280532 0.        ]\n",
            " [0.         0.43877674 0.54197657 0.43877674 0.         0.\n",
            "  0.35872874 0.         0.43877674]]\n"
          ]
        }
      ],
      "source": [
        "corpus = ['This is the first document.',\n",
        "           'this is the second second document.',\n",
        "           'And the third one.',\n",
        "           'Is this the first document?',\n",
        "          ]\n",
        "\n",
        "vectorizer = sk_text.TfidfVectorizer(min_df=1)\n",
        "X = vectorizer.fit_transform(corpus)\n",
        "print (vectorizer.get_feature_names_out())\n",
        "print(X.toarray())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "metadata": {
        "id": "9f1Mewnkg4xX",
        "outputId": "6a07142b-7e62-499b-e96f-16ef0c7386b3",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[1.91629073 1.22314355 1.51082562 1.22314355 1.91629073 1.91629073\n",
            " 1.         1.91629073 1.22314355]\n"
          ]
        }
      ],
      "source": [
        "print(vectorizer.idf_)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "metadata": {
        "id": "75Tnl6JZg4xf",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "d56f4614-2a18-403e-8435-44ac12d3ce92"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array([[0.65919112, 0.42075315, 0.51971385, 0.        , 0.        ,\n",
              "        0.        , 0.34399327, 0.        , 0.        ]])"
            ]
          },
          "metadata": {},
          "execution_count": 33
        }
      ],
      "source": [
        "vectorizer.transform(['The first and    last,document.']).toarray()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yv1G352Kg4xf"
      },
      "source": [
        "Removing stop-words"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "metadata": {
        "id": "yDQQ9QYgg4xf",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "b0776447-faf2-4550-9107-aed921da2f82"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "['document' 'second']\n",
            "[[1.         0.        ]\n",
            " [0.30403549 0.9526607 ]\n",
            " [0.         0.        ]\n",
            " [1.         0.        ]]\n"
          ]
        }
      ],
      "source": [
        "vectorizer = sk_text.TfidfVectorizer(stop_words = 'english',min_df=1)\n",
        "X = vectorizer.fit_transform(corpus)\n",
        "print (vectorizer.get_feature_names_out())\n",
        "print(X.toarray())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0eezPHZeg4xg"
      },
      "source": [
        "SciKit datasets: http://scikit-learn.org/stable/datasets/\n",
        "\n",
        "We will use the 20-newsgroups datasets which consists of postings on 20 different newsgroups.\n",
        "\n",
        "More information here: http://scikit-learn.org/stable/datasets/#the-20-newsgroups-text-dataset\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "metadata": {
        "id": "K7KlNylXg4xg",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "12f5c89f-9b66-482c-c8a4-9b7867ded7a3"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[2 0 0 ... 2 1 2]\n",
            "1781\n"
          ]
        }
      ],
      "source": [
        "from sklearn.datasets import fetch_20newsgroups\n",
        "\n",
        "categories = ['comp.os.ms-windows.misc', 'sci.space','rec.sport.baseball']\n",
        "#categories = ['alt.atheism', 'sci.space','rec.sport.baseball']\n",
        "news_data = sk_data.fetch_20newsgroups(subset='train',\n",
        "                               remove=('headers', 'footers', 'quotes'),\n",
        "                               categories=categories)\n",
        "print (news_data.target)        # 0: Windows , 1: Space , 2: Baseball\n",
        "print (len(news_data.target))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "metadata": {
        "id": "TZjj7em7g4xh",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "80ef5752-c5f5-4db4-fc2b-b2bb671d4351"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "<class 'sklearn.utils._bunch.Bunch'>\n",
            "\n",
            "\n",
            "['/root/scikit_learn_data/20news_home/20news-bydate-train/sci.space/60940'\n",
            " '/root/scikit_learn_data/20news_home/20news-bydate-train/comp.os.ms-windows.misc/9955'\n",
            " '/root/scikit_learn_data/20news_home/20news-bydate-train/comp.os.ms-windows.misc/9846'\n",
            " ...\n",
            " '/root/scikit_learn_data/20news_home/20news-bydate-train/sci.space/60891'\n",
            " '/root/scikit_learn_data/20news_home/20news-bydate-train/rec.sport.baseball/104484'\n",
            " '/root/scikit_learn_data/20news_home/20news-bydate-train/sci.space/61110']\n",
            "\n",
            "\n",
            "[2 0 0 2 0 0 1 2 2 1]\n",
            "\n",
            "\n",
            "Recently the following problem has arrisen.  The first time I turn on my  \n",
            "computer when windows starts (from my autoexec) after the win31 title screen  \n",
            "the computer reboots on its own.  Usually the second time (after reboot) or  \n",
            "from the DOS prompt everything works fine.\n",
            "\n",
            " s far as I remember I have not changed my config.sys or autoxec.bat or  \n",
            "win.ini.  I can't remember whether this problem occured before I  \n",
            "optimized/defragmented my disk and created a larger swap file (Thank you  \n",
            "MathCAD 4 :(  )\n",
            "\n",
            "System 386sx, 4MB, stacker 2.0, win31, DOS 5\n",
            "\n",
            "---\n",
            "---------------------------------------------------------------------\n",
            "\n",
            "\n",
            "1781\n"
          ]
        }
      ],
      "source": [
        "print (type(news_data))     # Dictionary-like Object\n",
        "print(\"\\n\")\n",
        "print (news_data.filenames)\n",
        "print(\"\\n\")\n",
        "print (news_data.target[:10])\n",
        "print(\"\\n\")\n",
        "print (news_data.data[1])\n",
        "print(\"\\n\")\n",
        "print (len(news_data.data))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "metadata": {
        "id": "kbHEyFSfg4xh",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "3d1a5880-a834-4d85-8ccd-4317f7f90c49"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "<class 'scipy.sparse._csr.csr_matrix'>\n",
            "['00' '000' '01' ... 'zw' 'zy' 'zz']\n",
            "\n",
            "\n",
            "['00' '000' '01' '02' '03' '04' '05' '06' '07' '08' '086' '09' '0_' '0b'\n",
            " '0c' '0d' '0f' '0g' '0h' '0hd' '0i' '0iv' '0j' '0k' '0l' '0m' '0m75u'\n",
            " '0n' '0p' '0q' '0qax' '0qq' '0r' '0s' '0t' '0tbxn' '0tbxom' '0tq' '0tq6'\n",
            " '0u' '0v' '0va' '0w' '0x' '0y' '10' '100' '1000' '101' '102']\n",
            "\n",
            "\n",
            "['z2' 'z3' 'z4' 'z5' 'z6e' 'z6e1' 'z6e1t' 'z6ei' 'z6ei4' 'z7' 'z8' 'z9'\n",
            " 'z_' 'zai' 'zb' 'zc' 'zd' 'zd9' 'zeile' 'zero' 'zf' 'zh' 'zi' 'zip' 'zj'\n",
            " 'zl' 'zm' 'zn' 'znb' 'zng' 'znh' 'znk' 'znkjz' 'znl' 'zo' 'zone' 'zoo'\n",
            " 'zp' 'zq' 'zr' 'zrck' 'zri' 'zrlk' 'zs' 'zt' 'zu' 'zv' 'zw' 'zy' 'zz']\n"
          ]
        }
      ],
      "source": [
        "vectorizer = sk_text.TfidfVectorizer(stop_words='english',\n",
        "                             #max_features = 100,\n",
        "                             min_df=4, max_df=0.8)\n",
        "data = vectorizer.fit_transform(news_data.data)\n",
        "print(type(data))\n",
        "print(vectorizer.get_feature_names_out())\n",
        "#print(data[0][:10])\n",
        "print(\"\\n\")\n",
        "print(vectorizer.get_feature_names_out()[:50])   # First 50 words\n",
        "print(\"\\n\")\n",
        "print(vectorizer.get_feature_names_out()[-50:])  # Last 50 words"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "print(vectorizer.idf_)"
      ],
      "metadata": {
        "id": "2peSZGipGjph",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "80622662-9090-4a6f-ee90-9998971ca14c"
      },
      "execution_count": 38,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[4.98898405 4.65685021 5.01975571 ... 6.8760537  6.8760537  6.8760537 ]\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Which words have the highest score?"
      ],
      "metadata": {
        "id": "DwdqJL5cuwGZ"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "idf_scores = vectorizer.idf_\n",
        "\n",
        "# 2. Get the list of all feature names\n",
        "feature_names = vectorizer.get_feature_names_out()\n",
        "\n",
        "print(\"\\n--- Top 10 Rarest Terms (Highest IDF Score) ---\")\n",
        "\n",
        "top_10_indices = np.argsort(idf_scores)[-10:]\n",
        "\n",
        "for i in top_10_indices[::-1]:\n",
        "    word = feature_names[i]\n",
        "    score = idf_scores[i]\n",
        "    print(f\"  {word:<20} (IDF Score: {score:.4f})\")"
      ],
      "metadata": {
        "id": "NE-rTa5cuyVn",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "1c7c83bf-f46b-47fe-e1d8-be39636ade83"
      },
      "execution_count": 39,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "--- Top 10 Rarest Terms (Highest IDF Score) ---\n",
            "  zz                   (IDF Score: 6.8761)\n",
            "  lonnie               (IDF Score: 6.8761)\n",
            "  longest              (IDF Score: 6.8761)\n",
            "  loan                 (IDF Score: 6.8761)\n",
            "  ln                   (IDF Score: 6.8761)\n",
            "  lowenstein           (IDF Score: 6.8761)\n",
            "  loved                (IDF Score: 6.8761)\n",
            "  louisville           (IDF Score: 6.8761)\n",
            "  loud                 (IDF Score: 6.8761)\n",
            "  losses               (IDF Score: 6.8761)\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tlr4P9bgg4xh"
      },
      "source": [
        "## Feature normalization\n",
        "\n",
        "Python provides some functionality for normalizing and standardizing the data. Be careful though, some operations work only with dense data.\n",
        "\n",
        "http://scikit-learn.org/stable/modules/preprocessing.html#preprocessing"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3dEfxMHIg4xi"
      },
      "source": [
        "Use the function **<a href = \"https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.scale.html\">preprocessing.scale</a>** to normalize by removing the mean and dividing by the standard deviation. This is done per **feature**, that is, per column of the dataset."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WKm3963lg4xi"
      },
      "source": [
        "The **scale** command transforms data so that they have zero mean and standard deviation 1\n",
        "\n",
        "It can be applied to columns (axis=0)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "metadata": {
        "id": "fQM7AvSlg4xi",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "c0f2cef4-da1f-4c87-d27d-128536366e6f"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "column means:  [1.         0.         0.66666667]\n",
            "column std:  [0.81649658 0.81649658 1.24721913]\n",
            "\n",
            "\n",
            "after feature normalization\n",
            "[[ 0.         -1.22474487  1.06904497]\n",
            " [ 1.22474487  0.          0.26726124]\n",
            " [-1.22474487  1.22474487 -1.33630621]]\n",
            "\n",
            "\n",
            "normalized column means:  [0.00000000e+00 0.00000000e+00 1.48029737e-16]\n",
            "normalized column std:  [1. 1. 1.]\n"
          ]
        }
      ],
      "source": [
        "from sklearn import preprocessing\n",
        "\n",
        "X = np.array([[ 1., -1.,  2.],\n",
        "              [ 2.,  0.,  1.],\n",
        "              [ 0.,  1., -1.]])\n",
        "print(\"column means: \",X.mean(axis = 0))   # axis = 0 columns , axis = 1 rows\n",
        "print(\"column std: \",X.std(axis = 0))\n",
        "X_scaled = preprocessing.scale(X)\n",
        "print(\"\\n\")\n",
        "print(\"after feature normalization\")\n",
        "print(X_scaled)\n",
        "print(\"\\n\")\n",
        "print(\"normalized column means: \",X_scaled.mean(axis=0))\n",
        "print(\"normalized column std: \",X_scaled.std(axis = 0))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IIjRXrLVg4xi"
      },
      "source": [
        "Or to rows (axis=1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "metadata": {
        "id": "AGHWnQu9g4xj",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "e5c75bb0-054c-4864-e3ea-76d90c88ed63"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "row means:  [0.66666667 1.         0.        ]\n",
            "row std:  [1.24721913 0.81649658 0.81649658]\n",
            "\n",
            "\n",
            "after row normalization\n",
            "[[ 0.26726124 -1.33630621  1.06904497]\n",
            " [ 1.22474487 -1.22474487  0.        ]\n",
            " [ 0.          1.22474487 -1.22474487]]\n",
            "\n",
            "\n",
            "normalized row means:  [1.48029737e-16 0.00000000e+00 0.00000000e+00]\n",
            "normalized row std:  [1. 1. 1.]\n"
          ]
        }
      ],
      "source": [
        "print(\"row means: \",X.mean(axis = 1))\n",
        "print(\"row std: \",X.std(axis = 1))\n",
        "X_scaled = preprocessing.scale(X, axis = 1)\n",
        "print(\"\\n\")\n",
        "print(\"after row normalization\")\n",
        "print(X_scaled)\n",
        "print(\"\\n\")\n",
        "print(\"normalized row means: \",X_scaled.mean(axis=1))\n",
        "print(\"normalized row std: \",X_scaled.std(axis = 1))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ho9uBQvbg4xj"
      },
      "source": [
        "Feature normalization will not work with sparse data. In this case, the zeros are treated as values, so the sparse matrix will become non-sparse after normalization."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 43,
      "metadata": {
        "id": "BArniIdgg4xj",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 339
        },
        "outputId": "3e8bb42f-bbab-4a92-eb50-efabea54cd91"
      },
      "outputs": [
        {
          "output_type": "error",
          "ename": "ValueError",
          "evalue": "Cannot center sparse matrices: pass `with_mean=False` instead See docstring for motivation and alternatives.",
          "traceback": [
            "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
            "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
            "\u001b[0;32m/tmp/ipython-input-2383923202.py\u001b[0m in \u001b[0;36m<cell line: 0>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mscipy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msparse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0mcX\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mscipy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msparse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcsc_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mcX_scaled\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpreprocessing\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscale\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcX\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      4\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcX_scaled\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/sklearn/utils/_param_validation.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m    214\u001b[0m                     )\n\u001b[1;32m    215\u001b[0m                 ):\n\u001b[0;32m--> 216\u001b[0;31m                     \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    217\u001b[0m             \u001b[0;32mexcept\u001b[0m \u001b[0mInvalidParameterError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    218\u001b[0m                 \u001b[0;31m# When the function is just a wrapper around an estimator, we allow\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/sklearn/preprocessing/_data.py\u001b[0m in \u001b[0;36mscale\u001b[0;34m(X, axis, with_mean, with_std, copy)\u001b[0m\n\u001b[1;32m    226\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0msparse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0missparse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    227\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mwith_mean\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 228\u001b[0;31m             raise ValueError(\n\u001b[0m\u001b[1;32m    229\u001b[0m                 \u001b[0;34m\"Cannot center sparse matrices: pass `with_mean=False` instead\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    230\u001b[0m                 \u001b[0;34m\" See docstring for motivation and alternatives.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;31mValueError\u001b[0m: Cannot center sparse matrices: pass `with_mean=False` instead See docstring for motivation and alternatives."
          ]
        }
      ],
      "source": [
        "import scipy.sparse\n",
        "cX = scipy.sparse.csc_matrix(X)\n",
        "cX_scaled = preprocessing.scale(cX)\n",
        "print(cX_scaled)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XF4X3J1rg4xk"
      },
      "source": [
        "You should be careful when working with sparse data. In these cases you may want to normalize only the non-zero values (e.g., subtract the mean of only the non-zero values for the non-zero values). This should be done **manually**."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "odN6oFvHg4xk"
      },
      "source": [
        "The same scaling with preprocessing.scale can also be done with the **<a href = \"https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html\">StandardScaler</a>** from the preprocessing library of sklearn.\n",
        "\n",
        "The function **fit()** computes the parameters for scaling, and **transform()** applies the scaling"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 44,
      "metadata": {
        "id": "pRCtIW60g4xk",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "68a9a53e-9857-4e53-9878-99dc6bdf602d"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[[ 1. -1.  2.]\n",
            " [ 2.  0.  1.]\n",
            " [ 0.  1. -1.]]\n",
            "\n",
            "\n",
            "Column Means:\n",
            "[1.         0.         0.66666667]\n",
            "\n",
            "Column Std:\n",
            "[0.81649658 0.81649658 1.24721913]\n",
            "\n",
            "\n",
            "scaled data:\n",
            "[[ 0.         -1.22474487  1.06904497]\n",
            " [ 1.22474487  0.          0.26726124]\n",
            " [-1.22474487  1.22474487 -1.33630621]]\n"
          ]
        }
      ],
      "source": [
        "from sklearn import preprocessing\n",
        "std_scaler = preprocessing.StandardScaler()\n",
        "std_scaler.fit(X)\n",
        "print(X)\n",
        "print(\"\\n\")\n",
        "print(\"Column Means:\")\n",
        "print(std_scaler.mean_)\n",
        "print(\"\\nColumn Std:\")\n",
        "print(std_scaler.scale_)\n",
        "X_std = std_scaler.transform(X)\n",
        "print(\"\\n\")\n",
        "print(\"scaled data:\")\n",
        "print(X_std)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2sL6cjO4g4xl"
      },
      "source": [
        "The advantage is that we can now apply the transform to new data.\n",
        "\n",
        "For example, we compute the parameters for the training data and we apply the scaling to the test data."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 45,
      "metadata": {
        "id": "Onwqx8qAg4xl",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "6fd5573a-dd97-41ea-d411-3ef8fd4f10f7"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[[1.22474487 3.67423461 0.26726124]\n",
            " [0.         2.44948974 0.26726124]]\n"
          ]
        }
      ],
      "source": [
        "y = np.array([[2.,3.,1.],\n",
        "              [1.,2.,1.]])\n",
        "print(std_scaler.transform(y))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "t-QVQ5iog4xl"
      },
      "source": [
        "The **MinMaxScaler** subbtracts from each column the minimum and then divides by the max-min.\n",
        "\n",
        "![image.png](data:image/png;base64,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)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "metadata": {
        "id": "osODdmt3g4xm",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "077cab5a-0c98-4bf7-b2c0-db7241d1f537"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Original Matrix:\n",
            "[[ 1. -1.  2.]\n",
            " [ 2.  0.  1.]\n",
            " [ 0.  1. -1.]]\n",
            "\n",
            "\n",
            "MinMax Scaled Matrix:\n",
            "[[0.5        0.         1.        ]\n",
            " [1.         0.5        0.66666667]\n",
            " [0.         1.         0.        ]]\n",
            "\n",
            "\n",
            "[[1.         2.         0.66666667]\n",
            " [0.5        1.5        0.66666667]]\n"
          ]
        }
      ],
      "source": [
        "min_max_scaler = preprocessing.MinMaxScaler()\n",
        "X_minmax = min_max_scaler.fit_transform(X)\n",
        "print(\"Original Matrix:\")\n",
        "print(X)\n",
        "print(\"\\n\")\n",
        "print(\"MinMax Scaled Matrix:\")\n",
        "print(X_minmax)\n",
        "print(\"\\n\")\n",
        "print(min_max_scaler.transform(y))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "metadata": {
        "id": "2ZKllP9Ug4xm",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "2b5641fa-bc2f-48ef-b90f-b50da8e47b8f"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array([[ 1.5       , -0.5       ,  0.33333333]])"
            ]
          },
          "metadata": {},
          "execution_count": 47
        }
      ],
      "source": [
        "z = np.array([3,-2,0]).reshape(1,3)  # Reshape to make it into 1 feature\n",
        "min_max_scaler.transform(z)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HC4S6xU5g4xm"
      },
      "source": [
        "The **MaxAbsScaler** divides with the maximum absolute value.\n",
        "\n",
        "The MaxAbsScaler can work with sparse data, since it does not destroy the data sparseness. For the other datasets, removing the mean (or min) can destroy the sparseness of the data.\n",
        "\n",
        "![image.png](data:image/png;base64,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)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "metadata": {
        "id": "wuYZE2M0g4xm",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "e784eee4-1554-46e3-c845-c56c2cc69da6"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Original Matrix:\n",
            "[[ 1. -1.  2.]\n",
            " [ 2.  0.  1.]\n",
            " [ 0.  1. -1.]]\n",
            "\n",
            "\n",
            "MaxAbs Scaled Matrix:\n",
            "[[ 0.5 -1.   1. ]\n",
            " [ 1.   0.   0.5]\n",
            " [ 0.   1.  -0.5]]\n"
          ]
        }
      ],
      "source": [
        "print(\"Original Matrix:\")\n",
        "print(X)\n",
        "max_abs_scaler = preprocessing.MaxAbsScaler()\n",
        "X_maxabs = max_abs_scaler.fit_transform(X)\n",
        "print(\"\\n\")\n",
        "print(\"MaxAbs Scaled Matrix:\")\n",
        "print(X_maxabs)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "metadata": {
        "id": "Or8D7h8Sg4xn",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "64747195-e4a4-4671-b930-f51defa9ccd0"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "<Compressed Sparse Column sparse matrix of dtype 'float64'\n",
            "\twith 7 stored elements and shape (3, 3)>\n",
            "  Coords\tValues\n",
            "  (0, 0)\t0.5\n",
            "  (1, 0)\t1.0\n",
            "  (0, 1)\t-1.0\n",
            "  (2, 1)\t1.0\n",
            "  (0, 2)\t1.0\n",
            "  (1, 2)\t0.5\n",
            "  (2, 2)\t-0.5\n"
          ]
        }
      ],
      "source": [
        "# works with sparse data\n",
        "cX_scaled = max_abs_scaler.transform(cX)\n",
        "print(cX_scaled)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qCHXMzNVg4xn"
      },
      "source": [
        "The **normalize** function normalizes the **rows** so that they become unit vectors in some norm that we specify. It can be applied to sparse matrices without destroying the sparsity."
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "![image.png](data:image/png;base64,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)"
      ],
      "metadata": {
        "id": "g-RSJ8jPoiKb"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "metadata": {
        "scrolled": true,
        "id": "0OJKll5Tg4xo",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "dfc13f77-e58c-477c-c0f0-e0981db3a037"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Original Matrix:\n",
            "[[ 1. -1.  2.]\n",
            " [ 2.  0.  1.]\n",
            " [ 0.  1. -1.]]\n",
            "\n",
            "\n",
            "L2 Normalized Matrix:\n",
            "[[ 0.40824829 -0.40824829  0.81649658]\n",
            " [ 0.89442719  0.          0.4472136 ]\n",
            " [ 0.          0.70710678 -0.70710678]]\n"
          ]
        }
      ],
      "source": [
        "#works with sparse data\n",
        "\n",
        "X_normalized = preprocessing.normalize(X, norm='l2')\n",
        "\n",
        "print(\"Original Matrix:\")\n",
        "print(X)\n",
        "print(\"\\n\")\n",
        "print(\"L2 Normalized Matrix:\")\n",
        "print(X_normalized)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 51,
      "metadata": {
        "id": "fYRr8L4Lg4xo",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "7e41de63-ebd8-42c4-bca3-5ca18485e454"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "<Compressed Sparse Row sparse matrix of dtype 'float64'\n",
            "\twith 7 stored elements and shape (3, 3)>\n",
            "  Coords\tValues\n",
            "  (0, 0)\t0.25\n",
            "  (0, 1)\t-0.25\n",
            "  (0, 2)\t0.5\n",
            "  (1, 0)\t0.6666666666666666\n",
            "  (1, 2)\t0.3333333333333333\n",
            "  (2, 1)\t0.5\n",
            "  (2, 2)\t-0.5\n"
          ]
        }
      ],
      "source": [
        "crX = scipy.sparse.csr_matrix(X)\n",
        "crX_scaled = preprocessing.normalize(crX,norm='l1')\n",
        "print(crX_scaled)"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "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.3"
    },
    "colab": {
      "provenance": []
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}