Added hll space comparison
diff --git a/jupyter/comparison-to-datasketch/space-comparison.ipynb b/jupyter/comparison-to-datasketch/space-comparison.ipynb new file mode 100644 index 0000000..7ee2994 --- /dev/null +++ b/jupyter/comparison-to-datasketch/space-comparison.ipynb
@@ -0,0 +1,560 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "472743a8-4868-4329-af35-50ab12b95662", + "metadata": {}, + "source": [ + "# Space Comparison \n", + "\n", + "We compare the space usage between the $4, 6, 8$ bit versions of the Apache DataSketches (ASF) HyperLogLog implementation alongside the `datasketch` HyperLogLogPlusPlus implementation. We show that the `datasketch` version has approximately the same size as the ASF $8$ bit implementation when the latter is in full estimation mode. However, smaller sketches can be made without compromising on the accuracy if $6$ or $4$ bits per bucket are used which use $75\\%$ and $50\\%$ of the space consumed by either of the $8$ bit variants." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "71570575-d16b-4b9b-a066-f921b58eb179", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from datetime import datetime\n", + "import pandas as pd\n", + "import numpy as np\n", + "from utils import distinct_number_sequence\n", + "import datasketches as ds\n", + "import datasketch as d\n", + "import mmh3\n", + "import matplotlib.pyplot as plt\n", + "from timeit import default_timer\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "82fac9dc-d5f2-4d19-9f18-8edc96d7c045", + "metadata": {}, + "outputs": [], + "source": [ + "class SpaceProfile:\n", + " \"\"\"Generates an experiment evaluating the update time for different cardinality inputs\"\"\"\n", + " def __init__(self, sketch_lgk:int, lg_trials:int, max_lgN:int):\n", + " self.sketch_lgk = sketch_lgk\n", + " self.num_trials = 2**lg_trials\n", + " self.max_lgN = max_lgN\n", + " self.max_num_distincts = np.uint64(2 ** self.max_lgN)\n", + " self.directory_name = \"hll_space_profile_\"\n", + " if not os.path.exists(self.directory_name):\n", + " os.mkdir(self.directory_name)\n", + " self.file_extension = \"_\" + datetime.today().strftime('%H%M') + f\"lgK_{self.sketch_lgk}_lgT_{lg_trials}\"\n", + "\n", + " # Need to remove repeated items for the program logic in self.run()\n", + " self.plot_points = self._generate_plot_points()\n", + " self.plot_points.extend(self._generate_plot_points())\n", + " self.plot_points = list(set(self.plot_points))\n", + " self.plot_points.sort()\n", + " print(f\"Testing {len(self.plot_points)} points with {self.num_trials} trials for average update times:\")\n", + " print(self.plot_points)\n", + "\n", + " # Initialise the data structures for results; one for each method tested 8, 6, datasketch\n", + " # np.ndarrays\n", + " self.DataSketches_results_arr = np.zeros((len(self.plot_points), self.num_trials), dtype=float)\n", + " self.DataSketches_results_arr_6 = np.zeros_like(self.DataSketches_results_arr)\n", + " self.DataSketches_results_arr_4 = np.zeros_like(self.DataSketches_results_arr)\n", + " self.datasketch_results_arr = np.zeros_like(self.DataSketches_results_arr)\n", + "\n", + " # pd.DataFrames\n", + " self.DataSketches_results_df = pd.DataFrame(index=self.plot_points, columns=None)\n", + " self.DataSketches_results_df_6 = pd.DataFrame(index=self.plot_points, columns=None)\n", + " self.DataSketches_results_df_4 = pd.DataFrame(index=self.plot_points, columns=None)\n", + " self.datasketch_results_df = pd.DataFrame(index=self.plot_points, columns=None)\n", + " \n", + " def _generate_plot_points(self) -> list:\n", + " \"\"\"\n", + " Generates the standard sequence defining the input cardinalites for the experiment\n", + " This is just two points at each power of 2\n", + " \"\"\"\n", + " all_plot_points = []\n", + " for lgk in range(1, self.max_lgN+1):\n", + " points = np.unique(np.logspace(start=lgk, stop=lgk+1, num=4, endpoint=False, base=2, dtype=np.uint64))\n", + " all_plot_points.extend(points)\n", + " all_plot_points.sort()\n", + " return all_plot_points\n", + "\n", + " def _is_power_of_two(self, a:np.uint64) -> bool:\n", + " \"\"\"Bitwise operations to check value a is a power of two\"\"\"\n", + " return (a & (a-1) == 0) and a != 0\n", + "\n", + " def run(self) -> None:\n", + " \"\"\"Runs the experiment and writes the files every power of two trials.\"\"\"\n", + " seq_start = np.uint64(2345234)\n", + " distinct_number = np.uint64(3462)\n", + " previous_log_trial_index = 0\n", + " ds_all_results = np.zeros((self.num_trials, len(self.plot_points)))\n", + " ds_all_results_6 = np.zeros_like(ds_all_results)\n", + " ds_all_results_4 = np.zeros_like(ds_all_results)\n", + " d_all_results = np.zeros_like(ds_all_results)\n", + "\n", + " for trial in range(1, self.num_trials+1):\n", + "\n", + " # Initialise the sketches\n", + " hll = ds.hll_sketch(self.sketch_lgk, ds.HLL_8)\n", + " hll6 = ds.hll_sketch(self.sketch_lgk, ds.HLL_6)\n", + " hll4 = ds.hll_sketch(self.sketch_lgk, ds.HLL_4)\n", + " h = d.HyperLogLogPlusPlus(p=self.sketch_lgk, hashfunc=lambda x: mmh3.hash64(x, signed=False)[0])\n", + " plot_point_index = 0 # Return to the start of the plot points list to generate the data\n", + " plot_point_value = self.plot_points[plot_point_index]\n", + " total_updates = 0\n", + " seq_start += distinct_number # Start a new input sequence\n", + "\n", + " # Temporary result data structure\n", + " ds_results = np.zeros((len(self.plot_points),))\n", + " ds_results_6 = np.zeros_like(ds_results)\n", + " ds_results_4 = np.zeros_like(ds_results)\n", + " d_results = np.zeros_like(ds_results)\n", + "\n", + "\n", + " for new_number in distinct_number_sequence(seq_start):\n", + " d_input = new_number.tobytes()\n", + " \n", + " hll.update(d_input)\n", + " hll6.update(d_input)\n", + " hll4.update(d_input)\n", + " h.update(d_input)\n", + " total_updates += 1\n", + "\n", + " if total_updates == plot_point_value:\n", + " ds_results[plot_point_index] = hll.get_compact_serialization_bytes()\n", + " ds_results_6[plot_point_index] = hll6.get_compact_serialization_bytes()\n", + " ds_results_4[plot_point_index] = hll4.get_compact_serialization_bytes()\n", + " d_results[plot_point_index] = h.bytesize()\n", + " plot_point_index += 1\n", + "\n", + " if plot_point_index < len(self.plot_points):\n", + " plot_point_value = self.plot_points[plot_point_index]\n", + " else:\n", + " break\n", + "\n", + " # After the break statement, control returns here. Now need to decide whether to write or continue.\n", + " # subtract 1 as we use 1-based indexing for the trial count.\n", + " ds_all_results[trial-1, :] = ds_results \n", + " ds_all_results_6[trial-1, :] = ds_results_6 \n", + " ds_all_results_4[trial-1, :] = ds_results_4\n", + " d_all_results[trial - 1, :] = d_results \n", + " if self._is_power_of_two(trial) and trial > 1:\n", + " # write the array only a logarithmic number of times\n", + " temporary_ds_results = ds_all_results[0:trial, : ]\n", + " temporary_ds_results_6 = ds_all_results_6[0:trial, : ]\n", + " temporary_ds_results_4 = ds_all_results_4[0:trial, : ]\n", + " temporary_d_results = d_all_results[0:trial, :]\n", + " print(f\"#################### PARTIAL RESULTS FOR {trial} TRIALS: DATASKETCHES ####################\")\n", + " previous_log_trial_index = trial\n", + "\n", + " # Write 8 bit results\n", + " self.DataSketches_results_df = pd.DataFrame(temporary_ds_results.T, \n", + " index=self.DataSketches_results_df.index, \n", + " columns=np.arange(trial).tolist())\n", + " self.DataSketches_results_df.to_csv(\n", + " self.directory_name + \"/DataSketches_hll\" + self.file_extension + f\"trials_{trial}_8_bit.csv\",\n", + " index_label=\"n\")\n", + "\n", + " # Write 6 bit results\n", + " self.DataSketches_results_df_6 = pd.DataFrame(temporary_ds_results_6.T, \n", + " index=self.DataSketches_results_df_6.index, \n", + " columns=np.arange(trial).tolist())\n", + " self.DataSketches_results_df_6.to_csv(\n", + " self.directory_name + \"/DataSketches_hll\" + self.file_extension + f\"trials_{trial}_6_bit.csv\",\n", + " index_label=\"n\")\n", + "\n", + " # Write 4 bit results\n", + " self.DataSketches_results_df_4 = pd.DataFrame(temporary_ds_results_4.T, \n", + " index=self.DataSketches_results_df_4.index, \n", + " columns=np.arange(trial).tolist())\n", + " self.DataSketches_results_df_4.to_csv(\n", + " self.directory_name + \"/DataSketches_hll\" + self.file_extension + f\"trials_{trial}_4_bit.csv\",\n", + " index_label=\"n\")\n", + "\n", + " # Write datasketch results\n", + " self.datasketch_results_df = pd.DataFrame(temporary_d_results.T,\n", + " index=self.datasketch_results_df.index,\n", + " columns=np.arange(trial).tolist())\n", + " self.datasketch_results_df.to_csv(\n", + " self.directory_name + \"/datasketch_hll\" + self.file_extension + f\"trials_{trial}.csv\",\n", + " index_label=\"n\"\n", + " )\n", + " print(self.DataSketches_results_df)" + ] + }, + { + "cell_type": "markdown", + "id": "01003378-cb60-45d0-9b19-a74b028dd4a1", + "metadata": {}, + "source": [ + "The experiment updates each of the four sketches with a number of distinct values determined by the input parameters `MAX_LG_N`. A single pass is taken for each trial and all four methods are updated in that pass. The number of bytes required to write the sketch to memory is evaluated and written to a results file.\n", + "\n", + "There is essentially no variation in the sketch sizes over these trials so only a small number of trials are performed. We set the default experimental parameters below. \n", + "\n", + "```SKETCH_LGK = 12, LG_TRIALS = 2, MAX_LG_N = 21``` " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "45e22a9a-4b30-4a2b-9a49-0336e4d27bd8", + "metadata": {}, + "outputs": [], + "source": [ + "SKETCH_LGK = 12\n", + "LG_TRIALS = 3\n", + "MAX_LG_N = 21" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b748aead-1563-485f-adb5-378b1655bf38", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Testing 81 points with 8 trials for average update times:\n", + "[2, 3, 4, 5, 6, 8, 9, 11, 13, 16, 19, 22, 26, 32, 38, 45, 53, 64, 76, 90, 107, 128, 152, 181, 215, 256, 304, 362, 430, 512, 608, 724, 861, 1024, 1217, 1448, 1722, 2048, 2435, 2896, 3444, 4096, 4870, 5792, 6888, 8192, 9741, 11585, 13777, 16384, 19483, 23170, 27554, 32768, 38967, 46340, 55108, 65536, 77935, 92681, 110217, 131072, 155871, 185363, 220435, 262144, 311743, 370727, 440871, 524288, 623487, 741455, 881743, 1048576, 1246974, 1482910, 1763487, 2097152, 2493948, 2965820, 3526975]\n" + ] + } + ], + "source": [ + "space_experiment = SpaceProfile(SKETCH_LGK, LG_TRIALS, MAX_LG_N)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "cc7b9b3b-7976-4fb9-adc7-d7691134df93", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "#################### PARTIAL RESULTS FOR 2 TRIALS: DATASKETCHES ####################\n", + " 0 1\n", + "2 16.0 16.0\n", + "3 20.0 20.0\n", + "4 24.0 24.0\n", + "5 28.0 28.0\n", + "6 32.0 32.0\n", + "... ... ...\n", + "1763487 4136.0 4136.0\n", + "2097152 4136.0 4136.0\n", + "2493948 4136.0 4136.0\n", + "2965820 4136.0 4136.0\n", + "3526975 4136.0 4136.0\n", + "\n", + "[81 rows x 2 columns]\n", + "#################### PARTIAL RESULTS FOR 4 TRIALS: DATASKETCHES ####################\n", + " 0 1 2 3\n", + "2 16.0 16.0 16.0 16.0\n", + "3 20.0 20.0 20.0 20.0\n", + "4 24.0 24.0 24.0 24.0\n", + "5 28.0 28.0 28.0 28.0\n", + "6 32.0 32.0 32.0 32.0\n", + "... ... ... ... ...\n", + "1763487 4136.0 4136.0 4136.0 4136.0\n", + "2097152 4136.0 4136.0 4136.0 4136.0\n", + "2493948 4136.0 4136.0 4136.0 4136.0\n", + "2965820 4136.0 4136.0 4136.0 4136.0\n", + "3526975 4136.0 4136.0 4136.0 4136.0\n", + "\n", + "[81 rows x 4 columns]\n", + "#################### PARTIAL RESULTS FOR 8 TRIALS: DATASKETCHES ####################\n", + " 0 1 2 3 4 5 6 7\n", + "2 16.0 16.0 16.0 16.0 16.0 16.0 16.0 16.0\n", + "3 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0\n", + "4 24.0 24.0 24.0 24.0 24.0 24.0 24.0 24.0\n", + "5 28.0 28.0 28.0 28.0 28.0 28.0 28.0 28.0\n", + "6 32.0 32.0 32.0 32.0 32.0 32.0 32.0 32.0\n", + "... ... ... ... ... ... ... ... ...\n", + "1763487 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0\n", + "2097152 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0\n", + "2493948 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0\n", + "2965820 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0\n", + "3526975 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0\n", + "\n", + "[81 rows x 8 columns]\n", + "CPU times: user 2min 9s, sys: 229 ms, total: 2min 9s\n", + "Wall time: 2min 9s\n" + ] + } + ], + "source": [ + "%%time\n", + "space_experiment.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8afd8986-9641-4cc9-ac4d-5ead4c359367", + "metadata": {}, + "outputs": [], + "source": [ + "asf8 = pd.read_csv(f\"hll_space_profile_/DataSketches_hll{space_experiment.file_extension}trials_{1<<LG_TRIALS}_8_bit.csv\", index_col=0)\n", + "asf6 = pd.read_csv(f\"hll_space_profile_/DataSketches_hll{space_experiment.file_extension}trials_{1<<LG_TRIALS}_6_bit.csv\", index_col=0)\n", + "asf4 = pd.read_csv(f\"hll_space_profile_/DataSketches_hll{space_experiment.file_extension}trials_{1<<LG_TRIALS}_4_bit.csv\", index_col=0)\n", + "dsk = pd.read_csv(f\"hll_space_profile_/datasketch_hll{space_experiment.file_extension}trials_{1<<LG_TRIALS}.csv\", index_col=0)" + ] + }, + { + "cell_type": "markdown", + "id": "85c00f74-7e5c-4aa5-81ef-6c39d61a49e1", + "metadata": {}, + "source": [ + "Next, we will plot the median number of bytes required to write the sketch to memory for each method. There is no variation for the sizes of ASF8 and ASF6; similarly, for ASF4, although there is slight variation in the size of the sketches in estimation mode. The `datasketch` implementation has no variation." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "702c160f-f5db-4c81-96ba-03935a48c9da", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "( 0 1 2 3 4 5 6 7\n", + " n \n", + " 2 16.0 16.0 16.0 16.0 16.0 16.0 16.0 16.0\n", + " 3 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0\n", + " 4 24.0 24.0 24.0 24.0 24.0 24.0 24.0 24.0\n", + " 5 28.0 28.0 28.0 28.0 28.0 28.0 28.0 28.0\n", + " 6 32.0 32.0 32.0 32.0 32.0 32.0 32.0 32.0,\n", + " 0 1 2 3 4 5 6 7\n", + " n \n", + " 1763487 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0\n", + " 2097152 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0\n", + " 2493948 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0\n", + " 2965820 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0\n", + " 3526975 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0 4136.0)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "asf8.head(), asf8.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4764f965-4e40-4ff9-a99f-001f8fe7a7a9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "( 0 1 2 3 4 5 6 7\n", + " n \n", + " 2 16.0 16.0 16.0 16.0 16.0 16.0 16.0 16.0\n", + " 3 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0\n", + " 4 24.0 24.0 24.0 24.0 24.0 24.0 24.0 24.0\n", + " 5 28.0 28.0 28.0 28.0 28.0 28.0 28.0 28.0\n", + " 6 32.0 32.0 32.0 32.0 32.0 32.0 32.0 32.0,\n", + " 0 1 2 3 4 5 6 7\n", + " n \n", + " 1763487 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0\n", + " 2097152 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0\n", + " 2493948 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0\n", + " 2965820 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0\n", + " 3526975 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0 3113.0)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "asf6.head(), asf6.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d8513334-4c91-450d-ad97-12418bc8c5d6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "( 0 1 2 3 4 5 6 7\n", + " n \n", + " 2 16.0 16.0 16.0 16.0 16.0 16.0 16.0 16.0\n", + " 3 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0\n", + " 4 24.0 24.0 24.0 24.0 24.0 24.0 24.0 24.0\n", + " 5 28.0 28.0 28.0 28.0 28.0 28.0 28.0 28.0\n", + " 6 32.0 32.0 32.0 32.0 32.0 32.0 32.0 32.0,\n", + " 0 1 2 3 4 5 6 7\n", + " n \n", + " 1763487 2092.0 2088.0 2096.0 2096.0 2092.0 2100.0 2092.0 2096.0\n", + " 2097152 2092.0 2092.0 2104.0 2096.0 2096.0 2100.0 2092.0 2088.0\n", + " 2493948 2092.0 2092.0 2104.0 2088.0 2100.0 2104.0 2096.0 2088.0\n", + " 2965820 2092.0 2104.0 2088.0 2088.0 2092.0 2100.0 2096.0 2088.0\n", + " 3526975 2092.0 2104.0 2088.0 2088.0 2092.0 2100.0 2096.0 2088.0)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "asf4.head(), asf4.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "a0b4bf36-8fec-4007-8a67-dda92ba49234", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "( 0 1 2 3 4 5 6 7\n", + " n \n", + " 2 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0\n", + " 3 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0\n", + " 4 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0\n", + " 5 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0\n", + " 6 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0,\n", + " 0 1 2 3 4 5 6 7\n", + " n \n", + " 1763487 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0\n", + " 2097152 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0\n", + " 2493948 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0\n", + " 2965820 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0\n", + " 3526975 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0 4097.0)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dsk.head(), dsk.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "0bd8f65e-ff85-4857-a79a-87687128adff", + "metadata": {}, + "outputs": [], + "source": [ + "# Plotting parameters\n", + "method_plot_params = {\n", + " \"asf8\" : {\"color\": \"C0\", \"marker\": '.'},\n", + " \"asf6\" : {\"color\": \"C2\", \"marker\": '+'},\n", + " \"asf4\" : {\"color\": \"C3\", \"marker\": 'd'},\n", + " \"datasketch\" : {\"color\": \"C1\", \"marker\": \"^\"}\n", + "}\n", + "asf8_color = method_plot_params[\"asf8\"][\"color\"]\n", + "asf6_color = method_plot_params[\"asf6\"][\"color\"]\n", + "asf4_color = method_plot_params[\"asf4\"][\"color\"]\n", + "ds_color = method_plot_params[\"datasketch\"][\"color\"]\n", + "q90_ls = \"--\"\n", + "\n", + "params = {'legend.fontsize': 'x-large',\n", + " 'axes.labelsize': 'x-large',\n", + " 'axes.titlesize':'x-large',\n", + " 'xtick.labelsize':'x-large',\n", + " 'ytick.labelsize':'x-large',\n", + " \"lines.linewidth\": 2.5}\n", + "plt.rcParams.update(params)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "cb2980aa-c47c-40c1-a69f-ff8271cbcb15", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 0, 'Input cardinality $n$')" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+UAAAHHCAYAAADZOPmeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/SrBM8AAAACXBIWXMAAA9hAAAPYQGoP6dpAACCNUlEQVR4nO3dd3xTdfv/8XeSpovSxSx7D9kb2QiyVFBBURFBcTAERL1/rlspKvDV763cgHor8hVwcauALJE9BAWUpYIgWzZIoQvaNE3O74/S0NC0tKFt2vJ6Ph59QM75nJPrtFfSXP2MYzIMwxAAAAAAAChwZl8HAAAAAADAzYqiHAAAAAAAH6EoBwAAAADARyjKAQAAAADwEYpyAAAAAAB8hKIcAAAAAAAfoSgHAAAAAMBHKMoBAAAAAPARP18HAKDostvtcjgcvg4DAADcAIvFIqvV6uswgJsWRTmAXIuPj9f58+dls9l8HQoAAMgDAQEBKl26tEJDQ30dCnDToSgHkCvx8fE6efKkQkJCVLp0aVmtVplMJl+HBQAAvGAYhux2u+Li4nTy5ElJojAHCpjJMAzD10EAKDoOHz4sq9WqSpUqUYwDAFBMGIahEydOyG63q0aNGr4OB7ipsNAbgByz2+2y2WwKCwujIAcAoBgxmUwKCwuTzWaT3W73dTjATYWiHECOpS/qxmIwAAAUP+m/31nEFShYFOUAco1ecgAAih9+vwO+QVEOAAAAAICPUJQDAAAAAOAjFOUAgGLj6NGjMplMGjp0qNv2oUOHymQy6ejRoz6JC4C79evXy2QyKTo62m17ly5dGEIN4KZDUQ4AN2jixIkymUwymUz6888/s2176tQpjRs3TrfccouCg4MVFBSkKlWqqHPnznrllVd06NAht/bpxWRWX126dHFr73A49MUXX6hjx44qX768goODVadOHT366KPas2dPXl86UKQUptfqtd58801X29WrV9/opQIAihA/XwcAAEWZYRiaOXOmTCaTDMPQxx9/rH/9618e2+7evVudO3fWhQsX1KhRIw0ZMkSRkZE6d+6cfv75Z02aNEnVq1dXzZo1Mx3br18/NW3aNNP2atWquT1+6KGH9PXXX6tSpUq69957VbJkSf3++++aM2eOvvzyS33//fe67bbb8uLSi5TJkyfrxRdfVMWKFX0dCnyksL1WM9qxY4def/11hYSEKDEx0dtLLBY+/fRTXb582ddhAECBoigHgBuwcuVKHT16VEOHDtXy5cs1Z84cTZo0Sf7+/pnaPvPMM7pw4YKio6M1fvz4TPsPHz6slJQUj89z9913ZxqSfa1ffvlFX3/9tRo0aKCff/5ZwcHBrn2zZs3SY489pjfffPOmLMqjoqIUFRXl6zDgQ4XptZpRcnKyBg8erFatWqlmzZr67LPPcnxscVSlShVfhwAABY7h6wBwAz7++GNJ0hNPPKFBgwbp/Pnz+vbbbz22/emnnyRJY8eO9bi/Ro0aqlevntexHD58WJLUrVs3t4JcSuu9k6S///7b6/Onmz17tkwmk2bPnq1Vq1apY8eOCgkJUZkyZfToo48qNjZWkrRz507deeedioiIUEhIiPr27ZvlnO4LFy7opZdeUv369RUUFKSwsDB169ZNK1eu9Ng+ISFBzz77rCpVqqTAwEDVq1dP7777rpxOp8f2Wc0pnz17tvr3768aNWooKChIoaGhat++vT7//HOP50mf75qamqpJkyapdu3aCggIUOXKlfXCCy9kWajB9wrTazWjl156SUeOHNHs2bNlNuftx7Lo6GiZTCatX79ec+fOVYsWLRQcHKwKFSro2Weflc1mkyStXbtWXbp0UWhoqCIiIjR48GDFxMR4POeJEyf09NNPq0aNGgoICFCpUqXUt29f/fLLLx7bnz17VsOGDVO5cuUUFBSkpk2bas6cOVnG7GlOeUpKit577z316dNHVatWVUBAgCIjI9W9e3d9//33Hs9TrVo1VatWTZcuXdI//vEPValSRQEBAapVq5beeustGYaRk28hABQIinIA8NLZs2e1ePFi1alTR+3atXP1js2YMcNj+1KlSkmS9u/fny/xNGjQQFLaB+ykpCS3fUuXLpUkde/ePdNx6fNYc2vx4sW64447VKZMGQ0fPly1a9fW7Nmzdc8992jLli3q0KGDUlNTNWzYMLVv315LlizRnXfemalw/uuvv9SiRQv9z//8j+tcAwcO1N69e9WrVy9XMZXOZrOpW7dumjJlikqXLq2xY8eqc+fOeuONNzRu3LhcXcOIESP0119/qVOnTnrmmWf0wAMP6K+//tLgwYP16quvZnncQw89pOnTp6tjx44aMWKEgoKC9Pbbb+upp57K1fOjYBS212q6tWvXaurUqZo8ebJq166dbdv0RQyzGwaflenTp2vYsGGqW7euRowYoVKlSmnKlCl66qmn9O2336p3796KjIzUk08+qfr16+vzzz/Xww8/nOk8O3bsUNOmTfXBBx+obt26Gj16tO666y798MMP6tChg5YtW+bW/vz582rXrp0++eQT1alTR88884yaNm2q4cOHa8qUKTmO/8KFCxo7dqwSEhJ0++2369lnn1Xfvn21c+dO9enTRzNnzvR4nN1uV8+ePTV//nz17t1bjz/+uJKSkvTiiy/q9ddfz903EQDyEcPXAeSJZLtDMZeKTi9hqRL+CrRabugcs2bNkt1ud33Ab9iwoVq0aKF169bp4MGDqlWrllv7gQMH6p133lHfvn01YsQIde3aVU2bNlVoaOh1n2vhwoUee5mfeeYZhYeHu55/3LhxmjJliurVq6c777xTJUuW1J49e7R8+XI98MADevPNN2/omjNavHix1qxZo86dO0uSnE6nevbsqdWrV6tPnz6aMWOGBg0a5Go/bNgwffLJJ1qyZImr516ShgwZor/++ktz587VAw884NoeGxurLl26aMyYMerbt6/KlSsnSXrnnXf0yy+/6N5779U333zj6l188cUX1aJFi1xdw+7duzPNC05JSVHv3r31P//zPxo+fLjHeeiHDh3Snj17FBkZKSltAbEmTZro008/1eTJk1W+fPlcxVGQnDabHFn0ghZGllKlZA4IuKFzFLbXqiTFxcVp6NCh6tixo8aMGXMjl3ddq1ev1vbt21W/fn1JaX/Yat68uT777DMtWbJEK1euzPQ6Xr58uXbt2uWaH5+amqr7779fiYmJWrdunau9lLYoXqtWrTRs2DAdPXpUAVd+Xi+//LIOHz6sZ555xq0If/rpp3XrrbfmOP6IiAj99ddfqlSpktv2uLg4tW/fXv/v//0/DRo0SEFBQW77T506pSZNmmjVqlWufePHj1edOnU0ZcoUvfzyy7JarTmOAwDyC0U5gDwRcylFX/9y3Ndh5Nj9rSqrYnjQ9RtmIX3RKLPZrEceecS1fejQodq+fbs+/vhjvfXWW27HTJw4UfHx8Zo1a5aio6NdQ0vr1KmjXr16acyYMapRo4bH51u0aJEWLVqUafvQoUPdPui/++67qlu3rsaNG6cPPvjAtb1FixYaMmSISpQokekce/fuze3lS5IefPBBtw/mZrNZgwcP1urVq9WwYUO3glySHnnkEX3yySfatWuXqyj/9ddftWHDBg0YMMCtIJek8PBwTZgwQXfffbfmz5+vkSNHSkorsMxms95++2234b7Vq1fXmDFjNGHChBxfg6eFuvz9/TVq1CitXbtWa9ascfv5pnvrrbdcBbkklShRQoMGDdLrr7+ubdu26c4778xxDAXNEROj2PkLfB1GjoX3v1fmChW8Pr6wvlZHjx6tCxcuuG4Ndj0VK1bU3r17vSoix4wZ4yrIJSkgIEADBw7U+PHjdccdd2R6HT/88MNavXq1fv31V1dR/t133+nQoUN6/vnn3dpLUoUKFfT//t//0zPPPKM1a9aoT58+stvt+uKLL1SyZMlMtz1r2bKlBg0alO0w9owCAgIyFeSSFBYWpscee0zPPfecfvnlF3Xq1ClTm2nTprkV62XLllW/fv306aef6s8//1TDhg1zFAMA5CeGrwOAF9auXatDhw7p9ttvd+tJfeihh+Tv76/Zs2fLbre7HRMQEKAZM2boxIkTmj17tkaMGKHWrVvr4MGDmjp1qho2bOgaZn6tWbNmyTCMTF8Zh7IahqExY8Zo1KhReu2113T8+HElJCRo48aNMplM6t27t95///1M565Xr55X82NbtmyZaVuFK8WTpx7r9O/TiRMnXNs2b94sKa3HK734yfiVPqc8/Q8HCQkJOnjwoCpWrOixoL7ebaeudezYMY0aNUr16tVTcHCwayh///79JUknT570eJyna69cubIk6eLFi7mKAfmrML5W58+fr88++0xvv/12lsX9taxWq+rVq+cx768nL1+rf/31l8fX6s8//yzp6mt13759unz5spo2baqwsLBMz5Hb1+qePXs0dOhQ1/oP6a/V5557TpLn12pYWFimURASr1UAhQ895QDghfS5qNeushwZGam77rpL8+fP16JFizRgwIBMx5YrV05DhgzRkCFDJKXNl3zhhRc0c+ZMPfbYYzpx4oTHFaGvZ86cOZo+fbrGjRunF1980bW9Q4cOWrJkiWrUqKEXX3xRQ4YMUUhISK7Pfy1PH7T9/Pyuuy9jAZS+mNSqVau0atWqLJ8r/TZRcXFxkuQayn6t3AwbP3z4sFq3bq2LFy+qY8eO6tGjh8LCwmSxWHT06FHNmTPHtRDWtTL2eKZLvz6Hw5HjGJD/Cttr9cKFCxo+fLi6deumESNGeHdRuZSXr9Vvvvkm2+fKj9fqli1bdNtttyk1NVXdunVT3759FRoaKrPZrF27dmnRokUeX6ueXqcSr1UAhQ9FOYA8UaqEv+5vVdnXYeRYqRK5L3rT/f3331q4cKGktCHcDz74oMd2M2bM8PhB/1qRkZH66KOPtHLlSh07dky7d+9W8+bNcx1Xes9d165dM+0rX7686tWrp507d+rPP//M9dzr/JJeEEydOjVH82rT2589e9bj/jNnzuT4ud99913FxMRo1qxZmQq2uXPn5nhobVFjKVVK4f3v9XUYOWa5suiaNwrja/XYsWM6f/681qxZk+Vq67fffrskacqUKXrmmWdydf78kv7aW7Rokfr27Zvj9nnxWn3zzTeVlJSkdevWZephnzx5ssfpAgBQlFCUA8gTgVbLDc3RLkrmzJmjlJQUtWjRwjXf8lqLFy/W6tWrdeTIEVWvXv265zSbza753t7eqie9pyir256lb/emFz6/tG3bVpK0cePGHBXlJUuWVK1atXT48GEdOnQo01De9evX5/i5Dx48KEmuoeoZbdiwIcfnKWrMAQE3NEe7KCmMr9VSpUpp2LBhHvf98MMPOnDggHr37q0KFSoUqvnOGV+rOSnK06eE7Nq1S3FxcZl65HP7Wo2MjPQ45L04v1YB3DyYUw4AuZR+i64PPvhAM2fO9Pj11FNPuRaYSjdhwoQs79M9b9487du3TxEREV5/EO/YsaOktB7g9KGj6T788EOdOHFC5cuX1y233OK2b9++fdq3b59Xz3mjWrZsqY4dO2rBggX65JNPPLb5/fffde7cOdfjRx99VE6nUy+88ILb7dWOHDmiadOm5fi50+f4XlscrFixIstbLKFoKYyv1cqVK2cZS7t27SRJzz77rGbOnOl2C0O73a59+/bp0KFDuX7OvNCvXz/VrFlT77//fqZbn6XbvHmzLl++LCltDvygQYOUkJCQaaG3bdu26Ysvvsjxc1erVk0XLlzQb7/95rb9//7v/7RixYrcXQgAFEL0lANALqxfv1779+9Xo0aN1Lp16yzbDRs2TBMnTtSsWbM0YcIE+fn5acqUKYqOjlazZs3UsmVLlSlTRnFxcdqxY4c2b94sPz8/ffjhh67bCeXWyJEj9cUXX+i3335TnTp11LdvX4WHh2vHjh1au3atLBaL3n//fVks7reCS1+V2dse+hv15Zdf6rbbbtOwYcM0bdo0tWnTRuHh4Tpx4oR+++037d69W5s3b1bZsmUlSc8995wWLlyo+fPnq3nz5urZs6diY2P19ddfq1OnTlq8eHGOnnfkyJGaNWuW7rvvPg0YMEAVKlTQ7t27tXz5ct1///366quv8vOykc8K82vVGydPnlT9+vVVtWrVLP9gkJ+sVqsWLFignj176o477lC7du3UtGlTBQcH6/jx4/rll190+PBhnT59WsHBwZKkSZMmac2aNfr3v/+tbdu2qUOHDjp9+rS++uor9enTJ8ev1WeeeUYrVqxQhw4ddP/99yssLEzbtm3Tpk2bNGDAAM2bNy8/Lx0A8h1FOQDkQnrP2+OPP55tu2rVqql79+5atWqVlixZonvuuUdLly7V999/rw0bNmj58uU6e/as/Pz8VKlSJT3++OMaM2aMGjVq5HVsISEh+vHHH/Xuu+9qwYIF+vLLL5WSkqIyZcrovvvu0/PPP59tceIrlSpV0vbt2zV9+nTNnz9fX3zxhRwOh6tXf/To0W7fl4CAAK1evVrR0dH66quvNHXqVFWrVk3//Oc/dc899+T4g37jxo21bt06/fOf/9R3332n1NRUNWnSRAsWLFB4eDhFeRFXmF+rRVXjxo3166+/6t1339XSpUtdtyeMiopSs2bNNGHCBJUuXdrVvnTp0vrxxx/18ssva8mSJdq2bZvq1q2r//znP6pWrVqOX6u9evXSkiVL9Oabb+qrr76SxWJR69attW7dOh0+fJiiHECRZzJ81TUCoMhJTk52zbsMDAz0dTgAACAP8Xse8A3mlAMAAAAA4CMU5QAAAAAA+AhFOQAAAAAAPkJRDgAAAACAj1CUAwAAAADgIxTlAAAAAAD4CEU5AAAAAAA+QlEOAAAAAICPUJQDAAAAAOAjFOUAAAAAAPgIRTkAAAAAAD5CUQ4AAAAAgI9QlAMAAAAA4CMU5QCAYuPo0aMymUwaOnSo2/ahQ4fKZDLp6NGjPokLhVO1atVUrVo1X4fhtdmzZ8tkMmn27Nm+DiVb69evl8lkUnR0tK9D8RnemwBkh6IcAG7QxIkTZTKZZDKZ9Oeff2bb9tSpUxo3bpxuueUWBQcHKygoSFWqVFHnzp31yiuv6NChQ27t0z+wZfXVpUsXj88zb9489ezZU6VLl1ZgYKCqVKmifv36acuWLXl12QCuyO61WFRlVUQCAPKen68DAICizDAMzZw5UyaTSYZh6OOPP9a//vUvj213796tzp0768KFC2rUqJGGDBmiyMhInTt3Tj///LMmTZqk6tWrq2bNmpmO7devn5o2bZpp+7W9fKmpqRoyZIi+/PJL1a5dWwMHDlRYWJjOnDmjzZs3a/v27Wrbtm1eXHqRMnnyZL344ouqWLGir0MBABfemwBIFOUAcENWrlypo0ePaujQoVq+fLnmzJmjSZMmyd/fP1PbZ555RhcuXFB0dLTGjx+faf/hw4eVkpLi8XnuvvvuHPVYjR8/Xl9++aVeeeUVvf766zKb3QdE2e32nF1YMRMVFaWoqChfhwEAbnhvAiAxfB0AbsjHH38sSXriiSc0aNAgnT9/Xt9++63Htj/99JMkaezYsR7316hRQ/Xq1fM6ljNnzuhf//qX2rZtqzfffDNTQS5JVqvV6/OnyziPddWqVerYsaNCQkJUpkwZPfroo4qNjZUk7dy5U3feeaciIiIUEhKivn37Zjlv8sKFC3rppZdUv359BQUFKSwsTN26ddPKlSs9tk9ISNCzzz6rSpUqKTAwUPXq1dO7774rp9PpsX1W8zZnz56t/v37q0aNGgoKClJoaKjat2+vzz//3ON5unTpIpPJpNTUVE2aNEm1a9dWQECAKleurBdeeCHLP6rAdwzD0HvvvacGDRooMDBQFStW1NNPP624uDiP7ePi4vS///u/uu2221SpUiX5+/urTJky6tu3rzZv3uzWNv21IEkbNmxwm1qScf50bvPs8OHDevLJJ1WrVi0FBQUpMjJSjRo10vDhwxUTE3Pda7548aI6deoks9msyZMnu7anpqbqgw8+UNu2bRUaGqrg4GA1a9ZM7733nttrJzo6WtWrV5ckzZkzx+26rp2/vnLlSt11110qW7as67XQr18/rV692mNsu3bt0h133KHw8HAFBwerc+fOrvfGG8V7E+9NQFFFTzkAeOns2bNavHix6tSpo3bt2ik0NFTvvPOOZsyYoYEDB2ZqX6pUKZ04cUL79+9X69at8zyeefPmKSUlRQ888ICSkpL03Xff6eDBgypZsqQ6dOigJk2aeDwuvagwDCNXz7d48WItXbpUd955p4YPH66ffvpJs2fP1tGjRzV58mR169ZNHTt21LBhw/T7779ryZIlOnz4sH777Te3Pxj89ddf6tKli44e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KEjcpxwAcin93uQffPCBZs6c6fHrqaeeci0Gl27ChAlZ3gt33rx52rdvnyIiItSwYUOv4urYsaOaNm2qTZs2ZbpX+scff6y9e/eqVq1ameYc7tu3T/v27fPqOW9Uy5Yt1bFjRy1YsECffPKJxza///67axE7SXr00UfldDr1wgsvuN3798iRI5o2bVqOn7tatWqSMn9YXrFihdvPDUVXei/jxIkTdeHCBdf25ORkvfTSSx6PqVatmg4cOKBTp065thmGoejoaP3xxx8ejylVqpSOHz+e5fmknOfZ9u3bPd5DPb0HNjg42OPzWK1WzZ07Vw8//LDmzp2rgQMHuoZj+/n5afTo0Tp9+rTGjBnjcbTM6dOn3a4vIiJCJpNJx44d8/h8o0ePliQ999xzHuc3ZzXnOTd4b1rvtp33JqD4oqccAHJh/fr12r9/vxo1apTtvcaHDRumiRMnatasWZowYYL8/Pw0ZcoURUdHq1mzZmrZsqXKlCmjuLg47dixQ5s3b5afn58+/PBDBQQEeBWbyWTSnDlz1LlzZ/Xv31933XWX6tSpoz179uj7779XiRIlNGfOnEyLW6X3CHrbQ3+jvvzyS912220aNmyYpk2bpjZt2ig8PFwnTpzQb7/9pt27d2vz5s0qW7aspLQiYOHChZo/f76aN2+unj17KjY2Vl9//bU6deqkxYsX5+h5R44cqVmzZum+++7TgAEDVKFCBe3evVvLly/X/fffr6+++io/LxsFoH379ho9erSmT5+uhg0basCAAa77lEdERCgqKvPonnHjxmn48OFq1qyZ+vfvL6vVqh9//FF//PGH7rrrLi1ZsiTTMd26ddN///tf3XXXXWrevLmsVqs6deqkTp065TrPPvvsM3300Ufq0KGDatasqYiICB06dEhLlixRQECAnnnmmSyv12KxaM6cOQoMDNTMmTN17733at68eQoICNCrr76qX3/9VR9++KGWLFmi2267TRUrVtS5c+d04MAB/fjjj5o4caJuueUWSVJISIjatGmjjRs3atCgQapTp44sFov69u2rxo0bq0ePHvrnP/+pN998U/Xr13fdp/zs2bPatGmT2rZtm+lOD7nFexPvTcDNgqIcAHIhvZf88ccfz7ZdtWrV1L17d61atUpLlizRPffco6VLl+r777/Xhg0btHz5cp09e1Z+fn6qVKmSHn/8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585l2+bIkSMqXbp0rs/dvXt3HTx4UJs2bdLOnTsVGxurEiVKqE6dOho8eLDGjBmjyMhIb8IGABSgFIdTGe/vkbEot5qtslpYxAYAAMCrorxVq1ZaunSpEhISVLJkyUz7T58+rWXLlunOO+/M9bkbNmyo9957z5uwAACFiNsib5JSlez6P0PXAQAA0ng1p3zs2LGKiYlRnz59tHfvXrd9e/fu1X333afk5GSNGTMmT4IEABQ91xblDuNqT3mwlaHrAAAAkpc95T179tT48eM1YcIENWzY0HUPttKlS+vixYsyDENvvfWW2rVrl6fBAgCKjoyLvElSqmGTTGn/p6ccAAAgjVc95ZI0fvx4rVmzRn379lVERIQsFotMJpP69Omj1atX6x//+EdexgkAKGIyDV83rg5fZ5E3AACANF71lKfr2rWrunbtmlexAACKkYxFuWEYsjuTFWBJ+1swPeUAAABpvOop//TTT/Xbb79l2+b333/Xp59+6lVQAICiz5ahKHcYKTKZru5jTjkAAEAar4ryoUOHauHChdm2Wbx4sR599FFvTg8AKAYyFuV2I1kW89WqnOHrAAAAabyeU349DodDpozdIgCAm0rG4etO2ZShJmf4OgAAwBX5VpTv379fERER+XV6AEAh57b6utnm9odahq8DAACkyfFCb4899pjb44ULF+ro0aOZ2jkcDh07dkwbN27UHXfcccMBAgCKJrfV180pbvvoKQcAAEiT46J89uzZrv+bTCbt2rVLu3bt8tjWZDKpTZs2mjJlyo3GBwAootxWXzddLcr9zH6ymq2+CAkAAKDQyXFRfuTIEUlpt7WpUaOGnnnmGY0dOzZTO4vFooiICJUoUSLvogQAFDkpDsfVByab679BfkGsOQIAAHBFjovyqlWruv4/fvx4de3a1W0bAAAZuS30lqEoZ+V1AACAq3JclGd01113qXnz5nkdCwCgGLl29fV0zCcHAAC4yqvV11u2bKk2bdrok08+0eXLl/M6JgBAMZDxPuUOI0NPOSuvAwAAuHhVlN9xxx3asWOHnnjiCVWoUEGjR4/W77//ntexAQCKsPSi3DAMOegpBwAA8MironzJkiU6cuSIXn31VYWGhur9999X06ZN1b59e3366aey2WzXPwkAoNgyDEP2K/cpdyhFJtPVXnPmlAMAAFzlVVEuSZUqVVJ0dLSO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KgAAgJuPV0V5kyZNdPDgwWzbHDx4UI0bN/YqKABA/rl0OVFL//M/UnKSa1twg2bqecd9PowKAADg5uTVnPKXX35Z99xzj77//nv17t070/7vvvtO3377rRYuXHij8eWazWaTzWZzPY6Pj5ckV6+9t733KL7IDWSlOOaGzWbTV9Nfl+PsKdc2U+myuuvhUcXqOgtCccwP5A1yA1khN5Ad8qN4yc3P0auiPCYmRr1799add96pbt26qVOnTipXrpzOnj2rDRs2aO3atbrrrrt0/vx5ffrpp27HPvLII948ZY5NnjxZEyZMyLR93bp1Cg4O1qpVq/L1+VF0kRvISnHJDafDqQM/r5D/meOubQ6rv0rXaaX169f7LrAirrjkB/IeuYGskBvIDvlRPFy+fDnHbU2GkWFCYQ6ZzWaZTCZd79CMc8INw5DJZJLD4cjVc61fv15du3bN8S3RPPWUV65cWadPn9bWrVt1++23y2q15ioGFG92u12rVq0iN5BJccoNh8OhBbOnKeHXn69utFrV+rFxatigme8CK8KKU34gb5EbyAq5geyQH8VLfHy8Spcurbi4OIWGhmbb1que8lmzZnkVWEEICAhQQEBApu3piW21WklyeERuICvFITeWfvOJEn/75eofS81mNR00Us2atvZtYMVAccgP5A9yA1khN5Ad8qN4yM3P0KuifMiQId4cBgDwge8Xz9XfmzLcotJkUr3+Q9S6TUffBQUAAABJXq6+DgAoGtavWarjK75121alV3916trHRxEBAAAgI696yj1ZvHix1q5dK8Mw1KlTJ/Xv3z+vTg0A8MLPW9Zr/7efud2LvGzHHup110AfRgUAAICMclyUL1myRP/7v/+rN954Q507d3bb9+ijj+rTTz91Lfz23nvv6e6779b8+fO9CmrhwoWu26mdOXNGkrR582YNHTpUklS6dGn961//8urcAHAz+O23X7Rr7gzJ6XRtC21+q+66/zEfRgUAAIBr5bgoX7x4sXbs2KE2bdq4bV+6dKnmzJmjEiVKaNy4cSpZsqRmzJihhQsXau7cuXrwwQdzHdSuXbs0Z84ct22HDx/W4cOHJUlVq1alKAeALBw8uE9bZk2TMtwfM6huI9336FhZLBYfRgYAAIBr5XhO+c8//6yOHTsqMDDQbfsnn3wik8mkWbNm6fXXX9c//vEPbdy4UYGB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AABX7NixQwkJCZm2p98arFmzZq5tERERkqTjx49nan/w4MFMverpQ7YdDkeu42rbtq0k6fvvv79u24MHD0qSa3h3Rhs2bMj1c6fPgfZ07KZNm7y6npzK62vJKKc/j5EjR0qSPvroI82YMUOS9NRTT+X4edKnPLRs2dLjPsMwPBbsAICbB0U5AABXxMXF6fXXX3fbtm3bNn3xxRcKCwvTPffc49per149hYaGatGiRTp37pxre1JSksaMGZPp3BERETKZTDp27Fiu4xoxYoT8/Pz0xhtv6I8//si0/8SJE67/p98bPf0PCelWrFihmTNn5vq503uoJ06cqAsXLri2Jycn66WXXsr1+XIjr68lo5z+PLp166Y6depozpw5+vrrr1W3bt1s719+rR07dsjf31+NGzfOtG/79u2SlGnxNwDAzYXh6wAAXNGpUyfNnDlTW7duVfv27V33KXc6nfroo49c9+2WJKvVqrFjx+qNN95Qs2bNdM899yg1NVWrVq1ShQoVVKFCBbdzh4SEqE2bNtq4caMGDRqkOnXquO457qlgy+iWW27RBx98oOHDh6tZs2bq16+fateurZiYGP3yyy8KDQ3VunXrJKX17M6aNUv33XefBgwYoAoVKmj37t1avny57r//fn311Ve5+p60b99eo0eP1vTp09WwYUMNGDDAdZ/yiIiIHN2n21t5fS0Z5fTnYTKZNHz4cD377LOS0uaZ55TNZtMff/yhxo0be7yXe3pRTk85ANzc6CkHAOCK6tWr66efflJERIQ+/PBDff3112revLmWLVumgQMHZmo/YcIETZ48WYGBgZoxY4aWLVum/v37a8WKFbJarZnaf/bZZ7rjjju0fPlyTZgwQa+++qprzvH1PPHEE9q0aZPuvPNOrV+/Xv/7v/+rxYsXq0yZMho1apSrXePGjbVu3Tq1a9dO3333nf7zn/8oPj5eCxYs0PDhw736vkydOlXTp09XWFiYPvroI82dO1c9e/bU6tWrPRabeSU/riWjnP48hg4dKrPZrMDAQA0ZMiTH59+9e7dSU1M9Dl2X0orykJAQ1alTx+trAAAUfSbDMAxfBwEAgC8dPXpU1atX15AhQzR79mxfh4NCZv369eratasefvhhffbZZ74OBwBQzNBTDgAAkI23335bkvT000/7OBIAQHHEnHIAAIBr/P7771q6dKm2b9+u77//XnfeeafatGnj67AAAMUQRTkAAMA1tm/frpdfflmhoaG677779MEHH/g6JABAMcWccgAAAAAAfIQ55QAAAAAA+AhFOQAAAAAAPkJRDgAAAACAj1CUAwAAAADgIxTlAAAAAAD4CEU5AAAAAAA+QlEOAAAAAICPUJQDAAAAAOAjFOUAAAAAAPgIRTkAAAAAAD5CUQ4AAAAAgI/8fzTRYeZtx1mYAAAAAElFTkSuQmCC", + "text/plain": [ + "<Figure size 1200x400 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize=(12,4))\n", + "\n", + "methods = [\"ASF8\", \"ASF6\", \"ASF4\", \"datasketch\"]\n", + "\n", + "for i, (method, colour, df) in enumerate(zip(methods, [asf8_color, asf6_color, asf4_color, ds_color], [asf8, asf6, asf4, dsk])):\n", + " xn = df.index \n", + " median = df.median(axis=1)\n", + " ax.plot(xn, median / 1024,\n", + " color=colour, label=method+\": median\", alpha=0.5)\n", + "\n", + "ax.set_xscale('log', base=10)\n", + "ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.25),\n", + " ncol=2, fancybox=True)\n", + "ax.grid()\n", + "ax.set_ylabel(\"Space (kilobytes)\")\n", + "ax.set_xlabel(r\"Input cardinality $n$\")\n", + "# ax.set_ylim(0.6E-6, 1.6E-6)" + ] + }, + { + "cell_type": "markdown", + "id": "46851818-f89e-4f02-9968-beb0b3594909", + "metadata": {}, + "source": [ + "Each of the ASF methods go through some resizing and growth stages until a maximum size is reached. At this point the sketches grow no further. As expected, the $4$ bit version is half the size of the $8$ bit version and the $6$ bit version is $3/4$ the size of the $8$ bit version. Once the $8$ bit ASF HLL enters its final stage, it is the same size as the datasketch HLL, but can be much smaller prior to this stage." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}