{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cf48057a",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Analyzing Python List Operations\n",
    "#### CS 66: Introduction to Computer Science II"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fdf94b54",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## References for this lecture\n",
    "\n",
    "Problem Solving with Algorithms and Data Structures using Python\n",
    "\n",
    "Sections 3.6: [https://runestone.academy/ns/books/published/pythonds/AlgorithmAnalysis/Lists.html](https://runestone.academy/ns/books/published/pythonds/AlgorithmAnalysis/Lists.html)\n",
    "\n",
    "Python Documentation on Time Complexity: [https://wiki.python.org/moin/TimeComplexity](https://wiki.python.org/moin/TimeComplexity)\n",
    "\n",
    "Section 1.13: [https://runestone.academy/ns/books/published/pythonds/Introduction/ObjectOrientedProgramminginPythonDefiningClasses.html](https://runestone.academy/ns/books/published/pythonds/Introduction/ObjectOrientedProgramminginPythonDefiningClasses.html)\n",
    "\n",
    "Section 2.1: [https://runestone.academy/ns/books/published/pythonds/ProperClasses/a_proper_python_class.html](https://runestone.academy/ns/books/published/pythonds/ProperClasses/a_proper_python_class.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29aaa28a",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Review: Anagram Detection Activity\n",
    "\n",
    "An _anagram_ is a word or phrase that can be formed by rearranging the letters of a different word or phrase.\n",
    "\n",
    "Examples of anagrams include\n",
    "\n",
    "* silent, listen\n",
    "\n",
    "* night, thing\n",
    "\n",
    "* the morse code, here come dots\n",
    "\n",
    "* eleven plus two, twelve plus one"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd2df3fd",
   "metadata": {},
   "source": [
    "__Problem:__ Write a function that will tell you if two strings are anagrams."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27a4c87f",
   "metadata": {},
   "source": [
    "The book provides four different solutions in Section 3.4. Three of them are reproduced below. \n",
    "\n",
    "Each group will be assigned one of these solutions. Do the following as a group.\n",
    "\n",
    "1. Test the code on several inputs of different sizes.\n",
    "2. Instrument the code to measure the time it takes on different-sized inputs.\n",
    "3. Give examples of best, worst, and average case inputs.\n",
    "4. Determine what the Big-O of the algorithm is, and be ready to explain why.\n",
    "\n",
    "If you have time, you can check out what it says in the book, but try to analyze it without looking first!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65c5d191",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## Solution 1: Checking off\n",
    "\n",
    "This code works by converting the second string into a list and then search through the list for each character from the first string and replacing it with `None` when found.\n",
    "\n",
    "For example, if given `\"silent\"` and `\"listen\"`, the list would start out as\n",
    "\n",
    "`['l','i','s','t','e','n']`\n",
    "\n",
    "when searching for `'s'`, it becomes `['l','i',None,'t','e','n']`\n",
    "\n",
    "when searching for `'i'`, it becomes `['l',None,None,'t','e','n']`\n",
    "\n",
    "... and so until the list becomes `[None,None,None,None,None,None]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "af51d272",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "def anagramSolution1(s1,s2):\n",
    "    stillOK = True\n",
    "    if len(s1) != len(s2):\n",
    "        stillOK = False\n",
    "\n",
    "    alist = list(s2)\n",
    "    pos1 = 0\n",
    "\n",
    "    while pos1 < len(s1) and stillOK:\n",
    "        pos2 = 0\n",
    "        found = False\n",
    "        while pos2 < len(alist) and not found:\n",
    "            if s1[pos1] == alist[pos2]:\n",
    "                found = True\n",
    "            else:\n",
    "                pos2 = pos2 + 1\n",
    "\n",
    "        if found:\n",
    "            alist[pos2] = None\n",
    "        else:\n",
    "            stillOK = False\n",
    "\n",
    "        pos1 = pos1 + 1\n",
    "        \n",
    "        #uncomment this if you want to see what the list looks like at each step\n",
    "        #print(alist)\n",
    "\n",
    "    return stillOK\n",
    "\n",
    "print(anagramSolution1('silent','listen'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4da1220a",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## Solution 2: Sort and Compare\n",
    "\n",
    "This solution starts by converting both strings to lists and then sorting them. Once in sorted order, it goes through and checks that each corresponding item in the list is the same.\n",
    "\n",
    "For example, if given `\"silent\"` and `\"listen\"`, it would turn them into lists `['s', 'i', 'l', 'e', 'n', 't']` and `['l', 'i', 's', 't', 'e', 'n']`.\n",
    "\n",
    "Then, after sorting each list, we get `['e', 'i', 'l', 'n', 's', 't']` and `['e', 'i', 'l', 'n', 's', 't']`.\n",
    "\n",
    "We then compare `e` to `e`, then `i` to `i`, then `l` to `l` and so on. If we ever find two that don't match, we know it isn't an anagram. If we get to the end and they all match, it is an anagram."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "be615d05",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "def anagramSolution2(s1,s2):\n",
    "    alist1 = list(s1)\n",
    "    alist2 = list(s2)\n",
    "\n",
    "    alist1.sort()\n",
    "    alist2.sort()\n",
    "    \n",
    "    #uncomment these if you want to see the sorted lists\n",
    "    #print(alist1)\n",
    "    #print(alist2)\n",
    "\n",
    "    pos = 0\n",
    "    matches = True\n",
    "\n",
    "    while pos < len(s1) and matches:\n",
    "        if alist1[pos]==alist2[pos]:\n",
    "            pos = pos + 1\n",
    "        else:\n",
    "            matches = False\n",
    "\n",
    "    return matches\n",
    "\n",
    "print(anagramSolution2('silent','listen'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53db1474",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## Solution 4: Count and Compare\n",
    "\n",
    "This solution creates a list of letter frequencies for each string. Since there aree 26 letters in the alphabet, the strings will each have 26 entries - the first entry is the number of occurrences of `'a'`, the secondd is the number of occurrences of `'b'`, and so on. \n",
    "\n",
    "We can then loop through these frequency lists and compare them item by item to see if they're the same.\n",
    "\n",
    "For example, given inputs `'elevenplustwo'` and `'twelveplusone'`, you end up with the frequency lists\n",
    "\n",
    "`[0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0]`\n",
    "\n",
    "and \n",
    "`[0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0]`\n",
    "\n",
    "looping through this list entry by entry will show that they are the same.\n",
    "\n",
    "On the other hand, if given inputs `'granma'` and `'anagram'`, you'd get the frequency lists\n",
    "\n",
    "`[2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]`\n",
    "\n",
    "`[3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]`\n",
    "\n",
    "And you can determine they are not anagrams, because the first list has a 2 in the `a` position while the second one has a `3`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "74ded9dd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "def anagramSolution4(s1,s2):\n",
    "    c1 = [0]*26\n",
    "    c2 = [0]*26\n",
    "\n",
    "    for i in range(len(s1)):\n",
    "        pos = ord(s1[i])-ord('a')\n",
    "        c1[pos] = c1[pos] + 1\n",
    "\n",
    "    for i in range(len(s2)):\n",
    "        pos = ord(s2[i])-ord('a')\n",
    "        c2[pos] = c2[pos] + 1\n",
    "        \n",
    "    #uncomment these if you want to see the word frequency lists\n",
    "    #print(c1) \n",
    "    #print(c2)\n",
    "\n",
    "    j = 0\n",
    "    stillOK = True\n",
    "    while j<26 and stillOK:\n",
    "        if c1[j]==c2[j]:\n",
    "            j = j + 1\n",
    "        else:\n",
    "            stillOK = False\n",
    "\n",
    "    return stillOK\n",
    "\n",
    "print(anagramSolution4('elevenplustwo','twelveplusone'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58e3410c",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Group Activity Problem 1\n",
    "\n",
    "We're going to run some experiments to see if we can guess what the Big-O of various Python list operations is. \n",
    "\n",
    "* Get the [timing_list_operations.py](../../assets/code/timing_list_operations.py) file.\n",
    " - generates lots of different random lists of different sizes\n",
    " - times the code and plots the running times\n",
    " - as is, will generate 20 different lists each of sizes 100000, 200000, ... , 1000000"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f8a3a6b",
   "metadata": {},
   "outputs": [],
   "source": [
    "list_sizes = range(100000,1000001,100000)\n",
    "results = run_list_op_timing_experiments(list_sizes,20)\n",
    "plot_results(list_sizes,results)\n",
    "display_results(list_sizes,results)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "275473ae",
   "metadata": {},
   "source": [
    "you can change what code is timed by finding this part"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a91bbaf2",
   "metadata": {},
   "outputs": [],
   "source": [
    "        ### BEGIN CODE BEING TIMED\n",
    "\n",
    "        0 in test_list_copy  #testing the built-in search operator\n",
    "        #x = test_list_copy[random_index]  #testing list access\n",
    "        #test_list_copy.sort()\n",
    "        \n",
    "        ### END CODE  BEING TIMED"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9240aec5",
   "metadata": {},
   "source": [
    "What can you conclude about the run time of the `in` operator based on what you see?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2b53e1e",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Group Activity Problem 2\n",
    "\n",
    "In the above example, you tested the `0 in test_list_copy` operation. Now try the following operations. For each one, try to guess the Big-O from the run time results (you may need to try some bigger $n$ to see some of these clearly).\n",
    "\n",
    "If you see any result like `171.45672101µ`, it means $171.45672101x10^{-6}$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19773189",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = test_list_copy[random_index]  #access a random item in a list - you'll have to generate a random int first\n",
    "test_list_copy.sort() #sort the list\n",
    "test_list_copy.pop()  #remove the last item in the list\n",
    "test_list_copy.pop(0) #remove the first item in the list"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88a1fdee",
   "metadata": {},
   "source": [
    "Discuss: Are you surprised by any of these results?\n",
    "\n",
    "Once you have an idea of what the Big-O is for these operations, look to see what the textbook authors say it is here: \n",
    "[https://runestone.academy/ns/books/published/pythonds/AlgorithmAnalysis/Lists.html](https://runestone.academy/ns/books/published/pythonds/AlgorithmAnalysis/Lists.html)\n",
    "\n",
    "Or, consult the official Python documentation here: [https://wiki.python.org/moin/TimeComplexity](https://wiki.python.org/moin/TimeComplexity)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f946bca",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## White Board Talk\n",
    "\n",
    "If there's time, we'll draw some pictures on the white board to see how Python lists are stored in memory and how that affects the Big-O of all these operations.\n",
    "\n",
    "Points to note:\n",
    "\n",
    "* Python keeps track of lists in consecutive chunks of computer memory - this data structure is often called an _array_\n",
    "* Consecutive memory locations allow $O(1)$ access to items by index in the list - this is often called _random access_\n",
    "* Python allocates a certain amount of space for the list. If it outgrows that, it will allocate a new, bigger memory space and copy everything over.\n",
    "* The _worst case_ for `append()` happens when it triggers a re-allocation to the bigger memory space, so it is technically $O(n)$. But, this is guaranteed to happen infrequently enough that it's still $O(n)$ to append $n$ items, thus thee __ammortized worst case__ for appending one item is $O(1)$, which is why the textbook says `append()` is $O(1)$. For more information, see [https://wiki.python.org/moin/TimeComplexity](https://wiki.python.org/moin/TimeComplexity)"
   ]
  },
  {
   "attachments": {
    "emptyarray.png": {
     "image/png": "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aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgrAwNMxAAAAHGlET1QAAAACAAAAAAAAACkAAAAoAAAAKQAAACkAABy0vOBSdwAAHIBJREFUeAHsXc1yHMWy7rkvABHAViM2x7A59tleCEu8wDU7dhr5CWxewCNe4B77BSR5xw78ApbNzxqbSwSwwfbdYiLgBc6c/LKcUrstHUvqzi/DoS8j7B7NjCpVX1VmffVVdfVsZdbJhIAQEAJCQAgIASEgBISAEBACQkAICAEhIASEgBAQAkJACAiBFARmEuJTcFWhQkAICAEhIASEgBAQAkJACAgBISAEhIAQEAJCQAgIASEgBBwBCfHqCEJACAgBISAEhIAQEAJCQAgIASEgBISAEBACQkAICAEhIAQSEZAQnwiuihYCQkAICAEhIASEgBAQAkJACAgBISAEhIAQEAJCQAgIASEgIV59QAgIASEgBISAEBACQkAICAEhIASEgBAQAkJACAgBISAEhEAiAhLiE8FV0UJACAgBISAEhIAQEAJCQAgIASEgBISAEBACQkAICAEhIAQkxKsPCAEhIASEgBAQAkJACAgBISAEhIAQEAJCQAgIASEgBISAEEhEQEJ8IrgqWggIASEgBISAEBACQkAICAEhIASEgBAQAkJACAgBISAEhICEePUBISAEhIAQEAJCQAgIASEgBISAEBACQkAICAEhIASEgBAQAokISIhPBFdFCwEhIASEgBAQAkJACAgBISAEhIAQEAJCQAgIASEgBISAEBgtxN++fbv76aefug8//LB75513utls1q1Wq0Nk8fO//rWy9w/feunF8PsvfWg/oCx8B9b/7nfffde9++673QcffPDSZ/3vxO9M5f/XX3/tfv/99+7jjz92n/3/wm9c4zP8PMY//MEv6vnee++9hG34wDX8xjU+O6//X375pXv+/Hn30UcfHYt/lB/X8BvX/vunrT/8/Wp+L1ld0bawk9o/yo9r+I1r//3X+f/222/968e1a5b/wDfiJv7e4XVq/yfFzdBv/DyF/59//rl7/scf3cfWl4Y2bK/h52P8o66Imb/97ZL14WHJ7eep/Q9zBMp/Xf9DHU+y09Tf48ZyxKVLl17JEZn+gS8McRM4xjXqM7V/5EPEDvJh5Aj4Cr9xndo/6jqb/Zflw/+Ool+6ht+4xof4+bztj3qi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"
    }
   },
   "cell_type": "markdown",
   "id": "5362421e",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "\n",
    "Here's a blank memory diagram in case I need to draw digitally\n",
    "\n",
    "<div>\n",
    "    <img src=\"attachment:emptyarray.png\" width=\"auto\"/>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b304842a",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Defining new types\n",
    "\n",
    "In Python, we use classes to create new _types_\n",
    "\n",
    "A class defines two things:\n",
    "1. Data/Attributes: what do objects of this type _look_ like?\n",
    "2. Methods: what can you do with objects of this type?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9509de04",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Example: Date object\n",
    "\n",
    "Let's look at the `date` type\n",
    "\n",
    "The `date` class is defined in the `datetime` module, and we can import it and use it in our code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "079c792b",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'datetime.date'>\n",
      "Here's what it looks like when we print a date: 1776-07-04\n",
      "Here's what the data that makes up a date looks like:\n",
      "7\n",
      "4\n",
      "1776\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "import datetime\n",
    "\n",
    "#creating a new date object\n",
    "decl_ind_date = datetime.date(1776,7,4) \n",
    "\n",
    "#datetime.date is a type\n",
    "print( type(decl_ind_date) )\n",
    "\n",
    "print(\"Here's what it looks like when we print a date:\",decl_ind_date)\n",
    "\n",
    "print(\"Here's what the data that makes up a date looks like:\")\n",
    "print( decl_ind_date.month )\n",
    "print( decl_ind_date.day )\n",
    "print( decl_ind_date.year )\n",
    "\n",
    "#you can call methods on dates - here's one thing you can do with a date\n",
    "#weekday method returns the number of the day of the week this date fell on (0 = Monday, 6 = Sunday)\n",
    "print( decl_ind_date.weekday() ) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93df592e",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "`month`, `day`, and `year` are __attributes__ - which data values associated with the object\n",
    "\n",
    "`weekday()` is a method - like a function, but you call it using dot notation on a `date` object"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c82c7b5",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## How do we write our own classes?\n",
    "\n",
    "Classes allow you to _encapsulate_ data and actions-on-that-data together into one thing - this is an _abstraction_ technique - it's good programming.\n",
    "\n",
    "A _class_ defines how objects behave - it is a blueprint that can be used to create many different objects of that type\n",
    "\n",
    "Syntax:\n",
    "* keyword `class`\n",
    "* a name you decide (by convention, start with uppercase letter)\n",
    "* a colon `:`\n",
    "* indented list of function definitions (i.e., _method_ definitions)\n",
    "    - each method has a parameter called `self` which refers to the particular object being used at that time\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "12858ced",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Motivator:\n",
    "    \n",
    "    def message1(self):\n",
    "        print(\"You can do it!\")\n",
    "        \n",
    "    def message2(self):\n",
    "        print(\"I'm proud of you!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "d772a142",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I'm proud of you!\n",
      "<class '__main__.Motivator'>\n"
     ]
    }
   ],
   "source": [
    "m = Motivator()\n",
    "m.message2()\n",
    "print( type(m) )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f06a599",
   "metadata": {},
   "source": [
    "Notice that you always have to make `self` a parameter, but you don't send it as an argument in parantheses like other arguments. \n",
    "\n",
    "`self` is the object (here, `m`) that the method was called on"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "66ced12a",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## Defining Classes with Attributes\n",
    "\n",
    "Any attribute can be accessed in any of the class's methods using `self`. Each object of the class has a different set of all the attributes (just like different date objects represent different dates on the calendar)\n",
    "\n",
    "Initialize attributes using the special `__init__()` method, which will be invoked whenever a new object of this type is created."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "49556805",
   "metadata": {},
   "outputs": [],
   "source": [
    "class PersonalMotivator:\n",
    "    \n",
    "    def __init__(self,n):\n",
    "        self.name = n\n",
    "    \n",
    "    def message1(self):\n",
    "        print(\"You can do it,\",self.name)\n",
    "        \n",
    "    def message2(self):\n",
    "        print(\"I'm proud of you,\",self.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "5ba138ae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "You can do it, Eric\n",
      "I'm proud of you, Eric\n",
      "You can do it, Tim\n"
     ]
    }
   ],
   "source": [
    "#creates two objects of the PersonalMotivator class\n",
    "eric_motivator = PersonalMotivator(\"Eric\")\n",
    "tim_motivator = PersonalMotivator(\"Tim\")\n",
    "\n",
    "\n",
    "eric_motivator.message1()\n",
    "eric_motivator.message2()\n",
    "tim_motivator.message1()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0016fe3",
   "metadata": {},
   "source": [
    "## Group Activity Problem 3:\n",
    "* Where does `self.name` get its value from?\n",
    "* When I call `eric_motivator.message2()`, what is `self`?\n",
    "* How would I create a third object of the `PersonalMotivator` class? Do I have to pass it a name?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae7f3f6f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "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.10.6"
  },
  "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": 5
}
