diff --git a/Exercises/00-setup/jupyter.md b/Exercises/00-setup/jupyter.md index 63571c7..4f14a79 100644 --- a/Exercises/00-setup/jupyter.md +++ b/Exercises/00-setup/jupyter.md @@ -1,92 +1,92 @@ # Using Jupyter Notebooks and Jupyter Lab ## What is a Jupyter Notebook? From the [Project Jupyter website](https://jupyter.org/): > The Jupyter Notebook is an open-source web application that allows you to create and share documents that contain live code, equations, visualizations and narrative text. Uses include: data cleaning and transformation, numerical simulation, statistical modeling, data visualization, machine learning, and much more. ## What is Jupyter Lab? Jupyter Lab is a web-based development environment for Jupyter Notebooks, code and data. You can also use it to view Markdown, PDF and text files. ## Interacting with Jupyter Lab and launching a Notebook When you open EPFL noto, you should see a screen as follows: You will notice there will be a directory structure on the left-hand side of your window. On the right-hand side, we have a "launcher" interface where we can create a Jupyter Notebook (boxed in red), or several other types of files. To start a new notebook, click on the "Python 3" logo beneath "Notebook". You can also open an existing notebook from the directory. Once you open a notebook, you will be brought to the following screen. Some of the most important components of the notebook are highlighted in colored boxes. - To rename your notebook (shown here as Untitled), you can right-click your document and select Rename. - In purple is the cell formatting assignment. By default, it is set to "Code", but it can also be set to "Markdown". - In red is a code cell, in which you can write Python code. - In blue is a markdown cell, which is used to display nicely formatted text, images and mathematical equations. #### Markdown cells Here are some useful resources for Markdown cells: - https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet - https://www.datacamp.com/community/tutorials/markdown-in-jupyter-notebook #### Code cells Code cells support Python code (and other languages with different kernels, although we will only use Python in this course). They can be executed in any order. - To run a single cell, use **Shift + Enter** or press on the ▶ button. - To run all the cells starting from the beginning, go to `Run` -> `Run All Cells...`. -:warning: Code cells can be executed in any order. This means that you can overwrite your current variables by running cells out of order. This also means that variables declared in cells that were executed then deleted will still be present (known as a hidden state) unless the kernel is restarted. Therefore, **when coding in notebooks, be cautious of the order in which you run cells.** One solution to avoid hidden states is to frequently restart your kernel (and run up to your currently selected cell). This is covered more in depth in [this section](#the-kernel). +**Warning:** Code cells can be executed in any order. This means that you can overwrite your current variables by running cells out of order. This also means that variables declared in cells that were executed then deleted will still be present (known as a hidden state) unless the kernel is restarted. Therefore, **when coding in notebooks, be cautious of the order in which you run cells.** One solution to avoid hidden states is to frequently restart your kernel (and run up to your currently selected cell). #### Autocompletion and documentation Autocompletion is possible with JupyterLab too! Use **Tab**. To view the documentation for a function or class, use **Shift + Tab**. #### Keyboard shortcuts You can gain a lot of time using [keyboard shortcuts](https://cheatography.com/weidadeyue/cheat-sheets/jupyter-notebook/pdf/) to navigate through notebooks. ## The kernel The kernel maintains the state of a notebook's computations (such as current variables, declared functions and loaded data). For notebooks that do not take too long to run, it is desirable to frequently restart the kernel to ensure that there are no hidden states. Here is a list of what the different kernel related actions do: ![](images/kernel_1.png) - **Interrupt ( or ■ button):** Causes the kernel to stop performing the current task without actually shutting the kernel down. You can use this option when you want to stop a very long task (eg. stop processing a large dataset). - **Restart (or ↻ button):** Stops the kernel and starts it again. This action causes you to lose all the state data. In some cases, this is precisely what you need to do when the environment has become "dirty" with hidden state data. - **Restart & Clear Output:** Stops the kernel, starts it again, and clears all the existing cell outputs. - **Restart Kernel & Run Up to Selected Cell:** Stops the kernel, starts it again, and then runs every cell starting from the top cell and ending with the currently selected cell. Use this when you are working on a Notebook and want to make sure there are no hidden states. - **Restart Kernel & Run All Cells:** Stops the kernel, starts it again, and then runs every cell starting from the top cell and ending with the last cell. Use this when you're done working on a Notebook and want to make sure everything runs properly and doesn't depend on hidden states. - **Shutdown:** Shuts the kernel down. You may perform this step in preparation for using a different kernel. - **Change Kernel:** Selects a different kernel from the list of kernels you have installed. For example, you may want to test an application using various Python versions to ensure that it runs on all of them. ## Additional resources **Jupyter Notebook tutorial: https://www.dataquest.io/blog/jupyter-notebook-tutorial/** **Jupyter Notebook documentation: https://jupyterlab.readthedocs.io/en/stable/user/notebook.html** **Jupyter Lab interface documentation: https://jupyterlab.readthedocs.io/en/stable/user/interface.html** diff --git a/Exercises/00-setup/noto.md b/Exercises/00-setup/noto.md index d7b6ce1..262adad 100644 --- a/Exercises/00-setup/noto.md +++ b/Exercises/00-setup/noto.md @@ -1,20 +1,17 @@ # EPFL Noto - -we need to provide instructions on git pull to update. - EPFL provides a centralized JupyterLab platform for students called [Noto](https://www.epfl.ch/education/educational-initiatives/cede/digitaltools/noto/), allowing you to run all these notebooks without having to install anything on your computer. This is because those notebooks are running in the cloud (i.e. on a remote server, with which you directly interact through the JupyterLab interface). -To clone this repo on EPFL Noto, click on this link:https://go.epfl.ch/ME-390-2022. +To clone this repository on EPFL Noto, click on this link: https://go.epfl.ch/ME-390-2022. As in a local Python installation, you will need to frequently `git pull` the most recent changes in order to ensure that your files are up-to-date. -:warning: You'll need a stable internet connection to run these labs through Noto, as it runs in the cloud. This is not the case when running the notebooks locally, even though we're still using a web browser to access Jupyter Lab. +**Warning**: You'll need a stable internet connection to run these labs through Noto, as it runs in the cloud. This is not the case when running the notebooks locally, even though we're still using a web browser to access Jupyter Lab. ## Other cloud-based notebook platforms -There exists other online platforms offering cloud-based Jupyter Notebooks. These platforms could enable faster computation time. Notably: +There exist other online platforms offering cloud-based Jupyter Notebooks. These platforms could enable faster computation time. Notably: - [Google Colab](https://colab.research.google.com/), which offers free GPU runtimes, which is very useful for some machine learning tasks (such as training neural networks). - [Deepnote](https://deepnote.com/), which offers real-time collaboration (useful for group projects). **Note**: We do not provide any support for using these platforms. diff --git a/Exercises/01-python/intro_to_python.ipynb b/Exercises/01-python/intro_to_python.ipynb index 42c2119..15bb71a 100644 --- a/Exercises/01-python/intro_to_python.ipynb +++ b/Exercises/01-python/intro_to_python.ipynb @@ -1,1528 +1,1528 @@ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "This notebook was developed for the CS-233 Introduction to Machine Learning course at EPFL, adapted for the CIVIL-226 Introduction to Machine Learning for Engineers course, and re-adapted for the ME-390. We thank contributers in CS-233 ([CVLab](https://www.epfl.ch/labs/cvlab)) and CIVIL-226 ([VITA](https://www.epfl.ch/labs/vita/)).\n", " \n", - "**Author(s):** [Sena Kiciroglu](mailto:sena.kiciroglu@epfl.ch), minor changes by [Tom Winandy](mailto:tom.winandy@epfl.ch) and [David Mizrahi](mailto:david.mizrahi@epfl.ch)\n", + "**Author(s):** Sena Kiciroglu, minor changes by Tom Winandy and David Mizrahi\n", "
\n", "Welcome to the first exercise of Introduction to Machine Learning. Today we will get familiar with Python, the language we will use for all the exercises of this course. \n", "\n", "This week we will introduce some important concepts in the basics of Python. Next week, you will learn how to work with NumPy, a popular Python library used for scientific computing. \n", "\n", "Python is a popular language to use for machine learning tasks. This is especially true because of the selection of **libraries and frameworks**, developed specifically for machine learning and scientific computing. To name a few, you have Keras, TensorFlow and PyTorch for developing neural networks, SciPy and NumPy used for scientific computing, Pandas for data analysis, etc. (You might also get to dabble in PyTorch in the upcoming weeks.)\n", "\n", "Python also allows you to write quick, readable, high-level code. It's great for fast prototyping. \n", "\n", "You can find a useful Python cheatsheet at: https://www.pythoncheatsheet.org/\n", "\n", "Let's get into it!\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## 1. Jupyter Notebook\n", "\n", "In these exercises we will use Jupyter Notebooks, which contain Python code, text explanations and visuals. \n", "\n", "The Jupyter Notebook document (such as the one you are looking at right now) consists of cells containing Python code, text or other content. You can run each cell by clicking on the button `Run` in the top toolbar, or you can use a keyboard shortcut `Ctrl` + `Enter` (run current cell) or `Shift` + `Enter` (run current cell and move to the cell below)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Indentation and Control Flow" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally we get to start doing some coding!\n", "\n", "First thing to know: Python does not separate different lines of code with a semicolon `;`. So just RUN the following cell with no worries." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# This is a Python comment. Start the line with `#` for a comment\n", "print(\"First line of code. I will declare some variables\")\n", "a = 1 # second line!!\n", "b = 2\n", "c = \"Fish\"\n", "print(f\"My variables are: a = {a}, b = {b}, c = {c}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Easy! However, in Python you have to be careful and have perfect indentation (a reason why Python code is so readable). The reason is, Python uses indentation to keep track of what is part of the if statement, the loops and the functions. This is different from Java (this is assuming you know Java) where you would have curly brackets `{ }` for this purpose. \n", "\n", "Let's start with the if statement.\n", "\n", "### 2.1. If Statement\n", "\n", "The rule is, all indented parts after the `if condition :` belong to that branch of the if statement. \n", "\n", "```python\n", "if condition :\n", " inside the statement\n", " still inside the statement\n", "elif condition:\n", " inside the else-if part of the statement\n", "else:\n", " inside the else part of the statement\n", "outside the statement\n", " ```\n", " \n", "Let's see it in action:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if a + b == 3:\n", " print(\"It's me again! We are inside the first if statement\")\n", " print(\"It's optional to use parentheses for the condition a + b ==3\")\n", " print(\"Don't forget to put a `:` at the end of the condition!!\")\n", " if (c == \"Fish\"):\n", " print(\"This is a second if statement inside the first one\")\n", " print(\"I'm out of the second if statement, but still inside the first one\")\n", "else:\n", " print(\"This is the else part of the first if statement.\")\n", " print(\"These lines will never be printed!\")\n", "print(\"I'm not inside any of the if statements\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Exercise \n", "\n", "Let's see another if statement example. Try to figure out what the output will be **BEFORE** running the cell below.\n", "\n", "Reminder, we declared\n", "\n", "```python\n", "a = 1\n", "b = 2\n", "c = \"Fish\"\n", " ```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Don't run me until you find the output first!\n", "if a == 5:\n", " print (\"1\")\n", " if b == 1:\n", " print(\"2\")\n", "# here comes an else-if \n", "elif a == 2 or c == \"Fish\":\n", " print(\"3\")\n", " \n", " if b == 1:\n", " print(\"4\")\n", " if b == 2:\n", " print(\"5\")\n", " if b == 2:\n", " print(\"6\")\n", " if c == \"Fish\":\n", " if a == 1:\n", " if b == 100:\n", " print(\"7\")\n", " else:\n", " print(\"8\")\n", " elif a == 1:\n", " print(\"9\")\n", "print (\"10\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2. Loops\n", "\n", "Let's talk about loops. The syntax for a while-loop is:\n", "\n", "```python\n", "while condition:\n", " inside the loop\n", " inside the loop\n", " inside the loop\n", "outside the loop\n", " ```\n", " \n", " A small example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "count = 0\n", "while count < 3:\n", " count += 1 # this is the same as count = count +1\n", " print(f\"Count is {count}\")\n", "print(\"Left the loop!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For-loops iterate through sequences, in this way:\n", "\n", "```python\n", "for x in sequence:\n", " inside the loop\n", " inside the loop\n", " inside the loop\n", "outside the loop\n", "```\n", " \n", " An example is shown below:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Here is a basic list of strings\n", "fish_list = [\"salmon\", \"trout\", \"parrot\", \"clown\", \"dory\"]\n", "\n", "#The for loop:\n", "for fish in fish_list:\n", " print(fish)\n", " print(\"*\")\n", "print(\"fish list over!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An incredibly useful built-in function to use in for loops is `range()`. Range allows you to create a sequence of integers from the start (default is 0), to the stop, with a given step size (default is 1). We can use `range()` in for loops as shown in the example below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# \"default start is 0, default step size is 1\"\n", "for number in range(7):\n", " print (number)\n", "print(\"**\")\n", "\n", "# now we also provide the start as 2.\n", "# Default step size 1 is still used.\n", "for number in range(2,7):\n", " print(number)\n", "print(\"**\")\n", "\n", "# now we also provide the step size as 2.\n", "for number in range(2,7,2):\n", " print(number)\n", "print(\"**\") \n", "\n", "# what happens if step size is -1?\n", "for number in range(6,-1,-1):\n", " print(number)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One more useful built-in function will be `enumerate()`. Let's go back to the fish list.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for fish in fish_list:\n", " print(fish)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What if I also want to keep track of the index of the list element? You can use `enumerate()` which creates a sequence of 2-tuples, where each tuple contains an integer index and an actual element of the original list. Here is how it looks like:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for item_index, fish in enumerate(fish_list):\n", " print(f\"{item_index}: {fish}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Data Types and Basic Operations\n", "\n", "Python is a **dynamically typed** language. This means that the data type is inferred at run-time and can be changed during run-time. To check the type of a variable you can use the function `type()`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# var_1 is first defined as an integer\n", "var_1 = 1\n", "print(f\"{var_1} is {type(var_1)}\")\n", "\n", "# var_1's type is changed to string\n", "var_1 = \"hi!\"\n", "print(f\"{var_1} is {type(var_1)}\")\n", "\n", "# more types\n", "var_1 = 0.312\n", "print(f\"{var_1} is {type(var_1)}\")\n", "var_1 = 3.\n", "print(f\"{var_1} is {type(var_1)}\")\n", "var_1 = 3+2j\n", "print(f\"{var_1} is {type(var_1)}\")\n", "var_1 = True\n", "print(f\"{var_1} is {type(var_1)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.1. Type Casting\n", "\n", "Some examples of type casting in Python:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# From int to float\n", "var_1 = 42\n", "print(f\"{var_1} is {type(var_1)}\")\n", "var_1 = float(var_1)\n", "print(f\"{var_1} is {type(var_1)}\")\n", "print (\"**\")\n", "\n", "# From float to int\n", "var_2 = 3.14\n", "print(f\"{var_2} is {type(var_2)}\")\n", "var_2 = int(var_2)\n", "# This operations does FLOOR, not round!\n", "print(f\"{var_2} is {type(var_2)}\")\n", "print (\"**\")\n", "\n", "# From string to int\n", "var_3 = \"100\"\n", "print(f\"{var_3} is {type(var_3)}\")\n", "var_3 = int(var_3)\n", "print(f\"{var_3} is {type(var_3)}\")\n", "print(\"**\")\n", "\n", "# From float to string\n", "var_4 = 1.23\n", "print(f\"{var_4} is {type(var_4)}\")\n", "var_4 = str(var_4)\n", "print(f\"{var_4} is {type(var_4)}\")\n", "print(\"**\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.2. Basic Operations\n", "\n", "Arithmetic operations are fairly standard. There are some examples below. \n", "* Look out for the difference between `/` division and `//` integer division.\n", "* `**` is used for power.\n", "* `%` is modulo." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = 50\n", "b = 7\n", "\n", "print(f\"a + b = {a + b}\")\n", "print(f\"a - b = {a - b}\")\n", "print(f\"a * b = {a * b}\")\n", "print(f\"a / b = {a / b}\")\n", "print(f\"a // b = {a // b}\") # integer divison\n", "print(f\"a ** b = {a ** b}\") # power\n", "print(f\"a % b = {a % b}\") # modulo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Boolean operations are also fairly standard:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"(True and False) = {True and False}\")\n", "print(f\"(True or False) = {True or False}\")\n", "print(f\"((True and False) or True) = {(True and False) or True}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can declare strings with a single quote `'`, a double quote `\"` or a three double quotes `\"\"\"`. The string declared with `\"\"\"` is known as a *docstring*, it can span multiple lines and is usually used to comment functions and classes.\n", "\n", "**Note:** Throughout the exercises, we will be using f-strings to format our strings nicely. You can learn more about them [here](https://realpython.com/python-f-strings/)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = 'Life\\'s but a walking shadow, a poor player,' \n", "print(a)\n", "a = \"That struts and frets his hour upon the stage,\"\n", "print(a)\n", "a = \"\"\"And then is heard no more. It is a tale\n", "Told by an idiot, full of sound and fury,\n", "Signifying nothing.\"\"\"\n", "print(a)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The types of quotes do not change anything!\n", "a = \"fish\" # double quote\n", "b = 'fish' # single quote\n", "print(a == b) # the string is the same!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Lists\n", "\n", "Lists are data types containing a sequence of values. The size of the list can change during run-time, as you add and remove elements from the list. \n", "\n", "Here is how you can create lists:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_a = [] # empty\n", "print(f\"list_a {list_a}\")\n", "\n", "list_b = [1, 2, 3, 4] # 4 elements\n", "print(f\"list_b {list_b}\")\n", "\n", "list_c = [1, 'cat', 0.23] # mixed types\n", "print(f\"list_c {list_c}\")\n", "\n", "list_d = [1, ['cat', 'dog'], 2, 3] # list in list\n", "print(f\"list_d {list_d}\")\n", "\n", "list_e = [1] * 10 # a list of 1s of length 10\n", "print(f\"list_e {list_e}\")\n", "\n", "list_f = list(range(5)) # turns range object into a list\n", "print(f\"list_f {list_f}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below we introduce some common operations with lists.\n", "* Use `len(list1)` to find the length of the list.\n", "* `list1.append(element)` to add an element to the end of the list.\n", "* `list1.insert(index, element)` to add an element to an index in the list\n", "* `list1.extend(list2)` to extend the elements of list1 with the elements of list2\n", "* `list1.pop()` removes last element from the list\n", "* `list1.pop(index)` removes the element at the given index\n", "* `list1.remove(element)` removes the first instance of the given element" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Some common operations\n", "b = [\"great\", \"minds\", \"think\", \"alike\"]\n", "print(f\"b: {b}\")\n", "\n", "# finding the length\n", "print(f\"length of b is {len(b)}\")\n", "\n", "# append element to list\n", "b.append(\"sometimes\")\n", "print(f\"b.append(\\\"sometimes\\\")= {b}\")\n", "\n", "# extend list\n", "c = [\"-\", \"Abraham\", \"Lincoln\"]\n", "b.extend(c)\n", "print(f\"c: {c}\")\n", "print(f\"b.extend(c) = {b}\")\n", "\n", "# removes element and specific index\n", "b.pop(6) \n", "print(f\"b.pop(6) = {b}\")\n", "\n", "# remove specific element\n", "b.remove(\"Lincoln\") \n", "b.remove(\"-\")\n", "print(f\"b.remove(\\\"Lincoln\\\"); b.remove(\\\"-\\\") = {b}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also check whether an element is in a list in the following way:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_1 = [\"a\", \"b\", \"c\"]\n", "if \"b\" in list_1:\n", " print(\"\\\"b\\\" is in list\")\n", "else:\n", " print(\"\\\"b\\\" is not in list\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.1. List Indexing and Slicing:\n", "\n", "You can extract a single element from a list in the following way:\n", "`list1[index]`\n", "\n", "In lists, the indices start from 0. You can also index elements from the end of the list to the beginning by $-1, -2, -3...$. Check out the image below for the example list:\n", "\n", "`list_1 = [\"a\", \"b\", \"c\", \"d\", \"e\"]`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* You can extract multiple elements by slicing. This will give you elements from the start up to **(but not including)** the end index.\n", "\n", " `list1[start_index:end_index]`\n", "\n", "\n", "* If you do not specify the `start_index`, you will retrieve the elements from index $0$ up to the `end_index`.\n", "\n", " `list1[:end_index]` is the same as `list1[0:end_index]`\n", "\n", "\n", "* If you do not specify the `end_index`, you will retrieve the elements from the `start_index` up to (and **including**) the end of the list.\n", "\n", " `list1[start_index:]`\n", "\n", "\n", "* You can provide a step size.\n", " `list1[start_index:end_index:step_size]`\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Exercise\n", "\n", "Try to write the output of the following code **BEFORE** running the cell." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Don't run BEFORE you solve it!\n", "list_1 = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n", "\n", "print(f\"list_1[-3] = {list_1[-3]}\")\n", "print(f\"list_1[0:2] = {list_1[0:2]}\")\n", "print(f\"list_1[:4:2] = {list_1[:4:2]}\")\n", "print(f\"list_1[::-1] = {list_1[::-1]}\")\n", "print(f\"list_1[-4:-1] = {list_1[-4:-1]}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also assign new values to indices using slicing. Here is an example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_1 = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n", "\n", "list_1[-1]= \"<3\"\n", "print(list_1)\n", "\n", "list_1[0:2] = [\"x\", \"y\"]\n", "print(list_1)\n", "\n", "list_1[::2] = [\":)\",\":(\", \":O\"]\n", "print(list_1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.2. Copying\n", "\n", "We have one last thing to say about lists. Observe the behaviour of the following code:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Case 1:\n", "\n", "list_1 = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n", "print(f\"list_1 before {list_1}\")\n", "\n", "list_2 = list_1\n", "list_2.append(\"Z\")\n", "\n", "print(f\"list_1 after {list_1}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Case 2:\n", "\n", "list_1 = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n", "print(f\"list_1 before function {list_1}\")\n", "\n", "def function_that_changes_list(input_list):\n", " input_list.append(\"Z\")\n", "\n", "function_that_changes_list(list_1)\n", "\n", "print(f\"list_1 after function {list_1}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We never changed list_1 explicitly, but the values changed anyway. What's going on?\n", "\n", "Well, in Python, when you say `list_2 = list_1`, you are not actually creating a new list, you are only copying the **reference** to the same list. This means that they are actually two variables pointing to the same list! So when you change the values of `list_2`, the values of `list_1` also change (since they are referring to the same list). Something similar is at play when you pass this list to a function. So be careful!\n", "\n", "If you do not want this to happen, you can use the function `.copy()` to create a new object with the same values. \n", "\n", "#### Exercise\n", "\n", "Change the code below and fix the two cases given above using the `.copy()` function. Make sure the contents of `list_1` do not change." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Case 1\n", "list_1 = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n", "print(f\"list_1 before {list_1}\")\n", "\n", "list_2 = list_1\n", "list_2.append(\"Z\")\n", "\n", "print(f\"list_1 after {list_1}\")\n", "print(\"**\")\n", "\n", "# Case 2\n", "list_1 = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n", "print(f\"list_1 before function {list_1}\")\n", "\n", "def function_that_changes_list(input_list):\n", " input_list.append(\"Z\")\n", "\n", - "list_2 = list_1.copy()\n", + "list_2 = list_1\n", "function_that_changes_list(list_2)\n", "\n", "print(f\"list_1 after function {list_1}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Exercise\n", "\n", "Now that we know how lists work, here is a quick exercise for you. Fill in the function below that takes a list and returns True if it is a palindrome, False if it is not. Palindromes are defined as sequences that read the same forwards and backwards.\n", "Examples of palindrome lists:\n", "* [\"cat\", \"dog\", \"fish\", \"dog\", \"cat\"]\n", "* [0, 1, 2, 3, 3, 2, 1, 0]\n", "* [1]\n", "* []\n", "\n", "You may use a for-loop in this exercise. However, if you're feeling ambitious try to do it in 1 line, without using a for-loop (hint: use slicing)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def function_is_palindrome(input_list):\n", " is_palindrome = True\n", " # Your code here\n", " return is_palindrome" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_list_1 = [\"cat\", \"dog\", \"fish\", \"dog\", \"cat\"]\n", "res_1 = function_is_palindrome(test_list_1)\n", "\n", "test_list_2 = [\"cat\", \"dog\", \"fish\", \"bird\", \"dog\", \"cat\"]\n", "res_2 = function_is_palindrome(test_list_2)\n", "\n", "test_list_3 = [\"cat\"]\n", "res_3 = function_is_palindrome(test_list_3)\n", "\n", "test_list_4 = [\"cat\", \"cat\"]\n", "res_4 = function_is_palindrome(test_list_4)\n", "\n", "if not (res_1 and not res_2 and res_3 and res_4):\n", " print(\"Test failed\")\n", "else:\n", " print(\"Correct! :)\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Tuples\n", "\n", "Tuples are similar to lists but they are fixed in size and **immutable**, which means that change is not allowed.\n", "We declare tuples in the following way using parentheses`()`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tuple_1 = (\"wash\", \"your\", \"hands\", \"with\", \"soap\")\n", "\n", "print(f\"tuple_1 = {tuple_1}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since change is not allowed, observe the result of the following piece of code." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tuple_1[2] = (\"face\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can typecast from list to tuple and vice versa! " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sequence_1 = [\"here\", \"comes\", \"the\", \"sun\"]\n", "print(f\"{sequence_1} is {type(sequence_1)}\")\n", "\n", "\n", "# from list to tuple\n", "sequence_1 = tuple(sequence_1)\n", "print(f\"{sequence_1} is {type(sequence_1)}\")\n", "\n", "#from tuple to list\n", "sequence_1 = list(sequence_1)\n", "print(f\"{sequence_1} is {type(sequence_1)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6. Dictionaries\n", "\n", "An incredibly useful data type to know, you might also know dictionaries as \"hash maps\". Dictionaries are collections of \"key: value\" pairs. You can access the values using the keys in $O(1)$ time.\n", "\n", "The keys of a dictionary must be **immutable** and **unique**. Below we show how to define a dictionary.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "shopping_list = {\"apples\": 3, \"pears\":2, \"eggs\":6, \"bread\":1, \"yogurt\":1}\n", "print(f\"shopping_list = {shopping_list}\")\n", "print(\"**\")\n", "\n", "\n", "book_dict = {}\n", "print(f\"book_dict = {book_dict}\")\n", "#add key value pairs\n", "book_dict[\"vonnegut\"] = \"cat\\'s cradle\"\n", "book_dict[\"ishiguro\"] = \"never let me go\"\n", "print(f\"book_dict = {book_dict}\")\n", "print(\"**\")\n", "\n", "# we can retrieve the dict keys:\n", "print(book_dict.keys())\n", "# and the dict values:\n", "print(book_dict.values())\n", "print(\"**\")\n", "\n", "#we can also iterate through the dict keys and values with a for-loop\n", "for key, value in book_dict.items():\n", " print(f\"{key} : {value}\")\n", "\n", "print(\"**\")\n", "#we can modify the value of a key\n", "book_dict[\"ishiguro\"] = \"a pale view of hills\"\n", "print(f\"modified book_dict = {book_dict}\")\n", "print(\"**\")\n", "\n", "#and we can remove a key completely\n", "removed_value = book_dict.pop(\"ishiguro\")\n", "print(f\"book_dict with removed value = {book_dict}\")\n", "print(f\"removed_value = {removed_value}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 7. Functions\n", "\n", "You can define a function in Python in the following way:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def multiply(a, b):\n", " return a * b\n", "\n", "print(f\"multiply(100, 2) = {multiply(100, 2)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can have default arguments by specifying their default value in the parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def add(a, b, c=0, d=1):\n", " return a + b + c + d\n", "\n", "# use no default arguments\n", "print(f\"add(1, 2, 100, 1000) = {add(1, 2, 100, 1000)}\")\n", "\n", "# use the default value of d\n", "print(f\"add(1, 2, 100) = {add(1, 2, 100)}\")\n", "\n", "# use the default value of c and d\n", "print(f\"add(1, 2) = {add(1, 2)}\")\n", "\n", "# use the default value of c\n", "print(f\"add(1, 2, d=1000) = {add(1, 2, d=1000)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A function can return multiple values in a tuple. You can assign the values of the tuple to separate variables. This is called **tuple unpacking**." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def min_max(input_list):\n", " return min(input_list), max(input_list)\n", "\n", "\n", "test_list = [1,2,3,4]\n", "min_val, max_val = min_max(test_list)\n", "print(f\"min_val: {min_val}, max_val: {max_val}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: You have seen tuple unpacking when using function `enumerate` in for-loop." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 7.1. Common Built-in Functions\n", "\n", "Here we introduce some nifty commonly used built-in functions. \n", "\n", "* You already learned `range()`, `enumerate()`!\n", "* We have also seen `type()` to return the type of the object. We use `str()`, `int()`, `float()`, `list()`, `tuple()` for typecasting.\n", "* The functions `len()`, `sum()`, `min()`, `max()`, `any()`, `all()`, `sorted()`, `zip()` are useful for lists and tuples.\n", "\n", "Let's see them in action below" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_1 = list(range(5))\n", "print(f\"list_1 = {list_1}\")\n", "\n", "print(f\"len(list_1) = {len(list_1)}\")\n", "print(f\"sum(list_1) = {sum(list_1)}\")\n", "print(f\"min(list_1) = {min(list_1)}\")\n", "print(f\"max(list_1) = {max(list_1)}\")\n", "print(\"**\")\n", "\n", "\n", "list_2 = [5,3,1,2,0,6]\n", "print(f\"list_2 = {list_2}\")\n", "print(f\"sorted(list_2) = {sorted(list_2)}\")\n", "print(\"**\")\n", "\n", "\n", "# any checks whether there are any 1s in the list (OR)\n", "# all checks whether all elements are 1s. (AND)\n", "# in Python: 1 = True, 0 = False\n", "list_3 = [1, 1, 1]\n", "print(f\"list_3 = {list_3}\")\n", "print(f\"any(list_3) = {any(list_3)}\")\n", "print(f\"all(list_3) = {all(list_3)}\")\n", "\n", "list_4 = [0, 1, 1]\n", "print(f\"list_4 = {list_4}\")\n", "print(f\"any(list_4) = {any(list_4)}\")\n", "print(f\"all(list_4) = {all(list_4)}\")\n", "\n", "list_5 = [0, 0, 0]\n", "print(f\"list_5 = {list_5}\")\n", "print(f\"any(list_5) = {any(list_5)}\")\n", "print(f\"all(list_5) = {all(list_5)}\")\n", "print(\"**\")\n", "\n", "# zip function:\n", "x = [1,2,3]\n", "y = [4,5,6]\n", "zipped = zip(x,y)\n", "print(f\"x {x}\")\n", "print(f\"y {y}\")\n", "print(f\"zipped {list(zipped)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 7.2. List Comprehensions\n", "\n", "One of the most practical things about Python is that you can do many things on just a single line. One popular example is so called *list comprehensions*, a specific syntax to create and initalize lists of objects. Here are some examples.\n", "\n", "A syntax for list comprehension is shown below:\n", "`[thing for thing in list]`\n", "\n", "Let's make it more concrete with an example." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_of_numbers = [1, 2, 3, 101, 102, 103]\n", "print(f\"list_of_numbers = {list_of_numbers}\")\n", "\n", "#I want to create a new list with all these items doubled.\n", "doubled_list = [2 * elem for elem in list_of_numbers]\n", "print(f\"doubled_list = {doubled_list}\")\n", "\n", "#A new list with all these items as floats\n", "float_list = [float(elem) for elem in list_of_numbers]\n", "print(f\"float_list = {float_list}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make it more interesting by adding an if in there:\n", "\n", "`[thing for thing in list if condition]`\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#I want to create a new list with all these items doubled\n", "#IF the element is above 100\n", "conditional_doubled_list = [2 * elem for elem in list_of_numbers if elem > 100]\n", "print(f\"conditional_doubled_list = {conditional_doubled_list}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Exercise\n", "\n", "You will be given a list of vocabulary words. Your task is to use list comprehensions to iterate through a document and create a new list including the words that are included in the vocabulary. You don't need to worry about duplicates.\n", "\n", "Example: \n", "```python\n", "vocabulary = [\"a\" \"c\", \"e\"]\n", "document = [\"a\", \"b\", \"c\", \"d\"]\n", "new_list = [\"a\", \"c\"]\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vocabulary = ['epfl', 'europe', 'swiss', 'switzerland', 'best', 'education', 'high', 'higher', 'research', 'school', 'science', 'students', 'technology', 'top-tier', 'university']\n", "\n", "document = \"\"\"The École polytechnique fédérale de Lausanne (EPFL) is a research institute\n", "and university in Lausanne, Switzerland, that specializes in natural sciences and engineering.\n", "It is one of the two Swiss Federal Institutes of Technology, and it has three main missions: \n", "education, research and technology transfer at the highest international level. EPFL is widely regarded \n", "as a world leading university. The QS World University Rankings ranks EPFL 12th in the world \n", "across all fields in their 2017/2018 ranking, whilst Times Higher Education World \n", "University Rankings ranks EPFL as the world's 11th best school for Engineering and Technology.\"\"\"\n", "document_parsed = document.split()\n", "document_parsed = [word.lower() for word in document_parsed]\n", "new_list = []\n", "\n", "#your code here\n", "new_list = ...\n", "\n", "#We convert the list to a set and then back to a list. We do this because converting it to a set automatically\n", "#removes duplicates (since sets are sequences that do not contain duplicates). afterwards we sort it.\n", "new_list = sorted(list(set(new_list)))\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "correct_result = ['best', 'education', 'epfl', 'higher', 'research', 'school', 'swiss', 'technology', 'university']\n", "\n", "if new_list == correct_result:\n", " print (\"Correct! :)\")\n", "else:\n", " print (\"Incorrect :(\")\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## 8. (optional now - useful later on) Object-Oriented Programming\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Object-oriented programming is a programming paradigm that provides a means of structuring programs so that properties and behaviors are bundled into individual objects.\n", "\n", "For this end, we use classes. Classes are used to create user-defined data structures. Classes define functions called methods, which identify the behaviors and actions that an object created from the class can perform with its data." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All class definitions start with the class keyword, which is followed by the name of the class and a colon. Any code that is indented below the class definition is considered part of the class’s body. To start, let's declare an `EPFL_faculty` class." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class EPFL_faculty:\n", "\n", " def __init__(self, name, number_of_students):\n", " self.name = name\n", " self.number_of_students = number_of_students\n", "\n", " # Instance method\n", " def description(self):\n", " return f\"The faculty {self.name} has {self.number_of_students} students\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While the class is the blueprint, an instance is an object that is built from a class and contains real data. `.__init__()` sets the initial state of the object by assigning the values of the object’s properties. That is, `.__init__()` initializes each new instance of the class." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sti = EPFL_faculty(\"STI\", 500)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that the instance `sti` has been created, we can call the method description." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sti.description()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inheritance is the process by which one class takes on the attributes and methods of another. Newly formed classes are called child classes, and the classes that child classes are derived from are called parent classes. Child classes inherit from the parent's attributs and methods but it can overwrite methods. Let's define a class `EPFL_section` that inherits from `EPFL_faculty` and that overwrites the method `description(self)`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class EPFL_section(EPFL_faculty):\n", " \n", " def description(self):\n", " return f\"The section {self.name} has {self.number_of_students} students\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "genie_mechanique = EPFL_section(\"GM\", 200)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "genie_mechanique.description()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Disclaimer__: This part of the tutorial and its text was inspired by and taken from \"Object-Oriented Programming (OOP) in Python 3\" by David Amos. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Additional OOP resources" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For further information on object-oriented programming and classes in Python, here are two useful resources:\n", "* Object-Oriented Programming (OOP) in Python 3: https://realpython.com/python3-object-oriented-programming/\n", "* Classes from the official Python Tutorial: https://docs.python.org/3/tutorial/classes.html" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9. (optional now - useful later on) Matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Perhaps the most widely used plotting library in Python is Matplotlib. If you've ever used MATLAB, you'll find that the functions look pretty similar. \n", "\n", "In the following exercise sessions, we won't ask you to do any plotting. So this part is optional for those who are interested in having a short introduction.\n", "\n", "First, we will import Matplotlib." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Importing in Python\n", "\n", "* A short note on importing: to be able to use modules in our code, we import them. \n", " \n", " example: `import numpy`\n", " \n", "\n", "* We can also select a name for the imported module.\n", " \n", " example: `import numpy as np`. Now when we call numpy functions, we will always use `np.` as a prefix, i.e. `np.zeros()`\n", " \n", "\n", "* You can also choose to only import selected functions/variables/classes from the module. \n", " \n", " example: `from numpy import arange`. Now you can use this function as `arange(5)`. You cannot use any other functions from the numpy module as you did not import them." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# To import Matplotlib we do:\n", "\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's do some plotting! \n", "\n", "Let's start with the simplest of plots, the good old line-plot. The function we will use is `plot()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Let's create some data first to plot\n", "x = list(range(10))\n", "y = [2,3,5,1,0,2,3,0,0,1]\n", "\n", "#first create a figure\n", "fig = plt.figure()\n", "\n", "#now do the plotting\n", "#specifying a color and marker are optional.\n", "#check out the documentation to see what else you can do with the plot function\n", "plt.plot(x, y, marker=\"*\", color=\"r\")\n", "\n", "#axis labels and title\n", "plt.xlabel(\"x\")\n", "plt.ylabel(\"y\")\n", "plt.title(\"just a random plot\")\n", "\n", "#so that we see the plot\n", "plt.show()\n", "\n", "#close the plot\n", "plt.close(fig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Exercise\n", "\n", "You can plot two lines on top of one another by calling the `plt.plot()` function consecutively. Try to implement this! Also, specify the parameter `label` of the `plt.plot()` function and call the function `plt.legend()` to create a legend for your graph. It should look like the figure shown below." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![result](images/two_lines_plot.png)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Let's create some data first to plot\n", "x = list(range(10))\n", "y1 = [2,3,5,1,0,2,3,0,0,1]\n", "y2 = [1,2,3,5,1,0,2,3,0,0]\n", "\n", "#first create a figure\n", "fig = plt.figure()\n", "#your code here\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can create scatter plots (line plots without lines) with `scatter()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Let's create some data first to plot\n", "x = list(range(10))\n", "y1 = [2,3,5,1,0,2,3,0,0,1]\n", "\n", "#first create a figure\n", "fig = plt.figure()\n", "\n", "#now do the plotting\n", "p1 = plt.scatter(x, y1, marker=\"*\", color=\"r\")\n", "\n", "#axis labels and title\n", "plt.xlabel(\"x\")\n", "plt.ylabel(\"y\")\n", "plt.title(\"just a random scatter plot\")\n", "\n", "#so that we see the plot\n", "plt.show()\n", "\n", "#close the plot\n", "plt.close(fig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And you can read and display images with `imread()` and `imshow()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#first create a figure\n", "fig = plt.figure()\n", "\n", "#now do the plotting\n", "im = plt.imread(\"images/krabby_patty.jpg\")\n", "plt.imshow(im)\n", "\n", "#axis labels and title\n", "plt.xlabel(\"x\")\n", "plt.ylabel(\"y\")\n", "plt.title(\"krabby patty\")\n", "\n", "#so that we see the plot\n", "plt.show()\n", "\n", "#close the plot\n", "plt.close(fig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And that's all for this exercise! If you have any problems, just ask (or even Google) them. You can check out the official Python tutorials for further learning.\n", "\n", "https://docs.python.org/3/tutorial/" ] } ], "metadata": { "kernelspec": { "display_name": "Python", "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.8.10" } }, "nbformat": 4, "nbformat_minor": 4 }