{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": "true" }, "source": [ "# Table of Contents\n", "

1  Short study of the Lempel-Ziv complexity
1.1  Short definition
1.2  Python implementation
1.3  Tests (1/2)
1.4  Cython implementation
1.5  Numba implementation
1.6  Tests (2/2)
1.7  Benchmarks
1.8  Complexity ?
1.9  Conclusion
1.10  (Experimental) Julia implementation
1.11  Ending notes
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Short study of the Lempel-Ziv complexity\n", "\n", "In this short [Jupyter notebook](https://www.Jupyter.org/) aims at defining and explaining the [Lempel-Ziv complexity](https://en.wikipedia.org/wiki/Lempel-Ziv_complexity).\n", "\n", "[I](http://perso.crans.org/besson/) will give examples, and benchmarks of different implementations.\n", "\n", "- **Reference:** Abraham Lempel and Jacob Ziv, *« On the Complexity of Finite Sequences »*, IEEE Trans. on Information Theory, January 1976, p. 75–81, vol. 22, n°1." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Short definition\n", "The Lempel-Ziv complexity is defined as the number of different substrings encountered as the stream is viewed from begining to the end.\n", "\n", "As an example:\n", "\n", "```python\n", ">>> s = '1001111011000010'\n", ">>> lempel_ziv_complexity(s) # 1 / 0 / 01 / 11 / 10 / 110 / 00 / 010\n", "8\n", "```\n", "\n", "Marking in the different substrings, this sequence $s$ has complexity $\\mathrm{Lempel}$-$\\mathrm{Ziv}(s) = 6$ because $s = 1001111011000010 = 1 / 0 / 01 / 11 / 10 / 110 / 00 / 010$.\n", "\n", "- See the page https://en.wikipedia.org/wiki/Lempel-Ziv_complexity for more details." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Other examples:\n", "\n", "```python\n", ">>> lempel_ziv_complexity('1010101010101010') # 1, 0, 10, 101, 01, 010, 1010\n", "7\n", ">>> lempel_ziv_complexity('1001111011000010000010') # 1, 0, 01, 11, 10, 110, 00, 010, 000\n", "9\n", ">>> lempel_ziv_complexity('100111101100001000001010') # 1, 0, 01, 11, 10, 110, 00, 010, 000, 0101\n", "10\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Python implementation" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [], "source": [ "def lempel_ziv_complexity(sequence):\n", " \"\"\"Lempel-Ziv complexity for a binary sequence, in simple Python code.\"\"\"\n", " sub_strings = set()\n", " n = len(sequence)\n", " ind = 0\n", " inc = 1\n", " # this while loop runs at most n times\n", " while True:\n", " if ind + inc > len(sequence):\n", " break\n", " # this can take some time, takes O(inc)\n", " sub_str = sequence[ind : ind + inc]\n", " # and this also, takes a O(log |size set|) in worst case\n", " # max value for inc = n / size set at the end\n", " # so worst case is that the set contains sub strings of the same size\n", " # and the worst loop takes a O(n / |S| * log(|S|))\n", " # ==> so if n/|S| is constant, it gives O(n log(n)) at the end\n", " # but if n/|S| = O(n) then it gives O(n^2)\n", " if sub_str in sub_strings:\n", " inc += 1\n", " else:\n", " sub_strings.add(sub_str)\n", " ind += inc\n", " inc = 1\n", " return len(sub_strings)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Tests (1/2)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = '1001111011000010'\n", "lempel_ziv_complexity(s) # 1 / 0 / 01 / 11 / 10 / 110 / 00 / 010" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6.47 µs ± 891 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n" ] } ], "source": [ "%timeit lempel_ziv_complexity(s)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lempel_ziv_complexity('1010101010101010') # 1, 0, 10, 101, 01, 010, 1010" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lempel_ziv_complexity('1001111011000010000010') # 1, 0, 01, 11, 10, 110, 00, 010, 000" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lempel_ziv_complexity('100111101100001000001010') # 1, 0, 01, 11, 10, 110, 00, 010, 000, 0101" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "8.82 µs ± 795 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n" ] } ], "source": [ "%timeit lempel_ziv_complexity('100111101100001000001010')" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "import random\n", "\n", "def random_string(size, alphabet=\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"):\n", " return \"\".join(random.choices(alphabet, k=size))\n", "\n", "def random_binary_sequence(size):\n", " return random_string(size, alphabet=\"01\")" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'JYLRJBDBFGTBMLFKNYQMJXIZJKKEVONGVKUCHNSSJCYROTATJDACWKCLWDEULMZWSQHJFFCQGMRCINHRIOLMEWWEPTOUUECJWAAN'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "'1110000010101011101000110010001100000011101110011010100101100110100010110110111000111000101100001010'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_string(100)\n", "random_binary_sequence(100)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "For random strings in A..Z...\n", " of sizes 10, Lempel-Ziv complexity runs in:\n", "7.64 µs ± 1.09 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n", " of sizes 100, Lempel-Ziv complexity runs in:\n", "49.6 µs ± 6.46 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", " of sizes 1000, Lempel-Ziv complexity runs in:\n", "591 µs ± 78.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", " of sizes 10000, Lempel-Ziv complexity runs in:\n", "5.2 ms ± 770 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", " of sizes 100000, Lempel-Ziv complexity runs in:\n", "52.2 ms ± 2.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", "\n", "For random binary sequences...\n", " of sizes 10, Lempel-Ziv complexity runs in:\n", "6.04 µs ± 208 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n", " of sizes 100, Lempel-Ziv complexity runs in:\n", "46.8 µs ± 3.22 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", " of sizes 1000, Lempel-Ziv complexity runs in:\n", "491 µs ± 57.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", " of sizes 10000, Lempel-Ziv complexity runs in:\n", "5.76 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", " of sizes 100000, Lempel-Ziv complexity runs in:\n", "65.3 ms ± 16.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" ] } ], "source": [ "for (r, name) in zip(\n", " [random_string, random_binary_sequence],\n", " [\"random strings in A..Z\", \"random binary sequences\"]\n", " ):\n", " print(\"\\nFor {}...\".format(name))\n", " for n in [10, 100, 1000, 10000, 100000]:\n", " print(\" of sizes {}, Lempel-Ziv complexity runs in:\".format(n))\n", " %timeit lempel_ziv_complexity(r(n))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can start to see that the time complexity of this function seems to grow linearly as the size grows." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Cython implementation\n", "As [this blog post](https://jakevdp.github.io/blog/2013/06/15/numba-vs-cython-take-2/) explains it, we can easily try to use [Cython](http://Cython.org/) in a notebook cell.\n", "\n", "> See [the Cython documentation](http://docs.cython.org/en/latest/src/quickstart/build.html#using-the-jupyter-notebook) for more information." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "%load_ext cython" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "%%cython\n", "import cython\n", "\n", "ctypedef unsigned int DTYPE_t\n", "\n", "@cython.boundscheck(False) # turn off bounds-checking for entire function, quicker but less safe\n", "def lempel_ziv_complexity_cython(str sequence not None):\n", " \"\"\"Lempel-Ziv complexity for a string, in simple Cython code (C extension).\"\"\"\n", " \n", " cdef set sub_strings = set()\n", " cdef str sub_str = \"\"\n", " cdef DTYPE_t n = len(sequence)\n", " cdef DTYPE_t ind = 0\n", " cdef DTYPE_t inc = 1\n", " while True:\n", " if ind + inc > len(sequence):\n", " break\n", " sub_str = sequence[ind : ind + inc]\n", " if sub_str in sub_strings:\n", " inc += 1\n", " else:\n", " sub_strings.add(sub_str)\n", " ind += inc\n", " inc = 1\n", " return len(sub_strings)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let try it!" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = '1001111011000010'\n", "lempel_ziv_complexity_cython(s) # 1 / 0 / 01 / 11 / 10 / 110 / 00 / 010" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4.97 µs ± 590 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n", "843 ns ± 38.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n" ] } ], "source": [ "%timeit lempel_ziv_complexity(s)\n", "%timeit lempel_ziv_complexity_cython(s)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lempel_ziv_complexity_cython('1010101010101010') # 1, 0, 10, 101, 01, 010, 1010" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lempel_ziv_complexity_cython('1001111011000010000010') # 1, 0, 01, 11, 10, 110, 00, 010, 000" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lempel_ziv_complexity_cython('100111101100001000001010') # 1, 0, 01, 11, 10, 110, 00, 010, 000, 0101" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now for a test of the speed?" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "For random strings in A..Z...\n", " of sizes 10, Lempel-Ziv complexity in Cython runs in:\n", "3.9 µs ± 193 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n", " of sizes 100, Lempel-Ziv complexity in Cython runs in:\n", "25.8 µs ± 1.03 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", " of sizes 1000, Lempel-Ziv complexity in Cython runs in:\n", "276 µs ± 50.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", " of sizes 10000, Lempel-Ziv complexity in Cython runs in:\n", "2.43 ms ± 111 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", " of sizes 100000, Lempel-Ziv complexity in Cython runs in:\n", "28 ms ± 1.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", "\n", "For random binary sequences...\n", " of sizes 10, Lempel-Ziv complexity in Cython runs in:\n", "4.06 µs ± 444 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n", " of sizes 100, Lempel-Ziv complexity in Cython runs in:\n", "29 µs ± 2.29 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", " of sizes 1000, Lempel-Ziv complexity in Cython runs in:\n", "270 µs ± 18 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", " of sizes 10000, Lempel-Ziv complexity in Cython runs in:\n", "2.74 ms ± 405 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", " of sizes 100000, Lempel-Ziv complexity in Cython runs in:\n", "30.9 ms ± 5.29 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" ] } ], "source": [ "for (r, name) in zip(\n", " [random_string, random_binary_sequence],\n", " [\"random strings in A..Z\", \"random binary sequences\"]\n", " ):\n", " print(\"\\nFor {}...\".format(name))\n", " for n in [10, 100, 1000, 10000, 100000]:\n", " print(\" of sizes {}, Lempel-Ziv complexity in Cython runs in:\".format(n))\n", " %timeit lempel_ziv_complexity_cython(r(n))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> $\\implies$ Yay! It seems faster indeed! but only x2 times faster..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Numba implementation\n", "As [this blog post](https://jakevdp.github.io/blog/2013/06/15/numba-vs-cython-take-2/) explains it, we can also try to use [Numba](http://Numba.PyData.org/) in a notebook cell." ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [], "source": [ "from numba import jit" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [], "source": [ "@jit\n", "def lempel_ziv_complexity_numba(sequence : str) -> int:\n", " \"\"\"Lempel-Ziv complexity for a sequence, in Python code using numba.jit() for automatic speedup (hopefully).\"\"\"\n", "\n", " sub_strings = set()\n", " n : int= len(sequence)\n", "\n", " ind : int = 0\n", " inc : int = 1\n", " while True:\n", " if ind + inc > len(sequence):\n", " break\n", " sub_str : str = sequence[ind : ind + inc]\n", " if sub_str in sub_strings:\n", " inc += 1\n", " else:\n", " sub_strings.add(sub_str)\n", " ind += inc\n", " inc = 1\n", " return len(sub_strings)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let try it!" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ ":1: NumbaWarning: \n", "Compilation is falling back to object mode WITH looplifting enabled because Function \"lempel_ziv_complexity_numba\" failed type inference due to: Internal error at .\n", "\n", "[1] During: resolving callee type: BoundFunction(set.add for set(undefined))\n", "[2] During: typing of call at (17)\n", "\n", "Enable logging at debug level for details.\n", "\n", "File \"\", line 17:\n", "def lempel_ziv_complexity_numba(sequence : str) -> int:\n", " \n", " else:\n", " sub_strings.add(sub_str)\n", " ^\n", "\n", " @jit\n", ":1: NumbaWarning: \n", "Compilation is falling back to object mode WITHOUT looplifting enabled because Function \"lempel_ziv_complexity_numba\" failed type inference due to: cannot determine Numba type of \n", "\n", "File \"\", line 9:\n", "def lempel_ziv_complexity_numba(sequence : str) -> int:\n", " \n", " ind : int = 0\n", " inc : int = 1\n", " ^\n", "\n", " @jit\n", "/usr/local/lib/python3.7/dist-packages/numba/object_mode_passes.py:178: NumbaWarning: Function \"lempel_ziv_complexity_numba\" was compiled in object mode without forceobj=True, but has lifted loops.\n", "\n", "File \"\", line 2:\n", "@jit\n", "def lempel_ziv_complexity_numba(sequence : str) -> int:\n", "^\n", "\n", " state.func_ir.loc))\n", "/usr/local/lib/python3.7/dist-packages/numba/object_mode_passes.py:187: NumbaDeprecationWarning: \n", "Fall-back from the nopython compilation path to the object mode compilation path has been detected, this is deprecated behaviour.\n", "\n", "For more information visit http://numba.pydata.org/numba-doc/latest/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit\n", "\n", "File \"\", line 2:\n", "@jit\n", "def lempel_ziv_complexity_numba(sequence : str) -> int:\n", "^\n", "\n", " warnings.warn(errors.NumbaDeprecationWarning(msg, state.func_ir.loc))\n", ":1: NumbaWarning: \n", "Compilation is falling back to object mode WITHOUT looplifting enabled because Function \"lempel_ziv_complexity_numba\" failed type inference due to: non-precise type pyobject\n", "[1] During: typing of argument at (9)\n", "\n", "File \"\", line 9:\n", "def lempel_ziv_complexity_numba(sequence : str) -> int:\n", " \n", " ind : int = 0\n", " inc : int = 1\n", " ^\n", "\n", " @jit\n", "/usr/local/lib/python3.7/dist-packages/numba/object_mode_passes.py:178: NumbaWarning: Function \"lempel_ziv_complexity_numba\" was compiled in object mode without forceobj=True.\n", "\n", "File \"\", line 9:\n", "def lempel_ziv_complexity_numba(sequence : str) -> int:\n", " \n", " ind : int = 0\n", " inc : int = 1\n", " ^\n", "\n", " state.func_ir.loc))\n", "/usr/local/lib/python3.7/dist-packages/numba/object_mode_passes.py:187: NumbaDeprecationWarning: \n", "Fall-back from the nopython compilation path to the object mode compilation path has been detected, this is deprecated behaviour.\n", "\n", "For more information visit http://numba.pydata.org/numba-doc/latest/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit\n", "\n", "File \"\", line 9:\n", "def lempel_ziv_complexity_numba(sequence : str) -> int:\n", " \n", " ind : int = 0\n", " inc : int = 1\n", " ^\n", "\n", " warnings.warn(errors.NumbaDeprecationWarning(msg, state.func_ir.loc))\n" ] }, { "data": { "text/plain": [ "8" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = '1001111011000010'\n", "lempel_ziv_complexity_numba(s) # 1 / 0 / 01 / 1110 / 1100 / 0010" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "43.7 µs ± 3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" ] } ], "source": [ "%timeit lempel_ziv_complexity_numba(s)" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lempel_ziv_complexity_numba('1010101010101010') # 1, 0, 10, 101, 01, 010, 1010" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lempel_ziv_complexity_numba('1001111011000010000010') # 1, 0, 01, 11, 10, 110, 00, 010, 000\n", " 9" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lempel_ziv_complexity_numba('100111101100001000001010') # 1, 0, 01, 11, 10, 110, 00, 010, 000, 0101" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "48.1 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" ] } ], "source": [ "%timeit lempel_ziv_complexity_numba('100111101100001000001010')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> $\\implies$ Well... It doesn't seem that much faster from the naive Python code.\n", "> We specified the signature when calling [`@numba.jit`](http://numba.pydata.org/numba-doc/latest/user/jit.html), and used the more appropriate data structure (string is probably the smaller, numpy array are probably faster).\n", "> But even these tricks didn't help that much.\n", "\n", "> I tested, and without specifying the signature, the fastest approach is using string, compared to using lists or numpy arrays.\n", "> Note that the [`@jit`](http://numba.pydata.org/numba-doc/latest/user/jit.html)-powered function is compiled at runtime when first being called, so the signature used for the *first* call is determining the signature used by the compile function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Tests (2/2)\n", "\n", "To test more robustly, let us generate some (uniformly) random binary sequences." ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [], "source": [ "from numpy.random import binomial\n", "\n", "def bernoulli(p, size=1):\n", " \"\"\"One or more samples from a Bernoulli of probability p.\"\"\"\n", " return binomial(1, p, size)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1])" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bernoulli(0.5, 20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's probably not optimal, but we can generate a string with:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'11110010000011111101'" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "''.join(str(i) for i in bernoulli(0.5, 20))" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [], "source": [ "def random_binary_sequence(n, p=0.5):\n", " \"\"\"Uniform random binary sequence of size n, with rate of 0/1 being p.\"\"\"\n", " return ''.join(str(i) for i in bernoulli(p, n))" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'10111010011001100110111110001111100001100111111010'" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "'00000001000000001000000000001000100100000000000000'" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "'00011000000000101010110000010011000100001000100001'" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "'00000111111111000101000100100001100000101100000110'" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "'11111100111011110010110111111101110011111011111111'" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "'11111011111110111111111111111111111111111111111110'" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_binary_sequence(50)\n", "random_binary_sequence(50, p=0.1)\n", "random_binary_sequence(50, p=0.25)\n", "random_binary_sequence(50, p=0.5)\n", "random_binary_sequence(50, p=0.75)\n", "random_binary_sequence(50, p=0.9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And so, this function can test to check that the three implementations (naive, Cython-powered, Numba-powered) always give the same result." ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [], "source": [ "def tests_3_functions(n, p=0.5, debug=True):\n", " s = random_binary_sequence(n, p=p)\n", " c1 = lempel_ziv_complexity(s)\n", " if debug:\n", " print(\"Sequence s = {} ==> complexity C = {}\".format(s, c1))\n", " c2 = lempel_ziv_complexity_cython(s)\n", " c3 = lempel_ziv_complexity_numba(s)\n", " assert c1 == c2 == c3, \"Error: the sequence {} gave different values of the Lempel-Ziv complexity from 3 functions ({}, {}, {})...\".format(s, c1, c2, c3)\n", " return c1" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sequence s = 11111 ==> complexity C = 2\n" ] }, { "data": { "text/plain": [ "2" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tests_3_functions(5)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sequence s = 11000110100011110101 ==> complexity C = 9\n" ] }, { "data": { "text/plain": [ "9" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tests_3_functions(20)" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sequence s = 01000001001110111101111110011100101001011001111001 ==> complexity C = 17\n" ] }, { "data": { "text/plain": [ "17" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tests_3_functions(50)" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sequence s = 10110110111101100010011010110001001001101100110000110110010100111001110100001110001111110111100011011011001010000001111011110000101101011110011010111000111111101110111110010100110011000111011101100101101101000000111001010100100010000010011011100111010101001100001110101100101000000111010110011001010000100001111110110010011111100100001000011010011001001010100001111011101101000001100001011101010101100101100011000101101010010101001010011011011010000010100101101000110100100000111001100100110011011101 ==> complexity C = 99\n" ] }, { "data": { "text/plain": [ "99" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tests_3_functions(500)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sequence s = 10100100011000100101101111111001010001101101010100011101100101101100101111100110010110001010010101010000011101110011101111110110010101001011010010101000000111001011001000000101001010111001110000100101001111100001100000010111001100000010100010011010110001111111001111001101001101111111001111111110010110101100110010111001110101101010111010011000010100000000000111010100110000001110010101001101000100100111101100110110111001101011110101110011000001001101100101010100101100101010000100111111110100011101111110001110110011011001110100101000101000110010101010010000100101111010001111000100001110001000101011110111010000000110110000111110101110110010000110110110010110101110100011100010011011100101010001011111101001100100000100001101011010000011010001101101001011001100010101101110011100011111111111001001111100110101011110011100000010010100001110010111011011111011000101111001011101000001100000000101000010010110101010000011100110100101100111110101011010101100111010000100011111000111110001011100010011111110000001010010010000001101101001111110010100000101101111110101110001110001110001000001001011010101000011101000000110001001110001110101110011100111000011001100111011011000111110001111011100001001110011101100001000010010100011001101001001110110101101010111001001001001011000001011001111010001000110111011101101001100100001100001100010001011011010001001000101010001011101011111001010011000010011000110111111100110010101100111010010111101110110111011101110001101001000010010001001111001111111101000101111010101110100011110100001101100000100100010101011001101001111110001111010111101001100101000100100011000100011100101001110110010100011100001011010110110111000100011000101101000010100100101100100001000110010000110100100011010011000100001101011011010111010010110100110011111101011010101011101111111101011111001011010111010100110111001101001101011011001100001110000001011110001010100110101000010100101111000110111001101110000000011010101100010111100111011111100111110001110101010001000101111111110011111100010110010111011110111110111011110001110001101101101111110111100110101000001101111110111100010001110111110001001100011100000010111100100101011001101101011010110110011010110100111001011011011110000000010001110000011111111100011011001001001100100001110011000111110010110100100001011100001001000101000101001001101001000000110001100100001101111101001100100101000011011011111101000110101101101010000110001111011110011111001110111001100111010101010011110111001110111000000110101001010010111101000100100100101001001000111010100010111101011110100100111100110101010011101110011101110110101101111000111001111001010100001111110000100000001011000110001110101000001001001010001001000101110011010100100000110010000011100111001011111001001110010001011100101011101101011101111001000101101101011011110100001101100001011001101011000101000000001000110011011100010011010000111010011100010100010010010101000111101110110101011110100000011011010100011010100101011001100100000000110101011000100111001110001101001101001001110111011100111001001001000101011011111110100100000010111011100000101001001101110100010111100000000010101010000010100110011111001000010000111101111100100001000110000111110011001011011111011101111111011010001101111100010010010010010101000001000011110000000000101110111000001110000000111111011111001000110000000001010000010010110111000100101100111111010011101101111101111111011101101011100000011101111111111001111001100101001011000111010000110001001010111010100011111100011001101011110000100000010001100001000011111111111010111100111001010110000001011111110101000000100101010001001001111011110010011110000001100100110011110110110001110100100001000100001010110011110000100001110111011101101110100001010110111001101101010000011001101001100001111011000100110000101110100011011001000001100100101001110001000010111101110000111100101100011011011100110100001110000010111111001100011010001101110010000011000001111011010010100110101010011011101000111000100011010110101111011011001111100010001010110001111001000001101111010010001001010110111010000011110110101000010011001100100100000111011110001011110111000101001011010100000111111011100100101001001001100000010110110001100101011110001011000000010110111010111000000000110110100010111000111011101001110011101001100011000000101001111000101011101000010111111010100001000100000000110101100010110000111000110001101001010010100111010101000111101000110010100110010001001110110101000010010101001011001100001000100000101110100011000010000100010101100111001010100010111111010110000001000011100110000110110110010111111011010110001011000011011111000001001100100110110001100100110111110000101011000100001000110110100000001101110001100010101001011111111110100010001001001101101101101011000110000011000100010011111000110011011011100000101001001110011010100001011001011110100110001011011010001100111001011001101111110101101011101000111010111000000001010001001011000001001000101001010011010001110000001000010001110011001001110110100011101100110101110001100101010000010001000101101 ==> complexity C = 654\n" ] }, { "data": { "text/plain": [ "654" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tests_3_functions(5000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Benchmarks\n", "\n", "On two example of strings (binary sequences), we can compare our three implementation." ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6.28 µs ± 295 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n", "1.22 µs ± 49.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n", "50.1 µs ± 11.7 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" ] } ], "source": [ "%timeit lempel_ziv_complexity('100111101100001000001010')\n", "%timeit lempel_ziv_complexity_cython('100111101100001000001010')\n", "%timeit lempel_ziv_complexity_numba('100111101100001000001010')" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "25.7 µs ± 2.91 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "5.43 µs ± 442 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n", "84.4 µs ± 11.5 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" ] } ], "source": [ "%timeit lempel_ziv_complexity('10011110110000100000101000100100101010010111111011001111111110101001010110101010')\n", "%timeit lempel_ziv_complexity_cython('10011110110000100000101000100100101010010111111011001111111110101001010110101010')\n", "%timeit lempel_ziv_complexity_numba('10011110110000100000101000100100101010010111111011001111111110101001010110101010')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let check the time used by all the three functions, for longer and longer sequences:" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "229 µs ± 35.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "148 µs ± 52 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "123 µs ± 6.62 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "205 µs ± 22.6 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "288 µs ± 18.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "819 µs ± 135 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n" ] } ], "source": [ "%timeit tests_3_functions(10, debug=False)\n", "%timeit tests_3_functions(20, debug=False)\n", "%timeit tests_3_functions(40, debug=False)\n", "%timeit tests_3_functions(80, debug=False)\n", "%timeit tests_3_functions(160, debug=False)\n", "%timeit tests_3_functions(320, debug=False)" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [], "source": [ "def test_cython(n):\n", " s = random_binary_sequence(n)\n", " c = lempel_ziv_complexity_cython(s)\n", " return c" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "23.9 µs ± 5.09 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "40 µs ± 4.91 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "53.4 µs ± 5.36 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "109 µs ± 11.2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "207 µs ± 25.4 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "369 µs ± 26.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n" ] } ], "source": [ "%timeit test_cython(10)\n", "%timeit test_cython(20)\n", "%timeit test_cython(40)\n", "%timeit test_cython(80)\n", "%timeit test_cython(160)\n", "%timeit test_cython(320)" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "679 µs ± 135 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "1.85 ms ± 211 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "2.97 ms ± 363 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "5.47 ms ± 494 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], "source": [ "%timeit test_cython(640)\n", "%timeit test_cython(1280)\n", "%timeit test_cython(2560)\n", "%timeit test_cython(5120)" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10.7 ms ± 891 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "20.9 ms ± 2.43 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" ] } ], "source": [ "%timeit test_cython(10240)\n", "%timeit test_cython(20480)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Complexity ?\n", "$\\implies$ The function `lempel_ziv_complexity_cython` seems to be indeed (almost) linear in $n$, the length of the binary sequence $S$.\n", "\n", "But let check more precisely, as it could also have a complexity of $\\mathcal{O}(n \\log n)$." ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "%matplotlib inline\n", "sns.set(context=\"notebook\", style=\"darkgrid\", palette=\"hls\", font=\"sans-serif\", font_scale=1.4)" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import timeit" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [], "source": [ "sizes = np.array(np.trunc(np.logspace(1, 6, 30)), dtype=int)\n", "\n", "times = np.array([\n", " timeit.timeit(\n", " stmt=\"lempel_ziv_complexity_cython(random_string({}))\".format(n),\n", " globals=globals(),\n", " number=10,\n", " )\n", " for n in sizes\n", "])" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "[]" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "Text(0.5, 0, 'Length $n$ of the binary sequence $S$')" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "Text(0, 0.5, 'Time in $\\\\mu\\\\;\\\\mathrm{s}$')" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "Text(0.5, 1.0, 'Time complexity of Lempel-Ziv complexity')" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(15, 10))\n", "plt.plot(sizes, times, 'o-')\n", "plt.xlabel(\"Length $n$ of the binary sequence $S$\")\n", "plt.ylabel(r\"Time in $\\mu\\;\\mathrm{s}$\")\n", "plt.title(\"Time complexity of Lempel-Ziv complexity\")\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "[]" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "Text(0.5, 0, 'Length $n$ of the binary sequence $S$')" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "Text(0, 0.5, 'Time in $\\\\mu\\\\;\\\\mathrm{s}$')" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "Text(0.5, 1.0, 'Time complexity of Lempel-Ziv complexity, loglog scale')" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(15, 10))\n", "plt.loglog(sizes, times, 'o-')\n", "plt.xlabel(\"Length $n$ of the binary sequence $S$\")\n", "plt.ylabel(r\"Time in $\\mu\\;\\mathrm{s}$\")\n", "plt.title(\"Time complexity of Lempel-Ziv complexity, loglog scale\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is linear in $\\log\\log$ scale, so indeed the algorithm seems to have a linear complexity.\n", "\n", "To sum-up, for a sequence $S$ of length $n$, it takes $\\mathcal{O}(n)$ basic operations to compute its Lempel-Ziv complexity $\\mathrm{Lempel}-\\mathrm{Ziv}(S)$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Conclusion\n", "\n", "- The Lempel-Ziv complexity is not too hard to implement, and it indeed represents a certain complexity of a binary sequence, capturing the regularity and reproducibility of the sequence.\n", "\n", "- Using the [Cython](http://Cython.org/) was quite useful to have a $\\simeq \\times 100$ speed up on our manual naive implementation !\n", "\n", "- The algorithm is not easy to analyze, we have a trivial $\\mathcal{O}(n^2)$ bound but experiments showed it is more likely to be $\\mathcal{O}(n \\log n)$ in the worst case, and $\\mathcal{O}(n)$ in practice for \"not too complicated sequences\" (or in average, for random sequences)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## (Experimental) [Julia](http://julialang.org) implementation\n", "\n", "I want to (quickly) try to see if I can use [Julia](http://julialang.org) to write a faster version of this function.\n", "See [issue #1](https://github.com/Naereen/Lempel-Ziv_Complexity/issues/1)." ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\"Lempel-Ziv complexity for a sequence, in simple Julia code.\"\n", "lempel_ziv_complexity (generic function with 1 method)\n", "\"1001111011000010\"\n", "9\n", "1000\n", "10000\n", "9\n", "CPU times: user 6.89 ms, sys: 8 ms, total: 14.9 ms\n", "Wall time: 3.85 s\n" ] } ], "source": [ "%%time\n", "%%script julia\n", "\n", "\"\"\"Lempel-Ziv complexity for a sequence, in simple Julia code.\"\"\"\n", "function lempel_ziv_complexity(sequence)\n", " sub_strings = Set()\n", " n = length(sequence)\n", "\n", " ind = 1\n", " inc = 1\n", " while true\n", " if ind + inc > n\n", " break\n", " end\n", " sub_str = sequence[ind : ind + inc]\n", " if sub_str in sub_strings\n", " inc += 1\n", " else\n", " push!(sub_strings, sub_str)\n", " ind += inc\n", " inc = 1\n", " end\n", " end\n", " return length(sub_strings)\n", "end\n", "\n", "s = \"1001111011000010\"\n", "lempel_ziv_complexity(s) # 1 / 0 / 01 / 1110 / 1100 / 0010\n", "\n", "M = 1000;\n", "N = 10000;\n", "for _ in 1:M\n", " s = join(rand(0:1, N));\n", " lempel_ziv_complexity(s);\n", "end\n", "lempel_ziv_complexity(s) # 1 / 0 / 01 / 1110 / 1100 / 0010" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And to compare it fairly, let us use [Pypy](http://pypy.org) for comparison." ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 4.24 ms, sys: 8 ms, total: 12.2 ms\n", "Wall time: 1.39 s\n" ] } ], "source": [ "%%time\n", "%%pypy\n", "\n", "def lempel_ziv_complexity(sequence):\n", " \"\"\"Lempel-Ziv complexity for a binary sequence, in simple Python code.\"\"\"\n", " sub_strings = set()\n", " n = len(sequence)\n", "\n", " ind = 0\n", " inc = 1\n", " while True:\n", " if ind + inc > len(sequence):\n", " break\n", " sub_str = sequence[ind : ind + inc]\n", " if sub_str in sub_strings:\n", " inc += 1\n", " else:\n", " sub_strings.add(sub_str)\n", " ind += inc\n", " inc = 1\n", " return len(sub_strings)\n", "\n", "s = \"1001111011000010\"\n", "lempel_ziv_complexity(s) # 1 / 0 / 01 / 11 / 10 / 110 / 00 / 010\n", "\n", "from random import random\n", "\n", "M = 1000\n", "N = 10000\n", "for _ in range(M):\n", " s = ''.join(str(int(random() < 0.5)) for _ in range(N))\n", " lempel_ziv_complexity(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we can check that on these 1000 random trials on strings of size 10000, the naive Julia version is slower than the naive Python version (executed by Pypy for speedup)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Ending notes\n", "> Thanks for reading!\n", "> My implementation is [now open-source and available on GitHub](https://github.com/Naereen/Lempel-Ziv_Complexity), on https://github.com/Naereen/Lempel-Ziv_Complexity.\n", "\n", "> It will be available from PyPi very soon, see https://pypi.python.org/pypi/lempel_ziv_complexity.\n", "\n", "> See [this repo on GitHub](https://github.com/Naereen/notebooks/) for more notebooks, or [on nbviewer.jupyter.org](https://nbviewer.jupyter.org/github/Naereen/notebooks/).\n", "\n", "> That's it for this demo! See you, folks!" ] } ], "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.5" }, "notify_time": "10", "toc": { "colors": { "hover_highlight": "#DAA520", "running_highlight": "#FF0000", "selected_highlight": "#FFD700" }, "moveMenuLeft": true, "nav_menu": { "height": "236px", "width": "251px" }, "navigate_menu": true, "number_sections": true, "sideBar": true, "threshold": 4, "toc_cell": true, "toc_section_display": "block", "toc_window_display": true }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }