diff --git a/.gitignore b/.gitignore index 85ce87d..f21aa6b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,14 @@ index_files/ build/ .DS_Store +.ipynb_checkpoints + # IDE files .idea/ + +# Files generated by Python snippets +example.txt +out.csv + +# Vscode files +.vscode/ diff --git a/img/github-mark-white.png b/img/github-mark-white.png new file mode 100644 index 0000000..50b8175 Binary files /dev/null and b/img/github-mark-white.png differ diff --git a/img/github-mark-white.svg b/img/github-mark-white.svg new file mode 100644 index 0000000..d5e6491 --- /dev/null +++ b/img/github-mark-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/github-mark.png b/img/github-mark.png new file mode 100644 index 0000000..6cb3b70 Binary files /dev/null and b/img/github-mark.png differ diff --git a/img/github-mark.svg b/img/github-mark.svg new file mode 100644 index 0000000..37fa923 --- /dev/null +++ b/img/github-mark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/matplotlib_logo.svg b/img/matplotlib_logo.svg new file mode 100644 index 0000000..3b133c9 --- /dev/null +++ b/img/matplotlib_logo.svg @@ -0,0 +1 @@ + diff --git a/img/miniforge_window_add_to_path.png b/img/miniforge_window_add_to_path.png new file mode 100644 index 0000000..5e0969c Binary files /dev/null and b/img/miniforge_window_add_to_path.png differ diff --git a/img/numpylogoicon.svg b/img/numpylogoicon.svg new file mode 100644 index 0000000..262f030 --- /dev/null +++ b/img/numpylogoicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/pandas_logo.svg b/img/pandas_logo.svg new file mode 100644 index 0000000..e51ebaf --- /dev/null +++ b/img/pandas_logo.svg @@ -0,0 +1 @@ + diff --git a/img/python-ecosystem.png b/img/python-ecosystem.png new file mode 100644 index 0000000..e625d40 Binary files /dev/null and b/img/python-ecosystem.png differ diff --git a/img/python_environment.png b/img/python_environment.png new file mode 100644 index 0000000..a8e8fd6 Binary files /dev/null and b/img/python_environment.png differ diff --git a/img/pytorch_logo.svg b/img/pytorch_logo.svg new file mode 100644 index 0000000..8461b14 --- /dev/null +++ b/img/pytorch_logo.svg @@ -0,0 +1 @@ + diff --git a/img/scikit-image_logo.png b/img/scikit-image_logo.png new file mode 100644 index 0000000..6f72eb8 Binary files /dev/null and b/img/scikit-image_logo.png differ diff --git a/img/scipy_logo.png b/img/scipy_logo.png new file mode 100644 index 0000000..34ecf59 Binary files /dev/null and b/img/scipy_logo.png differ diff --git a/index.qmd b/index.qmd index 43919ff..bf9dc70 100644 --- a/index.qmd +++ b/index.qmd @@ -1,14 +1,13 @@ --- -title: A template presentation -subtitle: so much fun -author: SWC Neuroinformatics Unit +title: Introduction to Python +author: Igor Tatarnikov, Laura Porta execute: enabled: true format: revealjs: - theme: [default, niu-dark.scss] - logo: img/logo_niu_dark.png - footer: "Edit this footer | 2023-07-26" + theme: [default, niu-light.scss] + logo: img/logo_niu_light.png + footer: "Sainsbury Wellcome Centre | 2025-09-30" slide-number: c menu: numbers: true @@ -17,6 +16,7 @@ format: preview-links: false view-distance: 10 mobile-view-distance: 10 + from: "markdown+emoji" auto-animate: true auto-play-media: true code-overflow: wrap @@ -26,9 +26,9 @@ format: fontFamily: arial curve: linear html: - theme: [default, niu-dark.scss] - logo: img/logo_niu_dark.png - date: "2023-07-05" + theme: [default, niu-light.scss] + logo: img/logo_niu_light.png + date: "2025-09-30" toc: true code-overflow: scroll highlight-style: atom-one @@ -39,73 +39,208 @@ format: margin-left: 0 embed-resources: true page-layout: full -my-custom-stuff: - my-reuseable-variable: "I can use this wherever I want in the markdown, and change it in only once place :)" --- -## Contents +## Schedule {.smaller} + +::: {.incremental} +* Aims +* Why learn Python? +* Basics + + Variables + + Data types + + Loops + + Conditional statements + + List comprehensions +* How to work with Python? (using IDEs, environments, etc...) +* Writing your first Python script +* Loading and saving data +::: -Some example slides - [also look at example RevealJS slides in the Quarto docs](https://quarto.org/docs/presentations/revealjs/) +## Schedule {.smaller} +::: {.incremental} +* Recap and Q&A +* Using third party libraries from pip and conda +* Functions and methods +* Classes and objects +* Errors and exceptions +* Organising your code +* Documenting your code +::: -* Non-executable and executable code-blocks -* bullet points with highlighting -* two-column slides -* how to include a slide from a separate MD file -* preview and link to a webpage +## Aims +::: {.incremental} +* Introduce Python +* Introduce some basic programming concepts +* Show you *one* (our) way of doing things + + Not necessarily the best or only way +* Give you the confidence to start doing it yourself +* Prepare you for other courses +::: -## Just a code block, nothing gets executed... +::: {.fragment} +Please ask any **questions at any time**! +::: -... but there is some fancy highlighting +# Why learn Python? -```{.python code-line-numbers="1|3|4-9"} -from pathlib import Path +## Why learn Python? {.smaller} +::: {.fragment} +Popularity -home_path = Path.home() -if home_path.exists(): - data_path = home_path / "data" -else: - pass - # raise some error maybe? -``` +- Python is the most popular programming language for data science[$^1$](https://aimresearch.co/product/data-science-skills-study-2023/) +::: -## A code block that's actually executed at render-time +::: {.fragment} +Free and open source -```{python} -#| echo: true -#| code-fold: true +- Unlike MATLAB, IGOR, etc... +- Anyone can use your code + + 9.7M open Python repositories[$^2$](As 2024-04-06, github.com/oprogramador/github-languages) on ![GitHub logo](img/github-mark-white.svg){width=1em} +::: -from pathlib import Path +::: {.fragment} +Versatile +Not just for data analysis / machine learning -print("Hello world") -``` +- Visualisation +- Web development +- Data acquisition +::: -## You can execute code without showing that you have by using #|echo: false -```{python} -#| echo: false +## Why learn Python? {.smaller} +Packages for everything! -from pathlib import Path +::: {.fragment} +![NumPy logo](img/numpylogoicon.svg){width=2em style="margin-bottom: 0px; vertical-align: bottom;"} Numpy: arrays +::: -print("Hello world") -``` +::: {.fragment} +![Pandas logo](img/pandas_logo.svg){width=2em style="margin-bottom: 0px; vertical-align: bottom;"} Pandas: dataframes +::: + +::: {.fragment} +![SciPy logo](img/scipy_logo.png){width=2em style="margin-bottom: 0px; vertical-align: bottom;"} SciPy: scientific computing +::: + +::: {.fragment} +![Scikit-image logo](img/scikit-image_logo.png){width=2em style="margin-bottom: 0px; vertical-align: bottom;"} Scikit-image: image analysis +::: + +::: {.fragment} +![PyTorch logo](img/pytorch_logo.svg){width=2em style="margin-bottom: 0px; vertical-align: bottom;"} PyTorch: machine learning +::: + +::: {.fragment} +![Matplotlib logo](img/matplotlib_logo.svg){width=2em style="margin-bottom: 0px; vertical-align: bottom;"} Matplotlib: plotting +::: + +::: {.fragment} +*People have spent lots of time optimising these packages! Don't reinvent the wheel!* + +::: + +# Basics of Python + +{{< include slides/basics.qmd >}} + +# Writing your first Python script + +{{< include slides/first_script.qmd >}} + +# Loading and saving data + +{{< include slides/loading_and_saving.qmd >}} + +# Functions + +{{< include slides/functions.qmd >}} + +# Classes and objects + +{{< include slides/classes_and_objects.qmd >}} + +# Errors and exceptions + +{{< include slides/errors_and_exceptions.qmd >}} -{{< include slides/extra_slide.qmd >}} +# How to work with Python? -## An example image +{{< include slides/working_with_python.qmd >}} -Include an image: +# Virtual environments -![](img/bg_logo_wide.png){width=900 fig-align=center} +{{< include slides/virtual_environments.qmd >}} +# Modules and packages -## Link and a preview a webpage: +{{< include slides/modules_and_packages.qmd >}} -::: {style="text-align: center; margin-top: 1em"} -[interoperable Python-based tools for computational neuroanatomy](https://brainglobe.info/index.html){preview-link="true" style="text-align: center"} +# Organising your code + +{{< include slides/structuring_your_code.qmd >}} + +# Documenting your code + +{{< include slides/documenting_your_code.qmd >}} + +# Next steps +## Next steps {.smaller .incremental} +* We've covered some of the basics +* Next steps -- Practice!: + + Mess around with code + + Solve problems + + Google stuff + + Contact + + Additional courses +::: + +## Further resources {.smaller .incremental} +* [CS50](https://pll.harvard.edu/course/cs50-introduction-computer-science) +* UCL ARC courses + + [An introduction to programming for research using Python](https://rits.github-pages.ucl.ac.uk/doctoral-programming-intro/) + + [Research software engineering with Python](https://github-pages.ucl.ac.uk/rsd-engineeringcourse/) + + [Software carpentry courses](https://software-carpentry.org/lessons/) + + [Exercism](https://exercism.org/tracks/python) + +## Troubleshooting tips {.smaller .incremental} +* Make sure the correct environment is activated + + `which python` or `which pip` to check +* Check the scope of your variable +* Pay attention to whether an operation is `in_place` or not + + Especially with `pandas` and `numpy` +* Structure your code as a package as soon as you can + + `pyproject.toml` → more on this in future sessions! +* Look out for deep vs shallow copies + + Especially with collections (lists, dicts, sets) + +## Question +::::: {.columns} +::: {.column .incremental width="55%"} +* What does this return? +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +a = [1, 2, 3] +b = a +b[0] = 42 + +print(a[0]) +``` +:::: ::: +::::: -## Use a variable several times -Variables defined in the metadata is re-useable anywhere +## Troubleshooting tips {.smaller .incremental} +* Read the error messages! + + Google is your friend + + LLMs can help too + + Ask a colleague or contact us +* Use a debugger (e.g. `pdb`, or IDE built-in debuggers) +* Write tests for your code (e.g. using `pytest`) -* {{< meta my-custom-stuff.my-reuseable-variable >}} -* {{< meta my-custom-stuff.my-reuseable-variable >}} diff --git a/requirements.txt b/requirements.txt index d4424c2..7d02dd9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,5 @@ jupyter jupyter-cache +matplotlib +numpy +pandas diff --git a/slides/basics.qmd b/slides/basics.qmd new file mode 100644 index 0000000..5ae84ca --- /dev/null +++ b/slides/basics.qmd @@ -0,0 +1,660 @@ +## Variables {.smaller} + +::: {.incremental} +* A variable is a name that refers to a value +* You can use variables to store data in memory +* You can change the data stored in a variable at any time +* Allows you to reuse data without having to retype it +::: + +## Variables +```{python} +#| echo: true +#| output-location: fragment +a = 1 +print("The value of a is:") +print(a) +``` + +
+ +```{python} +#| echo: true +#| output-location: fragment +b = 2 +c = a + b +print("The value of c is:") +print(c) +``` + +## Variables +```{python} +#| echo: true +#| output-location: fragment +b = 2 +a = b +print("The value of a is now:") +print(a) +``` + +
+ +```{python} +#| echo: true +#| output-location: fragment +x, y = 10, 20 + +print("The value of x is:") +print(x) +print("The value of y is:") +print(y) +``` + +## Data types +::: {.fragment .incremental .smaller} +* Different kinds of data are stored in different ways in memory +* You can use the `type()` function to find out what type of data a variable contains +::: + +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +print(type(1)) +print(type(1.0)) +print(type("Hello")) +``` + +::: + +## Strings + +::: {.incremental} +* A string is a sequence of characters +* Strings are enclosed in single or double quotes +* Used to represent text +::: + +## Strings +```{python} +#| echo: true +#| output-location: fragment + +a = "I want to learn " +b = 'Python!' + +print(type(a)) + +print(a + b) +``` + +
+ +::: {.fragment .fade-in style="font-size: 80%;"} +* Algebraic operations can have different meanings for different data types + + Try `a*3` in the code above +::: + +## f-strings +::: {.incremental} +* f-strings are a way to format strings +* Use `f` before the string +* Use `{variable}` to insert variables into the string +::: +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +a = 42 +print(f"The value of a is: {a}") +``` +::: + +## f-strings + +You can also use f-strings to format numbers + +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +print(f"A rounded number: {8.333333333333333:.2f}") +``` +::: + +::: {.fragment .fade-in} +And compute values in the string +```{python} +#| echo: true +#| output-location: fragment +print(f"7 / 3 is: {7 / 3:.2f}") +``` +::: + +## Question + +::: {.incremental} +* What is the type of...? + + `100` + + `'Dog'` + + `3.14` + + `print` + + `False` + + `'50'` + + `None` +::: + +## Question +* Can you add an **int** and a **float**? + +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +print(1 + 2.1) +``` + +::: + +::: {.fragment .fade-in} +* What about an **int** and a **str**? +
+```{python} +#| echo: true +#| output-location: fragment +#| error: true +print(1 + '2') +``` + +::: + +## Lists {.smaller} + +::::: {.columns} +::: {.column .incremental width="55%"} +* Ordered collection of values +* Enclosed in square brackets `[]` +* Any data type, including mixed types +* Indexed from 0 with square brackets `[]` +* Mutable (can be changed) +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +my_list = [1, 2.0, 'three', 'dog'] +print(type(my_list)) +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +first = my_list[0] +second = my_list[1] +last = my_list[-1] +print(first, second, last) +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +my_list.append('cat') +print(my_list) +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +my_list[-1] = 'new_element' +print(my_list) +``` +:::: +::: +::::: + +## Question {.smaller} +::::: {.columns} +::: {.column width="55%"} + +:::: {.fragment .fade-in fragment-index=1} +* Find the third element from `my_list`? +:::: + +:::: {.fragment .fade-in fragment-index=4} +* Change the second element to `3.0`? +:::: + +:::: {.fragment .fade-in fragment-index=6} +* What happens if you request `my_list[5]`? +:::: + +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in fragment-index=2} +```{python} +#| echo: true +print(my_list) +``` +:::: + +:::: {.fragment .fade-in fragment-index=3} +```{python} +#| echo: true +print(my_list[2]) +``` +:::: + +:::: {.fragment .fade-in fragment-index=5} +```{python} +#| echo: true +my_list[1] = 3.0 +print(my_list) +``` +:::: + +:::: {.fragment .fade-in fragment-index=7} +```{python} +#| echo: true +#| error: true +print(my_list[5]) +``` +:::: + +::: +::::: + +## Tuples {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* Ordered collection of values +* Enclosed in parentheses `()` +* Any data type, including mixed types +* Immutable (cannot be changed) +* When would you use a tuple instead of a list? +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true +my_tuple = (1, 2.0, 'cat', 'dog') + +print(type(my_tuple)) +print(my_tuple[2]) + +my_tuple[2] = 'rabbit' +``` +:::: +::: +::::: + +## Unpacking {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* Unpack a list or tuple into multiple variables +* Convenient for reassigning variables +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +my_tuple = (1, 2, 3, 4, 5) + +a, b, c, d, e = my_tuple +print(a, b, c, d, e) +``` +:::: +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +a, *b, c = my_tuple +print(a, b, c) +print(type(b)) +``` +:::: +::: +::::: + +## Question {.smaller} +::::: {.columns} +::: {.column width="55%"} + +:::: {.fragment .fade-in fragment-index=1} +* How would you turn `my_list` into a tuple? +:::: + +:::: {.fragment .fade-in fragment-index=3} +* How would you turn `my_tuple` into a list? +:::: + +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in fragment-index=1} +```{python} +#| echo: true +my_tuple = (1, 2, 3, 4, 5) +my_list = [1, 2.0, 'three', 'dog'] +``` + +
+ +:::: + +:::: {.fragment .fade-in fragment-index=2} +```{python} +#| echo: true +my_list_as_tuple = tuple(my_list) +print(type(my_list_as_tuple)) +``` +:::: + +:::: {.fragment .fade-in fragment-index=4} +```{python} +#| echo: true +my_tuple_as_list = list(my_tuple) +print(type(my_tuple_as_list)) +``` +:::: +::: +::::: + +## Dictionaries {.smaller} +::::: {.columns} +::: {.column .incremental width="45%"} +* Collection of key-value pairs +* Enclosed in curly braces `{}` +* Keys are unique and immutable +::: + +::: {.column width="55%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +my_dict = { + 'a_number': 1, + 'number_list': [1, 2, 3], + 'a_string': 'string', + 5.0: 'a float key' +} + +print(my_dict.keys()) +print(my_dict.values()) +print(my_dict.items()) +print(my_dict['a_string']) +print(my_dict[5.0]) +``` +:::: +::: +::::: + +## Question {.smaller} +::::: {.columns} +::: {.column width="45%"} +* What does `my_dict['a_number']` return? +::: + +::: {.column width="55%"} +```{python} +#| echo: true +my_dict = { + 'a_number': 1, + 'number_list': [1, 2, 3], + 'a_string': 'string', + 'a_number': 2 +} +``` + +:::: {.fragment fragment-index=1} +
+ +```{python} +#| echo: true +#| output-location: fragment +print(my_dict['a_number']) +``` +:::: +::: +::::: + +## Loops {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* Do the same thing multiple times + + E.g.analyse N images +* Watch out for indentation! + + Python uses indentation to define blocks of code + + Usually 2-4 spaces (or a tab) +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment + +for i in [0, 1, 2]: + print(i) +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment + +for i in range(5): + print(i) +``` +:::: +::: +::::: + +## Loops {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* You can loop over any iterable +* Use `_` if you don't need the loop variable +* `enumerate()` gives you the index and the value +::: +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +my_list = ['cat', 'dog', 'rabbit'] +for animal in my_list: + print("My pet is a: " + animal) +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +for _ in range(3): + print("Hello!") +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +for index, animal in enumerate(my_list): + print(f"My pet #{index} is a: {animal}") +``` +:::: +::: +::::: + +## Question {.smaller} +::: {.fragment .fade-in } +* Create a list of integers from 0 to 5 +* Use a loop to find the sum of the squares of the integers in the list +* Print the result +::: +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment + +numbers = [0, 1, 2, 3, 4, 5] +sum_of_squares = 0 + +for number in numbers: + sum_of_squares = sum_of_squares + number**2 + +print(sum_of_squares) +``` +::: + +
+ +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|5|" +numbers = [0, 1, 2, 3, 4, 5] +sum_of_squares = 0 + +for number in numbers: + sum_of_squares += number**2 + +print(sum_of_squares) +``` +::: + +## Conditional statements {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* Execute code only if a condition is met +* Use `if`, `elif` (else if), and `else` +::: +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|1|3|5|7,8|" + +porridge_temp = 45 + +if porridge_temp < 40: + print("Too cold!") +elif porridge_temp > 50: + print("Too hot!") +else: + print("Just right!") +``` +:::: +::: +::::: + +## Question {.smaller} +* Write a loop that goes through the numbers 0 to 10 +* For each number, print whether it is even or odd + + Hint: use the modulus operator `%` and `==` to check for evenness + +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|1|2,3|4,5|" + +for i in range(11): + if i % 2 == 0: + print(f"{i} is Even") + else: + print(f"{i} is Odd") +``` +::: + +## List comprehensions {.smaller} +::::: {.columns} +::: {.column width="55%"} + +:::: {.fragment .fade-in fragment-index=1} +* Concise way to create lists +* `[expression for item in iterable]` +:::: + +:::: {.fragment .fade-in fragment-index=4} +* Can include conditions +* `[expression for item in iterable if condition]` +:::: + +::: +::: {.column width="45%"} +:::: {.fragment .fade-in fragment-index=2} +Classic: +```{python} +#| echo: true + +squares = [] +for num in range(5): + squares.append(num**2) +print(squares) +``` + +:::: +:::: {.fragment .fade-in fragment-index=3} +List comprehension: +```{python} +#| echo: true +squares = [num**2 for num in range(5)] +print(squares) +``` +:::: +::: +:::: {.fragment .fade-in fragment-index=5} +With condition: +```{python} +#| echo: true +#| output-location: fragment +large_squares = [num**2 for num in range(10) if num > 3] +print(large_squares) +``` +:::: +::::: + +## Question {.smaller} +* Create a list of square even numbers from 1 to 10 using a list comprehension + +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +even_squares = [num**2 for num in range(1, 11) if num % 2 == 0] +print(even_squares) +``` +::: + diff --git a/slides/classes_and_objects.qmd b/slides/classes_and_objects.qmd new file mode 100644 index 0000000..45b2e79 --- /dev/null +++ b/slides/classes_and_objects.qmd @@ -0,0 +1,88 @@ +## Objects {.smaller} +::::: {.columns} +::: {.column width="55%"} +* Everything in Python is an object + + Integer, float, string, list, functions... +* Objects have attributes and methods + + Attributes: properties of the object + + Methods: functions that belong to the object +::: +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +a_string = "penguin" +print(a_string.capitalize()) +``` +:::: +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|1|2,3|4|" +file_obj = open("example.txt", "a") +print(file_obj.name) +print(file_obj.mode) +file_obj.close() +``` +:::: +::: +::::: + +## Classes {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* A class is a blueprint for creating objects +* Defined using the `class` keyword +* Can have attributes and methods (functions) +* `__init__` method is called when an object is created +* `self` refers to the instance of the class +::: +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|1|2,3|5|6|" + +class Animal(): + def __init__(self, species): + self.species = species + +pingu = Animal("penguin") +print(pingu.species) +``` +::::: +::: +::::: + +## Exercise {.smaller} +* Try adding a new attribute `noise` to the `Animal` class +* Add a method `make_noise` that prints the noise of the animal + +```{.python} +class Animal(): + your code here + +pingu = Animal("penguin", "noot") +pingu.make_noise() # Output: noot +``` + +## Exercise {.smaller} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|2|2,4|6,7|" + +class Animal(): + def __init__(self, species, noise): + self.species = species + self.noise = noise + + def make_noise(self): + print(self.noise) + +pingu = Animal("penguin", "noot") +pingu.make_noise() +``` diff --git a/slides/documenting_your_code.qmd b/slides/documenting_your_code.qmd new file mode 100644 index 0000000..059005f --- /dev/null +++ b/slides/documenting_your_code.qmd @@ -0,0 +1,186 @@ +## Documenting your code {.smaller} +::::: {.columns} +::: {.column width="55%"} +:::: {.fragment .fade-in fragment-index=1} +* Aim to need as little documentation as possible +:::: +:::: {.fragment .fade-in fragment-index=4} +* Which is easier to understand? + + Use meaningful pronounceable names +:::: +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in fragment-index=2} +```{.python} +f = 378 +c = 22 +fpc = f / c +``` +:::: + +
+ +:::: {.fragment .fade-in fragment-index=3} +```{.python} +foci_number = 378 +cell_number = 22 +foci_per_cell = foci_number / cell_number +``` +:::: +::: +::::: + +## Documenting your code {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* Each function should do one thing +* Give functions descriptive names +* This is a form of documentation too! +::: +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{.python} +def analyse_well(well_data): + ... + +def save_analysed_well(well_data): + ... + +def process_single_well(well_data): + analysed = analyse_well(well_data) + save_analysed_well(analysed) + +def process_plate(plate_data): + for well_data in plate_data: + process_single_well(well_data) +``` +:::: +::: +::::: + +## Comments {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* Sometimes the code isn't self-explanatory +* Use comments to explain why, not what +* Use `#` for single-line comments +* Use triple quotes `"""` for multi-line comments or docstrings +* What's wrong with this example? +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{.python} +def analyse_image(image): + # run gaussian blur + image = gaussian(image) + # run otsu thresholding + image = otsu(image) + # run watershed + image = watershed(image) + return image +``` +:::: +::: +::::: + +## Comments +::::: {.columns} +::: {.column width="50%"} +```{.python} +def analyse_image(image): + # run gaussian blur + image = gaussian(image) + # run otsu thresholding + image = otsu(image) + # run watershed + image = watershed(image) + return image +``` +::: + +::: {.column width="50%"} +```{.python} +def analyse_image(image): + # remove noise + image = gaussian(image) + # binarise cells + image = otsu(image) + # split merged cells + image = watershed(image) + return image +``` +::: +::::: + +## Docstrings {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* Use docstrings to describe what a function does and how to use it +* Detail inputs and outputs +* Use triple quotes `"""` for docstrings +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{.python code-line-numbers="|2-16|2|4-10|12-15|"} +def add_numbers(num_0, num_1): + """Adds two numbers together + + Parameters + ---------- + num_0 : float + First number to be added + + num_1 : float + Second number to be added + + Returns + ------- + float + Sum of numbers + """ + return num_0 + num_1 +``` +:::: +::: +::::: + +## README files {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* Single file, usually `README.md` or `README.txt` +* Outlines most important information + + What the project does + + How to install + + How to use +* Very useful for others (and future you!) +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{.plaintext code-line-numbers="|5|7-8|9-16|"} +My awesome Python package +Adam Tyson 2025-01-01 +code@adamltyson.com + +Analyses data from X, Y, Z + +To install: +... +Data requirements: +... +To run: +... +Output: +... +Troubleshooting: +... + +``` +:::: +::: +::::: + + diff --git a/slides/errors_and_exceptions.qmd b/slides/errors_and_exceptions.qmd new file mode 100644 index 0000000..22a1b3d --- /dev/null +++ b/slides/errors_and_exceptions.qmd @@ -0,0 +1,219 @@ +## Errors and Exceptions {.smaller} +* You've probably seen an error already +* If not try these, why don't they work? + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true + +import london +``` +:::: + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true +len(print) +``` +:::: + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true +print("Hello" +``` +:::: + +## SyntaxError {.smaller} +* Something is wrong with the structure of your code, code cannot run +* What's wrong below? + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true + +print hello +``` +:::: +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true + for i in [0, 1, 2] + print(i) +``` +:::: +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true +"hello" = some_string +``` +:::: + +## Exceptions {.smaller} +* Something went wrong while your code was running +* What's wrong in these examples? + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true +1 / 0 +``` +:::: + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true +giraffe * 10 +``` +:::: + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true +a_list = [1, 2, 3] +a_list[5] +``` +:::: + +## Tracebacks {.smaller} +* Help you find the source of the error +* Read from the bottom up +* Look for the last line that is *your* code +* [Debugging manifesto](https://wizardzines.com/images/debugging-manifesto.pdf) :bug: + +## Tracebacks {.smaller} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|7|4,5|1,2|" +#| error: true + +def divide_0(x): + return x / 0 + +def call_func(x): + y = divide_0(x) + +z = call_func(10) +``` + +## Handling exceptions {.smaller} +* What if you know an error might happen? + +:::: {.fragment .fade-in fragment-index=2} +```{python} +#| echo: true +#| error: true +def divide(x, y): + print(x / y) # This might raise an error + +divide(10, 2) +divide(10, 0) +``` +:::: + +## Handling exceptions {.smaller} +* What if you know an error might happen? +* You can handle it with `try` and `except` + +:::: {.fragment .fade-in fragment-index=4} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|2|3,4|5,6|" +def safe_divide(x, y): + try: + value = x / y + print(f"Result: {value}") + except: + print("Y cannot be zero!") +safe_divide(10, 2) +safe_divide(10, 0) +``` +:::: + +## Handling exceptions {.smaller} +* What's the problem with this code? + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true + +safe_divide("10", 2) +``` +:::: + +:::: {.fragment .fade-in} +* Don't make your `except` too general! +:::: + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true +#| code-line-numbers: "|2|3,4|5,6|7,8|" + +def better_safe_divide(x, y): + try: + value = x / y + print(f"Result: {value}") + except ZeroDivisionError: + print("Y cannot be zero!") + except TypeError: + print("Both x and y must be numbers!") + +better_safe_divide(10, 2) +better_safe_divide(10, 0) +better_safe_divide("10", 2) +``` +:::: + +## Exercise {.smaller} +* Write a function to return the length of the input +* If the object has no length, return `None` + +:::: {.fragment .fade-in} +```{.python} +def safe_len(x): + # Your code here + pass +``` +:::: + +## Exercise {.smaller} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|2,3|4,5|" +#| error: true +def safe_len(x): + try: + return len(x) + except TypeError: + return None + +print(safe_len("hello")) +print(safe_len(42)) +print(safe_len([1, 2, 3])) +``` \ No newline at end of file diff --git a/slides/extra_slide.qmd b/slides/extra_slide.qmd deleted file mode 100644 index 1d699ad..0000000 --- a/slides/extra_slide.qmd +++ /dev/null @@ -1,3 +0,0 @@ -## A slide imported from outside the qmd - -This will become an example about how to use a common slide deck for the group. \ No newline at end of file diff --git a/slides/first_script.qmd b/slides/first_script.qmd new file mode 100644 index 0000000..681423f --- /dev/null +++ b/slides/first_script.qmd @@ -0,0 +1,2 @@ +## Writing your first Python script +* Placeholder slide \ No newline at end of file diff --git a/slides/functions.qmd b/slides/functions.qmd new file mode 100644 index 0000000..5dffdd1 --- /dev/null +++ b/slides/functions.qmd @@ -0,0 +1,392 @@ +## Functions {.smaller} +::::: {.columns} +::: {.column width="60%"} +* Rectified Linear Unit (ReLU) + + `f(x) = max(0, x)` + +```{python} +import matplotlib.pyplot as plt +import numpy as np + +def relu(x): + return max(0, x) + +x = np.linspace(-1, 1, 100) +y = [relu(i) for i in x] + +ax = plt.gca() + +plt.plot(x, y, color='black', linewidth=2) +ax.spines['left'].set_position('zero') +ax.spines['bottom'].set_position('zero') +ax.spines['right'].set_color('none') +ax.spines['top'].set_color('none') +ax.set_ylim(-0.5, 1) +y_ticks = ax.yaxis.get_major_ticks() +y_ticks[3].label1.set_visible(False) # Hide the 0 label on +plt.show() +``` +::: + +::: {.column width="40%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +x = 5 +if x > 0: + y = x +else: + y = 0 +print(y) +``` +:::: +::: +::::: + +## Functions {.smaller} +:::: {.columns} +::: {.column width="55%"} + +::: {.incremental} +* Time consuming and error prone to repeat code +* Functions allow you to: + + Reuse code + + Break problems into smaller pieces + + Scope defined by indentation +* Defined using the `def` keyword +* Can take inputs (arguments) and return outputs +::: + +::: +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|1|2-5|3,5|" +def relu(x): + if x > 0: + return x + else: + return 0 + +y = relu(0.2) +print(y) + +print(relu(5)) +print(relu(-3)) +``` +:::: +::: +::::: + +## Exercise {.smaller} +:::: {.fragment} +* Write a function to check that a password is at least 8 characters long +* The function should take a string as input and return `True` if the password is long enough, and `False` otherwise +* You can use the built-in `len()` function to get the length of a string +:::: + +:::: {.fragment .fade-in} +```{python} +#| echo: true +def is_valid_password(password): + # Your code here + pass +``` +:::: + +## Exercise +```{python} +#| echo: true +#| output-location: fragment +def is_valid_password(password): + if len(password) >= 8: + return True + else: + return False + +print(is_valid_password("longpassword")) +print(is_valid_password("short")) +``` + +## Arguments {.smaller} +:::: {.columns} +::: {.column .incremental width="50%"} + +::: {.incremental} +* Functions can take multiple arguments +* Positional arguments + + Must be given in the correct order +* Keyword arguments + + Can be given in any order + + Use the syntax `name=value` + + Must come after positional arguments +* Default arguments + + Have a default value if not provided + + Must come after non-default arguments +::: + +::: +::: {.column width="50%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +def print_sum(num1, num2): + my_sum = num1 + num2 + print(my_sum) + +print_sum(3, 5) # Positional arguments +print_sum(num2=5, num1=3) # Keyword arguments +``` +:::: +::: +::::: + +## Arguments {.smaller} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +def list_animals(first="dog", second="cat", third="penguin"): + print(f"First animal: {first}") + print(f"Second animal: {second}") + print(f"Third animal: {third}") + +list_animals() +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +#| error: true +list_animals(second="elephant", first="cow") +``` +:::: + +
+ +:::: {.fragment .fade-in} +``` {python} +#| echo: true +#| output-location: fragment +#| error: true +list_animals(second="lion", "tiger") +``` +:::: + +## Arguments {.smaller} +``` {python} +#| echo: true +#| output-location: fragment +#| error: true +def list_animals(first="dog", second, third="penguin"): + print(f"First animal: {first}") + print(f"Second animal: {second}") + print(f"Third animal: {third}") + +list_animals() +``` + +## Exercise {.smaller} +:::: {.fragment} +* Write a function that takes two strings as arguments and prints the longer of the two strings +* One of the arguments should have a default value of an empty string +:::: +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment + +def longer_string(str1, str2=""): + # Your code here + pass +``` +:::: + +## Exercise {.smaller} + +```{python} +#| echo: true +#| output-location: fragment +#| code-line-numbers: "|1|2,4|" + +def longer_string(str1, str2=""): + if len(str1) > len(str2): + long_str = str1 + else: + long_str = str2 + + print(long_str) + +print(longer_string("apple", "banana")) +print(longer_string("apple")) +``` + +## Using `*` and `**` {.smaller} + +::: {.incremental .smaller} +* `*` and `**` are upacking operators, useful to unpack tuples, lists and dictionaries +* I't common to use `*` and `**` when calling functions +* You might have seen the syntax `*args` and `**kwargs` before +* This is just a convention to indicate that the function takes a variable number of arguments +::: + +::: {.fragment} +```{python} +#| echo: true +#| output-location: fragment +def my_func(*args, **kwargs): + print(f"Unpacking list: {args}") + print(f"Unpacking dictionary: {kwargs}") + +my_list = [1, 2, 3, 4, 5] +my_dict = {'a': 1, 'b': 2, 'c': 3} + +print("Unpacking list:") +my_func(*my_list) + +print("Unpacking dictionary:") +my_func(**my_dict) +``` +::: + +## How are `*args` and `**kwargs` useful? {.smaller} +:::: {.columns} +::: {.column width="55%"} +::: {.incremental .smaller} +* `*args` and `**kwargs` are useful when you don't know how many arguments will be passed to the function +::: + +::: +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +def my_func(**kwargs): + if 'name' in kwargs: + print(f"Hello, {kwargs['name']}!") + else: + print("Hello, world!") + +my_func(name="John") +my_func() +my_func(name="Jane") +``` +:::: +::: +::::: + +## Return values {.smaller} +::::: {.columns} +::: {.column width="55%"} + +::: {.incremental .smaller} +* Functions can return one or more values +* Use the `return` keyword +* A function can only return once +* If no return statement is given, the function returns `None` +::: + +::: +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +def a_func(a, b): + print(a + b) + +def b_func(a, b): + return a + b +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment + +a_func(10, 20) +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +b_func(10, 20) +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +result = b_func(10, 20) +print(result) +``` +:::: +::: +::::: + + +## Return values {.smaller} + +```{python} +#| echo: true +#| output-location: fragment + +def c_func(a, b, c): + sum1 = a + b + sum_all = a + b + c + + return sum1, sum_all +``` + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +result1, result2 = c_func(1, 2, 3) +print(result1) +print(result2) +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +result = c_func(1, 2, 3) +print(type(result)) +print(result) +``` +:::: + +
+ +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +_, result2 = c_func(1, 2, 3) +print(result2) +``` +:::: diff --git a/slides/loading_and_saving.qmd b/slides/loading_and_saving.qmd new file mode 100644 index 0000000..7dcec8f --- /dev/null +++ b/slides/loading_and_saving.qmd @@ -0,0 +1,86 @@ +## Writing files {.smaller} +::::: {.columns} +::: {.column width="55%"} +:::: {.fragment .fade-in fragment-index=1} +* Python can open files in various modes: + + 'r' - read (default) + + 'w' - write (create or overwrite) + + 'a' - append (adds to the end of the file) +* Always close the file after you're done +:::: + +:::: {.fragment .fade-in fragment-index=3} +* Use `with` to do this automatically +:::: +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in fragment-index=2} +```{python} +#| echo: true +new_file = open('example.txt', 'w') +new_file.write('Hello, world!\n') +new_file.close() +``` +:::: + +
+ +:::: {.fragment .fade-in fragment-index=4} +```{python} +#| echo: true +#| output-location: fragment +with open('example.txt', 'a') as new_file: + new_file.write("Appended line.\n") + new_file.write('Hello, world!\n') +``` +:::: +::: +::::: + +## Reading files {.smaller} +::::: {.columns} +::: {.column width="50%"} +* Open file in read mode to get content +* Files can also be read line by line +::: + +::: {.column width="50%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +with open('example.txt', 'r') as f: + contents = f.read() + +print(contents) +``` +:::: +::: +::::: + +## Demo +* Write a script that saves the below data to a comma-separated file + +```{python} +#| echo: true +column_labels = "sample_id,speed,distance" +samples = [(0, 12, 53), (1, 7, 23), (2, 15, 30)] +``` + +## Demo +```{python} +#| echo: true +column_labels = "sample_id,speed,distance" +samples = [(0, 12, 53), (1, 7, 23), (2, 15, 30)] + +with open("out.csv", "w") as file: + file.write(column_labels) + file.write("\n") + + for sample in samples: + for value in sample: + file.write(str(value) + ",") + file.write("\n") +``` + diff --git a/slides/modules_and_packages.qmd b/slides/modules_and_packages.qmd new file mode 100644 index 0000000..181deec --- /dev/null +++ b/slides/modules_and_packages.qmd @@ -0,0 +1,278 @@ +## Standard library {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* A module is a file containing Python code +* A package is a collection of modules +* The standard library is a set of modules that come with Python +* It provides a wide range of functionality + + File I/O + + Time and date handling + + Math and statistics +* Accessed using the `import` and `from` keywords +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +import os + +print(os.getcwd()) +``` +:::: + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +import math + +print(math.sqrt(16)) +``` +:::: + +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +from pathlib import Path +print(Path.home()) +``` +:::: +::: +::::: + +## Installing a third-party package +::: {.callout-important} +* In the terminal, not in the Python console! +* Remember to activate the conda environment first +::: + +::: {.fragment .fade-in} +```bash +conda activate python-intro +pip install pandas +``` +::: + +## Using packages {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* `pandas` is a popular package for data science +* Based on `DataFrames` (tables) and `Series` (columns) +* You can access it using the `import` keyword +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +import pandas + +data_frame = pandas.read_csv("out.csv") + +print(data_frame) +``` +:::: +::: +::::: + +## Importing packages {.smaller} +::::: {.columns} +::: {.column width="55%"} +:::: {.fragment .fade-in fragment-index=1} +* You can import all available functions in a package +:::: + +:::: {.fragment .fade-in fragment-index=3} +* You can import a specific function from a package + + Use `from ... import ...` +:::: + +:::: {.fragment .fade-in fragment-index=5} +* You can also rename the package or function using `as` + + Common convention for `pandas` is `pd` +:::: +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in fragment-index=2} +```{.python} +import pandas + +df = pandas.read_csv("out.csv") +``` +:::: + +
+ +:::: {.fragment .fade-in fragment-index=4} +```{.python} +from pandas import read_csv + +df = read_csv("out.csv") +``` +:::: + +
+ +:::: {.fragment .fade-in fragment-index=6} +```{.python} +import pandas as pd + +df = pd.read_csv("out.csv") +``` +:::: +::: +::::: + +## Installing packages {.smaller} +::::: {.columns} +::: {.column width="70%"} +Two main ways to install packages: + +:::: {.fragment .smaller .fade-in fragment-index=1} +pip + +* Python's built-in package manager +* Uses the Python Package Index (PyPI) +```{.bash} +pip install package_name +``` +:::: + +:::: {.fragment .smaller .fade-in fragment-index=3} +conda + +* Uses multiple channels (`conda-forge` is best, avoid `defaults`) +* Can install non-Python dependencies +```{.bash} +conda install package_name +``` +:::: +::: + +::: {.column width="30%"} +:::: {.fragment .fade-in fragment-index=2} +![](https://upload.wikimedia.org/wikipedia/commons/6/64/PyPI_logo.svg){width="100%" fig-alt="PyPI logo"} +:::: + +
+
+ +:::: {.fragment .fade-in fragment-index=4} +![](https://upload.wikimedia.org/wikipedia/commons/e/ea/Conda_logo.svg){width="100%" fig-alt="Conda logo"} +:::: +::: +::::: + +## Exercise +::::: {.columns} +::: {.column width="55%"} +In your conda environment, using `pip`: + +:::: {.incremental style="font-size: 80%;"} +* Install `numpy` +* Uninstall `numpy` +* Install `numpy` version `1.26.4` +* Update the installation of `numpy` +* Install `matplotlib` and `scikit-image` in one command +* List all installed packages with `pip list` +:::: +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```bash +pip install numpy +``` +:::: + +
+ +:::: {.fragment .fade-in} +```bash +pip uninstall numpy +``` +:::: + +
+ +:::: {.fragment .fade-in} +```bash +pip install numpy==1.26.4 +``` +:::: + +
+ +:::: {.fragment .fade-in} +```bash +pip install numpy -U +``` +:::: + +
+ +:::: {.fragment .fade-in} +```bash +pip install matplotlib scikit-image +``` +:::: +::: +::::: + +## Exercise +::::: {.columns} +::: {.column width="55%"} +In your conda environment, using `conda`: + +:::: {.incremental style="font-size: 80%;"} +* Install `scipy` +* Install `scipy` version `1.11.1` +* Update the installation of `scipy` +* List all installed packages with `conda list` +:::: +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```bash +conda install scipy +``` +:::: + +
+ +:::: {.fragment .fade-in} +```bash +conda install scipy=1.11.1 +``` +:::: + +
+ +:::: {.fragment .fade-in} +```bash +conda update scipy +``` +:::: +::: +::::: + +## Exercise {.smaller} +::::: {.columns} +::: {.column width="55%"} +* Find and install a package for opening Word documents in Python +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```bash +pip install python-docx +``` +:::: +::: +::::: diff --git a/slides/structuring_your_code.qmd b/slides/structuring_your_code.qmd new file mode 100644 index 0000000..d6f893b --- /dev/null +++ b/slides/structuring_your_code.qmd @@ -0,0 +1,103 @@ +## Structuring your code {.smaller} +::: {.incremental} +* As your code gets more complex, you will want to organise it into multiple files and folders +* This makes it easier to find and reuse code +* Python modules and packages help you do this +* You can also use version control (e.g. Git) to manage changes to your code (covered in the good practices lecture) +::: + +## Exercise {.smaller} +::::: {.columns} +::: {.column .incremental width="55%"} +* Create a file `my_funcs.py` with the functions `square_add_10` and `print_twice` +::: + +::: {.column width="45%"} +:::: {.fragment .fade-in} +```{python} +#| echo: true +def square_add_10(x): + y = x**2 + y = y + 10 + return y + +def print_twice(a_string): + print(a_string) + print(a_string) +``` +:::: +::: +::::: + +## Exercise {.smaller} +::: {.incremental} +* In a new file `analysis.py`, import the functions from `my_funcs.py` and use them +* **Modularity promotes reuse!** +::: + +::: {.fragment .fade-in} +```{.python} +from .my_funcs import square_add_10, print_twice +``` +::: + +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +result = square_add_10(5) +print("Result:", result) + +print_twice("Hello, world!") +``` +::: + +## Structuring your code {.smaller} +::: {.incremental} +* As your codebase grows, consider organizing files into directories +::: + +::: {.fragment .fade-in} +```{.python} +from .utils.my_funcs import square_add_10, print_twice +``` +::: + +::: {.fragment .fade-in} +```{python} +#| echo: true +#| output-location: fragment +result = square_add_10(5) +print("Result:", result) +print_twice("Hello, world!") +``` +::: + +## Organisation {.smaller} +::: {.incremental} +* Organise your code like you would any other project +* Use meaningful names for files and directories +* Group related functionality together +::: + +::: {.fragment .fade-in} +```plaintext +. +├── python_project/ +│ ├── analysis/ +│ │ ├── align.py +│ │ ├── fft.py +│ │ ├── preprocessing.py +│ │ └── register.py +│ ├── IO/ +│ │ ├── load.py +│ │ ├── save.py +│ │ ├── tools +│ │ ├── settings.py +│ │ └── system.py +│ └── visualisation/ +│ ├── preprocess.py +│ └── visualisation.py +└── README.md +``` +::: \ No newline at end of file diff --git a/slides/virtual_environments.qmd b/slides/virtual_environments.qmd new file mode 100644 index 0000000..dfef719 --- /dev/null +++ b/slides/virtual_environments.qmd @@ -0,0 +1,152 @@ +## Scientific Python ecosystem {.smaller} +![Diagram showing the scientific Python ecosystem.](/img/python-ecosystem.png) + +::: footer +[Aaron Meurer, ‘Python Array API Standard’, SciPy 2023](https://www.youtube.com/watch?v=16rB-fosAWw) +::: + +## Virtual environments {.smaller} + +::: {.incremental} +- Many packages are continuously developed 🔁 + - This means there will be breaking changes + - Some packages will require specific versions of other packages +- Similar with Python versions + - New versions of Python may introduce new features or deprecate old ones +::: + +## Virtual environments {.smaller} + +![[Comic from xkcd](https://xkcd.com/1987/)](https://imgs.xkcd.com/comics/python_environment.png){width="20%"} + +## Virtual environments {.smaller} + +::: {style="color: grey;"} +- Many packages are continuously developed 🔁 + - This means there will be breaking changes + - Some packages will require specific versions of other packages +- Similar with Python versions + - New versions of Python may introduce new features or deprecate old ones +::: + +::: {.fragment .fade-in} +- How can we have **two versions** of the same package installed at the same time? +- Virtual environments! :tada: +::: + +## Virtual environments {.smaller} + +::: {.incremental} +- A virtual environment is an isolated Python installation + - Each environment has its own Python version + - Each environment has its own set of installed packages +- Popular tools to manage virtual environments: + - [`venv`](https://docs.python.org/3/library/venv.html) (built-in) + - [`conda`](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html) (Anaconda/Miniconda) + - [`uv`](https://docs.astral.sh/uv/pip/environments/) + - [`pixi`](https://pixi.sh/latest/workspace/environment/) +::: + +## Conda environments {.smaller} + +::: {.incremental} +- Conda is a `package manager` and `virtual environment manager` +- Separates Python environments from each other (and from system Python) +- Can also manage non-Python packages (e.g. R, C libraries, etc...) +- Reproducible environments that can be shared with: + - Collaborators + - Readers of your paper + - Future you (HPC, etc...) +::: + +## Install miniforge + +[Click here to go to the download page](https://conda-forge.org/download/) + +::: {.callout-caution collapse="true"} +## Windows troubleshooting +- Did you had a previous version of Anaconda installed? You might need to uninstall it first. +- ![Remember to add Miniforge to your PATH! The installer will say is not required, but it is!](img/miniforge_window_add_to_path.png){width="25%"} +- `conda not found`? Run `conda init` in your terminal. +::: + +## Environment management + +In your terminal (use Anaconda Prompt on Windows): + +
+ +``` bash +conda create --name python-intro python=3.13 notebook +``` + +
+ +::: {.fragment .fade-in style="font-size: 80%;"} +- Creates a new environment named `python-intro` +- Installs Python 3.13 and Jupyter Notebook in that environment +::: + +## Environment management +In your terminal (use Anaconda Prompt on Windows): + +
+ +``` bash +conda activate python-intro +``` + +
+ +::: {.fragment .fade-in style="font-size: 80%;"} +- Remember to activate the environment before using it! +::: + +## Environment management +In your terminal (use Anaconda Prompt on Windows): + +
+ +``` bash +conda deactivate +``` + +
+ +::: {.fragment .fade-in style="font-size: 80%;"} +- Deactivates the current environment +::: + +## Environment management +In your terminal (use Anaconda Prompt on Windows): + +
+ +``` bash +conda env list +``` + +
+ +::: {.fragment .fade-in style="font-size: 80%;"} +- Lists all conda environments on your system +::: + +## Environment management +In your terminal (use Anaconda Prompt on Windows): + +
+ +``` bash +conda remove --name python-intro --all +``` + +
+ +::: {.fragment .fade-in style="font-size: 80%;"} +- Deletes the `python-intro` environment and all its packages +::: + +::: {.fragment .fade-in .callout-tip} +[Conda cheat sheet](https://docs.conda.io/projects/conda/en/latest/user-guide/cheatsheet.html) +::: diff --git a/slides/working_with_python.qmd b/slides/working_with_python.qmd new file mode 100644 index 0000000..2f7e16e --- /dev/null +++ b/slides/working_with_python.qmd @@ -0,0 +1,41 @@ +## Ways of working with Python {.incremental} + +::: {.incremental} +* `REPL` (Read-Eval-Print Loop), "interactive Python", "Python console" +* `Jupyter notebooks` (.ipynb) +* `Scripts` (.py) +::: + +::: {.fragment .fade-in} +Demo time! 🧑🏻‍💻👩🏻‍💻 +::: + + + +## IDEs: Integrated Development Environments {.smaller} + +::: {.incremental} +* Used by most developers +* Allows you to switch between the three main ways of working with Python +* Within the same program you can: + + Edit Text editor :pencil: + + Organise File explorer :file_folder: + + Run Console :computer: +::: + +## Example IDEs + +::: {.fragment .fade-in} +* [Visual Studio Code](https://code.visualstudio.com/download) - more customizable +* [PyCharm](https://www.jetbrains.com/pycharm/download/) - optimized for Python +* ... +::: + +::: {.fragment .fade-in} +You can click on the name to go to the download page. +::: + +::: {.fragment .fade-in} +Install the one you like most! +Igor prefers PyCharm, Laura prefers VS Code. +::: \ No newline at end of file