{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.preprocessing import KBinsDiscretizer\n",
    "from sklearn.datasets import load_iris"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "experiments_range = [\n",
    "    [0, 100, 1, 4, \"Q\"],\n",
    "    [0, 50, 1, 4, \"Q\"],\n",
    "    [0, 100, 1, 3, \"Q\"],\n",
    "    [0, 50, 1, 3, \"Q\"],\n",
    "    [0, 10, 1, 3, \"Q\"],\n",
    "    [0, 100, 1, 4, \"U\"],\n",
    "    [0, 50, 1, 4, \"U\"],\n",
    "    [0, 100, 1, 3, \"U\"],\n",
    "    [0, 50, 1, 3, \"U\"],\n",
    "# \n",
    "    [0, 10, 1, 3, \"U\"],\n",
    "    [1, 10, 1, 3, \"Q\"],\n",
    "    [1, 10, 1, 3, \"U\"],\n",
    "    [1, 11, 1, 3, \"Q\"],\n",
    "    [1, 11, 1, 3, \"U\"],\n",
    "    [1, 12, 1, 3, \"Q\"],\n",
    "    [1, 12, 1, 3, \"U\"],\n",
    "    [1, 13, 1, 3, \"Q\"],\n",
    "    [1, 13, 1, 3, \"U\"],\n",
    "    [1, 14, 1, 3, \"Q\"],\n",
    "    [1, 14, 1, 3, \"U\"],\n",
    "    [1, 15, 1, 3, \"Q\"],\n",
    "    [1, 15, 1, 3, \"U\"]\n",
    "]\n",
    "experiments_vectors = [\n",
    "    (3, [3.0, 1.0, 1.0, 3.0, 1.0, 1.0, 3.0, 1.0, 1.0]),\n",
    "    (3, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]),\n",
    "    (3, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0]),\n",
    "    (3, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0]),\n",
    "    (3, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0]),\n",
    "    (3, [15.0, 8.0, 12.0, 14.0, 6.0, 1.0, 13.0, 11.0, 10.0, 9.0, 7.0, 4.0, 3.0, 5.0, 2.0]),\n",
    "    (3, [0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 3.0, 4.0])\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/rmontanana/miniconda3/lib/python3.11/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 0 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "def write_lists(file, data, cuts):\n",
    "    sep = \"\"\n",
    "    for res in data:\n",
    "        file.write(f\"{sep}{int(res):d}\")\n",
    "        sep= \", \"\n",
    "    file.write(\"\\n\")\n",
    "    sep = \"\"\n",
    "    for res in cuts:\n",
    "        file.write(sep + str(round(res,5)))\n",
    "        sep = \", \"\n",
    "    file.write(\"\\n\")\n",
    "\n",
    "def normalize_cuts(cuts):\n",
    "    #\n",
    "    # Normalize the cutpoints to remove numerical errors such as 33.0000000001\n",
    "    # instead of 33\n",
    "    #\n",
    "    for k in range(cuts.shape[0]):\n",
    "        for i in range(len(cuts[k])):\n",
    "            cuts[k][i] = round(cuts[k][i], 5)\n",
    "\n",
    "with open(\"datasets/tests.txt\", \"w\") as file:\n",
    "    file.write(\"#\\n\")\n",
    "    file.write(\"# from, to, step, #bins, Q/U\\n\")\n",
    "    file.write(\"# discretized data\\n\")\n",
    "    file.write(\"# cut points\\n\")\n",
    "    file.write(\"#\\n\")\n",
    "    #\n",
    "    # Range experiments\n",
    "    #\n",
    "    file.write(\"#\\n\")\n",
    "    file.write(\"# Range experiments\\n\")\n",
    "    file.write(\"#\\n\")\n",
    "    for experiment in experiments_range:\n",
    "        file.write(\"RANGE\\n\")\n",
    "        (from_, to_, step_, bins_, strategy) = experiment\n",
    "        disc = KBinsDiscretizer(n_bins=bins_, encode='ordinal', strategy='quantile' if strategy.strip() == \"Q\" else 'uniform')\n",
    "        data = [[x] for x in range(from_, to_, step_)]\n",
    "        disc.fit(data)\n",
    "        normalize_cuts(disc.bin_edges_)\n",
    "        result = disc.transform(data)\n",
    "        file.write(f\"{from_}, {to_}, {step_}, {bins_}, {strategy}\\n\")\n",
    "        write_lists(file, result, disc.bin_edges_[0])\n",
    "    #\n",
    "    # Vector experiments\n",
    "    #\n",
    "    file.write(\"#\\n\")\n",
    "    file.write(\"# Vector experiments\\n\")\n",
    "    file.write(\"#\\n\")\n",
    "    for n_bins, experiment in experiments_vectors:\n",
    "        for strategy in [\"Q\", \"U\"]:\n",
    "            file.write(\"VECTOR\\n\")\n",
    "            file.write(f\"{strategy}{n_bins}{experiment}\\n\")\n",
    "            disc = KBinsDiscretizer(\n",
    "                n_bins=n_bins,\n",
    "                encode=\"ordinal\",\n",
    "                \n",
    "                strategy=\"quantile\" if strategy.strip() == \"Q\" else \"uniform\",\n",
    "            )\n",
    "            data = [[x] for x in experiment]\n",
    "            disc.fit(data)\n",
    "            normalize_cuts(disc.bin_edges_)\n",
    "            result = disc.transform(data)\n",
    "            write_lists(file, result, disc.bin_edges_[0])\n",
    "    #\n",
    "    # Vector experiments iris\n",
    "    #\n",
    "    file.write(\"#\\n\");\n",
    "    file.write(\"# Vector experiments with iris\\n\");\n",
    "    file.write(\"#\\n\");\n",
    "    X, y = load_iris(return_X_y=True)\n",
    "    for i in range(X.shape[1]):\n",
    "        for n_bins in [3, 4]:\n",
    "            for strategy in [\"Q\", \"U\"]:\n",
    "                file.write(\"VECTOR\\n\")\n",
    "                experiment = X[:, i]\n",
    "                file.write(f\"{strategy}{n_bins}{experiment.tolist()}\\n\")\n",
    "                disc = KBinsDiscretizer(\n",
    "                    n_bins=n_bins,\n",
    "                    encode=\"ordinal\",\n",
    "                    strategy=\"quantile\" if strategy.strip() == \"Q\" else \"uniform\")\n",
    "                data = [[x] for x in experiment]\n",
    "                disc.fit(data)\n",
    "                normalize_cuts(disc.bin_edges_)\n",
    "                result = disc.transform(data)\n",
    "                write_lists(file, result, disc.bin_edges_[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cut points:  [array([ 0., 33., 66., 99.])]\n",
      "Mistaken transformed data disc.transform([[33]]) = [[0.]]\n",
      "Reason of the mistake the cutpoint has decimals (double):  33.00000000000001\n"
     ]
    }
   ],
   "source": [
    "#\n",
    "# Proving the mistakes due to floating point precision\n",
    "#\n",
    "from sklearn.preprocessing import KBinsDiscretizer\n",
    "\n",
    "data = [[x] for x in range(100)]\n",
    "disc = KBinsDiscretizer(n_bins=3, encode=\"ordinal\", strategy=\"quantile\")\n",
    "disc.fit(data)\n",
    "print(\"Cut points: \", disc.bin_edges_)\n",
    "print(\"Mistaken transformed data disc.transform([[33]]) =\", disc.transform([[33]]))\n",
    "print(\"Reason of the mistake the cutpoint has decimals (double): \", disc.bin_edges_[0][1])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "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.1.undefined"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
