diff --git a/binf/index.ipynb b/binf/index.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..b77f5b6b58f8bea3cfeb850ce6fc917b59c8f67e --- /dev/null +++ b/binf/index.ipynb @@ -0,0 +1,1038 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "# to run in a presentation mode run from shell \n", + "# jupyter nbconvert 'Introduction2Python-Interactive.ipynb' --to slides --post serve" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Introduction to Bioinformatics, Winter Semester 2023/24\n", + "### <span style=\"color:green\">AG Matuszyńska</span> [cpbl.rwth-aachen.de](https://www.cpbl.rwth-aachen.de/go/id/sazuq/?lidx=1).\n", + "### Week 02 - (gentle) Introduction to Python\n", + "\n", + "\n", + "This interactive lecture provides a gentle introduction to syntax and semantics of Python programming language. It is based on the material created and curated by Marvin van Aalst, Tobias Pfennig and Anna Matuszyńska, iterated over the years while teaching introduction to Python at the university level and during the summer schools in computational modelling." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Why to learn how to program?\n", + "\n", + "* Teaches abstract thinking.\n", + "* Is solution oriented.\n", + "* Speeds up many processes." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Why to learn Python?\n", + "\n", + "* It is beginner-friendly and has accessible syntax.\n", + "* It is versatile.\n", + "* Python developers are in high demand on the job market. \n", + "* It is community driven and there is a lot of support on-line.\n", + "* It is the fastest-growing programming language." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Why Python and not R?\n", + "\n", + "- Many people are using it, including NASA (take a look at [lecture for astronomers](https://swift.gsfc.nasa.gov/results/BATbursts/ASTR_288C/Lecture6.pdf)\n", + "- Easy to start\n", + "- Created by a researcher for researchers (conceived in the late 1980s by Guido van Rossum at Centrum Wiskunde & Informatica (CWI) in the Netherlands)\n", + "- (A lot) more human friendly than basic languages like C\n", + "- Powerful (combine coding and ploting)\n", + "- Many, many supporing packages for various disciplines (including our own [modelbase](https://pypi.org/project/modelbase/))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Python philosophy\n", + "The core philosophy of the language is summarized by the document \"PEP 20 (The Zen of Python)”" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import this" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Who is using Python?\n", + "\n", + "* Web Development: Google, Yahoo, Shopzilla\n", + "* Games: Battelfield 2, Civilization 4, Star Trek Bridge Commander\n", + "* Financial: Altis Investment Management\n", + "* Graphics: Walt Disney Feature Animation\n", + "* Software Development: Nokia, Red Hat\n", + "* Science: The National Research Council of Canada,\n", + " Los Alamos National Laboratory Theoretical Physics Division\n", + " NASA\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Jupyter notebook overview\n", + "\n", + "* Splits code into cells\n", + " * Code cells\n", + " * Markdown cells\n", + "* Command mode (**no blue border around code cell**)\n", + " * `Esc` to enter\n", + " * `A` to create cell above\n", + " * `B` to create cell below\n", + " * `D + D` to delete cell\n", + " * `M` to change cell type to Markdown\n", + " * `Y` to change cell type to Code\n", + "* Edit mode (**with blue border around code cell**)\n", + " * `Enter`\n", + " * `Ctrl + Shift + -` Split cell at cursor position\n", + "* Execute code in a cell\n", + " * `Ctrl + Enter` run selected cell\n", + " * `Shift + Enter` run selected cell and select below" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Getting help and understanding errors\n", + "\n", + "Your code will produce errors and you will forget how certain functions work. \n", + "That's absolutely fine and nothing to worry about. \n", + "Since no-one can remember everything, programmers usually build helpers for themselves. \n", + "\n", + "Your first tool for that is a **comment**. You write comments following the `#` character. \n", + "\n", + "```python\n", + "# Future me: this is why I implemented the function in the following way\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# How to comment your code properly?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "## Code Tells You How, Comments Tell You Why" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "The second tool is a documentation string (or `docstring` for short). \n", + "You write docstrings in the first line of a function (more on that later). \n", + "\n", + "```python\n", + "def my_func():\n", + " \"\"\"Some very important information\"\"\"\n", + " return None\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def sum_this(a, b, c):\n", + " \"\"\"Use this multi-line string to explain what your function does,\n", + " which arguments it takes and what it returns\n", + "\n", + " Arguments\n", + " ---------\n", + " a specify type (int, float?)\n", + " b int\n", + " c int\n", + "\n", + " Returns\n", + " -------\n", + " Sum of a, b and c\n", + " \"\"\"\n", + " return a + b + c" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Other people also (hopefully) write `docstrings`. \n", + "For any function, you can call the `help` function to get its `docstring`.\n", + "\n", + "```python\n", + "help(print)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "If you want to know what methods are available for a given object, thet `dir()` function will tell you that. \n", + "Another quick way of checking this is to write `object.` and then use the `<TAB>` key to auto-complete. \n", + "\n", + "```python\n", + "dir(\"Hello, World\")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "You will frequently encounter errors when programming - that perfectly normal, however you should learn to understand what they *mean*. \n", + "For example, if you type `\"1\" + 1` you will get something along the lines of \n", + "\n", + "```TypeError: can only concatenate str (not \"int\") to str```. \n", + "\n", + "Don't worry if you don't understand yet what that means, it hopefully will get clear in the next minutes. \n", + "Of course, you can always ask us if you don't know how to proceed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Syntax and semantix" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "Assign value 5 to a variable called x. This is semantics." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "Programming languages offer different ways to provide the same semantix:\n", + " \n", + " R: x<- 5\n", + " \n", + " Pascal: x := 5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "#answer" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "The syntax is the set of rules that defines how a program should be written. It is language-specific constraint on how we express semantics." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Syntax and semantix\n", + "The **syntax** of a programming language describes which strings of of characters comprise a valid program. The **semantics** of a programming language describes what syntactically valid programs mean, what they do. In the larger world of linguistics, syntax is about the form of language, semantics about meaning" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Data types and containers in general\n", + "\n", + "* Data types\n", + " - Numbers: integer, floats and complex\n", + " - Strings: Text\n", + " - Boolean: True and False\n", + " - None\n", + "\n", + "* Containers\n", + " - Tuples\n", + " - Sets\n", + " - Lists\n", + " - Dictionaries" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Basic data types\n", + "\n", + "Python has several basic data types: \n", + "\n", + "| Type | Type | Value(s) | \n", + "| --- | --- | --- | \n", + "|Text type| `str` | ..., -1, 0, 1, ... |\n", + "|Numeric type| `float` | ..., -3.14, 0.0, 2.71, ... |\n", + "|Numeric type|`int` | ..., -1, 0, 1, ... |\n", + "|None type| `None` | `None` | \n", + "|Boolean type|`bool` | `True`, `False` |\n", + "\n", + "next to sequence, mappind, binary and set types. \n", + "\n", + "We will go through each the four most important types (`None`, `bool`, `int` and `float`), showing their use case and how they can be transformed.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Bool\n", + "\n", + "The boolean type `bool` can only hold the values `True` and `False`. \n", + "It can be used for simple logic operations. \n", + "There are five operators for `bool`. \n", + "\n", + "| Operator | `True` if |\n", + "| ------- | --------- |\n", + "| `and` | **both** the inputs are `True` | \n", + "| `or` | one or both of the inputs are `True` | \n", + "| `not` | the input is `False` | \n", + "| `==` | both inputs are equal | \n", + "| `!=` | both inputs are unequal | \n", + "\n", + "Now print the results and check if you were correct." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# answer" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Exercise: logic / truth table\n", + "\n", + "Fill out the following truth table for two inputs `p` and `q`\n", + "\n", + "| p | q | p and q | p or q | \n", + "| --- | --- | --- | --- |\n", + "| T | T | ? | ? |\n", + "| T | F | ? | ? |\n", + "| F | T | ? | ? |\n", + "| F | F | ? | ? |\n", + "\n", + "and next print the results.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# answer" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "In the example above, you can check the type of object x by typing`type(x)`. Try it" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "# answer" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Function definition\n", + "\n", + "Before we continue exploring things to do with those data types, let's introduce an important building block for programs: **functions**. \n", + "\n", + "With functions you can save a specific sequence of instructions to be run, so that you don't have to write out the entire sequence every time you want to run it." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "To create your own custom functions you need to define them before they can be called. \n", + "The basic syntax for this is \n", + "\n", + "- the keyword `def` followed by a `name` you can choose\n", + "- the function parameters in parentheses followed by colon (`:`), and a line break\n", + "- and then the **indented** function body\n", + "- a **return statement**, indicating where to exit the function while returning a value" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "A function can have zero `()`, one `(a)` or multiple `(a, b, ...)` arguments. \n", + "An example (with the **indentation of four spaces being part of the syntax**) below:\n", + "\n", + "\n", + "\n", + "\n", + "You have to use this syntax exactly. Try around a bit to see what happens, if you change minor things about it" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# define the function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# change the code 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "outputs": [], + "source": [ + "# change the code 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# change the code 3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "outputs": [], + "source": [ + "# change the code 4" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Function calls\n", + "\n", + "To call a function, you use it's name and then supply the arguments in parentheses:\n", + "\n", + "```python\n", + "print(True and False)\n", + "```\n", + "\n", + "Again, you have to be precise about the syntax." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Call the previously created function add to see what is the sum of 4 and 5." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# answer" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Try printing several boolean values, like `True` or `True, False`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# answer" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Exercise: xor\n", + "\n", + "`XOR` (exclusive or) is `True` when **only one** of its inputs is `True`, so its closer to how we usually think of what the word `or` means. \n", + "For two propositions `p` and `q` the `xor` function can be written as \n", + "\n", + "$(p \\land \\neg q) \\lor (\\neg p \\land q)$\n", + "\n", + "with $\\land$ meaning `and`, $\\lor$ meaning `or` and $\\neg$ meaning `not`.\n", + "\n", + "Write a function `xor(p, q)` that takes two boolean values and returns the exclusive or of them." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Note on solving the exercises\n", + "\n", + "In all following exercises you will be asked to write functions to implement certain behaviour. \n", + "In order to help you solve the puzzles on your own, we already supply the name of the functions, while you will have to replace the Ellipses (`...`) with actual code. \n", + "\n", + "```python\n", + "def xor(p, q):\n", + " ... # replace this\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Assert\n", + "Note that below the function we have already supplied `assert` statements. \n", + "\n", + "\n", + "```python\n", + "assert xor(True, True) == False\n", + "```\n", + "\n", + "These asserts will test your function, so you can check if it was implemented correctly and if not, for *which input* the output fails, which you can use as a guide to where the error in your implementation might be. \n", + "**Please don't change or remove those `assert` statements**. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# answer. Make sure your functions name is xor\n", + "\n", + "assert xor(True, True) == False\n", + "assert xor(True, False) == True\n", + "assert xor(False, True) == True\n", + "assert xor(False, False) == False" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Conditional control flow\n", + "\n", + "The real use case of boolean values becomes apparent when one uses them to control how the program executes. \n", + "This can be done with the `if` / `elif` / `else` statements, `elif` being short for `else if`. \n", + "Note that the `if` statement can stand completely alone, be optionally followed by an arbitrary amount of `elif` statements and lastly finished with an optional `else` statement. \n", + "So in the example below, both the `elif` and `else` block could have been omitted. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Just like with functions, **indentation of four spaces is part of the syntax**. \n", + "\n", + "```python\n", + "if it_rains():\n", + " pack_raincoat()\n", + "elif it_is_sunny(): \n", + " pack_tshirt()\n", + "else:\n", + " pack_jacket()\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Exercise: xor with branching\n", + "\n", + "Rewrite your `xor` function from above with `if` and `else` statements instead of the `and` or `or` operators." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# answer. Make sure your functions name is xor_if_else\n", + "\n", + "\n", + "assert xor_if_else(True, True) == False\n", + "assert xor_if_else(True, False) == True\n", + "assert xor_if_else(False, True) == True\n", + "assert xor_if_else(False, False) == False" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Numbers\n", + "\n", + "There are two basic number types in Python: `int` and `float`. `int` numbers don't have decimal digits, while `float` values do. The usual arithmetic operations are defined, however they come with some sharp edges one should be aware of. We will come back to them later on.\n", + "\n", + "| Operation | Operator | \n", + "| --- | --- |\n", + "| Addition | `+` |\n", + "| Subtraction | `-` |\n", + "| Multiplication | `*` |\n", + "| Exponentiation | `**` |\n", + "| Float Division | `/` |\n", + "| Int (Floor) Division | `//` |\n", + "| Modulus | `%` |" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Just like with booleans we can compare if two numbers are exactly equal\n", + "\n", + "| Operator | Meaning | \n", + "| --- | --- |\n", + "| `==` | Exactly equal |\n", + "| `!=` | Not equal |" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "But we can also check whether one is larger or smaller than the other\n", + "\n", + "| Operator | Meaning | \n", + "| --- | --- |\n", + "| `>` | Larger |\n", + "| `>=` | Larger or equal |\n", + "| `<` | Smaller |\n", + "| `<=` | Smaller or equal |" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Exercise: multiple of two numbers\n", + "\n", + "Create a function that checks if a number is a multiple of two other numbers `a` and `b`\n", + "\n", + "**Hint**: the **modulo operator (%)** returns the remainder of a division\n", + "\n", + "| Input | Output |\n", + "| ----- | ------ |\n", + "| 4 % 3 | 1 |\n", + "| 5 % 3 | 2 |\n", + "| 6 % 3 | 0 |" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# answer. Make sure your functions name is is_multiple_of_both\n", + "\n", + "\n", + "assert is_multiple_of_both(2, 2, 1)\n", + "assert is_multiple_of_both(6, 3, 2)\n", + "assert not is_multiple_of_both(7, 3, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Assignment\n", + "\n", + "Before we continue exploring things to do with numbers, let's talk about **assignment statements**. \n", + "You can assign a value to a variable using the `name = value` syntax, for example\n", + "\n", + "```python\n", + "x1 = 1\n", + "```\n", + "\n", + "After you assigned a value to a variable, you can use the name to refer to the value\n", + "\n", + "```python\n", + "print(x1) # This prints \"1\"\n", + "```\n", + "\n", + "Note that the syntax requires your name to start with an character. \n", + "\n", + "```python\n", + "1x = 1 # this doesn't work!\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Exercise: convert 24 hour clock to 12 hour clock face\n", + "\n", + "Write a function that transform a `[0, 24)` hour input to the `[1, 12]` hour range of a clock face. \n", + "\n", + "**Hint**: again use the modulo operator `%` \n", + "**Hint**: `0` and `12` are special cases - why? " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# answer. Make sure your functions name is clock_face\n", + "\n", + "\n", + "assert clock_face(1) == clock_face(13) == 1\n", + "assert clock_face(2) == clock_face(14) == 2\n", + "assert clock_face(3) == clock_face(15) == 3\n", + "assert clock_face(4) == clock_face(16) == 4\n", + "assert clock_face(5) == clock_face(17) == 5\n", + "assert clock_face(6) == clock_face(18) == 6\n", + "assert clock_face(7) == clock_face(19) == 7\n", + "assert clock_face(8) == clock_face(20) == 8\n", + "assert clock_face(9) == clock_face(21) == 9\n", + "assert clock_face(10) == clock_face(22) == 10\n", + "assert clock_face(11) == clock_face(23) == 11\n", + "assert clock_face(12) == clock_face(0) == 12" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "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.11.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}