├── runtime.txt
├── .gitignore
├── L6
├── img
│ ├── fmi.png
│ ├── yle-summer-2024.png
│ └── error-message-annotated.png
├── debugging.ipynb
├── errors.ipynb
└── gcp-5-assertions.ipynb
├── L4
├── img
│ ├── chatgpt.png
│ ├── chatgpt_signup.png
│ ├── new-python-file.png
│ ├── new-python-tab.png
│ ├── function_anatomy.png
│ ├── view_module_functions.png
│ ├── rename-python-file-part-1.png
│ └── rename-python-file-part-2.png
├── gcp-4-writing-scripts.ipynb
├── modules.ipynb
└── use-of-ai.ipynb
├── L7
├── img
│ ├── subplots.png
│ ├── lines-grayscale.png
│ ├── python-plotting.png
│ └── basic-elements-of-plot.png
├── line-plots.ipynb
├── python-plotting.ipynb
└── subplots.ipynb
├── L5
├── img
│ ├── Table_join_logic.png
│ ├── pandas-save-file-1.png
│ ├── pandas-save-file-2.png
│ └── pandas-structures-annotated.png
└── data
│ ├── Kumpula_Precipitation_Summer2024.txt
│ └── kumpula-summer-2024.txt
├── L2
├── img
│ ├── f-string-annotated.png
│ └── bill-the-vending-machine.png
└── gcp-2-describing-code.ipynb
├── L3
├── img
│ └── 2009-11-03-Cats-Eye.jpg
├── gcp-3-pep8.ipynb
├── conditional-statements.ipynb
└── for-loops.ipynb
├── requirements.txt
├── README.md
└── L1
├── gcp-1-variable-naming.ipynb
└── a-taste-of-python.ipynb
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.11
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .ipynb_checkpoints
2 | /*/.ipynb_checkpoints
3 | .nbgrader.log
4 |
--------------------------------------------------------------------------------
/L6/img/fmi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L6/img/fmi.png
--------------------------------------------------------------------------------
/L4/img/chatgpt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L4/img/chatgpt.png
--------------------------------------------------------------------------------
/L7/img/subplots.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L7/img/subplots.png
--------------------------------------------------------------------------------
/L4/img/chatgpt_signup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L4/img/chatgpt_signup.png
--------------------------------------------------------------------------------
/L4/img/new-python-file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L4/img/new-python-file.png
--------------------------------------------------------------------------------
/L4/img/new-python-tab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L4/img/new-python-tab.png
--------------------------------------------------------------------------------
/L6/img/yle-summer-2024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L6/img/yle-summer-2024.png
--------------------------------------------------------------------------------
/L7/img/lines-grayscale.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L7/img/lines-grayscale.png
--------------------------------------------------------------------------------
/L7/img/python-plotting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L7/img/python-plotting.png
--------------------------------------------------------------------------------
/L4/img/function_anatomy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L4/img/function_anatomy.png
--------------------------------------------------------------------------------
/L5/img/Table_join_logic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L5/img/Table_join_logic.png
--------------------------------------------------------------------------------
/L2/img/f-string-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L2/img/f-string-annotated.png
--------------------------------------------------------------------------------
/L3/img/2009-11-03-Cats-Eye.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L3/img/2009-11-03-Cats-Eye.jpg
--------------------------------------------------------------------------------
/L5/img/pandas-save-file-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L5/img/pandas-save-file-1.png
--------------------------------------------------------------------------------
/L5/img/pandas-save-file-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L5/img/pandas-save-file-2.png
--------------------------------------------------------------------------------
/L4/img/view_module_functions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L4/img/view_module_functions.png
--------------------------------------------------------------------------------
/L7/img/basic-elements-of-plot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L7/img/basic-elements-of-plot.png
--------------------------------------------------------------------------------
/L2/img/bill-the-vending-machine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L2/img/bill-the-vending-machine.png
--------------------------------------------------------------------------------
/L4/img/rename-python-file-part-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L4/img/rename-python-file-part-1.png
--------------------------------------------------------------------------------
/L4/img/rename-python-file-part-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L4/img/rename-python-file-part-2.png
--------------------------------------------------------------------------------
/L6/img/error-message-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L6/img/error-message-annotated.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy==2.1.0
2 | matplotlib==3.9.1
3 | imageio==2.35.1
4 | pandas==2.2.2
5 | jupyter-offlinenotebook==0.3.1
6 |
--------------------------------------------------------------------------------
/L5/img/pandas-structures-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geo-python/notebooks/HEAD/L5/img/pandas-structures-annotated.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # notebooks
2 |
3 | [](https://mybinder.org/v2/gh/geo-python/notebooks/HEAD)
4 |
5 | A repository for student versions of the Geo-Python course Jupyter notebooks. You can explore the notebooks using the Binder button above and find the course materials online at [the Geo-Python course website](https://geo-python.github.io).
6 |
--------------------------------------------------------------------------------
/L5/data/Kumpula_Precipitation_Summer2024.txt:
--------------------------------------------------------------------------------
1 | # Data file contents: Daily precipitation amounts for Kumpula, Helsinki
2 | # for June 1 - August 31, 2024
3 | # Data source: Finnish Meteorological Institute - downloaded from open data portal https://en.ilmatieteenlaitos.fi/download-observations
4 | # Data processing: Extracted precipitation from raw data file, converted to
5 | # comma-separated format - Created YEARMODA column
6 | #
7 | # Processed by Kamyar Hasanzadeh 26.09.2025
8 |
9 | DATE,PRCP
10 | 20240601,0.7
11 | 20240602,0.1
12 | 20240603,0.0
13 | 20240604,-1.0
14 | 20240605,-1.0
15 | 20240606,-1.0
16 | 20240607,-1.0
17 | 20240608,0.9
18 | 20240609,18.5
19 | 20240610,3.0
20 | 20240611,0.3
21 | 20240612,0.0
22 | 20240613,0.0
23 | 20240614,-1.0
24 | 20240615,-1.0
25 | 20240616,-1.0
26 | 20240617,1.0
27 | 20240618,-1.0
28 | 20240619,-1.0
29 | 20240620,-1.0
30 | 20240621,-1.0
31 | 20240622,-1.0
32 | 20240623,-1.0
33 | 20240624,1.4
34 | 20240625,-1.0
35 | 20240626,-1.0
36 | 20240627,0.0
37 | 20240628,0.1
38 | 20240629,-1.0
39 | 20240630,-1.0
40 | 20240701,0.0
41 | 20240702,-1.0
42 | 20240703,5.3
43 | 20240704,0.2
44 | 20240705,11.7
45 | 20240706,5.8
46 | 20240707,1.8
47 | 20240708,0.0
48 | 20240709,-1.0
49 | 20240710,0.0
50 | 20240711,5.9
51 | 20240712,-1.0
52 | 20240713,-1.0
53 | 20240714,9.1
54 | 20240715,0.7
55 | 20240716,-1.0
56 | 20240717,1.7
57 | 20240718,2.3
58 | 20240719,4.4
59 | 20240720,2.2
60 | 20240721,3.9
61 | 20240722,0.0
62 | 20240723,-1.0
63 | 20240724,0.0
64 | 20240725,0.0
65 | 20240726,-1.0
66 | 20240727,-1.0
67 | 20240728,-1.0
68 | 20240729,0.0
69 | 20240730,0.0
70 | 20240731,0.9
71 | 20240801,0.7
72 | 20240802,0.3
73 | 20240803,5.0
74 | 20240804,4.3
75 | 20240805,7.8
76 | 20240806,11.3
77 | 20240807,0.0
78 | 20240808,0.0
79 | 20240809,4.4
80 | 20240810,5.2
81 | 20240811,0.7
82 | 20240812,5.3
83 | 20240813,0.0
84 | 20240814,-1.0
85 | 20240815,0.0
86 | 20240816,-1.0
87 | 20240817,-1.0
88 | 20240818,0.0
89 | 20240819,0.0
90 | 20240820,0.0
91 | 20240821,1.1
92 | 20240822,-1.0
93 | 20240823,4.4
94 | 20240824,-1.0
95 | 20240825,-1.0
96 | 20240826,-1.0
97 | 20240827,-1.0
98 | 20240828,1.0
99 | 20240829,-1.0
100 | 20240830,12.2
101 | 20240831,6.3
102 |
--------------------------------------------------------------------------------
/L5/data/kumpula-summer-2024.txt:
--------------------------------------------------------------------------------
1 | # Data file contents: Daily temperatures (TEMP1 (10AM), TEMP2 (4PM), min, max) for Kumpula, Helsinki
2 | # for June 1 - August 31, 2024
3 | # Data source: Finnish Meteorological Institute - downloaded from open data portal https://en.ilmatieteenlaitos.fi/download-observations
4 | # Data processing: Extracted temperatures from raw data file, converted to
5 | # comma-separated format - Extracted TEMP1 and TEMP2 and created YEARMODA column
6 | #
7 | # Processed by Kamyar Hasanzadeh 15.09.2024
8 |
9 | YEARMODA,MAX,MIN,TEMP1,TEMP2
10 | 20240601,26.2,18.4,24.2,23.2
11 | 20240602,24.4,17.1,20.7,24.2
12 | 20240603,25.7,15.2,22.5,23.1
13 | 20240604,24.6,15.7,19.3,24.6
14 | 20240605,23.4,12.8,20.3,23.2
15 | 20240606,19.3,13.6,19.2,14.9
16 | 20240607,18.5,9.2,16.6,17.5
17 | 20240608,17.6,8.9,14.1,17.0
18 | 20240609,15.5,11.3,12.8,15.4
19 | 20240610,17.6,8.2,13.5,17.6
20 | 20240611,18.5,10.5,15.1,17.8
21 | 20240612,17.9,11.1,14.9,17.5
22 | 20240613,17.7,11.5,15.2,17.7
23 | 20240614,,11.5,15.7,18.1
24 | 20240615,19.0,9.0,16.1,18.9
25 | 20240616,21.5,14.0,18.0,20.2
26 | 20240617,22.9,15.4,19.7,20.6
27 | 20240618,21.7,14.7,18.6,21.4
28 | 20240619,17.8,12.8,16.1,16.8
29 | 20240620,20.8,13.2,15.5,20.5
30 | 20240621,22.2,11.5,18.7,22.1
31 | 20240622,21.4,10.8,18.6,20.9
32 | 20240623,23.1,11.6,20.4,22.5
33 | 20240624,,16.7,19.2,21.0
34 | 20240625,25.7,12.3,20.6,25.0
35 | 20240626,25.7,15.9,22.2,25.5
36 | 20240627,,15.3,22.9,26.9
37 | 20240628,27.2,17.0,25.4,27.2
38 | 20240629,23.9,16.6,19.5,22.0
39 | 20240630,22.7,14.2,20.1,21.1
40 | 20240701,21.4,16.8,19.9,19.9
41 | 20240702,23.4,14.0,19.8,23.4
42 | 20240703,17.4,13.1,14.2,16.6
43 | 20240704,20.6,13.5,17.7,20.2
44 | 20240705,19.6,14.0,18.3,19.0
45 | 20240706,19.0,11.3,11.4,18.7
46 | 20240707,19.7,15.0,16.6,18.5
47 | 20240708,21.3,13.8,18.6,21.3
48 | 20240709,,13.0,19.1,22.5
49 | 20240710,,12.5,20.1,22.0
50 | 20240711,,17.0,19.7,20.9
51 | 20240712,22.7,17.2,19.2,22.2
52 | 20240713,24.7,16.3,23.1,24.5
53 | 20240714,21.3,17.3,18.9,20.1
54 | 20240715,22.3,14.9,19.5,22.0
55 | 20240716,24.3,15.4,20.6,24.3
56 | 20240717,24.3,17.9,21.6,23.5
57 | 20240718,22.5,16.9,19.1,22.5
58 | 20240719,20.7,16.1,17.7,20.0
59 | 20240720,22.8,16.1,19.3,19.9
60 | 20240721,,16.7,19.0,20.7
61 | 20240722,25.1,16.9,22.3,24.7
62 | 20240723,,19.5,22.3,24.9
63 | 20240724,,20.2,22.4,24.2
64 | 20240725,26.1,18.5,22.3,26.1
65 | 20240726,26.7,17.9,24.3,26.4
66 | 20240727,25.1,,22.7,24.8
67 | 20240728,23.7,,20.9,23.6
68 | 20240729,24.9,,20.2,24.9
69 | 20240730,23.5,17.6,18.8,22.7
70 | 20240731,23.2,14.6,18.9,21.4
71 | 20240801,21.7,13.7,16.7,21.0
72 | 20240802,21.7,14.7,16.8,21.7
73 | 20240803,22.5,14.9,17.8,18.3
74 | 20240804,21.8,15.3,17.8,19.9
75 | 20240805,21.0,15.0,19.3,19.6
76 | 20240806,22.3,16.6,18.8,22.3
77 | 20240807,22.3,13.3,18.7,21.3
78 | 20240808,24.0,14.2,21.4,23.9
79 | 20240809,24.1,17.1,22.2,24.1
80 | 20240810,21.0,16.5,17.4,21.0
81 | 20240811,21.0,14.0,18.0,15.2
82 | 20240812,17.4,12.7,14.7,16.0
83 | 20240813,23.9,12.2,19.2,23.4
84 | 20240814,23.8,13.9,20.4,23.8
85 | 20240815,23.9,14.3,20.7,23.9
86 | 20240816,21.2,18.3,19.5,20.9
87 | 20240817,22.6,16.9,20.0,21.4
88 | 20240818,21.2,12.5,18.5,21.2
89 | 20240819,22.3,15.1,18.8,22.3
90 | 20240820,21.0,10.4,17.2,20.9
91 | 20240821,22.3,16.0,20.8,20.6
92 | 20240822,22.1,,19.7,20.8
93 | 20240823,20.3,13.0,18.7,18.2
94 | 20240824,19.1,15.4,17.8,18.6
95 | 20240825,25.9,14.4,19.0,25.9
96 | 20240826,20.0,14.5,17.3,19.7
97 | 20240827,20.5,15.3,17.9,20.4
98 | 20240828,21.6,14.2,18.7,21.3
99 | 20240829,21.9,,16.6,21.8
100 | 20240830,24.6,16.0,20.7,23.5
101 | 20240831,,13.7,13.9,18.7
--------------------------------------------------------------------------------
/L7/line-plots.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "54768bc4-9375-426b-9a99-0c7ccd4d0b75",
6 | "metadata": {
7 | "editable": true,
8 | "slideshow": {
9 | "slide_type": ""
10 | },
11 | "tags": []
12 | },
13 | "source": [
14 | "# Effective plot design: line plots\n",
15 | "\n",
16 | "As we have seen earlier in this chapter, you should be aiming to produce plots that include all of the elements that help make understanding the plotted data intuitive. Typically, this might include:\n",
17 | "\n",
18 | "- Axis labels, including units if needed\n",
19 | "- A legend\n",
20 | "- Grid lines\n",
21 | "- Plot and/or figure titles\n",
22 | "- Annotation on the plot, such as text\n",
23 | "\n",
24 | "In addition, there are several factors that can help improve the communication of the plotted information. When plotting line data, for example, the color of the lines might be an important consideration to tell different lines apart. This is especially true when plotting several lines on the same axes, as readers will need to be able to differentiate the lines and know which line refers to a given set of data. But there is more to plotting lines than simply choosing nice colors. \n",
25 | "\n",
26 | "Not all people viewing your plots will see them the same way. Some viewers may have color blindness, while others may have printed out a copy of your plot in grayscale from a printer. Thus, while choosing nice colors can help make your plots look visually pleasing to you, it is worthwhile to consider the other viewers or formats in which your plots may be viewed. In this way your visualizations can be as inclusive to different viewers as possible.\n",
27 | "\n",
28 | "Let's consider an illustrative example. In this case we will use four lines to plot hypothetical monthly temperatures for various mythical lands in the year 1680 [^nerds]. We will use a `pandas` `DataFrame` called `data` for this purpose with four columns and one year of data. We can see temperatures for the first four months in the data table below by typing `data.head(4)`."
29 | ]
30 | },
31 | {
32 | "cell_type": "code",
33 | "execution_count": null,
34 | "id": "a4801d4b-d318-44a7-b09c-6127b1465d1d",
35 | "metadata": {
36 | "tags": [
37 | "hide-cell"
38 | ]
39 | },
40 | "outputs": [],
41 | "source": [
42 | "# Load the libraries we need\n",
43 | "import matplotlib.pyplot as plt\n",
44 | "import pandas as pd\n",
45 | "\n",
46 | "dates = pd.date_range(start=\"16800101\", end=\"16801201\", freq=\"MS\")\n",
47 | "temperatures = {\n",
48 | " \"Asgard\": [3, 4, 7, 8, 11, 13, 11, 12, 8, 7, 3, 2],\n",
49 | " \"Camelot\": [7, 8, 8, 12, 15, 19, 19, 17, 11, 9, 7, 6],\n",
50 | " \"Nysa\": [10, 13, 13, 17, 21, 23, 22, 20, 19, 16, 11, 10],\n",
51 | " \"Paititi\": [8, 10, 18, 20, 31, 35, 29, 31, 17, 14, 9, 9],\n",
52 | "}\n",
53 | "\n",
54 | "data = pd.DataFrame(index=dates, data=temperatures)"
55 | ]
56 | },
57 | {
58 | "cell_type": "code",
59 | "execution_count": null,
60 | "id": "478a2a8b-9c62-4730-a36d-df91803a5342",
61 | "metadata": {},
62 | "outputs": [],
63 | "source": []
64 | },
65 | {
66 | "cell_type": "markdown",
67 | "id": "1bc0fea3-fede-4e78-b58d-793f49ef6484",
68 | "metadata": {},
69 | "source": [
70 | "Using this data we can create a plot (Figure 4.16) to visualize the temperatures for the four mythical lands using the `pandas` `.plot()` function."
71 | ]
72 | },
73 | {
74 | "cell_type": "code",
75 | "execution_count": null,
76 | "id": "fbf6888e-5b51-4c62-aeb1-bcd1bfa0ec28",
77 | "metadata": {},
78 | "outputs": [],
79 | "source": []
80 | },
81 | {
82 | "cell_type": "markdown",
83 | "id": "a1a3a474-7e97-461d-bde1-0109bed0a872",
84 | "metadata": {},
85 | "source": [
86 | "_**Figure 4.16**. Hypothetical temperatures for one year in different mythical lands._\n",
87 | "\n",
88 | "In Figure 4.16, we can see a visualization of the contents of the `data` `DataFrame` and many people will be able to distinguish the lines using the four colors that have been selected. However, not all people may see the figure in the same way, and those who have printed a copy in grayscale will see things quite differently.\n",
89 | "\n",
90 | "\n",
91 | "\n",
92 | "_**Figure 4.17**. Hypothetical mythical land temperatures in grayscale._\n",
93 | "\n",
94 | "In Figure 4.17, we see that it is nearly impossible to tell which line is which in the plot, so color alone is not helping in distinguishing the lines on this plot. In this case a better option is to vary both the color and line pattern for each line so they can be distinguished easily irrespective of the line colors and how they may be seen. This can be done using the `style` parameter in the `.plot()` function, as shown below."
95 | ]
96 | },
97 | {
98 | "cell_type": "code",
99 | "execution_count": null,
100 | "id": "bec06411-127b-4c54-b500-b6d3b894b275",
101 | "metadata": {},
102 | "outputs": [],
103 | "source": [
104 | "ax = data.plot(\n",
105 | " style=[\"r-\", \"b:\", \"g--\", \"k-.\"],\n",
106 | " xlabel=\"Date\",\n",
107 | " ylabel=\"Temperature (°C)\",\n",
108 | " figsize=(12, 6),\n",
109 | " title=\"Hypothetical temperatures of mythical lands\",\n",
110 | ")\n"
111 | ]
112 | },
113 | {
114 | "cell_type": "markdown",
115 | "id": "3def49d3-8c8c-4f20-bc58-2e604b890979",
116 | "metadata": {},
117 | "source": [
118 | "_**Figure 4.18**. Hypothetical mythical land temperatures with different line styles._\n",
119 | "\n",
120 | "Here in Figure 4.18, viewers can easily tell which line is which whether they have colorblindness or have printed a figure from a printer in grayscale. The difference, of course, is that this figure uses four different line styles: `-` for a solid line, `:` for a dotted line, `--` for a dashed line, and `-.` for a line with dots and dashes. These are defined using shorthand plot formatting for `matplotlib` [^shorthand], for which they are the only four available line styles. If your plots require more than four line styles, you will likely need to use `matplotlib` rather than `pandas` for the plotting. In that case, you can find more about the line styles for `matplotlib` plotting in the [`matplotlib` documentation online](https://matplotlib.org/stable/gallery/lines_bars_and_markers/linestyles.html) [^linestyles]. \n",
121 | "\n",
122 | "Although this plotting example may seem like a simple tip, it can make a great difference in ensuring all viewers see the same data effectively the same way. We will return to the topic of effective plot design to discuss selecting colors and other visualization tips in greater detail in Chapter 8."
123 | ]
124 | }
125 | ],
126 | "metadata": {
127 | "kernelspec": {
128 | "display_name": "Python 3 (ipykernel)",
129 | "language": "python",
130 | "name": "python3"
131 | },
132 | "language_info": {
133 | "codemirror_mode": {
134 | "name": "ipython",
135 | "version": 3
136 | },
137 | "file_extension": ".py",
138 | "mimetype": "text/x-python",
139 | "name": "python",
140 | "nbconvert_exporter": "python",
141 | "pygments_lexer": "ipython3",
142 | "version": "3.13.3"
143 | }
144 | },
145 | "nbformat": 4,
146 | "nbformat_minor": 5
147 | }
148 |
--------------------------------------------------------------------------------
/L6/debugging.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Debugging your Python code\n",
8 | "\n",
9 | "```{attention}\n",
10 | "Finnish university students are encouraged to use the CSC Noppe platform.
\n",
11 | "
\n",
12 | "\n",
13 | "Others can follow the lesson and fill in their student notebooks using Binder.
\n",
14 | "
\n",
15 | "```\n",
16 | "\n",
17 | "Debugging your code can be time consuming, and probably already uses most of the time you devote to working on it. There is no way to avoid spending time fixing bugs, especially when you're learning to program and simply may not yet know how to solve a given programming problem. That said, there are ways to be more effective when debugging, which can save you time and frustration. Below, we review some tips for debugging.\n",
18 | "\n",
19 | "## Source\n",
20 | "\n",
21 | "This lesson is based in part on the [Software Carpentry group's lesson on debugging](https://swcarpentry.github.io/python-novice-inflammation/11-debugging.html).\n",
22 | "\n",
23 | "## General recommendations\n",
24 | "\n",
25 | "Our main recommendations for debugging your Python codes are:\n",
26 | "\n",
27 | "1. Test your code with known outputs\n",
28 | "2. Make your code crash quickly and regularly\n",
29 | "3. Make small changes and track them\n",
30 | "\n",
31 | "We expand on these points further in the text below."
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {},
37 | "source": [
38 | "## Test your code with known outputs\n",
39 | "\n",
40 | "One of the biggest challenges to debugging your code once you solve the syntax issues is knowing whether or not the code actually works like it should. In order to be able to assess this, we need to know the \"answer\" the code should produce. In many cases this means *some form* of calculating a known value using simplified data or test cases.\n",
41 | "\n",
42 | "### Testing with a simplified data file\n",
43 | "\n",
44 | "Let's consider an example of calculating the maximum difference in daily temperature in Helsinki using observations for the past 50 years. First off, we don't know the answer in advance so we cannot simply work on the code until it gives the expected temperature difference. Secondly, we can expect that we should have more than 18 000 observations in our data file for the past 50 years, so it will be hard to confirm we get the right answer because of the size of the dataset. One thing that can be helpful here is to test your program using some small subset of the data. For instance, we could take the top 5 lines of data from the file, which might look like the following:\n",
45 | "\n",
46 | "```\n",
47 | "STATION ELEVATION LATITUDE LONGITUDE DATE PRCP TAVG TMAX TMIN \n",
48 | "----------------- ---------- ---------- ---------- -------- -------- -------- -------- -------- \n",
49 | "GHCND:FIE00142080 51 60.3269 24.9603 19520101 0.31 37 39 34 \n",
50 | "GHCND:FIE00142080 51 60.3269 24.9603 19520102 -9999 35 37 34 \n",
51 | "GHCND:FIE00142080 51 60.3269 24.9603 19520103 0.14 33 36 -9999 \n",
52 | "GHCND:FIE00142080 51 60.3269 24.9603 19520104 0.05 29 30 25 \n",
53 | "GHCND:FIE00142080 51 60.3269 24.9603 19520105 0.06 27 30 25 \n",
54 | "```\n",
55 | "\n",
56 | "From this, we know two things:\n",
57 | "\n",
58 | "1. We should expect the code to be able to handle no data values equal to `-9999`, and to not include days with no data values when calculating the maximum temperature difference (`TMAX` - `TMIN`).\n",
59 | "2. The maximum temperature difference if we test our code with this data file should be 5°.\n",
60 | "\n",
61 | "In this case, we now know that if we can get our code to return the correct answer with the small test file, perhaps the same can be done for the full dataset. In other cases, we may actually know the expected answer, in which case debugging should be a bit easier."
62 | ]
63 | },
64 | {
65 | "cell_type": "markdown",
66 | "metadata": {},
67 | "source": [
68 | "## Make your code crash quickly and regularly\n",
69 | "\n",
70 | "This may sound silly, but it is a good thing when your code crashes the same way every time you run it. If you have different behaviors when you run your code several times without making changes to the code, it will be much more difficult to isolate the source of the problem. What we ideally want in a code is to see behavior that is **consistent**.\n",
71 | "\n",
72 | "In addition, if you expect to debug your program efficiently, you can't afford to wait 30 minutes every time you run it in order for it to crash. If your code crashes when processing a massive data file, you can consider testing with some smaller part of the file. Does the code still crash? If not, why not? Are there some parts of the code that seem to run just fine every time? If you can reduce the time needed for a crash, and isolate where in the code the problem lies (perhaps in memory if you're dealing with really large datasets), you will save yourself time debugging."
73 | ]
74 | },
75 | {
76 | "cell_type": "markdown",
77 | "metadata": {},
78 | "source": [
79 | "## Make small changes and track them\n",
80 | "\n",
81 | "We're teaching you to use [GitHub.com](https://github.com/) to store your work, and to commit your changes regularly. This is for two reasons. First, by keeping track of the changes, you will have a better chance of isolating a problem if you find that suddenly your code doesn't work. You can simply go back to a version of the code that worked and look at what has changed in the version that doesn't work. **This is probably the greatest thing about version control**. Secondly, if you make small changes to the code it is easier to see exactly what changed and where. When it comes to debugging, this is one of the keys to solving problems quickly.\n",
82 | "\n",
83 | "It is worth noting that often we don't keep track and commit every single small change to our codes, but rather commit when things are working as we expected. This means that when you debug, you might not keep track of every little change you make. This is fine, but it is important when you are debugging that you make small changes in one part of the code, then re-test. You should change one thing at a time, test the code, and make more changes if needed. Changing several things at once might be appealing, but it will make it harder to see exactly what is causing the problem because you can't isolate the issue to a single line of the program."
84 | ]
85 | }
86 | ],
87 | "metadata": {
88 | "kernelspec": {
89 | "display_name": "Python 3 (ipykernel)",
90 | "language": "python",
91 | "name": "python3"
92 | },
93 | "language_info": {
94 | "codemirror_mode": {
95 | "name": "ipython",
96 | "version": 3
97 | },
98 | "file_extension": ".py",
99 | "mimetype": "text/x-python",
100 | "name": "python",
101 | "nbconvert_exporter": "python",
102 | "pygments_lexer": "ipython3",
103 | "version": "3.11.6"
104 | }
105 | },
106 | "nbformat": 4,
107 | "nbformat_minor": 4
108 | }
109 |
--------------------------------------------------------------------------------
/L1/gcp-1-variable-naming.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "tags": [
7 | "raises-exception"
8 | ]
9 | },
10 | "source": [
11 | "# GCP 1 - Selecting \"good\" variable names\n",
12 | "\n",
13 | "```{note}\n",
14 | "A video for this good coding practice can be found on the {doc}`overview page for Lesson 1<../../lessons/L1/overview>`.\n",
15 | "```\n",
16 | "\n",
17 | "This course aims to introduce you to programming in Python, but also to *good programming practices*. These comprise practical tips based on practices used by professional programmers that help them write better programs that are easier to understand and share with others. To say it differently, there are many ways to learn to program, and we want to help you learn how to program the right way!\n",
18 | "\n",
19 | "This week our good programming practice (GCP) is about selecting \"good\" variable names. \n",
20 | "\n",
21 | "**Note** that some terms are not even allowed on Python to be uses as variable names. This includes numbers, words starting with a number, and reserved keywords in Python. We can view a list of reserved terms in Python as follows: "
22 | ]
23 | },
24 | {
25 | "cell_type": "code",
26 | "execution_count": null,
27 | "outputs": [],
28 | "source": [
29 | "import keyword\n",
30 | "print(keyword.kwlist)"
31 | ],
32 | "metadata": {
33 | "collapsed": false
34 | }
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "source": [
39 | "## Some \"not-so-good\" variable names\n",
40 | "\n",
41 | "To illustrate the point, consider a few not-so-good examples below."
42 | ],
43 | "metadata": {
44 | "collapsed": false
45 | }
46 | },
47 | {
48 | "cell_type": "code",
49 | "execution_count": 1,
50 | "metadata": {},
51 | "outputs": [],
52 | "source": [
53 | "s = \"101533\""
54 | ]
55 | },
56 | {
57 | "cell_type": "markdown",
58 | "metadata": {},
59 | "source": [
60 | "or"
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": 2,
66 | "metadata": {},
67 | "outputs": [],
68 | "source": [
69 | "sid = \"101533\""
70 | ]
71 | },
72 | {
73 | "cell_type": "markdown",
74 | "metadata": {},
75 | "source": [
76 | "Any idea what these variables are for? Of course not, the variables ``s`` and ``sid`` are too short and cannot communicate what they should be used for in the code. You might think you know what they are for now, but imagine not looking at the code for a few months. Would you know then? What about if you share the code with a friend? Would they know? Probably not.\n",
77 | "\n",
78 | "Let's look at another example."
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": 3,
84 | "metadata": {},
85 | "outputs": [],
86 | "source": [
87 | "finnishmeteorlogicalinstituteobservationstationidentificationnumber = \"101533\""
88 | ]
89 | },
90 | {
91 | "cell_type": "markdown",
92 | "metadata": {},
93 | "source": [
94 | "OK, so now we have another issue. The variable name ``finnishmeteorologicalinstituteobservationstationidentificationnumber`` potentially provides more information about what the variable represents (the identification number of an FMI observation station), but it does so in a format that is not easy to read, nor something you're likely to want to type more than once. The previous example was too short, and now we have a variable name that is too long (and hard to read as a result)."
95 | ]
96 | },
97 | {
98 | "cell_type": "markdown",
99 | "metadata": {},
100 | "source": [
101 | "## Selecting \"good\" variable names\n",
102 | "\n",
103 | "A good variable name should:\n",
104 | "\n",
105 | "1. Be clear and concise. \n",
106 | "\n",
107 | "2. Be written in English. A general coding practice is to write code with variable names in English, as that is the most likely common language between programmers. Thus, variable names such as ``muuttuja`` (which is also not a good name on other levels) should be avoided.\n",
108 | "\n",
109 | "3. Not contain special characters. Python supports use of special characters by way of various encoding options that can be given in a program. That said, it is better to avoid variables such as ``lämpötila`` because encoding issues can arise in some cases. Better to stick to the [standard printable ASCII character set](https://en.wikipedia.org/wiki/ASCII#Printable_characters) to be safe.\n",
110 | "\n",
111 | "4. Not conflict with any [Python keywords](https://www.pythonforbeginners.com/basics/keywords-in-python), such as ``for``, ``True``, ``False``, ``and``, ``if``, or ``else``. These are reserved for speical operations in Python and cannot be used as variable names.\n",
112 | "\n",
113 | "With this in mind, let's now look at a few better options for variable names."
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "metadata": {},
119 | "source": [
120 | "## Formatting \"good\" variable names\n",
121 | "\n",
122 | "There are several possibilities for \"good\" variable name formats, of which we'll consider two:\n",
123 | "\n",
124 | "### Recommendation 1: pothole_case_naming\n",
125 | "\n",
126 | "*pothole_case_naming* uses lowercase words separated by underscores ``_``. This is our suggested format as the underscores make it easy to read the variable, and don't add too much to the length of the variable name. As an example, consider the variable ``temp_celsius``. "
127 | ]
128 | },
129 | {
130 | "cell_type": "code",
131 | "execution_count": 4,
132 | "metadata": {},
133 | "outputs": [],
134 | "source": [
135 | "fmi_station_id = \"101533\""
136 | ]
137 | },
138 | {
139 | "cell_type": "markdown",
140 | "metadata": {},
141 | "source": [
142 | "Here, our new variable name conveys all of the essential information we need, while remaining easy to read."
143 | ]
144 | },
145 | {
146 | "cell_type": "markdown",
147 | "metadata": {},
148 | "source": [
149 | "### Recommendation 2: camelCase naming\n",
150 | "\n",
151 | "*camelCase* or *CamelCase* uses capitalization of the first letter of words in a variable name to make it easier to read. In some cases the first letter of the variable may be capitalized. The variable ``tempFahrenheit`` was one example of camelCase."
152 | ]
153 | },
154 | {
155 | "cell_type": "code",
156 | "execution_count": 5,
157 | "metadata": {},
158 | "outputs": [],
159 | "source": [
160 | "fmiStationID = \"101533\""
161 | ]
162 | },
163 | {
164 | "cell_type": "code",
165 | "execution_count": 6,
166 | "metadata": {},
167 | "outputs": [],
168 | "source": [
169 | "stationID = \"101533\""
170 | ]
171 | },
172 | {
173 | "cell_type": "markdown",
174 | "metadata": {},
175 | "source": [
176 | "Again, this variable name is clear and easy to read.\n",
177 | "\n",
178 | "Our preference in the course is for pothole_case_naming, mostly because we feel it is the easiest to read and seems to be most common amongst Python programmers. We are happy with you using either option, as long as you are consistent in the use. It might take an extra second of thought, but could make a big difference in the ease of use of your programs!"
179 | ]
180 | }
181 | ],
182 | "metadata": {
183 | "kernelspec": {
184 | "display_name": "Python 3 (ipykernel)",
185 | "language": "python",
186 | "name": "python3"
187 | },
188 | "language_info": {
189 | "codemirror_mode": {
190 | "name": "ipython",
191 | "version": 3
192 | },
193 | "file_extension": ".py",
194 | "mimetype": "text/x-python",
195 | "name": "python",
196 | "nbconvert_exporter": "python",
197 | "pygments_lexer": "ipython3",
198 | "version": "3.8.10"
199 | }
200 | },
201 | "nbformat": 4,
202 | "nbformat_minor": 4
203 | }
204 |
--------------------------------------------------------------------------------
/L7/python-plotting.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "bb871819-7798-46bb-b6bb-c488e1a1fdd3",
6 | "metadata": {
7 | "editable": true,
8 | "slideshow": {
9 | "slide_type": ""
10 | },
11 | "tags": []
12 | },
13 | "source": [
14 | "# Plotting in Python\n",
15 | "\n",
16 | "Data visualization is an essential part of understanding and interpreting data, and Python has a large number of libraries available for use in visualizing different types of data. Below we provide a brief overview of some of the Python plotting landscape as well as an introduction to the terminology commonly used to refer to different parts of a plot created using Python."
17 | ]
18 | },
19 | {
20 | "cell_type": "markdown",
21 | "id": "9e5c1983-3184-4c38-8386-7568f8a21f8e",
22 | "metadata": {},
23 | "source": [
24 | "## Python plotting libraries\n",
25 | "\n",
26 | "\n",
27 | "\n",
28 | "_**Figure 4.1**. Plotting libraries available in Python. Interactive version online at ._\n",
29 | "\n",
30 | "Python has many nice, useful libraries that can be used for plotting. In the figure above, you can see a number of the available plotting library options, along with how they relate to one another. Of the options above we would like to highlight the following:\n",
31 | "\n",
32 | "- [Matplotlib](https://matplotlib.org/) [^matplotlib]: Matplotlib is one of the most widely used Python plotting libraries, sometimes referred to as \"*the grand old man of Python plotting*\". Plot examples can be found in the [Matplotlib gallery](https://matplotlib.org/stable/gallery/index.html).\n",
33 | "\n",
34 | " - [Matplotlib basemap](https://matplotlib.org/basemap/stable/) [^basemap]: The Matplotlib basemap toolkit is a plugin for visualizing maps in Python. Example plots available in the [Matplotlib basemap gallery](https://matplotlib.org/basemap/stable/users/examples.html).\n",
35 | " - [Seaborn](https://seaborn.pydata.org/) [^seaborn]: Seaborn is a high-level interface for drawing attractive statistical graphics that is built on top of Matplotlib. Example plots can be found in the [Seaborn gallery](https://seaborn.pydata.org/examples/index.html).\n",
36 | "\n",
37 | "- [Bokeh](https://docs.bokeh.org/en/latest/) [^bokeh]: Bokeh is a modern plotting library for static and interactive web-based plots including graphs, maps, and charts. Examples can be found in the [Bokeh gallery](https://docs.bokeh.org/en/latest/docs/gallery.html).\n",
38 | "- [Plotly](https://plotly.com/python/) [^plotly]: Similar in some ways to Bokeh, Plotly is a modern plotting library for static and interactive web-based plots. The Python library for Plotly is free and open source, but some Plotly features are commercial. Example plots are available in the [Plotly gallery](https://plotly.com/python/basic-charts/).\n",
39 | "- [Dash](https://plotly.com/dash/) [^dash]: Dash is a Python framework for building analytical web applications. No JavaScript required. Some parts of Dash are commercial.\n",
40 | "- [ggplot](https://yhat.github.io/ggpy/) [^ggplot]: ggplot is a Python plotting environment for those familiar with creating plots in R using ggplot2. You can use ggplot in Python too! Plot examples can be found in the [ggplot examples](https://yhat.github.io/ggpy/).\n",
41 | "- [HoloViews](https://holoviews.org/) [^holoviews]: and [GeoViews](https://geoviews.org/) [^geoviews]: HoloViews and GeoViews aim to let the data visualize itself. Learn more in the HoloViews introductory video at .\n",
42 | "\n",
43 | " - Both are modern and powerful visualization libraries built on top of Matplotlib and Bokeh that make exploring and visualizing your data quicker than ever before.\n",
44 | " - HoloViews is designed for basic plotting. More information available in the [HoloViews tutorial](https://holoviews.org/Tutorials/index.html) and the [HoloViews example plots](https://holoviews.org/Examples/index.html).\n",
45 | " - GeoViews is designed for creating nice and interactive maps. Examples can be found in the [GeoViews gallery](https://geoviews.org/gallery/index.html).\n",
46 | "\n",
47 | "You should explore the plotting galleries and examples of different visualization libraries above to learn what's possible to do in Python. As you will see, the plotting possibilities in Python are numerous and rich. To get started, we suggest starting by learning to use one that suits your needs best, and later extending your knowledge and skills to other visualization libraries as necessary."
48 | ]
49 | },
50 | {
51 | "cell_type": "markdown",
52 | "id": "bf79e251-0e37-44ab-8eb6-bbfa242a5b1a",
53 | "metadata": {},
54 | "source": [
55 | "## Anatomy of a plot\n",
56 | "\n",
57 | "There are a variety of different kinds of plots (also known as graphs, charts, diagrams, etc.) that have been designed to visually represent the characteristics of a data set. Here is a list of several different types of plots that can be used to present different kinds of data. You can find more information about this plots online in Wikipedia, for example.\n",
58 | "\n",
59 | "- [Bar chart](https://en.wikipedia.org/wiki/Bar_chart)\n",
60 | "- [Histogram](https://en.wikipedia.org/wiki/Histogram)\n",
61 | "- [Scatter plot](https://en.wikipedia.org/wiki/Scatter_plot)\n",
62 | "- [Line chart](https://en.wikipedia.org/wiki/Line_chart)\n",
63 | "- [Scatter plot](https://en.wikipedia.org/wiki/Scatter_plot)\n",
64 | "- [Pie chart](https://en.wikipedia.org/wiki/Pie_chart)\n",
65 | "- [Box plot](https://en.wikipedia.org/wiki/Box_plot)\n",
66 | "- [Violin plot](https://en.wikipedia.org/wiki/Violin_plot)\n",
67 | "- [Dendrogram](https://en.wikipedia.org/wiki/Dendrogram)\n",
68 | "- [Chord diagram](https://en.wikipedia.org/wiki/Chord_diagram)\n",
69 | "- [Treemap](https://en.wikipedia.org/wiki/Treemap)\n",
70 | "- [Network chart](https://en.wikipedia.org/wiki/Network_chart)\n",
71 | "\n",
72 | "However, before starting to visualize our data on a plot our data we need to address an obvious question: **What actually is a plot?** We will not go deep into the details of different types of plots such as those listed above, as it is not the purpose of this book, but rather we will provide a brief introduction to different plots that can be created using Python and the (essential) elements of a plot.\n",
73 | "\n",
74 | "\n",
75 | "\n",
76 | "_**Figure 4.2**. The basic elements of a plot. Image source: Tenkanen (2017)._\n",
77 | "\n",
78 | "In spite of the large variety of types of plots, there are certain elements that are common for most of them (not all). Thus, it is useful to know at least the basic terminology since it makes it easier to find help and information from the internet when you start creating or modifying your own plots. Figure 4.2 illustrates different elements of a basic line plot."
79 | ]
80 | },
81 | {
82 | "cell_type": "markdown",
83 | "id": "b432e853-d6a2-48b0-80d4-2cbbe940966d",
84 | "metadata": {},
85 | "source": [
86 | "### Common plotting terminology\n",
87 | "\n",
88 | "The common terms in Table 4.1 may vary a bit depending on the plotting library that you use. For the list provided here we are using the typical terms for plotting in Matplotlib (see also Figure 4.2).\n",
89 | "\n",
90 | ": _**Table 4.1**. Common terminology for plot features in Matplotlib._\n",
91 | "\n",
92 | "| Term | Description |\n",
93 | "|:-----------|:--------------------------------------------------|\n",
94 | "| Axis | Axis of the graph that are typically x, y and z |\n",
95 | "| | (for 3D plots). |\n",
96 | "| Title | Title of the whole plot. |\n",
97 | "| Label | Name for the whole axis (e.g. xlabel or ylabel). |\n",
98 | "| Legend | Legend for the plot. |\n",
99 | "| Tick label | Text or values that are represented on the axis. |\n",
100 | "| Symbol | Symbol for data point(s) (on a scatter plot) that |\n",
101 | "| | can be presented with different symbol shapes or |\n",
102 | "| | colors. |\n",
103 | "| Size | Size of, for example, a point on a scatter plot. |\n",
104 | "| | Also used for referring to text sizes on a plot. |\n",
105 | "| Linestyle | The style how the line should be drawn. Can be |\n",
106 | "| | solid or dashed, for example. |\n",
107 | "| Linewidth | The width of a line in a plot. |\n",
108 | "| Alpha | Transparency level of a filled element in a plot |\n",
109 | "| | (values range between 0.0 (fully transparent) to |\n",
110 | "| | 1.0 (no trasnparency)). |\n",
111 | "| Tick(s) | Refers to the tick marks on a plot. |\n",
112 | "| Annotation | Refers to the text added to a plot. |\n",
113 | "| Padding | The distance between a (axis/tick) label and the |\n",
114 | "| | axis. |\n"
115 | ]
116 | },
117 | {
118 | "cell_type": "markdown",
119 | "id": "88492595-c49f-429f-9c4f-7ede5ac94ab8",
120 | "metadata": {},
121 | "source": [
122 | "## Footnotes\n",
123 | "\n",
124 | "[^basemap]: \n",
125 | "[^bokeh]: \n",
126 | "[^dash]: \n",
127 | "[^geoviews]: \n",
128 | "[^ggplot]: \n",
129 | "[^holoviews]: \n",
130 | "[^matplotlib]: \n",
131 | "[^plotly]: \n",
132 | "[^seaborn]: "
133 | ]
134 | }
135 | ],
136 | "metadata": {
137 | "kernelspec": {
138 | "display_name": "Python 3 (ipykernel)",
139 | "language": "python",
140 | "name": "python3"
141 | },
142 | "language_info": {
143 | "codemirror_mode": {
144 | "name": "ipython",
145 | "version": 3
146 | },
147 | "file_extension": ".py",
148 | "mimetype": "text/x-python",
149 | "name": "python",
150 | "nbconvert_exporter": "python",
151 | "pygments_lexer": "ipython3",
152 | "version": "3.11.6"
153 | }
154 | },
155 | "nbformat": 4,
156 | "nbformat_minor": 5
157 | }
158 |
--------------------------------------------------------------------------------
/L6/errors.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "tags": [
7 | "raises-exception"
8 | ]
9 | },
10 | "source": [
11 | "# Dealing with errors\n",
12 | "\n",
13 | "```{attention}\n",
14 | "Finnish university students are encouraged to use the CSC Noppe platform.
\n",
15 | "
\n",
16 | "\n",
17 | "Others can follow the lesson and fill in their student notebooks using Binder.
\n",
18 | "
\n",
19 | "```\n",
20 | "\n",
21 | "## Interpreting error messages\n",
22 | "\n",
23 | "So far in the course we have encountered a number of different types of error messages in Python, but have not really discussed how to understand what the computer is trying to tell you when you get an error message.\n",
24 | "We'll do that below.\n",
25 | "For most Python errors you will see and exception raised when the error is encountered, providing some insight into what went wrong and where to look to fix it."
26 | ]
27 | },
28 | {
29 | "cell_type": "markdown",
30 | "metadata": {},
31 | "source": [
32 | "### Reading error messages\n",
33 | "\n",
34 | "Let's imagine you've written the code below called to convert wind speeds from km/hr to m/s and you're dying to figure out how windy it is in [Halifax, Nova Scotia, Canada](https://www.theweathernetwork.com/ca/weather/nova-scotia/halifax) where they report wind speeds in km/hr.\n",
35 | "\n",
36 | "Unfortunately, when you run your script you observe the following:"
37 | ]
38 | },
39 | {
40 | "cell_type": "code",
41 | "execution_count": null,
42 | "metadata": {
43 | "tags": [
44 | "raises-exception"
45 | ]
46 | },
47 | "outputs": [],
48 | "source": [
49 | "wind_speed_km = 50\n",
50 | "wind_speed_ms = wind_speed_km * 1000 / 3600\n",
51 | "\n",
52 | "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.)"
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {},
58 | "source": [
59 | "Let's break this example down and see what the error message says.\n",
60 | "\n",
61 | "\n",
62 | "*A SyntaxError, annotated*\n",
63 | "\n",
64 | "As you can see, there is quite a bit of useful information here. We have some information about the code cell that was run, as well as which line of the cell was a problem. We also have the type of error, a `SyntaxError` in this case, where it occurred on the line, and a bit more information about its meaning. The location on the line won't always be correct, but Python makes its best guess for where you should look to solve the problem. In this case, we can see that our f-string is missing the second set of quotation marks.\n",
65 | "\n",
66 | "Let's consider another example, where you have fixed the `SyntaxError` above and now have made a function for calculating a wind speeds in m/s.\n",
67 | "\n",
68 | "When you run this script you encounter a new and bigger error message:"
69 | ]
70 | },
71 | {
72 | "cell_type": "code",
73 | "execution_count": null,
74 | "metadata": {
75 | "tags": [
76 | "raises-exception"
77 | ]
78 | },
79 | "outputs": [],
80 | "source": [
81 | "def convert_wind_speed(speed):\n",
82 | " return speed * 1000 / 3600\n",
83 | "\n",
84 | "wind_speed_km = '30'\n",
85 | "wind_speed_ms = convert_wind_speed(wind_speed_km)\n",
86 | "\n",
87 | "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.\")"
88 | ]
89 | },
90 | {
91 | "cell_type": "markdown",
92 | "metadata": {},
93 | "source": [
94 | "In this case we see a `TypeError` that is part of a *traceback*, where the problem in the code arises from something other than on the line where the code was run. In this case, we have a `TypeError` where we try to divide a character string by a number, something Python cannot do. Hence, the `TypeError` indicating the data types are not compatible. That error, however, does not occur when the code is run until the point where the function is used. Thus, we see the traceback showing that not only does the error occur when the function is called, but also that the problem is in the function definition (note the two arrows indicating the problem lines).\n",
95 | "\n",
96 | "The traceback above may look a bit scarier, but if you take your time and read through what is output, you will again find that the information is helpful in finding the problem in your code. After all, the purpose of the error message is to help the user find a problem :).\n",
97 | "\n",
98 | "```{admonition} Error messages in Python 3.10 and newer\n",
99 | "\n",
100 | "Our course environment is using Python 3.11 which was released in October 2022. It includes changes to [improve error messages](https://docs.python.org/3.10/whatsnew/3.10.html#better-error-messages) that were part of the release of Python 3.10 a year earlier. Nice!\n",
101 | "```"
102 | ]
103 | },
104 | {
105 | "cell_type": "markdown",
106 | "metadata": {},
107 | "source": [
108 | "### Common errors and exceptions\n",
109 | "\n",
110 | "Now that we have some idea of how to read an error message, let's have a look at a few different types of common Python exceptions that are displayed for different program errors.\n",
111 | "\n",
112 | "#### IndexErrors\n",
113 | "\n",
114 | "An `IndexError` occurs when you attempt to reference a value with an index outside the range of values.\n",
115 | "We can easily produce an `IndexError` by trying to access the value at index 5 in the following list of cities: `cities = ['Paris', 'Berlin', 'London']`."
116 | ]
117 | },
118 | {
119 | "cell_type": "code",
120 | "execution_count": null,
121 | "metadata": {},
122 | "outputs": [],
123 | "source": [
124 | "cities = [\"Paris\", \"Berlin\", \"London\"]"
125 | ]
126 | },
127 | {
128 | "cell_type": "code",
129 | "execution_count": null,
130 | "metadata": {
131 | "tags": [
132 | "raises-exception"
133 | ]
134 | },
135 | "outputs": [],
136 | "source": [
137 | "cities[5]"
138 | ]
139 | },
140 | {
141 | "cell_type": "markdown",
142 | "metadata": {},
143 | "source": [
144 | "Here we get the rather clear error message that the index used for the list `cities` is outside of the range of index values for that list."
145 | ]
146 | },
147 | {
148 | "cell_type": "markdown",
149 | "metadata": {},
150 | "source": [
151 | "#### NameErrors\n",
152 | "\n",
153 | "A `NameError` typically occurs when you reference a variable that has not been defined. We can produce a `NameError` by trying `station_id = stations[1]`."
154 | ]
155 | },
156 | {
157 | "cell_type": "code",
158 | "execution_count": null,
159 | "metadata": {
160 | "tags": [
161 | "raises-exception"
162 | ]
163 | },
164 | "outputs": [],
165 | "source": [
166 | "station_id = stations[1]"
167 | ]
168 | },
169 | {
170 | "cell_type": "markdown",
171 | "metadata": {},
172 | "source": [
173 | "In this instance we receive a `NameError` because the list `stations` has not been defined, and we're thus not able to access a value in that list."
174 | ]
175 | },
176 | {
177 | "cell_type": "markdown",
178 | "metadata": {},
179 | "source": [
180 | "#### IndentationErrors\n",
181 | "\n",
182 | "An `IndentationError` is raised whenever a code block is expected to be indented and is either (1) not indented, or (2) is indented inconsistently. Let's see this in two examples below."
183 | ]
184 | },
185 | {
186 | "cell_type": "code",
187 | "execution_count": null,
188 | "metadata": {
189 | "tags": [
190 | "raises-exception"
191 | ]
192 | },
193 | "outputs": [],
194 | "source": [
195 | "for city in cities:\n",
196 | " city = city + \" is a city in Europe\"\n",
197 | " print(city)"
198 | ]
199 | },
200 | {
201 | "cell_type": "code",
202 | "execution_count": null,
203 | "metadata": {
204 | "tags": [
205 | "raises-exception"
206 | ]
207 | },
208 | "outputs": [],
209 | "source": [
210 | "for city in cities:\n",
211 | "city = city + \" is a city in Europe\"\n",
212 | "print(city)"
213 | ]
214 | },
215 | {
216 | "cell_type": "markdown",
217 | "metadata": {},
218 | "source": [
219 | "In both of the examples above, an `IndentationError` is raised. In the first case, the indentation level is inconsistent. In case two, indentation is expected for the code below a `for` statement."
220 | ]
221 | },
222 | {
223 | "cell_type": "markdown",
224 | "metadata": {},
225 | "source": [
226 | "#### TypeErrors\n",
227 | "\n",
228 | "A `TypeError` is raised whenever two incompatible data types are used together. For example, if we try to divide a character string by a number or add a number to a boolean variable, a `TypeError` will be raised."
229 | ]
230 | },
231 | {
232 | "cell_type": "code",
233 | "execution_count": null,
234 | "metadata": {
235 | "tags": [
236 | "raises-exception"
237 | ]
238 | },
239 | "outputs": [],
240 | "source": [
241 | "cities[0] / 5"
242 | ]
243 | },
244 | {
245 | "cell_type": "markdown",
246 | "metadata": {},
247 | "source": [
248 | "In this case, the `TypeError` is because it is not possible to divide a character string by a number."
249 | ]
250 | },
251 | {
252 | "cell_type": "markdown",
253 | "metadata": {},
254 | "source": [
255 | "#### Other kinds of errors\n",
256 | "\n",
257 | "There are certainly [other kinds of errors and exceptions in Python](https://docs.python.org/3/tutorial/errors.html), but this list comprises those you're most likely to encounter.\n",
258 | "As you can see, knowing the name of each error can be helpful in trying to figure out what has gone wrong, and knowing what these common error types mean will save you time trying to fix your programs."
259 | ]
260 | },
261 | {
262 | "cell_type": "markdown",
263 | "metadata": {},
264 | "source": [
265 | "### More information on errors\n",
266 | "\n",
267 | "You can find a bit more information about reading error messages on the [Software Carpentry](https://swcarpentry.github.io/python-novice-inflammation/09-errors.html) and [Python Software Foundation](https://docs.python.org/3/tutorial/errors.html) webpages."
268 | ]
269 | }
270 | ],
271 | "metadata": {
272 | "kernelspec": {
273 | "display_name": "Python 3 (ipykernel)",
274 | "language": "python",
275 | "name": "python3"
276 | },
277 | "language_info": {
278 | "codemirror_mode": {
279 | "name": "ipython",
280 | "version": 3
281 | },
282 | "file_extension": ".py",
283 | "mimetype": "text/x-python",
284 | "name": "python",
285 | "nbconvert_exporter": "python",
286 | "pygments_lexer": "ipython3",
287 | "version": "3.11.6"
288 | }
289 | },
290 | "nbformat": 4,
291 | "nbformat_minor": 4
292 | }
293 |
--------------------------------------------------------------------------------
/L2/gcp-2-describing-code.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# GCP 2 - Describing your code\n",
8 | "\n",
9 | "This week our good programming practice is about describing your code. What we mean here is providing some descriptive text that helps the user (you in many cases) understand what the code is used for, and how certain sections work. In a Jupyter notebook, this text can take two basic forms:\n",
10 | "\n",
11 | "- **Code comments**: Text included within a code cell to help the user understand what the code is doing\n",
12 | "- **Markdown text**: Text in Markdown cells that provides a broader view of the code or analysis that follows.\n",
13 | "\n",
14 | "We'll provide examples of both below, along with some tips."
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | "## Do I need to describe my code? Yes!\n",
22 | "\n",
23 | "\n",
24 | "\n",
25 | "Providing comments or Markdown text to describe your code is essential for a number of reasons:\n",
26 | "\n",
27 | "1. **Code descriptions make it easier to understand your code**. Although {doc}`good variable names` can help you and other users better understand what you code does, your Jupyter notebooks are seldom short enough for you to be able to read over the entire code at once and fully understand what is going on. For this reason, comments and Markdown text are essential parts of making sure your notebooks are easy to use and understand section by section.\n",
28 | "2. **Code descriptions make it easier for other users to use your code**. Even when writing simple software it is worthwhile to take the time to include some extra documentaion about how it works. Everyone has their own tendencies in how they write their software and by including a few comments and/or Markdown text you can make it much easier for people to understand and use your work.\n",
29 | "3. **Writing code descriptions can help you debug your code**. We'll all soon have more experience with not knowing why some piece of code doesn't work the way it should. One way to help fix (or debug) your code is to add comments stating what each line does. By taking things step by step, you may find that your code doesn't actually do what you thought, and fix issues in this way.\n",
30 | "4. **Code descriptions are a big part of why Jupyter notebooks are so powerful**. Code comments are quite helpful in general, but the biggest feature of a Jupyter notebook is the ability to mix rich text with your code. With this platform you can even write scientific texts with embedded code cells to be able to perform calculations, analyze data and visualize your results. This powerful platform is an excellent open science tool that provides a clear means to reproduce your results on demand.\n",
31 | "\n",
32 | "Below we review the main forms of code descriptions we'll use in our Jupyter notebooks."
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {},
38 | "source": [
39 | "## But seriously, the Internet says I shouldn't comment my code\n",
40 | "\n",
41 | "\n",
42 | "\n",
43 | "Yes, yes it does. But don't listen to everything you find on the Internet (except this, listen to this...).\n",
44 | "\n",
45 | "The fact of the matter is that programmers advocate people should not need to comment their code if it is easily understood. In essence, the argument is that if you need comments to understand what the code does, it isn't good code. They don't say you should never comment in your code, but rather that comments are really only needed to describe why something is done a given way (not what is done). While this is true to a degree, this advice doesn't apply to most of us.\n",
46 | "\n",
47 | "Most of us are only just getting started with programming, and our comments serve a bit different purpose. For us, comments make sure we understand what each line of our code does, and help the graders follow your thinking in the code in cases when you take a different path than expected to solve a given problem. In addition, the comments make you pause, look at your code, and then come up with a way to describe what you've done. For new programmers, this practice is excellent!"
48 | ]
49 | },
50 | {
51 | "cell_type": "markdown",
52 | "metadata": {},
53 | "source": [
54 | "## Code comments\n",
55 | "\n",
56 | "Code comments are text within your Python software that does not get executed when the software is run. This text is mixed within the code, and often used to describe parts or all of the code work. There are two types of code comments in Python, described below.\n",
57 | "\n",
58 | "### Line comments\n",
59 | "\n",
60 | "Line comments begin with the `#` character and everything to the right of that character will be ignored by the Python interpreter. These comments are most frequently used to describe single lines of code or a small group of related lines. Let's have a look at an example."
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": null,
66 | "metadata": {},
67 | "outputs": [],
68 | "source": [
69 | "# This is a line comment. It will be ignored when this cell is run"
70 | ]
71 | },
72 | {
73 | "cell_type": "markdown",
74 | "metadata": {},
75 | "source": [
76 | "When you run the cell above, nothing happens. The `#` character indicates the line contains a comment and the Python interpreter simply skips over this line.\n",
77 | "\n",
78 | "Let's have a look at a few more examples."
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": null,
84 | "metadata": {},
85 | "outputs": [],
86 | "source": [
87 | "# This list has the names of FMI observation stations in Helsinki\n",
88 | "station_names = [\n",
89 | " \"Helsinki Harmaja\",\n",
90 | " \"Helsinki Kaisaniemi\",\n",
91 | " \"Helsinki Kaivopuisto\",\n",
92 | " \"Helsinki Kumpula\",\n",
93 | "]"
94 | ]
95 | },
96 | {
97 | "cell_type": "code",
98 | "execution_count": null,
99 | "metadata": {},
100 | "outputs": [],
101 | "source": [
102 | "print(station_names[-1]) # This prints the last value in the list station_names"
103 | ]
104 | },
105 | {
106 | "cell_type": "code",
107 | "execution_count": null,
108 | "metadata": {},
109 | "outputs": [],
110 | "source": [
111 | "# This doesn't work, so I'm commenting it out for now\n",
112 | "# my_life.append(lots_of_money)"
113 | ]
114 | },
115 | {
116 | "cell_type": "markdown",
117 | "metadata": {},
118 | "source": [
119 | "In the examples above you can see some of the ways in which line comments can be used in Python. We encourage you to add line comments within your Python cells to help explain what your code does, especially if there are several lines within a given code cell."
120 | ]
121 | },
122 | {
123 | "cell_type": "markdown",
124 | "metadata": {},
125 | "source": [
126 | "### Block comments\n",
127 | "\n",
128 | "Block comments are similar to line comments in that they are embedded within your code and are not executed when a code cell is run. You can begin a line comment with three quotation marks `'''` and end it with the same thing, three quotation marks `'''`. Everything within the groups of quotation marks will be ignored and it does not make any difference whether you use three single `'''` or double `\"\"\"` quotation marks.\n",
129 | "Let's see some examples."
130 | ]
131 | },
132 | {
133 | "cell_type": "code",
134 | "execution_count": null,
135 | "metadata": {},
136 | "outputs": [],
137 | "source": [
138 | "\"\"\" This text will also be ignored.\n",
139 | "Even if it is spread across multiple lines.\n",
140 | "Cool! \"\"\""
141 | ]
142 | },
143 | {
144 | "cell_type": "code",
145 | "execution_count": null,
146 | "metadata": {},
147 | "outputs": [],
148 | "source": [
149 | "\"\"\" The list below contains names of FMI observation stations in Helsinki.\n",
150 | "More information and a complete list of stations can be found at \n",
151 | "https://en.ilmatieteenlaitos.fi/observation-stations \"\"\"\n",
152 | "station_names = [\n",
153 | " \"Helsinki Harmaja\",\n",
154 | " \"Helsinki Kaisaniemi\",\n",
155 | " \"Helsinki Kaivopuisto\",\n",
156 | " \"Helsinki Kumpula\",\n",
157 | "]"
158 | ]
159 | },
160 | {
161 | "cell_type": "code",
162 | "execution_count": null,
163 | "metadata": {},
164 | "outputs": [],
165 | "source": [
166 | "\"\"\" None of the code below works, commenting this out for now.\n",
167 | "step_one = learn_to_code()\n",
168 | "step_two = become_programmer()\n",
169 | "step_three = ???\n",
170 | "step_four = profit()\n",
171 | "\"\"\""
172 | ]
173 | },
174 | {
175 | "cell_type": "markdown",
176 | "metadata": {},
177 | "source": [
178 | "In Lesson 4 we'll be introduced to a common form of block comment called a docstring that can be used to provide information about certain parts of Python programs to the users."
179 | ]
180 | },
181 | {
182 | "cell_type": "markdown",
183 | "metadata": {},
184 | "source": [
185 | "## Markdown text\n",
186 | "\n",
187 | "Finally, as noted above we can also use Markdown text cells to provide information about how our code works. The Markdown text is not a replacement for line or block comments in the code cells, but rather a place to provide a broader description of the code. Let's see an example of the use of a Markdown cell below under the heading \"Data source\"."
188 | ]
189 | },
190 | {
191 | "cell_type": "markdown",
192 | "metadata": {},
193 | "source": [
194 | "### Data source\n",
195 | "\n",
196 | "Data used in this example comprises observtion station:\n",
197 | "\n",
198 | "- names\n",
199 | "- locations\n",
200 | "- types\n",
201 | "- identification codes\n",
202 | "\n",
203 | "These data are sourced from the [Finnish Meterological Institute website](https://en.ilmatieteenlaitos.fi/observation-stations) and are freely available. The data be easily merged into Python lists manually for further analysis. An example Python cell with select observation station names in Helsinki is below. **NOTE**: These are only some of the observation stations in Helsinki."
204 | ]
205 | },
206 | {
207 | "cell_type": "code",
208 | "execution_count": null,
209 | "metadata": {},
210 | "outputs": [],
211 | "source": [
212 | "station_names = [\n",
213 | " \"Helsinki Harmaja\",\n",
214 | " \"Helsinki Kaisaniemi\",\n",
215 | " \"Helsinki Kaivopuisto\",\n",
216 | " \"Helsinki Kumpula\",\n",
217 | "]"
218 | ]
219 | },
220 | {
221 | "cell_type": "markdown",
222 | "metadata": {},
223 | "source": [
224 | "In the example above, you clearly see the benefit of the Markdown cells for providing nicely formatted text to support the code block beneath it. We can also embed images and other features that make the Jupyter notebook document a powerful tool for studying and learning. We'll get more practice working with Markdown cells in the exercise for this week."
225 | ]
226 | }
227 | ],
228 | "metadata": {
229 | "kernelspec": {
230 | "display_name": "Python 3 (ipykernel)",
231 | "language": "python",
232 | "name": "python3"
233 | },
234 | "language_info": {
235 | "codemirror_mode": {
236 | "name": "ipython",
237 | "version": 3
238 | },
239 | "file_extension": ".py",
240 | "mimetype": "text/x-python",
241 | "name": "python",
242 | "nbconvert_exporter": "python",
243 | "pygments_lexer": "ipython3",
244 | "version": "3.11.6"
245 | }
246 | },
247 | "nbformat": 4,
248 | "nbformat_minor": 4
249 | }
250 |
--------------------------------------------------------------------------------
/L6/gcp-5-assertions.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "tags": [
7 | "raises-exception"
8 | ]
9 | },
10 | "source": [
11 | "# GCP 5 - Using assertions\n",
12 | "\n",
13 | "```{attention}\n",
14 | "Finnish university students are encouraged to use the CSC Noppe platform.
\n",
15 | "
\n",
16 | "\n",
17 | "Others can follow the lesson and fill in their student notebooks using Binder.
\n",
18 | "
\n",
19 | "```\n",
20 | "\n",
21 | "The goal of [defensive programming](https://en.wikipedia.org/wiki/Defensive_programming) is to try to maximize the reliability and overall quality of a piece of software. For us, this means that we should take steps to handle unexpected input values in our code, and to provide helpful error messages that provide meaningful guidance to the user when a program raises an exception. We can take steps toward writing more reliable software by utilizing a helpful features in Python: Assertions.\n",
22 | "\n",
23 | "## Assertions\n",
24 | "\n",
25 | "*Assertions* are a way to assert, or ensure, that the values being used in your scripts are going to be suitable for what the code does. Let's start by considering the function `convert_kph_ms` that converts wind speeds from kilometers per hour to meters per second. We can define and use our function in the cell below."
26 | ]
27 | },
28 | {
29 | "cell_type": "code",
30 | "execution_count": null,
31 | "metadata": {},
32 | "outputs": [],
33 | "source": [
34 | "def convert_kph_ms(speed):\n",
35 | " \"\"\"Converts velocity (speed) in km/hr to m/s\"\"\"\n",
36 | " return speed * 1000 / 3600\n",
37 | "\n",
38 | "\n",
39 | "wind_speed_km = 9\n",
40 | "wind_speed_ms = convert_kph_ms(wind_speed_km)\n",
41 | "\n",
42 | "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.\")"
43 | ]
44 | },
45 | {
46 | "cell_type": "markdown",
47 | "metadata": {},
48 | "source": [
49 | "This all seems fine, but you might want to ensure that the values for the wind speed are not negative numbers, since speed is simply the magnitude of the wind velocity, which should always be positive or zero. We can enforce this condition by adding an assertion to our function."
50 | ]
51 | },
52 | {
53 | "cell_type": "code",
54 | "execution_count": null,
55 | "metadata": {
56 | "tags": [
57 | "raises-exception"
58 | ]
59 | },
60 | "outputs": [],
61 | "source": [
62 | "def convert_kph_ms(speed):\n",
63 | " \"\"\"Converts velocity (speed) in km/hr to m/s\"\"\"\n",
64 | " assert speed >= 0.0\n",
65 | " return speed * 1000 / 3600\n",
66 | "\n",
67 | "\n",
68 | "wind_speed_km = 9\n",
69 | "wind_speed_ms = convert_kph_ms(wind_speed_km)\n",
70 | "\n",
71 | "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.\")"
72 | ]
73 | },
74 | {
75 | "cell_type": "markdown",
76 | "metadata": {},
77 | "source": [
78 | "OK, so everything still works when using a positive value, but what happens if we now give a negative value for the wind speed? Let's check!"
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": null,
84 | "metadata": {
85 | "tags": [
86 | "raises-exception"
87 | ]
88 | },
89 | "outputs": [],
90 | "source": [
91 | "wind_speed_km = -27\n",
92 | "wind_speed_ms = convert_kph_ms(wind_speed_km)\n",
93 | "\n",
94 | "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.\")"
95 | ]
96 | },
97 | {
98 | "cell_type": "markdown",
99 | "metadata": {},
100 | "source": [
101 | "OK, so now we get an `AssertionError` when a negative value is provided. This `AssertionError` is produced because of the `assert` statement we entered in the function definition. If the condition listed after `assert` is false, an `AssertionError` is raised.\n",
102 | "\n",
103 | "This is a definite improvement, however, it would be much better to provide the user with some information about why this assertion exists. Fortunately, we can do this simply by adding some text after the assertion condition."
104 | ]
105 | },
106 | {
107 | "cell_type": "code",
108 | "execution_count": null,
109 | "metadata": {
110 | "tags": [
111 | "raises-exception"
112 | ]
113 | },
114 | "outputs": [],
115 | "source": [
116 | "def convert_kph_ms(speed):\n",
117 | " \"\"\"Converts velocity (speed) in km/hr to m/s\"\"\"\n",
118 | " assert speed >= 0.0, \"Wind speed values must be positive or zero\"\n",
119 | " return speed * 1000 / 3600\n",
120 | "\n",
121 | "\n",
122 | "wind_speed_km = -27\n",
123 | "wind_speed_ms = convert_kph_ms(wind_speed_km)\n",
124 | "\n",
125 | "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.\")"
126 | ]
127 | },
128 | {
129 | "cell_type": "markdown",
130 | "metadata": {},
131 | "source": [
132 | "Nice! Now we see that when the `AssertionError` is raised, the message informs us about why it happened without having to interpret the code. The message also makes it easy to fix our value for `wind_speed_km` to work with the `convert_kph_ms` function.\n",
133 | "\n",
134 | "\n",
135 | "More generally, assertions take on the following form:\n",
136 | "\n",
137 | "```python\n",
138 | "assert , \"Error message to display\"\n",
139 | "```\n",
140 | "\n",
141 | "So we start with the `assert` statement, then give a logical test for some condition. If the test is true, nothing happens and the code continues. If not, the code stops and an `AssertionError` is displayed with the text written after the comma on the line containing the `assert` statement."
142 | ]
143 | },
144 | {
145 | "cell_type": "markdown",
146 | "metadata": {},
147 | "source": [
148 | "### Multiple assertions\n",
149 | "\n",
150 | "#### A bad example\n",
151 | "\n",
152 | "Of course, you may want to have several assertions in a function in order to ensure it works as expected and provides meaningful output. In our case, we might first want to check that the value provided to be converted is a number. If not, we would not be able to convert the units. Let's add a second assertion to make sure our function is \"safe\"."
153 | ]
154 | },
155 | {
156 | "cell_type": "code",
157 | "execution_count": null,
158 | "metadata": {
159 | "tags": [
160 | "raises-exception"
161 | ]
162 | },
163 | "outputs": [],
164 | "source": [
165 | "def convert_kph_ms(speed):\n",
166 | " \"\"\"Converts velocity (speed) in km/hr to m/s\"\"\"\n",
167 | " assert (\n",
168 | " type(speed) == int or type(speed) == float\n",
169 | " ), \"Wind speed values must be numbers\"\n",
170 | " assert speed >= 0.0, \"Wind speed values must be positive or zero\"\n",
171 | " return speed * 1000 / 3600\n",
172 | "\n",
173 | "\n",
174 | "wind_speed_km = \"dog\"\n",
175 | "wind_speed_ms = convert_kph_ms(wind_speed_km)\n",
176 | "\n",
177 | "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.\")"
178 | ]
179 | },
180 | {
181 | "cell_type": "markdown",
182 | "metadata": {},
183 | "source": [
184 | "OK, so that works. Now, if the user attempts to give a data type that is not `int` or `float`, the function will raise an `AssertionError` indicating a number is expected for the function to work. This is fine, but as noted below, there are reasons why you may not want to include assertions of this type in a function.\n",
185 | "\n",
186 | "```{warning}\n",
187 | "You might think that it would be useful to use an assertion to check the type of `speed` in our function in order to make sure that you don't get a `TypeError` as occurred in the previous section. It turns out that this is not really a good idea. The reason is that the philosophical idea of a `TypeError` is to indicate you have incompatible data types. With that in mind, why would you raise an `AssertionError` to do the same thing?\n",
188 | "```"
189 | ]
190 | },
191 | {
192 | "cell_type": "markdown",
193 | "metadata": {},
194 | "source": [
195 | "#### A better example\n",
196 | "\n",
197 | "So we don't want to check our data type compatibility using assertions, but we can include a second assertion to ensure the maximum of the input wind speed is a reasonable number. In this case, we can asssume that the wind speed being converted was measured on Earth, and thus should be lower than [the fastest wind speed ever measured](https://en.wikipedia.org/wiki/Wind_speed#Highest_speed), 408 km/hr. Let's add that condition."
198 | ]
199 | },
200 | {
201 | "cell_type": "code",
202 | "execution_count": null,
203 | "metadata": {
204 | "tags": [
205 | "raises-exception"
206 | ]
207 | },
208 | "outputs": [],
209 | "source": [
210 | "def convert_kph_ms(speed):\n",
211 | " \"\"\"Converts velocity (speed) in km/hr to m/s\"\"\"\n",
212 | " assert speed >= 0.0, \"Wind speed values must be positive or zero\"\n",
213 | " assert speed <= 408.0, \"Wind speed exceeds fastest winds ever measured\"\n",
214 | " return speed * 1000 / 3600\n",
215 | "\n",
216 | "\n",
217 | "wind_speed_km = \"409\"\n",
218 | "wind_speed_ms = convert_kph_ms(wind_speed_km)\n",
219 | "\n",
220 | "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.\")"
221 | ]
222 | },
223 | {
224 | "cell_type": "markdown",
225 | "metadata": {},
226 | "source": [
227 | "This is a better example for two reasons:\n",
228 | "\n",
229 | "1. We now allow a `TypeError` when incompatible data types are used in our function, which is a clear and familiar error message.\n",
230 | "2. We use assertions to check the values used in the function make sense for its intended use. If we want to help users convert wind speeds on Earth, we provide bounds that make sure they are using reasonable input values. Thus, we help them use our function the correct way.\n",
231 | "\n",
232 | "Combined, these assertions ensure our function handles common mistakes and provide the user with helpful feedback to be able to use the function properly.\n",
233 | "\n",
234 | "```{note}\n",
235 | "One thing that is important to note about assertions is that although we use them here to check that our function input values are reasonable, this is not generally the suggested use.\n",
236 | "Instead, more advanced programmers recommend using assertions only to test that your code is working properly internally.\n",
237 | "For example, you would use assertions to check for things that should not happen, such as functions that duplicate values in lists when they should not.\n",
238 | "The reason it is not recommended to use assertions for testing user input values or the existence of files is that assertions can be disabled using flags when running a Python program.\n",
239 | "Thus, it is possible they could be ignored entirely.\n",
240 | "This is fine when debugging code, but obviously not desired when users are running your programs.\n",
241 | "If you're interested in more details, feel free to check out the [article on using assertions in the Python wiki](https://wiki.python.org/moin/UsingAssertionsEffectively).\n",
242 | "```\n",
243 | "\n",
244 | "### More information about assertions\n",
245 | "\n",
246 | "More information about assertions can be found on the [Software Carpentry website](https://swcarpentry.github.io/python-novice-inflammation/10-defensive/index.html)."
247 | ]
248 | }
249 | ],
250 | "metadata": {
251 | "kernelspec": {
252 | "display_name": "Python 3 (ipykernel)",
253 | "language": "python",
254 | "name": "python3"
255 | },
256 | "language_info": {
257 | "codemirror_mode": {
258 | "name": "ipython",
259 | "version": 3
260 | },
261 | "file_extension": ".py",
262 | "mimetype": "text/x-python",
263 | "name": "python",
264 | "nbconvert_exporter": "python",
265 | "pygments_lexer": "ipython3",
266 | "version": "3.11.6"
267 | }
268 | },
269 | "nbformat": 4,
270 | "nbformat_minor": 4
271 | }
272 |
--------------------------------------------------------------------------------
/L4/gcp-4-writing-scripts.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# GCP 4 - Writing our scripts the \"right\" way\n",
8 | "\n",
9 | "The script example for functions we have seen so far works, but one of the big advantages of using scripts is including features that make the code easier to read and understand.\n",
10 | "These include comments in the code that explain what the code does, but are not executed when the code is run.\n",
11 | "As your scripts get longer and more complicated, features such as comments will become essential.\n",
12 | "Below are some suggestions to make sure your code is formatted nicely and easy to understand.\n",
13 | "\n",
14 | "Note that we also suggest you follow [the PEP 8 Style Guide for Python Code](https://peps.python.org/pep-0008/)."
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | "## Inline comments\n",
22 | "\n",
23 | "Inline comments are comments within the code that explain what certain lines of the code do.\n",
24 | "To you, it may seem obvious how the code works, but if you share it with another person perhaps they will not feel the same way.\n",
25 | "This is particularly true when you start using scripts instead of Jupyter notebooks, where you might not feel you can include as much text describing your code.\n",
26 | "Because we want to teach you to code in an effective way, it is a very good idea to make your scripts as easy to read as possible for people.\n",
27 | "Consider the example below.\n",
28 | "\n",
29 | "```python\n",
30 | "# Finnish Meterological Institute observation station name and location data\n",
31 | "# Station name for the station in Kaivopuisto, Helsinki, Finland\n",
32 | "stationName = \"Helsinki Kaivopuisto\"\n",
33 | "# Station latitude and longitude - Latitude is north, longitude is east\n",
34 | "stationLat = 60.15\n",
35 | "stationLong = 24.96\n",
36 | "# Print station name and location to the screen\n",
37 | "print(f\"The {stationName} station is located at {stationLat} N, {stationLong} E\")\n",
38 | "```\n",
39 | "\n",
40 | "Here, we have provided a a bit more information about the data in this script by adding inline comments.\n",
41 | "Inline comments begin with a `#` (number sign or hash), and all characters that follow on that line will be ignored by Python.\n",
42 | "Adding comments to scripts is essential for scientists like ourselves to both help us remember how a script works and to make it easier to share with colleagues.\n",
43 | "It is best to get into the habit of adding comments as you write, especially if you're not using a Jupyter notebook."
44 | ]
45 | },
46 | {
47 | "cell_type": "markdown",
48 | "metadata": {},
49 | "source": [
50 | "## Use line breaks wisely\n",
51 | "\n",
52 | "*Line breaks*, or blank lines, in your scripts can greatly improve readability, and help divide different sections of the script.\n",
53 | "Perhaps it is obvious, but Python will ignore blank lines in a script.\n",
54 | "\n",
55 | "```python\n",
56 | "# Finnish Meterological Institute observation station name and location data\n",
57 | "\n",
58 | "# Station name for the station in Kaivopuisto, Helsinki, Finland\n",
59 | "stationName = \"Helsinki Kaivopuisto\"\n",
60 | "\n",
61 | "# Station latitude and longitude - Latitude is north, longitude is east\n",
62 | "stationLat = 60.15\n",
63 | "stationLong = 24.96\n",
64 | "\n",
65 | "# Print station name and location to the screen\n",
66 | "print(f\"The {stationName} station is located at {stationLat} N, {stationLong} E\")\n",
67 | "```"
68 | ]
69 | },
70 | {
71 | "cell_type": "markdown",
72 | "metadata": {},
73 | "source": [
74 | "## Use a docstring\n",
75 | "\n",
76 | "A documentation string, or *docstring*, is a block of text that describes what a specific method or module does and how to use it. Surprise surprise, PEP 8 contains more guidance about [documentation strings](https://peps.python.org/pep-0008/#documentation-strings), and docstrings even have their own guide page: [PEP 257](https://peps.python.org/pep-0257/).\n",
77 | "\n",
78 | "A docstring is always the first statement in a module or a function. Docstrings are written using `\"\"\"triple double quotation marks\"\"\"`. We already discussed docstrings when defining functions earlier in lesson 4, and here is an example of using a **multi-line docstring** at the start of a script file.\n",
79 | "\n",
80 | "When adding docstrings at the start of a script file, you can also include your name and/or licensing information.\n",
81 | "\n",
82 | "```python\n",
83 | "\"\"\"Prints information about an FMI observation station to the screen.\n",
84 | "\n",
85 | "Usage:\n",
86 | " ./stationinfo.py\n",
87 | "\n",
88 | "Author:\n",
89 | " David Whipp - 23.9.2020\n",
90 | "\"\"\"\n",
91 | "\n",
92 | "# Finnish Meterological Institute observation station name and location data\n",
93 | "\n",
94 | "# Station name for the station in Kaivopuisto, Helsinki, Finland\n",
95 | "stationName = \"Helsinki Kaivopuisto\"\n",
96 | "\n",
97 | "# Station latitude and longitude - Latitude is north, longitude is east\n",
98 | "stationLat = 60.15\n",
99 | "stationLong = 24.96\n",
100 | "\n",
101 | "# Print station name and location to the screen\n",
102 | "print(f\"The {stationName} station is located at {stationLat} N, {stationLong} E\")\n",
103 | "```\n",
104 | "\n",
105 | "In this example the script is simple, but many Python programs have optional values that can be used by the code when it is run, making the **usage** statement crucial.\n",
106 | "\n",
107 | "Note that the closing quotes for the docstring are on a line by themselves. It is also possible to write [one-line docstrings](https://peps.python.org/pep-0257/#one-line-docstrings), for example with very simple functions, in which case the starting and closing quotation marks are on the same line."
108 | ]
109 | },
110 | {
111 | "cell_type": "markdown",
112 | "metadata": {},
113 | "source": [
114 | "## Advanced topics\n",
115 | "\n",
116 | "### Adding a license\n",
117 | "\n",
118 | "Depending on what you aim to do with your script, you may want to include a formal software license in the docstring to state the conditions under which the code can be used or modified.\n",
119 | "There are many helpful web resources to [teach you about software licenses](https://tldrlegal.com/) and [how to choose a license](http://choosealicense.com/).\n",
120 | "In most cases my preference is the [MIT License](https://opensource.org/licenses/MIT), which is simple and allows software use by anyone. An example is below.\n",
121 | "\n",
122 | "```python\n",
123 | "\"\"\"Prints information about an FMI observation station to the screen.\n",
124 | "\n",
125 | "Usage:\n",
126 | " ./stationinfo.py\n",
127 | "\n",
128 | "License:\n",
129 | " MIT License\n",
130 | "\n",
131 | " Copyright (c) 2020 David Whipp\n",
132 | "\n",
133 | " Permission is hereby granted, free of charge, to any person obtaining a copy\n",
134 | " of this software and associated documentation files (the \"Software\"), to deal\n",
135 | " in the Software without restriction, including without limitation the rights\n",
136 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n",
137 | " copies of the Software, and to permit persons to whom the Software is\n",
138 | " furnished to do so, subject to the following conditions:\n",
139 | "\n",
140 | " The above copyright notice and this permission notice shall be included in all\n",
141 | " copies or substantial portions of the Software.\n",
142 | "\n",
143 | " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
144 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
145 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n",
146 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
147 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n",
148 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n",
149 | " SOFTWARE.\n",
150 | "\"\"\"\n",
151 | "\n",
152 | "# Finnish Meterological Institute observation station name and location data\n",
153 | "\n",
154 | "# Station name for the station in Kaivopuisto, Helsinki, Finland\n",
155 | "stationName = \"Helsinki Kaivopuisto\"\n",
156 | "\n",
157 | "# Station latitude and longitude - Latitude is north, longitude is east\n",
158 | "stationLat = 60.15\n",
159 | "stationLong = 24.96\n",
160 | "\n",
161 | "# Print station name and location to the screen\n",
162 | "print(f\"The {stationName} station is located at {stationLat} N, {stationLong} E\")\n",
163 | "```\n",
164 | "\n",
165 | "Here the license is embedded in the docstring, so anyone using this part of the script will clearly see the license.\n",
166 | "Another possibility would be to include a separate `LICENSE` text file in a software repository that could cover one or more source code files.\n",
167 | "\n",
168 | "In this example I have taken the license information directly from an [online software license template](http://choosealicense.com/licenses/mit/).\n",
169 | "Software licensing is an important consideration when posting your software in online repositories such as GitHub.\n",
170 | "It is one way to protect your intellectual property from being used in ways you do not wish."
171 | ]
172 | },
173 | {
174 | "cell_type": "markdown",
175 | "metadata": {},
176 | "source": [
177 | "### Starting with a shebang\n",
178 | "\n",
179 | "Starting with a shebang is another thing to consider doing with your scripts.\n",
180 | "Why?\n",
181 | "Well, without going into too much detail, it makes it easier for users to run your script directly from a terminal, rather than needing to use a Jupyter notebook or open an Python console first.\n",
182 | "If this doesn’t make a great deal of sense, you can get a bit more information on [Wikipedia](https://en.wikipedia.org/wiki/Shebang_(Unix)).\n",
183 | "\n",
184 | "Starting with a shebang means that the first line of your program starts with the characters `#!` followed by the location of a program that will run the Python software installed on the computer.\n",
185 | "An example is below.\n",
186 | "\n",
187 | "```python\n",
188 | "#!/usr/bin/env python3\n",
189 | "'''Prints information about an FMI observation station to the screen.\n",
190 | "\n",
191 | "Usage:\n",
192 | " ./stationinfo.py\n",
193 | "\n",
194 | "Author:\n",
195 | " David Whipp - 10.9.2017\n",
196 | "'''\n",
197 | "\n",
198 | "# Finnish Meterological Institute observation station name and location data\n",
199 | "\n",
200 | "# Station name for the station in Kaivopuisto, Helsinki, Finland\n",
201 | "stationName = \"Helsinki Kaivopuisto\"\n",
202 | "\n",
203 | "# Station latitude and longitude - Latitude is north, longitude is east\n",
204 | "stationLat = 60.15\n",
205 | "stationLong = 24.96\n",
206 | "\n",
207 | "# Print station name and location to the screen\n",
208 | "print(f\"The {stationName} station is located at {stationLat} N, {stationLong} E\")\n",
209 | "```\n",
210 | "\n",
211 | "We’ll leave it at that for now, but if you have questions let us know."
212 | ]
213 | },
214 | {
215 | "cell_type": "markdown",
216 | "metadata": {},
217 | "source": [
218 | "## Page summary\n",
219 | "\n",
220 | "As we continue in the course we will mostly be working in Jupyter notebooks rather than writing scripts.\n",
221 | "That said, we can still create simple docstrings for functions that provide short statements about how the function works, the parameters that are input, and the value that is returned.\n",
222 | "In the event you end up writing scripts for other courses or your thesis research, don't forget to include inline comments, blank lines, and a docstring!"
223 | ]
224 | }
225 | ],
226 | "metadata": {
227 | "kernelspec": {
228 | "display_name": "Python 3 (ipykernel)",
229 | "language": "python",
230 | "name": "python3"
231 | },
232 | "language_info": {
233 | "codemirror_mode": {
234 | "name": "ipython",
235 | "version": 3
236 | },
237 | "file_extension": ".py",
238 | "mimetype": "text/x-python",
239 | "name": "python",
240 | "nbconvert_exporter": "python",
241 | "pygments_lexer": "ipython3",
242 | "version": "3.11.6"
243 | }
244 | },
245 | "nbformat": 4,
246 | "nbformat_minor": 4
247 | }
248 |
--------------------------------------------------------------------------------
/L3/gcp-3-pep8.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# GCP 3 - Writing readable code\n",
8 | "\n",
9 | "This week's good programming practice focuses on writing code that is easy to read. During the previous lessons we have already discussed good practices regarding [variable naming](../L1/gcp-1-variable-naming.html) and [describing your code using comments](../L2/gcp-2-describing-code.html), which are also part of writing readable code. Here we focus on how to format the actual Python code to make it more readable."
10 | ]
11 | },
12 | {
13 | "cell_type": "markdown",
14 | "metadata": {},
15 | "source": [
16 | "## Working code vs readable code\n",
17 | "\n",
18 | "As you have noticed, Python forces us to indent our code when writing loops and conditional statements. Without the indentation the code won't work at all and you likely will see an `IndentationError`.\n",
19 | "\n",
20 | "However, there are many cases in which you are able to write code that runs without errors, but you (or others!) might have a hard time reading it and understanding what the code actually does. \n",
21 | "\n",
22 | "Ideally, our Python code would be understandable both for the computer and for humans reading it. [Coding conventions](https://en.wikipedia.org/wiki/Coding_conventions) are a set of generally agreed ways of writing programming code in a specific programming language. Coding conventions help programmers to write code that is consistent and easy to read. Consistency and readability are important for sharing your code with others, and also for helping your own brain to follow along!\n",
23 | "\n",
24 | "
\n",
25 | "Source: https://xkcd.com/1513/"
26 | ]
27 | },
28 | {
29 | "cell_type": "markdown",
30 | "metadata": {},
31 | "source": [
32 | "## The PEP 8 Style Guide\n",
33 | "\n",
34 | "```{epigraph}\n",
35 | "Readability counts.\n",
36 | "\n",
37 | "-- [The Zen of Python](https://www.python.org/dev/peps/pep-0020/)\n",
38 | "```\n",
39 | "\n",
40 | "[The PEP 8 Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/) gives coding conventions that help us write code that is readable (by humans!) and consistent with code written by others.\n",
41 | "\n",
42 | "PEP 8 goes far beyond the scope of what we have learned so far during this course, and we recommend that you re-visit the guidelines every now and then when learning new things. Here, we will summarize some highlights that you can start applying to your code right away!\n",
43 | "\n",
44 | "- [Maximum line length](#Maximum-line-length)\n",
45 | "- [Indentation](#Indentation)\n",
46 | "- [Whitespace and binary operators](#Whitespace-and-binary-operators)\n",
47 | "- [Avoid extraneous whitespace](#Avoid-extraneous-whitespace)\n",
48 | "- [Write one statement per line](#Write-one-statement-per-line)"
49 | ]
50 | },
51 | {
52 | "cell_type": "markdown",
53 | "metadata": {},
54 | "source": [
55 | "### Maximum line length\n",
56 | "\n",
57 | "PEP 8 guides us to limit all lines to [79 characters max](https://www.python.org/dev/peps/pep-0008/#maximum-line-length). Comments (multi-line or single line) should be limited to 72 characters.\n",
58 | "\n",
59 | "```{note}\n",
60 | "We use a line length limit of 88 characters on this website. The character limit according to PEP 8 is 79 characters, but often a slightly longer line (up to 90 characters) helps with readability.\n",
61 | "```\n",
62 | "\n",
63 | "One of the guiding principles of Python is that [Simple is better than complex](https://www.python.org/dev/peps/pep-0020/#id3), but sometimes you might end up having a line of code that exceeds 79 characters, for example, when defining lists. \n",
64 | "\n",
65 | "Python is able to interpret the code correctly from multiple lines within parentheses, brackets and braces."
66 | ]
67 | },
68 | {
69 | "cell_type": "code",
70 | "execution_count": null,
71 | "metadata": {},
72 | "outputs": [],
73 | "source": [
74 | "# Implicit line continuation inside brackets\n",
75 | "\n",
76 | "us_cities = [\"Detroit\", \"Chicago\", \"Denver\", \"Boston\", \n",
77 | " \"Portland\", \"San Francisco\", \"Houston\", \"Orlando\"]"
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "metadata": {},
83 | "source": [
84 | "```{note}\n",
85 | "The backslash character (`\\`) might be required to break a line when using more complicated statements such as the `with` statement (not covered during this course). You can find more examples in the [PEP 8 documentation](https://www.python.org/dev/peps/pep-0008/#maximum-line-length).\n",
86 | "```"
87 | ]
88 | },
89 | {
90 | "cell_type": "markdown",
91 | "metadata": {},
92 | "source": [
93 | "### Indentation\n",
94 | "\n",
95 | "Indentation is an essential part of the Python code layout. As we already learned, for loops and conditional statements will not work correctly without consistent indentation. PEP 8 advices us to use [4 spaces per indentation level](https://www.python.org/dev/peps/pep-0008/#indentation). \n",
96 | "\n",
97 | "Let's have a look at an example with `if` statements. The indented line tells Python what to do *if* the condition is `True`. Notice the 4 spaces in the indentation."
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": null,
103 | "metadata": {},
104 | "outputs": [],
105 | "source": [
106 | "weather = \"Rain\"\n",
107 | "wind = \"Windy\"\n",
108 | "\n",
109 | "if (weather == \"Rain\") and (wind == \"Windy\"):\n",
110 | " print('Just stay at home')"
111 | ]
112 | },
113 | {
114 | "cell_type": "markdown",
115 | "metadata": {},
116 | "source": [
117 | "Following PEP 8, it is also possible to break the conditional expression into multiple lines if needed. Notice the extra parentheses."
118 | ]
119 | },
120 | {
121 | "cell_type": "code",
122 | "execution_count": null,
123 | "metadata": {},
124 | "outputs": [],
125 | "source": [
126 | "if ((weather == \"Rain\") \n",
127 | " and (wind == \"Windy\")):\n",
128 | " print(\"Just stay at home\")"
129 | ]
130 | },
131 | {
132 | "cell_type": "markdown",
133 | "metadata": {},
134 | "source": [
135 | "To increase readability of this if statement, we could add extra indetation to the continuation line of the conditional statement."
136 | ]
137 | },
138 | {
139 | "cell_type": "code",
140 | "execution_count": null,
141 | "metadata": {},
142 | "outputs": [],
143 | "source": [
144 | "if ((weather == \"Rain\") \n",
145 | " and (wind == \"Windy\")):\n",
146 | " print(\"Just stay at home\")"
147 | ]
148 | },
149 | {
150 | "cell_type": "markdown",
151 | "metadata": {},
152 | "source": [
153 | "In our case, the first option with the conditional expression on a single line is probably best, as it is not that long after all.\n",
154 | "\n",
155 | "In addition, indentation is needed when breaking one command into multiple lines, such as in our example with the list `us_cities` above. In that case we used the implied line continuation inside the brackets. Following the PEP 8 indentation guidelines, we can define `us_cities` also using a [hanging indent](https://www.python.org/dev/peps/pep-0008/#fn-hi). Note that there is no value on the first line of a list formatted this way."
156 | ]
157 | },
158 | {
159 | "cell_type": "code",
160 | "execution_count": null,
161 | "metadata": {},
162 | "outputs": [],
163 | "source": [
164 | "# Hanging indentation:\n",
165 | "us_cities = [\n",
166 | " \"Detroit\", \"Chicago\", \"Denver\", \"Boston\", \n",
167 | " \"Portland\", \"San Francisco\", \"Houston\", \"Orlando\"]"
168 | ]
169 | },
170 | {
171 | "cell_type": "markdown",
172 | "metadata": {},
173 | "source": [
174 | "We will discuss more about indentation during week 4 when defining functions :)."
175 | ]
176 | },
177 | {
178 | "cell_type": "markdown",
179 | "metadata": {},
180 | "source": [
181 | "\n",
182 | "### Whitespace and binary operators\n",
183 | "\n",
184 | "According to PEP 8 we should [surround binary operators with single space on either side](https://www.python.org/dev/peps/pep-0008/#other-recommendations).\n",
185 | "\n",
186 | "We should do this always with:\n",
187 | "\n",
188 | "- assignment (`=`)\n",
189 | "- augmented assignment (`+=`, `-=` etc.)\n",
190 | "- comparisons (`==`, `<`, `>`, `!=`, `<>`, `<=`, `>=`, `in`, `not in`, `is`, `is not`)\n",
191 | "- booleans (`and`, `or`, `not`)"
192 | ]
193 | },
194 | {
195 | "cell_type": "code",
196 | "execution_count": null,
197 | "metadata": {
198 | "collapsed": false,
199 | "jupyter": {
200 | "outputs_hidden": false
201 | }
202 | },
203 | "outputs": [],
204 | "source": [
205 | "# yes\n",
206 | "i = 1\n",
207 | "i = i + 1\n",
208 | "i += 1"
209 | ]
210 | },
211 | {
212 | "cell_type": "code",
213 | "execution_count": null,
214 | "metadata": {},
215 | "outputs": [],
216 | "source": [
217 | "# no\n",
218 | "i=1\n",
219 | "i=i+1\n",
220 | "i +=1"
221 | ]
222 | },
223 | {
224 | "cell_type": "markdown",
225 | "metadata": {},
226 | "source": [
227 | "If using operators with different priorities, you can also do this."
228 | ]
229 | },
230 | {
231 | "cell_type": "code",
232 | "execution_count": null,
233 | "metadata": {
234 | "collapsed": false,
235 | "jupyter": {
236 | "outputs_hidden": false
237 | }
238 | },
239 | "outputs": [],
240 | "source": [
241 | "# yes\n",
242 | "a = 1\n",
243 | "b = 2\n",
244 | "c = (a+b) * (a-b)"
245 | ]
246 | },
247 | {
248 | "cell_type": "markdown",
249 | "metadata": {},
250 | "source": [
251 | "### Avoid extraneous whitespace\n",
252 | "We should also [avoid having a space between the function name and parentheses when calling a function](https://www.python.org/dev/peps/pep-0008/#whitespace-in-expressions-and-statements)."
253 | ]
254 | },
255 | {
256 | "cell_type": "code",
257 | "execution_count": null,
258 | "metadata": {
259 | "collapsed": false,
260 | "jupyter": {
261 | "outputs_hidden": false
262 | }
263 | },
264 | "outputs": [],
265 | "source": [
266 | "# yes\n",
267 | "print(\"Hello\")"
268 | ]
269 | },
270 | {
271 | "cell_type": "code",
272 | "execution_count": null,
273 | "metadata": {
274 | "collapsed": false,
275 | "jupyter": {
276 | "outputs_hidden": false
277 | }
278 | },
279 | "outputs": [],
280 | "source": [
281 | "# no\n",
282 | "print (\"Hello\")"
283 | ]
284 | },
285 | {
286 | "cell_type": "markdown",
287 | "metadata": {},
288 | "source": [
289 | "### Write one statement per line \n",
290 | "\n",
291 | "For readability it is advised to [avoid writing multiple statements on the same line](https://www.python.org/dev/peps/pep-0008/#other-recommendations)."
292 | ]
293 | },
294 | {
295 | "cell_type": "code",
296 | "execution_count": null,
297 | "metadata": {
298 | "collapsed": false,
299 | "jupyter": {
300 | "outputs_hidden": false
301 | }
302 | },
303 | "outputs": [],
304 | "source": [
305 | "# yes\n",
306 | "print(\"Hello\")\n",
307 | "print(\"world\")"
308 | ]
309 | },
310 | {
311 | "cell_type": "code",
312 | "execution_count": null,
313 | "metadata": {
314 | "collapsed": false,
315 | "jupyter": {
316 | "outputs_hidden": false
317 | }
318 | },
319 | "outputs": [],
320 | "source": [
321 | "# no\n",
322 | "print(\"Hello\"); print(\"world\")"
323 | ]
324 | },
325 | {
326 | "cell_type": "code",
327 | "execution_count": null,
328 | "metadata": {},
329 | "outputs": [],
330 | "source": [
331 | "# yes\n",
332 | "temperature = 17\n",
333 | "if temperature > 25: \n",
334 | " print(f\"{temperature} is greater than 25\")"
335 | ]
336 | },
337 | {
338 | "cell_type": "code",
339 | "execution_count": null,
340 | "metadata": {
341 | "collapsed": false,
342 | "jupyter": {
343 | "outputs_hidden": false
344 | }
345 | },
346 | "outputs": [],
347 | "source": [
348 | "# no\n",
349 | "temperature = 17\n",
350 | "if temperature > 25: print(f\"{temperature} is greater than 25\")"
351 | ]
352 | },
353 | {
354 | "cell_type": "markdown",
355 | "metadata": {},
356 | "source": [
357 | "### Code readability versus code length?\n",
358 | "\n",
359 | "You often have to find a balance between code readability and code length when writing efficient and readable code. [Compound statements](https://docs.python.org/3/reference/compound_stmts.html#compound-statements) are a way of writing multiple statements on the same line to make the code shorter, but perhaps they are also more difficult to read. Thus, [PEP 8 recommends avoiding compound statements in general](https://www.python.org/dev/peps/pep-0008/#other-recommendations). However, sometimes squeezing multiple statements might your best option - you you just have to judge for yourself which option makes the code more readable and use for that."
360 | ]
361 | },
362 | {
363 | "cell_type": "markdown",
364 | "metadata": {},
365 | "source": [
366 | "```{admonition} List comprehensions\n",
367 | "One puzzling example regarding this guideline is the use of list comprehensions when defining lists. [List comprehensions](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions) are a useful approach for creating lists in a concise way. We are not covering list comprehensions during the lessons in this course, but here is a short example from the [Python documentation](https://docs.python.org/3/tutorial/datastructures.html). In some cases, list comprehensions might make your code more readable and concise. In other cases, you might end up writing an excessively long statement which is difficult to read. Let's have a look at two options that produce the same output:\n",
368 | "\n",
369 | "**Option A**: This for loop iterates over a range, squares all values and appends them to a list\n",
370 | "\n",
371 | " squares = []\n",
372 | " for x in range(10):\n",
373 | " squares.append(x**2)\n",
374 | "\n",
375 | "**Option B**: Square all values in the range using a list comprehension\n",
376 | "\n",
377 | " squares = [x**2 for x in range(10)]\n",
378 | "\n",
379 | "Both approaches are fine, and you are free to choose the option that you think makes the code more readable.\n",
380 | "```"
381 | ]
382 | }
383 | ],
384 | "metadata": {
385 | "anaconda-cloud": {},
386 | "kernelspec": {
387 | "display_name": "Python 3 (ipykernel)",
388 | "language": "python",
389 | "name": "python3"
390 | },
391 | "language_info": {
392 | "codemirror_mode": {
393 | "name": "ipython",
394 | "version": 3
395 | },
396 | "file_extension": ".py",
397 | "mimetype": "text/x-python",
398 | "name": "python",
399 | "nbconvert_exporter": "python",
400 | "pygments_lexer": "ipython3",
401 | "version": "3.11.6"
402 | }
403 | },
404 | "nbformat": 4,
405 | "nbformat_minor": 4
406 | }
407 |
--------------------------------------------------------------------------------
/L4/modules.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "editable": true,
7 | "slideshow": {
8 | "slide_type": ""
9 | },
10 | "tags": []
11 | },
12 | "source": [
13 | "# Loading and using modules\n",
14 | "\n",
15 | "In this section we provide some additional background on loading and using *{term}`modules `* in Python. Modules are actually not much different from the Python script files produced in the previous section, and there are many Python modules that are freely available. This is one of the best things about using Python; you can find modules to do *many* common programming tasks in Python!"
16 | ]
17 | },
18 | {
19 | "cell_type": "markdown",
20 | "metadata": {
21 | "editable": true,
22 | "slideshow": {
23 | "slide_type": ""
24 | },
25 | "tags": []
26 | },
27 | "source": [
28 | "## Modules, packages and libraries?\n",
29 | "\n",
30 | "A Python *{term}`module`* [^module] refers to a piece of Python code that is designed to execute a specific task. Technically, modules are simply Python script files (with the file extension `.py`) that contain function definitions and other statements. [Python packages](https://docs.python.org/3/tutorial/modules.html#packages) [^package] are a way of organizing modules into larger entities. \n",
31 | "\n",
32 | "Modules and packages are similar to what are more generally called libraries in programming languages, which again contain code related to specific tasks such as mathematical operations. There are a huge number of Python modules/packages, and many of them greatly extend what can be done in a normal Python program. In fact, the abundance of free Python modules is one of the best reasons to learn and start using Python.\n",
33 | "\n",
34 | "The words \"module\", \"package\" and \"*{term}`library`*\" are often used interchangeably."
35 | ]
36 | },
37 | {
38 | "cell_type": "markdown",
39 | "metadata": {
40 | "editable": true,
41 | "slideshow": {
42 | "slide_type": ""
43 | },
44 | "tags": []
45 | },
46 | "source": [
47 | "## Loading modules\n",
48 | "\n",
49 | "Python modules can be loaded in a number of different ways.\n",
50 | "\n",
51 | "Let's start simple with the math module. Here, we can load the math module using the `import` statement and then we are able to explore some of the functions in the module, such as the square root function `sqrt`."
52 | ]
53 | },
54 | {
55 | "cell_type": "code",
56 | "execution_count": null,
57 | "metadata": {},
58 | "outputs": [],
59 | "source": []
60 | },
61 | {
62 | "cell_type": "code",
63 | "execution_count": null,
64 | "metadata": {},
65 | "outputs": [],
66 | "source": []
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "metadata": {},
71 | "source": [
72 | "Here we have loaded the math module by typing `import math`, which tells Python to read in the functions in the math module and make them available for use. In this example, we see that we can use a function within the math library by typing the name of the module first, a period, and then the name of function we would like to use afterward (e.g., `math.sqrt()`)."
73 | ]
74 | },
75 | {
76 | "cell_type": "markdown",
77 | "metadata": {},
78 | "source": [
79 | "### Built-in functions\n",
80 | "\n",
81 | "[Built-in functions](https://docs.python.org/3/library/functions.html) [^builtin] such as `print()` are always available without importing anything."
82 | ]
83 | },
84 | {
85 | "cell_type": "code",
86 | "execution_count": null,
87 | "metadata": {},
88 | "outputs": [],
89 | "source": []
90 | },
91 | {
92 | "cell_type": "markdown",
93 | "metadata": {},
94 | "source": [
95 | "Technically, the built-in functions belong to a module called `builtins`."
96 | ]
97 | },
98 | {
99 | "cell_type": "markdown",
100 | "metadata": {},
101 | "source": [
102 | "### Renaming imported modules\n",
103 | "\n",
104 | "We can also rename modules when they are imported, which can be helpful when using modules with longer names. Let's import the `math` module, but rename it to `m` using the format `import module as name`. Then we can using the `sqrt` function from the math library and check the type of our module named `m`."
105 | ]
106 | },
107 | {
108 | "cell_type": "code",
109 | "execution_count": null,
110 | "metadata": {},
111 | "outputs": [],
112 | "source": []
113 | },
114 | {
115 | "cell_type": "code",
116 | "execution_count": null,
117 | "metadata": {},
118 | "outputs": [],
119 | "source": []
120 | },
121 | {
122 | "cell_type": "code",
123 | "execution_count": null,
124 | "metadata": {},
125 | "outputs": [],
126 | "source": []
127 | },
128 | {
129 | "cell_type": "markdown",
130 | "metadata": {
131 | "editable": true,
132 | "slideshow": {
133 | "slide_type": ""
134 | },
135 | "tags": []
136 | },
137 | "source": [
138 | "Here, we imported the `math` module with the name `m` instead of `math`. We will see other examples later in the first part of this book where using an alternate name is rather useful. For example, in the next chapter we will start using the `pandas` library for data analysis. It is customary to import pandas as `pd`."
139 | ]
140 | },
141 | {
142 | "cell_type": "code",
143 | "execution_count": null,
144 | "metadata": {
145 | "editable": true,
146 | "slideshow": {
147 | "slide_type": ""
148 | },
149 | "tags": []
150 | },
151 | "outputs": [],
152 | "source": [
153 | "import pandas as pd"
154 | ]
155 | },
156 | {
157 | "cell_type": "markdown",
158 | "metadata": {},
159 | "source": [
160 | "### Importing a single function\n",
161 | "\n",
162 | "It is also possible to import only a single function from a module, rather than the entire module. This is sometimes useful when needing only a small piece of a large module. We can do this using the form `from module import function`. Let's import the `sqrt` function from the `math` module using this approach. Again, we can test using our results by using the function afterwards."
163 | ]
164 | },
165 | {
166 | "cell_type": "code",
167 | "execution_count": null,
168 | "metadata": {},
169 | "outputs": [],
170 | "source": []
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": null,
175 | "metadata": {},
176 | "outputs": [],
177 | "source": []
178 | },
179 | {
180 | "cell_type": "markdown",
181 | "metadata": {
182 | "editable": true,
183 | "slideshow": {
184 | "slide_type": ""
185 | },
186 | "tags": []
187 | },
188 | "source": [
189 | "Though this can be useful, it has two major drawbacks: (1) the imported function could conflict with other built-in or imported function names, and (2) you lose the information about which module contains the imported function. Thus, you should only do this when you truly need to."
190 | ]
191 | },
192 | {
193 | "cell_type": "markdown",
194 | "metadata": {
195 | "editable": true,
196 | "slideshow": {
197 | "slide_type": ""
198 | },
199 | "tags": []
200 | },
201 | "source": [
202 | "### Importing a submodule\n",
203 | "\n",
204 | "Some modules have submodules that can also be imported without importing the entire module. We will see examples of this in later chapters in this book when making data plots using the `pyplot` submodule from the [Matplotlib module](http://matplotlib.org/) [^matplotlib]. In case you're already curious, here is an example."
205 | ]
206 | },
207 | {
208 | "cell_type": "code",
209 | "execution_count": null,
210 | "metadata": {
211 | "editable": true,
212 | "slideshow": {
213 | "slide_type": ""
214 | },
215 | "tags": []
216 | },
217 | "outputs": [],
218 | "source": [
219 | "import matplotlib.pyplot as plt"
220 | ]
221 | },
222 | {
223 | "cell_type": "code",
224 | "execution_count": null,
225 | "metadata": {
226 | "editable": true,
227 | "slideshow": {
228 | "slide_type": ""
229 | },
230 | "tags": []
231 | },
232 | "outputs": [],
233 | "source": [
234 | "# Plot a simple x y line graph with the default plot settings\n",
235 | "plt.plot([1, 2, 3, 4, 5], [5, 2, 3, 4, 1])"
236 | ]
237 | },
238 | {
239 | "cell_type": "markdown",
240 | "metadata": {
241 | "editable": true,
242 | "slideshow": {
243 | "slide_type": ""
244 | },
245 | "tags": []
246 | },
247 | "source": [
248 | "_**Figure 2.9**. Example plot using the Matplotlib module._\n",
249 | "\n",
250 | "You can read more about the `plot` plotting function in the [Matplotlib pyplot documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html) [^matplotdocs]. We will introduce Matplotlib in more detail in Chapter 4."
251 | ]
252 | },
253 | {
254 | "cell_type": "markdown",
255 | "metadata": {
256 | "editable": true,
257 | "slideshow": {
258 | "slide_type": ""
259 | },
260 | "tags": []
261 | },
262 | "source": [
263 | "## Checking available functions and modules\n",
264 | "\n",
265 | "As we saw above, the easiest way to use a module is to import it an then use its functions by typing `modulename.functionname()` and providing the necessary arguments. Yes, it is that simple.\n",
266 | "\n",
267 | "However, there are times that you may not know the names of all of the functions in a module, or which are part of a given module. You can view the list of functions that are part of a module using the `dir()` function.\n",
268 | "\n",
269 | "```python\n",
270 | "In [1]: dir(math)\n",
271 | "```\n",
272 | "\n",
273 | "```\n",
274 | "['__doc__',\n",
275 | " '__file__',\n",
276 | " '__loader__',\n",
277 | " '__name__',\n",
278 | " '__package__',\n",
279 | " '__spec__',\n",
280 | " 'acos',\n",
281 | " 'acosh',\n",
282 | "... (output truncated)\n",
283 | "```"
284 | ]
285 | },
286 | {
287 | "cell_type": "markdown",
288 | "metadata": {
289 | "editable": true,
290 | "slideshow": {
291 | "slide_type": ""
292 | },
293 | "tags": []
294 | },
295 | "source": [
296 | "In Jupyter notebooks you can also browse the available modules by typing in `modulename.` and then pressing the **tab** key:\n",
297 | "\n",
298 | "\n",
299 | "\n",
300 | "_**Figure 2.10**. Listing functions in the math module in JupyterLab._\n",
301 | "\n",
302 | "This may be quite helpful, but what about when you don't know what a given function does? The easiest solution is to use the `help()` function (after importing the module). Let's have a look at the `help()` function output for the `math.sin()` function. Note that we do not include the parentheses for the target function when using the `help()` function."
303 | ]
304 | },
305 | {
306 | "cell_type": "code",
307 | "execution_count": null,
308 | "metadata": {
309 | "editable": true,
310 | "slideshow": {
311 | "slide_type": ""
312 | },
313 | "tags": []
314 | },
315 | "outputs": [],
316 | "source": [
317 | "help(math.sin)"
318 | ]
319 | },
320 | {
321 | "cell_type": "markdown",
322 | "metadata": {
323 | "editable": true,
324 | "slideshow": {
325 | "slide_type": ""
326 | },
327 | "tags": []
328 | },
329 | "source": [
330 | "Another possible issue is that you may not know which modules are available for use in your Python environment. In this case you can type `help(\"modules\")` to see the complete list of installed Python packages. Be aware that although this works, the output can be a bit clumsy and hard to read. In addition, note that you may first see several warnings related to deprecated packages, etc. when running this command.\n",
331 | "\n",
332 | "```python\n",
333 | "In [13]: help(\"modules\")\n",
334 | "```\n",
335 | "\n",
336 | "```code\n",
337 | "Please wait a moment while I gather a list of all available modules...\n",
338 | "\n",
339 | "(Some warning messages may appear here)\n",
340 | "\n",
341 | "Cython click lib2to3 rioxarray\n",
342 | "IPython click_completion libmambapy rlcompleter\n",
343 | "OpenSSL click_log libpysal rtree\n",
344 | "PIL click_plugins linecache runpy\n",
345 | "__future__ cligj linkify_it rvlib\n",
346 | "... (output truncated)\n",
347 | "```"
348 | ]
349 | },
350 | {
351 | "cell_type": "markdown",
352 | "metadata": {
353 | "editable": true,
354 | "slideshow": {
355 | "slide_type": ""
356 | },
357 | "tags": []
358 | },
359 | "source": [
360 | "## Things to avoid\n",
361 | "\n",
362 | "Modules are very useful in Python, but there are a few things you should not do."
363 | ]
364 | },
365 | {
366 | "cell_type": "markdown",
367 | "metadata": {
368 | "editable": true,
369 | "slideshow": {
370 | "slide_type": ""
371 | },
372 | "tags": []
373 | },
374 | "source": [
375 | "### from X import *\n",
376 | "\n",
377 | "Do not import functions using `from X import *`. This may be easier to understand by way of an example, but assuming `X` above is a Python module, `from X import *` will import all of the functions in module X. Though you might think this is helpful, it is much better to simply `import X` or `import X as Y` to maintain the connection between the functions and their module. It is also much more likely you will encounter conflicting function names when using `from X import *`."
378 | ]
379 | },
380 | {
381 | "cell_type": "markdown",
382 | "metadata": {
383 | "editable": true,
384 | "slideshow": {
385 | "slide_type": ""
386 | },
387 | "tags": []
388 | },
389 | "source": [
390 | "### Poor names when renaming on import\n",
391 | "\n",
392 | "Do not use confusing names when renaming on import. Be smart when you import modules, and follow generally used conventions (`import pandas as pd` is a good way to do things!). If you want to make the module name shorter on import, pick a reasonable abbreviation. For instance, `import matplotlib as m` could be confusing, especially if we used `import math as m` above and might do so in other Jupyter notebooks or script files. Similarly, `import matplotlib as math` is perfectly OK syntax in Python, but bound to cause trouble. Remember, people need to be able to read and understand the code you write. Keep it simple and logical."
393 | ]
394 | },
395 | {
396 | "cell_type": "markdown",
397 | "metadata": {
398 | "editable": true,
399 | "slideshow": {
400 | "slide_type": ""
401 | },
402 | "tags": []
403 | },
404 | "source": [
405 | "### Importing modules within notebook or script files\n",
406 | "\n",
407 | "According to the good coding practices described in [PEP 8](https://peps.python.org/pep-0008/#imports) [^pep8], we should always import modules at the top of a file. In this lesson, we are demonstrating how to import different modules along the way, but in general it would be better to import required modules as the very first thing. PEP 8 refers more to traditional script files, but we can apply the guideline to Jupyter Notebook files by placing our imports the first code cell in the notebook."
408 | ]
409 | },
410 | {
411 | "cell_type": "markdown",
412 | "metadata": {
413 | "editable": true,
414 | "slideshow": {
415 | "slide_type": ""
416 | },
417 | "tags": []
418 | },
419 | "source": [
420 | "## Footnotes\n",
421 | "\n",
422 | "[^builtin]: \n",
423 | "[^matplotdocs]: \n",
424 | "[^matplotlib]: \n",
425 | "[^module]: \n",
426 | "[^package]: \n",
427 | "[^pep8]: "
428 | ]
429 | }
430 | ],
431 | "metadata": {
432 | "kernelspec": {
433 | "display_name": "Python 3 (ipykernel)",
434 | "language": "python",
435 | "name": "python3"
436 | },
437 | "language_info": {
438 | "codemirror_mode": {
439 | "name": "ipython",
440 | "version": 3
441 | },
442 | "file_extension": ".py",
443 | "mimetype": "text/x-python",
444 | "name": "python",
445 | "nbconvert_exporter": "python",
446 | "pygments_lexer": "ipython3",
447 | "version": "3.11.6"
448 | }
449 | },
450 | "nbformat": 4,
451 | "nbformat_minor": 4
452 | }
453 |
--------------------------------------------------------------------------------
/L3/conditional-statements.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "editable": true,
7 | "slideshow": {
8 | "slide_type": ""
9 | },
10 | "tags": []
11 | },
12 | "source": [
13 | "# Conditional statements\n",
14 | "\n",
15 | "In this section, we will learn how to make choices in our code using conditional statements (`if`, `elif`, `else`) and Boolean values (`True`, `False`). \n"
16 | ]
17 | },
18 | {
19 | "cell_type": "markdown",
20 | "metadata": {
21 | "editable": true,
22 | "slideshow": {
23 | "slide_type": ""
24 | },
25 | "tags": []
26 | },
27 | "source": [
28 | "## Basics of conditional statements\n",
29 | "\n",
30 | "Conditional statements can change the code behaviour based on certain conditions. The idea is simple: If a condition is met, then a set of actions will be performed. "
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {
36 | "editable": true,
37 | "slideshow": {
38 | "slide_type": ""
39 | },
40 | "tags": []
41 | },
42 | "source": [
43 | "### A simple conditional statement\n",
44 | "\n",
45 | "Let’s look at a simple example with temperatures, and check if a temperature of 17 degrees Celsius is hot or not."
46 | ]
47 | },
48 | {
49 | "cell_type": "code",
50 | "execution_count": null,
51 | "metadata": {
52 | "editable": true,
53 | "slideshow": {
54 | "slide_type": ""
55 | },
56 | "tags": []
57 | },
58 | "outputs": [],
59 | "source": [
60 | "temperature = 17\n",
61 | "\n",
62 | "# If the temperature is greater than 25, print the temperature is hot\n",
63 | "# Otherwise, print the temperature is not hot"
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "metadata": {
69 | "editable": true,
70 | "slideshow": {
71 | "slide_type": ""
72 | },
73 | "tags": []
74 | },
75 | "source": [
76 | "What did we do here? First, we used the `if` and `else` statements to determine what parts of the code to execute. The `if` statement checks to see whether the variable value for `temperature` is greater than 25. If this condition is true, `'17 is hot'` will be written to the screen. Since 17 is smaller than 25, the `if` condition is false and thus the code beneath the `else` statement is executed. The code under the `else` statement will run whenever the `if` condition is false."
77 | ]
78 | },
79 | {
80 | "cell_type": "markdown",
81 | "metadata": {
82 | "editable": true,
83 | "slideshow": {
84 | "slide_type": ""
85 | },
86 | "tags": []
87 | },
88 | "source": [
89 | "Let's update `temperature` to a \"hot\" temperature and repeat the same process."
90 | ]
91 | },
92 | {
93 | "cell_type": "code",
94 | "execution_count": null,
95 | "metadata": {
96 | "editable": true,
97 | "slideshow": {
98 | "slide_type": ""
99 | },
100 | "tags": []
101 | },
102 | "outputs": [],
103 | "source": []
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "metadata": {
108 | "editable": true,
109 | "slideshow": {
110 | "slide_type": ""
111 | },
112 | "tags": []
113 | },
114 | "source": [
115 | "In this case, the `if` statement is true and thus `'30 is hot'` is printed."
116 | ]
117 | },
118 | {
119 | "cell_type": "markdown",
120 | "metadata": {
121 | "editable": true,
122 | "slideshow": {
123 | "slide_type": ""
124 | },
125 | "tags": []
126 | },
127 | "source": [
128 | "How about `if` without `else`? The combination of `if` and `else` is very common, but the `else` statement is not strictly required. Python simply does nothing if the `if` statement is false and there is no `else` statement. "
129 | ]
130 | },
131 | {
132 | "cell_type": "code",
133 | "execution_count": null,
134 | "metadata": {
135 | "editable": true,
136 | "slideshow": {
137 | "slide_type": ""
138 | },
139 | "tags": []
140 | },
141 | "outputs": [],
142 | "source": [
143 | "# Try writing an if statement without an else statement\n"
144 | ]
145 | },
146 | {
147 | "cell_type": "markdown",
148 | "metadata": {
149 | "editable": true,
150 | "slideshow": {
151 | "slide_type": ""
152 | },
153 | "tags": []
154 | },
155 | "source": [
156 | "Makes sense, right? Conditional statements always check if the conditional expression evaluates as `True` or `False`. If true, the codeblock under the conditional statement gets executed. In this example, nothing is printed to the screen if temperature is smaller than 25."
157 | ]
158 | },
159 | {
160 | "cell_type": "markdown",
161 | "metadata": {
162 | "editable": true,
163 | "slideshow": {
164 | "slide_type": ""
165 | },
166 | "tags": []
167 | },
168 | "source": [
169 | "Let's look at another example from our daily lives. As it turns out, we all use logic similar to `if` and `else` conditional statements daily. Imagine you’re getting ready to leave your home for the day and want to decide what to wear. You might look outside to check the weather conditions. If it is raining, you will wear a rain jacket.\n",
170 | "Otherwise, you will not. Remember that Python uses the `==` operator to test if a value is exactly equal to another."
171 | ]
172 | },
173 | {
174 | "cell_type": "code",
175 | "execution_count": null,
176 | "metadata": {},
177 | "outputs": [],
178 | "source": [
179 | "weather = \"rain\"\n",
180 | "\n",
181 | "# If the weather is rain, print \"Wear a raincoat!\"\n",
182 | "# Otherwise, print \"No raincoat needed.\""
183 | ]
184 | },
185 | {
186 | "cell_type": "markdown",
187 | "metadata": {},
188 | "source": [
189 | "As with `for` loops, Python uses colons (`:`) and white space (indentations) to structure conditional statements. If the condition is `True`, the indented code block after the colon (`:`) is executed. The code block may contain several lines of code, but they all must be indented equally. You will receive an `IndentationError`, a `SyntaxError`, or unwanted behavior if you haven't indented your code correctly.\n",
190 | "\n",
191 | "Note also that the case of the text being compared (uppercase or lowercase) is important. For instance, in the example above, if we define `weather = 'Rain'`, the comparison `weather == 'rain'` would be false. One possible solution to this problem is to use the `.lower()` method for strings, which would convert the text to which it is applied to lowercase. In the example here, if we define `weather = Rain`, the comparison `weather.lower() == 'rain'` would be true!"
192 | ]
193 | },
194 | {
195 | "cell_type": "markdown",
196 | "metadata": {
197 | "editable": true,
198 | "slideshow": {
199 | "slide_type": ""
200 | },
201 | "tags": [
202 | "question"
203 | ]
204 | },
205 | "source": [
206 | "#### Question 2.9\n",
207 | "\n",
208 | "We might also need some other rain gear on a rainy day. Think about how you could add another instruction after the `weather == rain` condition so that the code would tell us to:\n",
209 | "\n",
210 | "``` \n",
211 | "Wear a raincoat\n",
212 | "Wear rain boots\n",
213 | "```"
214 | ]
215 | },
216 | {
217 | "cell_type": "code",
218 | "execution_count": null,
219 | "metadata": {
220 | "editable": true,
221 | "slideshow": {
222 | "slide_type": ""
223 | },
224 | "tags": [
225 | "remove_cell"
226 | ]
227 | },
228 | "outputs": [],
229 | "source": [
230 | "# Use this cell to enter your solution.\n"
231 | ]
232 | },
233 | {
234 | "cell_type": "markdown",
235 | "metadata": {},
236 | "source": [
237 | "### Comparison operators\n",
238 | "\n",
239 | "Comparison operators such as `>` and `==` compare the values on each side of the operator. Table 2.3 lists operators used for value comparisons in Python: \n",
240 | "\n",
241 | ": _**Table 2.3**. Comparison operators in Python._\n",
242 | "\n",
243 | "| Operator | Description | Comparison example | Result |\n",
244 | "|:--------:|:-------------------------|:------------------:|:-------:|\n",
245 | "| `==` | Equal to | `\"cat\" == \"dog\"` | `False` |\n",
246 | "| `!=` | Not equal to | `\"cat\" != \"dog\"` | `True` |\n",
247 | "| `<` | Less than | `2 < 1` | `False` |\n",
248 | "| `>` | Greater than | `2 > 1` | `True` |\n",
249 | "| `<=` | Less than or equal to | `2 <= 2` | `True` |\n",
250 | "| `>=` | Greater than or equal to | `2 >= 4` | `False` |"
251 | ]
252 | },
253 | {
254 | "cell_type": "markdown",
255 | "metadata": {},
256 | "source": [
257 | "### Boolean values\n",
258 | "\n",
259 | "As shown in Table 2.3, comparison operations yield Boolean values (`True` or `False`). In Python, the words `True` and `False` are reserved for these Boolean values and cannot be used for anything else. \n",
260 | "\n",
261 | "Let's check the current value of the conditions we used in the previous examples:"
262 | ]
263 | },
264 | {
265 | "cell_type": "code",
266 | "execution_count": null,
267 | "metadata": {},
268 | "outputs": [],
269 | "source": []
270 | },
271 | {
272 | "cell_type": "code",
273 | "execution_count": null,
274 | "metadata": {},
275 | "outputs": [],
276 | "source": []
277 | },
278 | {
279 | "cell_type": "markdown",
280 | "metadata": {},
281 | "source": [
282 | "### if, elif and else\n",
283 | "\n",
284 | "We can link several conditions together using the \"else if\" statement `elif`. Python checks the `elif` and `else` statements only if previous conditions were `False`. You can have multiple `elif` statements to check for additional conditions. Let's create a chain of `if`, `elif`, and `else` statements that are able to tell us if the temperature is above freezing, exactly at the freezing point, or below freezing."
285 | ]
286 | },
287 | {
288 | "cell_type": "code",
289 | "execution_count": null,
290 | "metadata": {},
291 | "outputs": [],
292 | "source": [
293 | "temperature = -3"
294 | ]
295 | },
296 | {
297 | "cell_type": "code",
298 | "execution_count": null,
299 | "metadata": {},
300 | "outputs": [],
301 | "source": [
302 | "# if temperature is greater than zero, print \"is above freezing\"\n",
303 | "# else if temperature is exactly zero, print \"is freezing\"\n",
304 | "# in all other cases, print \"is below freezing\""
305 | ]
306 | },
307 | {
308 | "cell_type": "markdown",
309 | "metadata": {
310 | "editable": true,
311 | "slideshow": {
312 | "slide_type": ""
313 | },
314 | "tags": [
315 | "question"
316 | ]
317 | },
318 | "source": [
319 | "#### Question 2.10\n",
320 | "\n",
321 | "Let's assume that yesterday it was 14 °C, it is 10 °C outside today, and tomorrow it will be 13 °C.\n",
322 | "The following code compares these temperatures and prints something to the screen based on the comparison.\n",
323 | "\n",
324 | "```python\n",
325 | "yesterday = 14\n",
326 | "today = 10\n",
327 | "tomorrow = 13\n",
328 | "\n",
329 | "if yesterday <= today:\n",
330 | " print('A')\n",
331 | "elif today != tomorrow:\n",
332 | " print('B')\n",
333 | "elif yesterday > tomorrow:\n",
334 | " print('C')\n",
335 | "elif today == today:\n",
336 | " print('D')\n",
337 | "```\n",
338 | "\n",
339 | "Which of the letters `A`, `B`, `C`, and `D` would be printed out?"
340 | ]
341 | },
342 | {
343 | "cell_type": "code",
344 | "execution_count": null,
345 | "metadata": {
346 | "editable": true,
347 | "slideshow": {
348 | "slide_type": ""
349 | },
350 | "tags": [
351 | "remove_cell"
352 | ]
353 | },
354 | "outputs": [],
355 | "source": [
356 | "# Use this cell to enter your solution.\n"
357 | ]
358 | },
359 | {
360 | "cell_type": "markdown",
361 | "metadata": {},
362 | "source": [
363 | "### Combining conditions\n",
364 | "\n",
365 | "We can also use `and` and `or` to combine multiple conditions on Boolean values (Table 2.4).\n",
366 | "\n",
367 | ": _**Table 2.4**. Logic for the `and` and `or` keywords in Python._\n",
368 | "\n",
369 | "| Operator | Description | Comparison example | Result |\n",
370 | "|:--------:|:-----------------------|:------------------:|:-------:|\n",
371 | "| `and` | True only if both | `2 > 1 and 1 < 0` | `False` |\n",
372 | "| | comparisons are true | | |\n",
373 | "| `or` | True if either | `2 > 1 or 1 < 0` | `True` |\n",
374 | "| | comparison is true | | |\n",
375 | "| `not` | False if comparison is | `not 2 > 1` | `False` |\n",
376 | "| | true and vice versa | | |\n",
377 | "\n",
378 | "With Table 2.4 in mind, let's consider a few examples."
379 | ]
380 | },
381 | {
382 | "cell_type": "code",
383 | "execution_count": null,
384 | "metadata": {
385 | "editable": true,
386 | "slideshow": {
387 | "slide_type": ""
388 | },
389 | "tags": []
390 | },
391 | "outputs": [],
392 | "source": [
393 | "hot_temperature = 35.0\n",
394 | "warm_temperature = 24.0\n",
395 | "cold_temperature = -4.0\n",
396 | "\n",
397 | "# Using the temperatures above, create an if statement that uses the \"and\"\n",
398 | "# keyword with two conditions.\n",
399 | "# If true, print \"Both parts are true\"\n",
400 | "# If false, print \"At least one part is not true\"\n",
401 | "\n",
402 | "if (hot_temperature > warm_temperature) and (cold_temperature > warm_temperature):\n",
403 | " print(\"Both parts are true\")\n",
404 | "else:\n",
405 | " print(\"At least one part is not true\")"
406 | ]
407 | },
408 | {
409 | "cell_type": "code",
410 | "execution_count": null,
411 | "metadata": {
412 | "editable": true,
413 | "slideshow": {
414 | "slide_type": ""
415 | },
416 | "tags": []
417 | },
418 | "outputs": [],
419 | "source": [
420 | "# Modify the if statement above to use the \"or\" keyword with two conditions.\n",
421 | "# If true, print \"At least one part is true\"\n"
422 | ]
423 | },
424 | {
425 | "cell_type": "markdown",
426 | "metadata": {
427 | "editable": true,
428 | "slideshow": {
429 | "slide_type": ""
430 | },
431 | "tags": []
432 | },
433 | "source": [
434 | "Later in this book we will also see how to use the bitwise operators `&` for `and`, and `|` for `or`."
435 | ]
436 | },
437 | {
438 | "cell_type": "markdown",
439 | "metadata": {
440 | "editable": true,
441 | "slideshow": {
442 | "slide_type": ""
443 | },
444 | "tags": [
445 | "question"
446 | ]
447 | },
448 | "source": [
449 | "#### Question 2.11\n",
450 | "\n",
451 | "Let's return to our example about making decisions on a rainy day. Imagine that we consider not only the rain but also the wind speed (in meters per second). If it is windy or raining, we’ll just stay at home. If it's not windy or raining, we can go out and enjoy the weather! \n",
452 | "\n",
453 | "Let's set 10 m/s as our comfort limit in the conditional statement and see what our Python program tells us to do in these conditions:"
454 | ]
455 | },
456 | {
457 | "cell_type": "code",
458 | "execution_count": null,
459 | "metadata": {
460 | "editable": true,
461 | "slideshow": {
462 | "slide_type": ""
463 | },
464 | "tags": [
465 | "remove_cell"
466 | ]
467 | },
468 | "outputs": [],
469 | "source": [
470 | "# Use this cell to enter your solution.\n",
471 | "weather = \"rain\"\n",
472 | "wind_speed = 14\n",
473 | "comfort_limit = 10\n",
474 | "\n",
475 | "# If it is windy or raining, print \"stay at home\",\n",
476 | "# otherwise (else) print \"go out and enjoy the weather!\"\n"
477 | ]
478 | },
479 | {
480 | "cell_type": "markdown",
481 | "metadata": {},
482 | "source": [
483 | "As you can see, we better just stay home if it is windy or raining! If you don't agree, you can modify the conditions and print statements accordingly."
484 | ]
485 | },
486 | {
487 | "cell_type": "markdown",
488 | "metadata": {
489 | "editable": true,
490 | "slideshow": {
491 | "slide_type": ""
492 | },
493 | "tags": []
494 | },
495 | "source": [
496 | "## Combining for loops and conditional statements\n",
497 | "\n",
498 | "Finally, we can also combine `for` loops and conditional statements. Let's iterate over a list of temperatures and check if the temperature is hot or not:"
499 | ]
500 | },
501 | {
502 | "cell_type": "code",
503 | "execution_count": null,
504 | "metadata": {
505 | "editable": true,
506 | "slideshow": {
507 | "slide_type": ""
508 | },
509 | "tags": []
510 | },
511 | "outputs": [],
512 | "source": [
513 | "temperatures = [0, 28, 12, 17, 30]\n",
514 | "\n",
515 | "# Loop over each temperature\n",
516 | "# If the temperature is greater than 25, print \"...is hot\"\n"
517 | ]
518 | }
519 | ],
520 | "metadata": {
521 | "kernelspec": {
522 | "display_name": "Python 3 (ipykernel)",
523 | "language": "python",
524 | "name": "python3"
525 | },
526 | "language_info": {
527 | "codemirror_mode": {
528 | "name": "ipython",
529 | "version": 3
530 | },
531 | "file_extension": ".py",
532 | "mimetype": "text/x-python",
533 | "name": "python",
534 | "nbconvert_exporter": "python",
535 | "pygments_lexer": "ipython3",
536 | "version": "3.11.6"
537 | }
538 | },
539 | "nbformat": 4,
540 | "nbformat_minor": 4
541 | }
542 |
--------------------------------------------------------------------------------
/L7/subplots.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "editable": true,
7 | "slideshow": {
8 | "slide_type": ""
9 | },
10 | "tags": []
11 | },
12 | "source": [
13 | "# Creating subplots\n",
14 | "\n",
15 | "At this point you should know the basics of making plots with `matplotlib`. Now we will expand on our basic plotting skills to learn how to create more advanced plots. In this section, we will show how to visualize data using `pandas` and `matplotlib` and create multi-panel plots such as the one below.\n",
16 | "\n",
17 | "\n",
18 | "\n",
19 | "_**Figure 4.10**. An example of seasonal temperatures for 2012-2013 using pandas and Matplotlib._"
20 | ]
21 | },
22 | {
23 | "cell_type": "markdown",
24 | "metadata": {},
25 | "source": [
26 | "## Preparing the data for plotting \n",
27 | "\n",
28 | "We will start again by reading in the data file."
29 | ]
30 | },
31 | {
32 | "cell_type": "code",
33 | "execution_count": null,
34 | "metadata": {},
35 | "outputs": [],
36 | "source": [
37 | "import pandas as pd\n",
38 | "import matplotlib.pyplot as plt\n",
39 | "\n",
40 | "fp = \"data/029740.txt\"\n",
41 | "\n",
42 | "data = pd.read_csv(\n",
43 | " fp,\n",
44 | " sep=r\"\\s+\",\n",
45 | " na_values=[\"*\", \"**\", \"***\", \"****\", \"*****\", \"******\"],\n",
46 | " usecols=[\"YR--MODAHRMN\", \"TEMP\", \"MAX\", \"MIN\"],\n",
47 | " parse_dates=[\"YR--MODAHRMN\"],\n",
48 | " index_col=\"YR--MODAHRMN\",\n",
49 | ")"
50 | ]
51 | },
52 | {
53 | "cell_type": "markdown",
54 | "metadata": {},
55 | "source": [
56 | "After reading the file, we can new rename the `TEMP` column as `TEMP_F`, since we will later convert our temperatures from Fahrenheit to Celsius."
57 | ]
58 | },
59 | {
60 | "cell_type": "code",
61 | "execution_count": null,
62 | "metadata": {},
63 | "outputs": [],
64 | "source": [
65 | "new_names = {\"TEMP\": \"TEMP_F\"}\n",
66 | "data = data.rename(columns=new_names)"
67 | ]
68 | },
69 | {
70 | "cell_type": "markdown",
71 | "metadata": {},
72 | "source": [
73 | "At this point we can quickly check the first rows of data to see whether the expected changes have occurred."
74 | ]
75 | },
76 | {
77 | "cell_type": "code",
78 | "execution_count": null,
79 | "metadata": {},
80 | "outputs": [],
81 | "source": []
82 | },
83 | {
84 | "cell_type": "markdown",
85 | "metadata": {},
86 | "source": [
87 | "Next, we have to deal with no-data values. Let's start by checking how many no-data values we have."
88 | ]
89 | },
90 | {
91 | "cell_type": "code",
92 | "execution_count": null,
93 | "metadata": {},
94 | "outputs": [],
95 | "source": []
96 | },
97 | {
98 | "cell_type": "markdown",
99 | "metadata": {},
100 | "source": [
101 | "So, there are 3579 missing values in the `TEMP_F` column and we should remove those. We need not worry about the no-data values in `MAX` and `MIN` columns since we will not use them for the plots produced below. We can remove rows from our `DataFrame` where `TEMP_F` is missing values using the `.dropna()` method."
102 | ]
103 | },
104 | {
105 | "cell_type": "code",
106 | "execution_count": null,
107 | "metadata": {},
108 | "outputs": [],
109 | "source": []
110 | },
111 | {
112 | "cell_type": "markdown",
113 | "metadata": {
114 | "editable": true,
115 | "slideshow": {
116 | "slide_type": ""
117 | },
118 | "tags": [
119 | "question"
120 | ]
121 | },
122 | "source": [
123 | "#### Question 4.2\n",
124 | "\n",
125 | "How many rows of data would remain if we removed all rows with any no-data values from our data (including no-data values in the `MAX` and `MIN` columns)? If you test this, be sure to save the modified `DataFrame` with another variable name or do not use the `inplace` parameter."
126 | ]
127 | },
128 | {
129 | "cell_type": "code",
130 | "execution_count": null,
131 | "metadata": {
132 | "editable": true,
133 | "slideshow": {
134 | "slide_type": ""
135 | },
136 | "tags": [
137 | "remove_cell"
138 | ]
139 | },
140 | "outputs": [],
141 | "source": [
142 | "# Use this cell to enter your solution.\n"
143 | ]
144 | },
145 | {
146 | "cell_type": "markdown",
147 | "metadata": {},
148 | "source": [
149 | "Now that we have loaded the data, we can convert the temperature values from Fahrenheit to Celsius, like we have in earlier chapters."
150 | ]
151 | },
152 | {
153 | "cell_type": "code",
154 | "execution_count": null,
155 | "metadata": {},
156 | "outputs": [],
157 | "source": []
158 | },
159 | {
160 | "cell_type": "markdown",
161 | "metadata": {},
162 | "source": [
163 | "We can once again now check the contents of our `DataFrame`."
164 | ]
165 | },
166 | {
167 | "cell_type": "code",
168 | "execution_count": null,
169 | "metadata": {},
170 | "outputs": [],
171 | "source": []
172 | },
173 | {
174 | "cell_type": "markdown",
175 | "metadata": {},
176 | "source": [
177 | "## Subplots\n",
178 | "\n",
179 | "Having processed and cleaned the data we can now continue working with it and learn how to create figures that contain {term}`subplots`. Subplots are used to display multiple plots in different panels of the same figure, as shown at the start of this section (Figure 4.10).\n",
180 | "\n",
181 | "We can start with creating the subplots by dividing the data in the data file into different groups. In this case we can divide the temperature data into temperatures for the four different seasons of the year. We can make the following selections:\n",
182 | "\n",
183 | "- Winter (December 2012 - February 2013)\n",
184 | "- Spring (March 2013 - May 2013)\n",
185 | "- Summer (June 2013 - August 2013)\n",
186 | "- Autumn (September 2013 - November 2013)"
187 | ]
188 | },
189 | {
190 | "cell_type": "code",
191 | "execution_count": null,
192 | "metadata": {},
193 | "outputs": [],
194 | "source": [
195 | "winter = data.loc[(data.index >= \"201212010000\") & (data.index < \"201303010000\")]\n",
196 | "winter_temps = winter[\"TEMP_C\"]\n",
197 | "\n",
198 | "spring = data.loc[(data.index >= \"201303010000\") & (data.index < \"201306010000\")]\n",
199 | "spring_temps = spring[\"TEMP_C\"]\n",
200 | "\n",
201 | "summer = data.loc[(data.index >= \"201306010000\") & (data.index < \"201309010000\")]\n",
202 | "summer_temps = summer[\"TEMP_C\"]\n",
203 | "\n",
204 | "autumn = data.loc[(data.index >= \"201309010000\") & (data.index < \"201312010000\")]\n",
205 | "autumn_temps = autumn[\"TEMP_C\"]"
206 | ]
207 | },
208 | {
209 | "cell_type": "markdown",
210 | "metadata": {},
211 | "source": [
212 | "Let's have a look at the data from two different seasons to see whether the preceding step appears to have worked as expected."
213 | ]
214 | },
215 | {
216 | "cell_type": "code",
217 | "execution_count": null,
218 | "metadata": {},
219 | "outputs": [],
220 | "source": []
221 | },
222 | {
223 | "cell_type": "markdown",
224 | "metadata": {},
225 | "source": [
226 | "_**Figure 4.11**. Winter temperatures for 2012-2013._"
227 | ]
228 | },
229 | {
230 | "cell_type": "code",
231 | "execution_count": null,
232 | "metadata": {},
233 | "outputs": [],
234 | "source": []
235 | },
236 | {
237 | "cell_type": "markdown",
238 | "metadata": {},
239 | "source": [
240 | "_**Figure 4.12**. Summer temperatures for 2012-2013._\n",
241 | "\n",
242 | "Based on the plots above it looks that the correct seasons have been plotted and the temperatures between winter and summer are quite different, as we would expect. One thing we might need to consider with this is that the y-axis range currently varies between the two plots and we may want to define axis ranges that ensure the data are plotted with the same y-axis ranges in all subplots. This will help make it easier to visually compare the temperatures between seasons.\n",
243 | "\n",
244 | "### Finding the data bounds\n",
245 | "\n",
246 | "In order to define y-axis limits that will include the data from all of the seasons and be consistent between subplots we first need to find the minimum and maximum temperatures from all of the seasons.\n",
247 | "In addition, we should consider that it would be beneficial to have some extra space (padding) between the y-axis limits and those values, such that, for example, the maximum y-axis limit is five degrees higher than the maximum temperature and the minimum y-axis limit is five degrees lower than the minimum temperature. We can do that below by calculating the minimum of each seasons minimum temperature and subtracting five degrees."
248 | ]
249 | },
250 | {
251 | "cell_type": "code",
252 | "execution_count": null,
253 | "metadata": {},
254 | "outputs": [],
255 | "source": [
256 | "# Find lower limit for y-axis\n",
257 | "\n",
258 | "\n",
259 | "# Find upper limit for y-axis\n",
260 | "\n",
261 | "\n",
262 | "# Print y-axis min, max\n",
263 | "print(f\"Minimum temperature: {min_temp}\")\n",
264 | "print(f\"Maximum temperature: {max_temp}\")"
265 | ]
266 | },
267 | {
268 | "cell_type": "markdown",
269 | "metadata": {},
270 | "source": [
271 | "We can now use this temperature range to standardize the y-axis ranges of our plots.\n",
272 | "\n",
273 | "### Displaying multiple subplots in a single figure\n",
274 | "\n",
275 | "With the data split into seasons and y-axis range defined we can now continue to plot data from all four seasons the same figure. We will start by creating a figure containing four subplots in a two by two panel using the `.subplots()` function from `matplotlib`. In the `.subplots()` function, the user can specify how many rows and columns of plots they want to have in their figure.\n",
276 | "We can also specify the size of our figure with the `figsize` parameter that takes the `width` and `height` values (in inches) as input."
277 | ]
278 | },
279 | {
280 | "cell_type": "code",
281 | "execution_count": null,
282 | "metadata": {},
283 | "outputs": [],
284 | "source": []
285 | },
286 | {
287 | "cell_type": "markdown",
288 | "metadata": {},
289 | "source": [
290 | "_**Figure 4.13**. Empty figure template with a 2x2 subplot panel._\n",
291 | "\n",
292 | "We can see from the output of the code cell that we now have a list containing two nested lists, where the first nested list contains the axes for column 1 and 2 of row 1 and the second contains the axes for columns 1 and 2 of row 2.\n",
293 | "\n",
294 | "To make it easier to keep track of things, we can parse these axes into their own variables as follows."
295 | ]
296 | },
297 | {
298 | "cell_type": "code",
299 | "execution_count": null,
300 | "metadata": {},
301 | "outputs": [],
302 | "source": []
303 | },
304 | {
305 | "cell_type": "markdown",
306 | "metadata": {},
307 | "source": [
308 | "Now we have four different axis variables for the different panels in our figure.\n",
309 | "Next we can use these axes to plot the seasonal temperature data.\n",
310 | "We can start by plotting the data for the different seasons with different colors for each of the lines, and we can specify the y-axis limits to be the same for all of the subplots.\n",
311 | "\n",
312 | "- We can use the `c` parameter to change the color of the line. You can define colors using RBG color codes, but it is often easier to use one of the [`matplotlib` named colors](https://matplotlib.org/stable/gallery/color/named_colors.html) [^matplotlib_colors].\n",
313 | "- We can also change the line width or weight using the `lw` parameter.\n",
314 | "- The `ylim` parameter can be used to define the y-axis limits.\n",
315 | "\n",
316 | "Putting all of this together in a single code cell we have the following:"
317 | ]
318 | },
319 | {
320 | "cell_type": "code",
321 | "execution_count": null,
322 | "metadata": {},
323 | "outputs": [],
324 | "source": [
325 | "# Create the figure and subplot axes\n",
326 | "fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(12, 8))\n",
327 | "\n",
328 | "# Define variables to more easily refer to individual axes\n",
329 | "ax11 = axs[0][0]\n",
330 | "ax12 = axs[0][1]\n",
331 | "ax21 = axs[1][0]\n",
332 | "ax22 = axs[1][1]\n",
333 | "\n",
334 | "# Set plot line width\n",
335 | "line_width = 1.5\n",
336 | "\n",
337 | "# Plot data\n",
338 | "\n",
339 | "\n",
340 | "# Display the plot\n",
341 | "# Note: This is not required, but suppresses text from being printed\n",
342 | "# in the output cell\n",
343 | "plt.show()"
344 | ]
345 | },
346 | {
347 | "cell_type": "markdown",
348 | "metadata": {},
349 | "source": [
350 | "_**Figure 4.14**. Seasonal temperatures for 2012-2013 plotted in a 2x2 panel._\n",
351 | "\n",
352 | "Great, now we have all the plots in same figure! However, we can see that there are some problems with our x-axis labels and a few other missing plot items we should add. \n",
353 | "\n",
354 | "Let's recreate the plot and make some improvements. In this version of the plot we will:\n",
355 | "\n",
356 | "- Modify the x- and y-axis labels using the `xlabel` and `ylabel` parameters in the `.plot()` function.\n",
357 | "- Enable grid lines on the plot using the `grid=True` parameter for the `.plot()` function.\n",
358 | "- Add a figure title using the `fig.suptitle()` function.\n",
359 | "- Rotate the x-axis labels using the `plt.setp()` function.\n",
360 | "- Add a text label for each plot panel using the `.text()` function."
361 | ]
362 | },
363 | {
364 | "cell_type": "code",
365 | "execution_count": null,
366 | "metadata": {},
367 | "outputs": [],
368 | "source": [
369 | "# Create the figure and subplot axes\n",
370 | "fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(12, 8))\n",
371 | "\n",
372 | "# Define variables to more easily refer to individual axes\n",
373 | "ax11 = axs[0][0]\n",
374 | "ax12 = axs[0][1]\n",
375 | "ax21 = axs[1][0]\n",
376 | "ax22 = axs[1][1]\n",
377 | "\n",
378 | "# Set plot line width\n",
379 | "line_width = 1.5\n",
380 | "\n",
381 | "# Plot data\n",
382 | "winter_temps.plot(\n",
383 | " ax=ax11,\n",
384 | " c=\"blue\",\n",
385 | " lw=line_width,\n",
386 | " ylim=[min_temp, max_temp],\n",
387 | " ylabel=\"Temperature [°C]\",\n",
388 | " grid=True,\n",
389 | ")\n",
390 | "spring_temps.plot(\n",
391 | " ax=ax12, c=\"orange\", lw=line_width, ylim=[min_temp, max_temp], grid=True\n",
392 | ")\n",
393 | "summer_temps.plot(\n",
394 | " ax=ax21,\n",
395 | " c=\"green\",\n",
396 | " lw=line_width,\n",
397 | " ylim=[min_temp, max_temp],\n",
398 | " xlabel=\"Date\",\n",
399 | " ylabel=\"Temperature [°C]\",\n",
400 | " grid=True,\n",
401 | ")\n",
402 | "autumn_temps.plot(\n",
403 | " ax=ax22,\n",
404 | " c=\"brown\",\n",
405 | " lw=line_width,\n",
406 | " ylim=[min_temp, max_temp],\n",
407 | " xlabel=\"Date\",\n",
408 | " grid=True,\n",
409 | ")\n",
410 | "\n",
411 | "# Set figure title\n",
412 | "fig.suptitle(\"2012-2013 Seasonal temperature observations\" \"- Helsinki-Vantaa airport\")\n",
413 | "\n",
414 | "# Rotate the x-axis labels so they don't overlap\n",
415 | "plt.setp(ax11.xaxis.get_majorticklabels(), rotation=20)\n",
416 | "plt.setp(ax12.xaxis.get_majorticklabels(), rotation=20)\n",
417 | "plt.setp(ax21.xaxis.get_majorticklabels(), rotation=20)\n",
418 | "plt.setp(ax22.xaxis.get_majorticklabels(), rotation=20)\n",
419 | "\n",
420 | "# Season label text\n",
421 | "ax11.text(pd.to_datetime(\"20130215\"), -25, \"Winter\")\n",
422 | "ax12.text(pd.to_datetime(\"20130515\"), -25, \"Spring\")\n",
423 | "ax21.text(pd.to_datetime(\"20130815\"), -25, \"Summer\")\n",
424 | "ax22.text(pd.to_datetime(\"20131115\"), -25, \"Autumn\")\n",
425 | "\n",
426 | "# Display the figure\n",
427 | "plt.show()"
428 | ]
429 | },
430 | {
431 | "cell_type": "markdown",
432 | "metadata": {},
433 | "source": [
434 | "_**Figure 4.15**. Seasonal temperatures for 2012-2013 plotted with season names and gridlines visible._\n",
435 | "\n",
436 | "The new version of the figure essentially conveys the same information as the first version, but the additional plot items help to make it easier to see the plot values and immediately understand the data being presented. Not bad."
437 | ]
438 | },
439 | {
440 | "cell_type": "markdown",
441 | "metadata": {
442 | "editable": true,
443 | "slideshow": {
444 | "slide_type": ""
445 | },
446 | "tags": [
447 | "question"
448 | ]
449 | },
450 | "source": [
451 | "#### Question 4.3\n",
452 | "\n",
453 | "Visualize only the winter and summer temperatures in a one by two panel figure. Save the resulting figure as a .png file."
454 | ]
455 | },
456 | {
457 | "cell_type": "code",
458 | "execution_count": null,
459 | "metadata": {
460 | "tags": [
461 | "remove_cell"
462 | ]
463 | },
464 | "outputs": [],
465 | "source": [
466 | "# Use this cell to enter your solution.\n"
467 | ]
468 | }
469 | ],
470 | "metadata": {
471 | "kernelspec": {
472 | "display_name": "Python 3 (ipykernel)",
473 | "language": "python",
474 | "name": "python3"
475 | },
476 | "language_info": {
477 | "codemirror_mode": {
478 | "name": "ipython",
479 | "version": 3
480 | },
481 | "file_extension": ".py",
482 | "mimetype": "text/x-python",
483 | "name": "python",
484 | "nbconvert_exporter": "python",
485 | "pygments_lexer": "ipython3",
486 | "version": "3.13.3"
487 | }
488 | },
489 | "nbformat": 4,
490 | "nbformat_minor": 4
491 | }
492 |
--------------------------------------------------------------------------------
/L3/for-loops.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "editable": true,
7 | "slideshow": {
8 | "slide_type": ""
9 | },
10 | "tags": []
11 | },
12 | "source": [
13 | "# for loops\n",
14 | "\n",
15 | "In the previous section we introduced *{term}`lists `* in Python, one form of a collection of values that can be represented by a single variable. In this lesson we will learn how to use *{term}`loops `*. Loops allow parts of code to be repeated some number of times, such as iterating over all of the items in a Python list and performing a calculation using each item. "
16 | ]
17 | },
18 | {
19 | "cell_type": "markdown",
20 | "metadata": {
21 | "editable": true,
22 | "slideshow": {
23 | "slide_type": ""
24 | },
25 | "tags": []
26 | },
27 | "source": [
28 | "## A (bad) example\n",
29 | "\n",
30 | "Let’s consider an example using the `list` below:"
31 | ]
32 | },
33 | {
34 | "cell_type": "code",
35 | "execution_count": null,
36 | "metadata": {},
37 | "outputs": [],
38 | "source": [
39 | "european_cities = [\"Helsinki\", \"Paris\", \"Barcelona\", \"Uppsala\"]"
40 | ]
41 | },
42 | {
43 | "cell_type": "markdown",
44 | "metadata": {},
45 | "source": [
46 | "Suppose we want to print out the name of each city in our list. We could use the index value for each city and do the following:"
47 | ]
48 | },
49 | {
50 | "cell_type": "code",
51 | "execution_count": null,
52 | "metadata": {},
53 | "outputs": [],
54 | "source": []
55 | },
56 | {
57 | "cell_type": "code",
58 | "execution_count": null,
59 | "metadata": {},
60 | "outputs": [],
61 | "source": []
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": null,
66 | "metadata": {},
67 | "outputs": [],
68 | "source": []
69 | },
70 | {
71 | "cell_type": "code",
72 | "execution_count": null,
73 | "metadata": {},
74 | "outputs": [],
75 | "source": []
76 | },
77 | {
78 | "cell_type": "markdown",
79 | "metadata": {
80 | "editable": true,
81 | "slideshow": {
82 | "slide_type": ""
83 | },
84 | "tags": []
85 | },
86 | "source": [
87 | "But this is a bad idea. Why? Well there are two reasons.\n",
88 | "First, it does not scale nicely for long lists and will take forever to type in.\n",
89 | "Second, it won’t work if the length of the list has fewer than 4 cities (or more than 4 cities for that matter...).\n",
90 | "Let's see an example with a new `list`."
91 | ]
92 | },
93 | {
94 | "cell_type": "code",
95 | "execution_count": null,
96 | "metadata": {
97 | "editable": true,
98 | "slideshow": {
99 | "slide_type": ""
100 | },
101 | "tags": []
102 | },
103 | "outputs": [],
104 | "source": [
105 | "european_cities = [\"Riga\", \"Rome\", \"Athens\"]"
106 | ]
107 | },
108 | {
109 | "cell_type": "code",
110 | "execution_count": null,
111 | "metadata": {},
112 | "outputs": [],
113 | "source": []
114 | },
115 | {
116 | "cell_type": "code",
117 | "execution_count": null,
118 | "metadata": {},
119 | "outputs": [],
120 | "source": []
121 | },
122 | {
123 | "cell_type": "code",
124 | "execution_count": null,
125 | "metadata": {},
126 | "outputs": [],
127 | "source": []
128 | },
129 | {
130 | "cell_type": "code",
131 | "execution_count": null,
132 | "metadata": {
133 | "tags": [
134 | "raises-exception"
135 | ]
136 | },
137 | "outputs": [],
138 | "source": []
139 | },
140 | {
141 | "cell_type": "markdown",
142 | "metadata": {
143 | "editable": true,
144 | "slideshow": {
145 | "slide_type": ""
146 | },
147 | "tags": []
148 | },
149 | "source": [
150 | "## Introducing the for loop\n",
151 | "\n",
152 | "In situations like those above, it is far more efficient to use a `for` loop. Let's see an example."
153 | ]
154 | },
155 | {
156 | "cell_type": "code",
157 | "execution_count": null,
158 | "metadata": {
159 | "editable": true,
160 | "slideshow": {
161 | "slide_type": ""
162 | },
163 | "tags": []
164 | },
165 | "outputs": [],
166 | "source": [
167 | "european_cities = [\"Amsterdam\", \"Brussels\", \"Lisbon\", \"Reykjavik\"]"
168 | ]
169 | },
170 | {
171 | "cell_type": "code",
172 | "execution_count": null,
173 | "metadata": {},
174 | "outputs": [],
175 | "source": []
176 | },
177 | {
178 | "cell_type": "markdown",
179 | "metadata": {},
180 | "source": [
181 | "Not only is this shorter, but it is also more flexible.\n",
182 | "Try printing out a different list of cities such as `['Detroit', 'Chicago', 'Denver', 'Boston', 'Portland', 'San Francisco', 'Houston', 'Orlando']`.\n",
183 | "Still works, right?"
184 | ]
185 | },
186 | {
187 | "cell_type": "code",
188 | "execution_count": null,
189 | "metadata": {},
190 | "outputs": [],
191 | "source": [
192 | "us_cities = [\n",
193 | " \"Detroit\",\n",
194 | " \"Chicago\",\n",
195 | " \"Denver\",\n",
196 | " \"Boston\",\n",
197 | " \"Portland\",\n",
198 | " \"San Francisco\",\n",
199 | " \"Houston\",\n",
200 | " \"Orlando\",\n",
201 | "]"
202 | ]
203 | },
204 | {
205 | "cell_type": "code",
206 | "execution_count": null,
207 | "metadata": {},
208 | "outputs": [],
209 | "source": []
210 | },
211 | {
212 | "cell_type": "markdown",
213 | "metadata": {
214 | "editable": true,
215 | "slideshow": {
216 | "slide_type": ""
217 | },
218 | "tags": []
219 | },
220 | "source": [
221 | "### for loop format\n",
222 | "\n",
223 | "`for` loops in Python have the general form below.\n",
224 | "\n",
225 | "```python\n",
226 | "for variable in collection:\n",
227 | " do things with variable\n",
228 | "```\n",
229 | "\n",
230 | "Let's break down the code above to see some essential aspect of `for` loops:\n",
231 | "\n",
232 | "- The `variable` can be any valid variable name you like.\n",
233 | "- The statement of the `for` loop must end with a `:`\n",
234 | "- The code that should be executed as part of the loop must be indented beneath the `for` statement. The recommended indentation is 4 spaces.\n",
235 | "- There is no additional special word needed to end the loop, you simply change the indentation back to normal.\n",
236 | "\n",
237 | "In summary, `for` loops are useful to repeat some part of the code a finite number of times."
238 | ]
239 | },
240 | {
241 | "cell_type": "markdown",
242 | "metadata": {
243 | "editable": true,
244 | "slideshow": {
245 | "slide_type": ""
246 | },
247 | "tags": []
248 | },
249 | "source": [
250 | "### Your daily for loop\n",
251 | "\n",
252 | "\n",
253 | "\n",
254 | "_**Figure 2.3**. A cat's daily routine as an example loop. Original comic from ._\n",
255 | "\n",
256 | "Like many other programming concepts, the idea of looping through actions is something that is already perhaps more familiar to you than you think. Consider your actions during a given weekday. Many people have certain routines they follow each weekday, such as waking up, taking a shower, eating breakfast, and brushing their teeth. In Python code, we might represent such actions as follows:"
257 | ]
258 | },
259 | {
260 | "cell_type": "markdown",
261 | "metadata": {
262 | "editable": true,
263 | "slideshow": {
264 | "slide_type": ""
265 | },
266 | "tags": []
267 | },
268 | "source": [
269 | "```python\n",
270 | "for day in my_week:\n",
271 | " wake_up()\n",
272 | " take_shower()\n",
273 | " eat_breakfast()\n",
274 | " brush_teeth()\n",
275 | " ...\n",
276 | "```\n",
277 | " \n",
278 | "Note that `my_week` would be a list of the days of the week, and the actions you take are represented as functions, such as `wake_up()`.\n",
279 | "Furthermore, by following this kind of list of repeating actions we're able to start the day effectively even before the first cup of coffee :)."
280 | ]
281 | },
282 | {
283 | "cell_type": "markdown",
284 | "metadata": {
285 | "editable": true,
286 | "slideshow": {
287 | "slide_type": ""
288 | },
289 | "tags": []
290 | },
291 | "source": [
292 | "### for loop variables\n",
293 | "\n",
294 | "Note that the variable used in a `for` loop is just a normal variable. This its value still exists after the loop has been run. Let's loop over a list of weather conditions and print them to the screen. "
295 | ]
296 | },
297 | {
298 | "cell_type": "code",
299 | "execution_count": null,
300 | "metadata": {
301 | "editable": true,
302 | "slideshow": {
303 | "slide_type": ""
304 | },
305 | "tags": []
306 | },
307 | "outputs": [],
308 | "source": [
309 | "weather_conditions = [\n",
310 | " \"rain\",\n",
311 | " \"sleet\",\n",
312 | " \"snow\",\n",
313 | " \"freezing fog\",\n",
314 | " \"sunny\",\n",
315 | " \"cloudy\",\n",
316 | " \"ice pellets\",\n",
317 | "]"
318 | ]
319 | },
320 | {
321 | "cell_type": "code",
322 | "execution_count": null,
323 | "metadata": {},
324 | "outputs": [],
325 | "source": []
326 | },
327 | {
328 | "cell_type": "markdown",
329 | "metadata": {},
330 | "source": [
331 | "What is its value of `weather` after the `for` loop has completed?"
332 | ]
333 | },
334 | {
335 | "cell_type": "code",
336 | "execution_count": null,
337 | "metadata": {},
338 | "outputs": [],
339 | "source": []
340 | },
341 | {
342 | "cell_type": "markdown",
343 | "metadata": {
344 | "editable": true,
345 | "slideshow": {
346 | "slide_type": ""
347 | },
348 | "tags": []
349 | },
350 | "source": [
351 | "## For loops using the range function\n",
352 | "\n",
353 | "A loop can be used to iterate over any list of values in Python. So far we have considered only iterating over lists, but we could also write a loop that performs a calculation a specified number of times by using the `range()` function. Let's consider an example where we use a `for` loop with `value` as the loop variable and `range(5)` as the collection. Let's see what happens if we print `value` at each iteration."
354 | ]
355 | },
356 | {
357 | "cell_type": "code",
358 | "execution_count": null,
359 | "metadata": {
360 | "editable": true,
361 | "slideshow": {
362 | "slide_type": ""
363 | },
364 | "tags": []
365 | },
366 | "outputs": [],
367 | "source": []
368 | },
369 | {
370 | "cell_type": "markdown",
371 | "metadata": {},
372 | "source": [
373 | "In this case, we use a special function called `range()` to give us a list of 5 numbers `[0, 1, 2, 3, 4]` and then we print a number in the list to the screen during each iteration.\n",
374 | "When given an integer (whole number) as an argument, `range()` will produce a list of numbers with a length equal to the specified `number`.\n",
375 | "The list starts at `0` and ends with `number - 1`.\n",
376 | "You can learn a bit more about range by typing `help(range)`.\n",
377 | "\n",
378 | "```python\n",
379 | "In [1]: help(range)\n",
380 | "```\n",
381 | "\n",
382 | "```\n",
383 | "Help on class range in module builtins:\n",
384 | "\n",
385 | "class range(object)\n",
386 | " | range(stop) -> range object\n",
387 | " | range(start, stop[, step]) -> range object\n",
388 | " | \n",
389 | " | Return an object that produces a sequence of integers from start (inclusive)\n",
390 | " | to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.\n",
391 | "... (output truncated)\n",
392 | "```"
393 | ]
394 | },
395 | {
396 | "cell_type": "markdown",
397 | "metadata": {
398 | "editable": true,
399 | "slideshow": {
400 | "slide_type": ""
401 | },
402 | "tags": [
403 | "question"
404 | ]
405 | },
406 | "source": [
407 | "#### Question 2.7\n",
408 | "\n",
409 | "The program below will print numbers to the screen using the `range()` function.\n",
410 | "\n",
411 | "```python\n",
412 | "for i in range(...):\n",
413 | " print(i)\n",
414 | "```\n",
415 | "\n",
416 | "Using the documentation that is produced when you run `help(range)`, what values would you replace the `...` in the parentheses of the `range()` function with to have the following output printed to the screen? Try to first think about the function without running any code.\n",
417 | "\n",
418 | "```python\n",
419 | "2\n",
420 | "5\n",
421 | "8\n",
422 | "```"
423 | ]
424 | },
425 | {
426 | "cell_type": "code",
427 | "execution_count": null,
428 | "metadata": {
429 | "editable": true,
430 | "slideshow": {
431 | "slide_type": ""
432 | },
433 | "tags": [
434 | "remove_cell"
435 | ]
436 | },
437 | "outputs": [],
438 | "source": [
439 | "# You can use this cell to enter your solution.\n"
440 | ]
441 | },
442 | {
443 | "cell_type": "markdown",
444 | "metadata": {
445 | "editable": true,
446 | "slideshow": {
447 | "slide_type": ""
448 | },
449 | "tags": []
450 | },
451 | "source": [
452 | "## Looping over lists using index values\n",
453 | "\n",
454 | "Since we already know how to find the length of a list using the `len()` function, we can now take advantage of this knowledge to make our `for` loops more flexible. Let's start by doing something we have done earlier, printing out city names using a `for` loop. However, this time we will also use the `range()` function to print the cities."
455 | ]
456 | },
457 | {
458 | "cell_type": "code",
459 | "execution_count": null,
460 | "metadata": {
461 | "editable": true,
462 | "slideshow": {
463 | "slide_type": ""
464 | },
465 | "tags": []
466 | },
467 | "outputs": [],
468 | "source": [
469 | "african_cities = [\"Dakar\", \"Addis Ababa\", \"Nairobi\", \"Cairo\", \"Rabat\", \"Kampala\"]"
470 | ]
471 | },
472 | {
473 | "cell_type": "code",
474 | "execution_count": null,
475 | "metadata": {
476 | "editable": true,
477 | "slideshow": {
478 | "slide_type": ""
479 | },
480 | "tags": []
481 | },
482 | "outputs": [],
483 | "source": []
484 | },
485 | {
486 | "cell_type": "markdown",
487 | "metadata": {
488 | "editable": true,
489 | "slideshow": {
490 | "slide_type": ""
491 | },
492 | "tags": []
493 | },
494 | "source": [
495 | "There are several important things to observe in this `for` loop:\n",
496 | "\n",
497 | "1. You can see that because we are using the `range()` function, the value assigned to the loop variable `i` starts with `0` and increases by `1` each time through the loop. \n",
498 | "2. In order to access individual cities in the `african_cities` list, we need to include the index value (e.g., `african_cities[i]`. Here, the variable `i` will be replaced by the current value assigned from the `range()` function.\n",
499 | "\n",
500 | "The variable `i` is commonly used to denote the index variable in loops. Loops can sometimes occur with another loop (referred to as nested loops), in which case other index variables such as `j` or `k` may be used."
501 | ]
502 | },
503 | {
504 | "cell_type": "markdown",
505 | "metadata": {
506 | "editable": true,
507 | "slideshow": {
508 | "slide_type": ""
509 | },
510 | "tags": []
511 | },
512 | "source": [
513 | "## Why use index value to loop over a list?\n",
514 | "\n",
515 | "Good question. There are two common case where you might need to loop using index values:\n",
516 | "\n",
517 | "1. If you want to update individual values in a list you're likely going to need a loop that includes the index values. To update a list value, you need to refer to both the list and index, which means looping using the list values directly will not work. There are functions such as `enumerate()` that can help, but their use can be somewhat confusing for new programmers.\n",
518 | "2. In cases where you have multiple lists that are related to one another, it can be handy to use a loop with the index values to be able to access corresponding locations in each list. For this, let's consider an example with the two lists below."
519 | ]
520 | },
521 | {
522 | "cell_type": "code",
523 | "execution_count": null,
524 | "metadata": {
525 | "editable": true,
526 | "slideshow": {
527 | "slide_type": ""
528 | },
529 | "tags": []
530 | },
531 | "outputs": [],
532 | "source": [
533 | "cities = [\"Helsinki\", \"Stockholm\", \"Oslo\", \"Reykjavik\", \"Copenhagen\"]"
534 | ]
535 | },
536 | {
537 | "cell_type": "code",
538 | "execution_count": null,
539 | "metadata": {},
540 | "outputs": [],
541 | "source": [
542 | "countries = [\"Finland\", \"Sweden\", \"Norway\", \"Iceland\", \"Denmark\"]"
543 | ]
544 | },
545 | {
546 | "cell_type": "markdown",
547 | "metadata": {},
548 | "source": [
549 | "As you can see we have 5 cities and 5 corresponding countries. Let's print out each pair using a single `for` loop."
550 | ]
551 | },
552 | {
553 | "cell_type": "code",
554 | "execution_count": null,
555 | "metadata": {},
556 | "outputs": [],
557 | "source": []
558 | },
559 | {
560 | "cell_type": "markdown",
561 | "metadata": {},
562 | "source": [
563 | "Cool. So as you can see, the index `i` is used in this case to access each item in the two lists of cities and countries and allow us to print out the city/country pairs. In the example above, we used the length of the list `cities` in the `range()` function. We could just as easily used the list `countries` to define the values of `i` since both lists are the same length."
564 | ]
565 | },
566 | {
567 | "cell_type": "markdown",
568 | "metadata": {
569 | "editable": true,
570 | "slideshow": {
571 | "slide_type": ""
572 | },
573 | "tags": [
574 | "question"
575 | ]
576 | },
577 | "source": [
578 | "#### Question 2.8\n",
579 | "\n",
580 | "What output would the following program produce? Try to first think about the loop without running any code.\n",
581 | "\n",
582 | "```python\n",
583 | "odd_numbers = [1, 3, 5, 7, 9]\n",
584 | "even_numbers = [10, 4, 6, 8, 2]\n",
585 | "for i in range(len(odd_numbers)):\n",
586 | " print(odd_numbers[i] + even_numbers[i])\n",
587 | "```"
588 | ]
589 | },
590 | {
591 | "cell_type": "code",
592 | "execution_count": null,
593 | "metadata": {
594 | "editable": true,
595 | "slideshow": {
596 | "slide_type": ""
597 | },
598 | "tags": [
599 | "remove_cell"
600 | ]
601 | },
602 | "outputs": [],
603 | "source": [
604 | "# Use this cell to enter your solution.\n"
605 | ]
606 | }
607 | ],
608 | "metadata": {
609 | "kernelspec": {
610 | "display_name": "Python 3 (ipykernel)",
611 | "language": "python",
612 | "name": "python3"
613 | },
614 | "language_info": {
615 | "codemirror_mode": {
616 | "name": "ipython",
617 | "version": 3
618 | },
619 | "file_extension": ".py",
620 | "mimetype": "text/x-python",
621 | "name": "python",
622 | "nbconvert_exporter": "python",
623 | "pygments_lexer": "ipython3",
624 | "version": "3.11.6"
625 | }
626 | },
627 | "nbformat": 4,
628 | "nbformat_minor": 4
629 | }
630 |
--------------------------------------------------------------------------------
/L1/a-taste-of-python.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "deletable": true,
7 | "editable": true
8 | },
9 | "source": [
10 | "# Basic elements of Python\n",
11 | "\n",
12 | "In this section, we will introduce some basic programming concepts in Python."
13 | ]
14 | },
15 | {
16 | "cell_type": "markdown",
17 | "metadata": {
18 | "deletable": true,
19 | "editable": true
20 | },
21 | "source": [
22 | "## Simple Python math\n",
23 | "\n",
24 | "We will start our Python introduction by learning a bit of the basic operations you can perform. Python can be used as a simple calculator. Let's try it out with some simple math operations such as `1 + 1` or `5 * 7`. When using a Jupyter Notebook you can press **Shift-Enter** to execute the code cells. "
25 | ]
26 | },
27 | {
28 | "cell_type": "code",
29 | "execution_count": null,
30 | "metadata": {
31 | "collapsed": false,
32 | "deletable": true,
33 | "editable": true,
34 | "jupyter": {
35 | "outputs_hidden": false
36 | }
37 | },
38 | "outputs": [],
39 | "source": []
40 | },
41 | {
42 | "cell_type": "code",
43 | "execution_count": null,
44 | "metadata": {
45 | "collapsed": false,
46 | "deletable": true,
47 | "editable": true,
48 | "jupyter": {
49 | "outputs_hidden": false
50 | }
51 | },
52 | "outputs": [],
53 | "source": []
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {
58 | "deletable": true,
59 | "editable": true
60 | },
61 | "source": [
62 | "If you want to edit and re-run some code, simply make changes to the Python cell and press **Shift-Enter** to execute the modified code."
63 | ]
64 | },
65 | {
66 | "cell_type": "markdown",
67 | "metadata": {
68 | "deletable": true,
69 | "editable": true
70 | },
71 | "source": [
72 | "### Functions\n",
73 | "\n",
74 | "You can use Python for more advanced math by using a *{term}`function`*. Functions are pieces of code that perform a single action such as printing information to the screen (e.g., the `print()` function). Functions exist for a huge number of operations in Python.\n",
75 | "\n",
76 | "Let's try out a few simple examples using functions to find the sine or square root of a value using the `sin()` and `sqrt()` functions."
77 | ]
78 | },
79 | {
80 | "cell_type": "code",
81 | "execution_count": null,
82 | "metadata": {
83 | "collapsed": false,
84 | "deletable": true,
85 | "editable": true,
86 | "jupyter": {
87 | "outputs_hidden": false
88 | },
89 | "tags": [
90 | "raises-exception"
91 | ]
92 | },
93 | "outputs": [],
94 | "source": []
95 | },
96 | {
97 | "cell_type": "code",
98 | "execution_count": null,
99 | "metadata": {
100 | "collapsed": false,
101 | "deletable": true,
102 | "editable": true,
103 | "jupyter": {
104 | "outputs_hidden": false
105 | },
106 | "tags": [
107 | "raises-exception"
108 | ]
109 | },
110 | "outputs": [],
111 | "source": []
112 | },
113 | {
114 | "cell_type": "markdown",
115 | "metadata": {
116 | "deletable": true,
117 | "editable": true
118 | },
119 | "source": [
120 | "Wait, what? Python can’t calculate square roots or do basic trigonometry? Of course it can, but we need one more step."
121 | ]
122 | },
123 | {
124 | "cell_type": "markdown",
125 | "metadata": {
126 | "deletable": true,
127 | "editable": true
128 | },
129 | "source": [
130 | "### Math operations\n",
131 | "\n",
132 | "The list of basic arithmetic operations that can be done by default in Python are listed in Table 2.1.\n",
133 | "\n",
134 | ": _**Table 2.1**. Basic math operations in Python._\n",
135 | "\n",
136 | "|Operation | Symbol | Example syntax | Returned value |\n",
137 | "|:--------------|:------:|:--------------:|:--------------:|\n",
138 | "|Addition | `+` | `2 + 2` | `4` |\n",
139 | "|Subtraction | `-` | `4 - 2` | `2` |\n",
140 | "|Multiplication | `*` | `2 * 3` | `6` | \n",
141 | "|Division | `/` | `4 / 2` | `2` |\n",
142 | "|Exponentiation | `**` | `2**3` | `8` |\n",
143 | "\n",
144 | "For anything more advanced, we need to load a *{term}`module`* or *{term}`library`*. For math operations, this module is called `math` and it can be loaded by typing `import math`."
145 | ]
146 | },
147 | {
148 | "cell_type": "code",
149 | "execution_count": null,
150 | "metadata": {
151 | "deletable": true,
152 | "editable": true
153 | },
154 | "outputs": [],
155 | "source": []
156 | },
157 | {
158 | "cell_type": "markdown",
159 | "metadata": {},
160 | "source": [
161 | "Now that we have access to functions in the math module, we can use it by typing the module name, a period (dot), and the the name of the function we want to use. For example, `math.sin(3)`."
162 | ]
163 | },
164 | {
165 | "cell_type": "code",
166 | "execution_count": null,
167 | "metadata": {
168 | "collapsed": false,
169 | "deletable": true,
170 | "editable": true,
171 | "jupyter": {
172 | "outputs_hidden": false
173 | }
174 | },
175 | "outputs": [],
176 | "source": []
177 | },
178 | {
179 | "cell_type": "code",
180 | "execution_count": null,
181 | "metadata": {
182 | "collapsed": false,
183 | "deletable": true,
184 | "editable": true,
185 | "jupyter": {
186 | "outputs_hidden": false
187 | }
188 | },
189 | "outputs": [],
190 | "source": []
191 | },
192 | {
193 | "cell_type": "markdown",
194 | "metadata": {
195 | "deletable": true,
196 | "editable": true
197 | },
198 | "source": [
199 | "Let's summarize what we've just seen with modules:\n",
200 | "\n",
201 | "1. A {index}`module` is a group of code items such as functions that are related to one another. Individual modules are often in a group referred to as a library.\n",
202 | "\n",
203 | "2. Modules can be loaded using the `import` statement. Functions that are part of the module `modulename` can then be used by typing `modulename.functionname()`. For example, `sin()` is a function that is part of the `math` module, and used by typing `math.sin()` with some number between the parentheses.\n",
204 | "\n",
205 | "3. In a Jupyter Notebook the variables you define earlier code cells will be available for use in the cells that follow as long as you have already executed the cells.\n",
206 | "\n",
207 | "Note that modules may also contain constants such as `math.pi`. Parentheses are not used when calling constant values."
208 | ]
209 | },
210 | {
211 | "cell_type": "code",
212 | "execution_count": null,
213 | "metadata": {
214 | "collapsed": false,
215 | "deletable": true,
216 | "editable": true,
217 | "jupyter": {
218 | "outputs_hidden": false
219 | }
220 | },
221 | "outputs": [],
222 | "source": []
223 | },
224 | {
225 | "cell_type": "markdown",
226 | "metadata": {
227 | "editable": true,
228 | "slideshow": {
229 | "slide_type": ""
230 | },
231 | "tags": [
232 | "question"
233 | ]
234 | },
235 | "source": [
236 | "#### Question 2.1\n",
237 | "\n",
238 | "Use your Python skills to calculate the sine of pi. What value do you expect for this calculation? Did you get the expected result?"
239 | ]
240 | },
241 | {
242 | "cell_type": "code",
243 | "execution_count": null,
244 | "metadata": {
245 | "editable": true,
246 | "slideshow": {
247 | "slide_type": ""
248 | },
249 | "tags": [
250 | "remove_cell"
251 | ]
252 | },
253 | "outputs": [],
254 | "source": [
255 | "# Note that lines starting with \"#\" are ignored in Python.\n",
256 | "# Use this cell to enter your solution.\n"
257 | ]
258 | },
259 | {
260 | "cell_type": "markdown",
261 | "metadata": {
262 | "deletable": true,
263 | "editable": true
264 | },
265 | "source": [
266 | "## Combining functions\n",
267 | "\n",
268 | "Functions can also be combined. The `print()` function returns values within the parentheses as text on the screen. Let's print the value of the square root of four."
269 | ]
270 | },
271 | {
272 | "cell_type": "code",
273 | "execution_count": null,
274 | "metadata": {
275 | "collapsed": false,
276 | "deletable": true,
277 | "editable": true,
278 | "jupyter": {
279 | "outputs_hidden": false
280 | }
281 | },
282 | "outputs": [],
283 | "source": []
284 | },
285 | {
286 | "cell_type": "markdown",
287 | "metadata": {},
288 | "source": [
289 | "You can also combine text with other calculated values using the `print()` function. For example, `print('Two plus two is', 2+2)` would generate text reading 'Two plus two is 4'. Let's combine the `print()` function with the `math.sqrt()` function in to produce text that reads `The square root of 4 is 2.0`."
290 | ]
291 | },
292 | {
293 | "cell_type": "code",
294 | "execution_count": null,
295 | "metadata": {
296 | "collapsed": false,
297 | "deletable": true,
298 | "editable": true,
299 | "jupyter": {
300 | "outputs_hidden": false
301 | }
302 | },
303 | "outputs": [],
304 | "source": []
305 | },
306 | {
307 | "cell_type": "markdown",
308 | "metadata": {
309 | "deletable": true,
310 | "editable": true
311 | },
312 | "source": [
313 | "## Variables\n",
314 | "\n",
315 | "A *{term}`variable`* can be used to store values calculated in expressions and used for other calculations."
316 | ]
317 | },
318 | {
319 | "cell_type": "markdown",
320 | "metadata": {},
321 | "source": [
322 | "### Variable assignment\n",
323 | "\n",
324 | "Assigning value to variables is straightforward. To assign a value, you simply type `variable_name = value`, where `variable_name` is the name of the variable you wish to define. Let's define a variable called `temp_celsius` and assign it a value of 10.0. Note that when the variable is assigned there is no output to the screen."
325 | ]
326 | },
327 | {
328 | "cell_type": "code",
329 | "execution_count": null,
330 | "metadata": {
331 | "collapsed": false,
332 | "deletable": true,
333 | "editable": true,
334 | "jupyter": {
335 | "outputs_hidden": false
336 | }
337 | },
338 | "outputs": [],
339 | "source": []
340 | },
341 | {
342 | "cell_type": "markdown",
343 | "metadata": {},
344 | "source": [
345 | "In order to see the value that has been assigned to the `temp_celsius` variable you need to either use the `print()` function (e.g., `print(temp_celsius)`) or simply type the name of the variable into the code cell to have its value displayed. This is a convenient way to see calculated values in code cells of Jupyter Notebooks, so for simplicity we will avoid using the `print()` function when possible in this book. In some cases, however, the `print()` function is needed, such as displaying output from multiple lines of a single code cell."
346 | ]
347 | },
348 | {
349 | "cell_type": "code",
350 | "execution_count": null,
351 | "metadata": {},
352 | "outputs": [],
353 | "source": []
354 | },
355 | {
356 | "cell_type": "markdown",
357 | "metadata": {},
358 | "source": [
359 | "It is also possible to combine text and numbers and even use some math when printing out variable values. The idea is similar to the examples of adding 2+2 or calculating the square root of four from the previous section. Next, we will print out the value of `temp_celsius` in degrees Fahrenheit by multiplying `temp_celsius` by 9/5 and adding 32. This should be done within the `print()` function to produce output that reads 'Temperature in Fahrenheit: 50.0'."
360 | ]
361 | },
362 | {
363 | "cell_type": "code",
364 | "execution_count": null,
365 | "metadata": {
366 | "collapsed": false,
367 | "deletable": true,
368 | "editable": true,
369 | "jupyter": {
370 | "outputs_hidden": false
371 | }
372 | },
373 | "outputs": [],
374 | "source": []
375 | },
376 | {
377 | "cell_type": "markdown",
378 | "metadata": {
379 | "deletable": true,
380 | "editable": true,
381 | "slideshow": {
382 | "slide_type": ""
383 | },
384 | "tags": [
385 | "question"
386 | ]
387 | },
388 | "source": [
389 | "#### Question 2.2\n",
390 | "\n",
391 | "Define a variable and display its value on the screen. The variable value can be anything you like, and you can even consider defining several variables and printing them out together. Consider using pothole_case_naming for your variable name."
392 | ]
393 | },
394 | {
395 | "cell_type": "code",
396 | "execution_count": null,
397 | "metadata": {
398 | "editable": true,
399 | "slideshow": {
400 | "slide_type": ""
401 | },
402 | "tags": [
403 | "remove_cell"
404 | ]
405 | },
406 | "outputs": [],
407 | "source": [
408 | "# Use this cell to enter your solution.\n"
409 | ]
410 | },
411 | {
412 | "cell_type": "markdown",
413 | "metadata": {
414 | "deletable": true,
415 | "editable": true
416 | },
417 | "source": [
418 | "### Updating variables\n",
419 | "\n",
420 | "Values stored in variables can also be updated. Let's redefine the value of `temp_celsius` to be equal to 15.0 and print its value using the `print()` function."
421 | ]
422 | },
423 | {
424 | "cell_type": "code",
425 | "execution_count": null,
426 | "metadata": {
427 | "deletable": true,
428 | "editable": true
429 | },
430 | "outputs": [],
431 | "source": []
432 | },
433 | {
434 | "cell_type": "code",
435 | "execution_count": null,
436 | "metadata": {
437 | "collapsed": false,
438 | "deletable": true,
439 | "editable": true,
440 | "jupyter": {
441 | "outputs_hidden": false
442 | }
443 | },
444 | "outputs": [],
445 | "source": []
446 | },
447 | {
448 | "cell_type": "markdown",
449 | "metadata": {
450 | "deletable": true,
451 | "editable": true
452 | },
453 | "source": [
454 | "Please note that if you try to run some code that accesses a variable that has not yet been defined you will get a `NameError` message."
455 | ]
456 | },
457 | {
458 | "cell_type": "code",
459 | "execution_count": null,
460 | "metadata": {
461 | "collapsed": false,
462 | "deletable": true,
463 | "editable": true,
464 | "jupyter": {
465 | "outputs_hidden": false
466 | },
467 | "tags": [
468 | "raises-exception"
469 | ]
470 | },
471 | "outputs": [],
472 | "source": []
473 | },
474 | {
475 | "cell_type": "markdown",
476 | "metadata": {
477 | "deletable": true,
478 | "editable": true
479 | },
480 | "source": [
481 | "When running the code in a Jupyter Notebook variables get stored in memory only after executing the code cell where the variable is defined. "
482 | ]
483 | },
484 | {
485 | "cell_type": "code",
486 | "execution_count": null,
487 | "metadata": {
488 | "deletable": true,
489 | "editable": true
490 | },
491 | "outputs": [],
492 | "source": []
493 | },
494 | {
495 | "cell_type": "markdown",
496 | "metadata": {},
497 | "source": [
498 | "Now that we have defined `tempFahrenheit`, we can run again the print statement without getting a `NameError`. Let's print out the values of `temp_celsius` and `tempFahrenheit` to check their current values."
499 | ]
500 | },
501 | {
502 | "cell_type": "code",
503 | "execution_count": null,
504 | "metadata": {
505 | "collapsed": false,
506 | "deletable": true,
507 | "editable": true,
508 | "jupyter": {
509 | "outputs_hidden": false
510 | }
511 | },
512 | "outputs": [],
513 | "source": []
514 | },
515 | {
516 | "cell_type": "markdown",
517 | "metadata": {},
518 | "source": [
519 | "The number beside the cell, for example `In [22]`, tells you the order in which the Python cells have been executed. This way you can see a history of the order in which you have run the cells."
520 | ]
521 | },
522 | {
523 | "cell_type": "markdown",
524 | "metadata": {
525 | "deletable": true,
526 | "editable": true
527 | },
528 | "source": [
529 | "### Variable values\n",
530 | "\n",
531 | "Changing the value of a variable does not affect other variable values. Let's redefine `temp_celsius` to be equal to 20.0, and print out the values of `temp_celsius` and `tempFahrenheit`."
532 | ]
533 | },
534 | {
535 | "cell_type": "code",
536 | "execution_count": null,
537 | "metadata": {
538 | "collapsed": false,
539 | "deletable": true,
540 | "editable": true,
541 | "jupyter": {
542 | "outputs_hidden": false
543 | }
544 | },
545 | "outputs": [],
546 | "source": []
547 | },
548 | {
549 | "cell_type": "markdown",
550 | "metadata": {
551 | "deletable": true,
552 | "editable": true
553 | },
554 | "source": [
555 | "## Data types\n",
556 | "\n",
557 | "A *{term}`data type`* determines the characteristics of data in a program.\n",
558 | "There are 4 basic data types in Python as shown in Table 2.2.\n",
559 | "\n",
560 | ": _**Table 2.2**. Basic data types in Python._\n",
561 | "\n",
562 | "|Data type name |Data type | Example |\n",
563 | "|:--------------|:--------------------|:----------:|\n",
564 | "|`int` |Whole integer values | `4` |\n",
565 | "|`float` |Decimal values | `3.1415` |\n",
566 | "|`str` |Character strings | `'Hot'` |\n",
567 | "|`bool` |True/false values | `True` |\n",
568 | "\n",
569 | "The data type can be found using the `type()` function. As you will see, the data types are important because some are not compatible with one another. Let's define a variable `weatherForecast` and assign it the value \"Hot\". After this, we can check its data type using the `type()` function."
570 | ]
571 | },
572 | {
573 | "cell_type": "code",
574 | "execution_count": null,
575 | "metadata": {
576 | "collapsed": false,
577 | "deletable": true,
578 | "editable": true,
579 | "jupyter": {
580 | "outputs_hidden": false
581 | }
582 | },
583 | "outputs": [],
584 | "source": []
585 | },
586 | {
587 | "cell_type": "markdown",
588 | "metadata": {},
589 | "source": [
590 | "Let's also check the type of `tempFahrenheit`. What happens if you try to combine `tempFahrenheit` and `weatherForecast` in a single math equation such as `tempFahrenheit = tempFahrenheit + 5.0 * weatherForecast`?"
591 | ]
592 | },
593 | {
594 | "cell_type": "code",
595 | "execution_count": null,
596 | "metadata": {
597 | "collapsed": false,
598 | "deletable": true,
599 | "editable": true,
600 | "jupyter": {
601 | "outputs_hidden": false
602 | },
603 | "tags": [
604 | "raises-exception"
605 | ]
606 | },
607 | "outputs": [],
608 | "source": []
609 | },
610 | {
611 | "cell_type": "markdown",
612 | "metadata": {
613 | "deletable": true,
614 | "editable": true
615 | },
616 | "source": [
617 | "In this case we get at `TypeError` because we are trying to execute a math operation with data types that are not compatible. There is no way in Python to multpily decimal values with a character string."
618 | ]
619 | },
620 | {
621 | "cell_type": "markdown",
622 | "metadata": {
623 | "deletable": true,
624 | "editable": true,
625 | "slideshow": {
626 | "slide_type": ""
627 | },
628 | "tags": [
629 | "question"
630 | ]
631 | },
632 | "source": [
633 | "#### Question 2.3\n",
634 | "\n",
635 | "As it turns out, it is possible some math with character strings in Python. Define two variables and assign them character string values. What happens if you try to add two character strings together? Can you subtract them? Which other math operations work for character strings?"
636 | ]
637 | },
638 | {
639 | "cell_type": "code",
640 | "execution_count": null,
641 | "metadata": {
642 | "editable": true,
643 | "slideshow": {
644 | "slide_type": ""
645 | },
646 | "tags": [
647 | "remove_cell"
648 | ]
649 | },
650 | "outputs": [],
651 | "source": [
652 | "# Use this cell to enter your solution.\n"
653 | ]
654 | }
655 | ],
656 | "metadata": {
657 | "anaconda-cloud": {},
658 | "kernelspec": {
659 | "display_name": "Python 3 (ipykernel)",
660 | "language": "python",
661 | "name": "python3"
662 | },
663 | "language_info": {
664 | "codemirror_mode": {
665 | "name": "ipython",
666 | "version": 3
667 | },
668 | "file_extension": ".py",
669 | "mimetype": "text/x-python",
670 | "name": "python",
671 | "nbconvert_exporter": "python",
672 | "pygments_lexer": "ipython3",
673 | "version": "3.11.6"
674 | }
675 | },
676 | "nbformat": 4,
677 | "nbformat_minor": 4
678 | }
679 |
--------------------------------------------------------------------------------
/L4/use-of-ai.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "5c3091b6-2f2d-400d-89bf-bfa33e4f5179",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# AI and programming in Python\n",
11 | "\n",
12 | "```{attention}\n",
13 | "Finnish university students are encouraged to use the CSC Noppe platform.
\n",
14 | "
\n",
15 | "\n",
16 | "Others can follow the lesson and fill in their student notebooks using Binder.
\n",
17 | "
\n",
18 | "```\n",
19 | "\n",
20 | "**Warning**: The aim of this lesson is to familarize you with the use of AI and large language models for producing code. The main objective is to show the potentials and risks of these newly arising tools for programming. However, other than where explicitly stated in the exercises, we do not encourage use of these tools in this course. \n",
21 | "\n",
22 | "\n",
23 | "**Note**: The codes produced by ChatGPT in this lesson are not necessarily examples of good Python codes.\n",
24 | "\n",
25 | "## Sources\n",
26 | "\n",
27 | "The Python code examples in this lesson were produced by the [ChatGPT](https://openai.com/chatgpt) application by [OpenAI](https://openai.com)."
28 | ]
29 | },
30 | {
31 | "cell_type": "markdown",
32 | "id": "6a98bb3d-9490-4183-bac9-10532463580a",
33 | "metadata": {},
34 | "source": [
35 | "## AI and LLM\n",
36 | "\n",
37 | "AI, or Artificial Intelligence, refers to the field of computer science dedicated to developing systems and algorithms that enable machines to perform tasks typically requiring human intelligence, such as problem-solving, learning, and natural language understanding. On the other hand, LLM, or Large Language Models, are a specific category of AI models designed to process and generate human language text. [ChatGPT](https://openai.com/chatgpt) (Chat Generative Pre-trained Transformer) is one common example of an LLM, created by [OpenAI](https://openai.com/). ChatGPT excels in understanding and generating human-like text, making it useful for tasks like chatbots, virtual assistants, and content generation."
38 | ]
39 | },
40 | {
41 | "cell_type": "markdown",
42 | "id": "3d54e6f1-edcd-48c6-8def-4ec22914e0cc",
43 | "metadata": {
44 | "jp-MarkdownHeadingCollapsed": true
45 | },
46 | "source": [
47 | "## Getting started with ChatGPT\n",
48 | "\n",
49 | "\n",
50 | "In order to use ChatGPT you will first need to create an account on OpenAI. If you already have an account, you can login using your account. If you are new to OpenAI, you can create a new account for free. Click on the **Get Started** button to go to the signup page. Here you can create an account using your email or by linking it to another of your accounts (such as Google). In order to sign up, you need to provide your email address, mobile phone number, and date of birth (!). Doing this is voluntary for this course. \n",
51 | "\n",
52 | "\n",
53 | "\n",
54 | "Once you have created your account and activated it, you can login to access [ChatGPT](https://chat.openai.com/)."
55 | ]
56 | },
57 | {
58 | "cell_type": "markdown",
59 | "id": "e9d8c2a2-aa06-4aed-a85b-6f2db9e64d77",
60 | "metadata": {},
61 | "source": [
62 | "## Using ChatGPT\n",
63 | "\n",
64 | "You can communicate with ChatGPT using the prompt at the bottom of the page (where it reads \"Send a message\"). \n",
65 | "\n",
66 | "\n",
67 | "\n",
68 | "A prompt is a specific instruction, query, or input provided to a computer program or artificial intelligence system to elicit a desired response or output. In the context of language models like ChatGPT, a prompt typically consists of a text-based request or question that users provide to the model. The model generates a response based on the content and context of the prompt. Prompts are used to interact with AI systems, instruct them on what task to perform, or seek information or assistance from them. The quality and clarity of the prompt can significantly impact the accuracy and relevance of the AI's response.\n",
69 | "\n",
70 | "Lets start with an example prompt we can type in:\n",
71 | "\n",
72 | "```\n",
73 | "Write a Python function that adds two numbers\n",
74 | "```\n",
75 | "\n",
76 | "Response:"
77 | ]
78 | },
79 | {
80 | "cell_type": "code",
81 | "execution_count": null,
82 | "id": "b708e8f8-2f21-4712-bd08-76d1bd913aa9",
83 | "metadata": {},
84 | "outputs": [],
85 | "source": [
86 | "def add_numbers(a, b):\n",
87 | " return a + b"
88 | ]
89 | },
90 | {
91 | "cell_type": "markdown",
92 | "id": "f840f754-7ab0-449d-b4b4-11885eb6904e",
93 | "metadata": {},
94 | "source": [
95 | "OK, so we clearly got back a Python function that appears to work to add two numbers. Cool!\n",
96 | "\n",
97 | "ChatGPT remembers your conversation (i.e., history of prompts). So you can ask it to modify a previously provided response for you. Let's try this with another prompt.\n",
98 | "\n",
99 | "```\n",
100 | "Give me an example of using the output of this function in an f-string\n",
101 | "```\n",
102 | "\n",
103 | "Response:"
104 | ]
105 | },
106 | {
107 | "cell_type": "code",
108 | "execution_count": null,
109 | "id": "7503b792-25af-4d21-ac55-6b2e0abfa1bf",
110 | "metadata": {},
111 | "outputs": [],
112 | "source": [
113 | "def add_numbers(a, b):\n",
114 | "\n",
115 | " return a + b\n",
116 | "\n",
117 | "num1 = 5\n",
118 | "num2 = 3\n",
119 | "result = add_numbers(num1, num2)\n",
120 | "formatted_string = f\"The sum of {num1} and {num2} is {result}.\"\n",
121 | "print(formatted_string)"
122 | ]
123 | },
124 | {
125 | "cell_type": "markdown",
126 | "id": "0bb41ac9-9bc6-45c8-8cd6-c2ed071b7b5d",
127 | "metadata": {},
128 | "source": [
129 | "OK, so it remembered our function and did as requested.\n",
130 | "\n",
131 | "Let's try another prompt:\n",
132 | "\n",
133 | "```\n",
134 | "Add inline comments to the code\n",
135 | "```\n",
136 | "\n",
137 | "Response:"
138 | ]
139 | },
140 | {
141 | "cell_type": "code",
142 | "execution_count": null,
143 | "id": "728718f4-41a5-4c1a-904a-4667383bbbf1",
144 | "metadata": {},
145 | "outputs": [],
146 | "source": [
147 | "# Define a function that takes two numbers as input and returns their sum.\n",
148 | "def add_numbers(a, b):\n",
149 | " \"\"\"\n",
150 | " This function takes two numbers as input and returns their sum.\n",
151 | " \n",
152 | " :param a: The first number to be added.\n",
153 | " :param b: The second number to be added.\n",
154 | " :return: The sum of the two input numbers.\n",
155 | " \"\"\"\n",
156 | " return a + b\n",
157 | "\n",
158 | "# Define two numbers to be added.\n",
159 | "num1 = 5\n",
160 | "num2 = 3\n",
161 | "\n",
162 | "# Call the add_numbers function and store the result in the 'result' variable.\n",
163 | "result = add_numbers(num1, num2)\n",
164 | "\n",
165 | "# Using an f-string to format the output and include variable values.\n",
166 | "formatted_string = f\"The sum of {num1} and {num2} is {result}.\"\n",
167 | "\n",
168 | "# Print the formatted string.\n",
169 | "print(formatted_string)"
170 | ]
171 | },
172 | {
173 | "cell_type": "markdown",
174 | "id": "55efa290-2d29-42c4-9735-a0c653613996",
175 | "metadata": {},
176 | "source": [
177 | "Wow! It seems that ChatGPT is quite capable for this kind of task.\n",
178 | "\n",
179 | "```{note}\n",
180 | "The same prompt can yield different responses from ChatGPT due to factors such as randomness in word selection, variations in model versions and training data, contextual influences from preceding dialogue, ambiguity in prompts, settings, response length parameters, and the model's exposure to diverse internet text. Additionally, fine-tuning for specific tasks, user feedback, and evolving preferences can all contribute to the diversity of generated responses.\n",
181 | "```"
182 | ]
183 | },
184 | {
185 | "cell_type": "markdown",
186 | "id": "1879955f-0bbf-4c2c-b1ee-f55d36191ccd",
187 | "metadata": {},
188 | "source": [
189 | "Let's try another prompt:\n",
190 | "\n",
191 | "```\n",
192 | "Write me a python function that converts degrees Fahrenheit to Celsius\n",
193 | "```\n",
194 | "\n",
195 | "Response:"
196 | ]
197 | },
198 | {
199 | "cell_type": "code",
200 | "execution_count": null,
201 | "id": "0f02864f-2606-425b-813b-a42e520fe11b",
202 | "metadata": {
203 | "tags": []
204 | },
205 | "outputs": [],
206 | "source": [
207 | "def fahrenheit_to_celsius(fahrenheit):\n",
208 | " \"\"\"\n",
209 | " Converts a temperature from degrees Fahrenheit to degrees Celsius.\n",
210 | "\n",
211 | " :param fahrenheit: Temperature in degrees Fahrenheit\n",
212 | " :return: Temperature in degrees Celsius\n",
213 | " \"\"\"\n",
214 | " celsius = (fahrenheit - 32) * 5/9\n",
215 | " return celsius"
216 | ]
217 | },
218 | {
219 | "cell_type": "markdown",
220 | "id": "7398673d-1059-45d6-a953-1030686836eb",
221 | "metadata": {},
222 | "source": [
223 | "Now let's test this function."
224 | ]
225 | },
226 | {
227 | "cell_type": "code",
228 | "execution_count": null,
229 | "id": "211b12b3-b24e-4cf6-952f-04542e8e1292",
230 | "metadata": {
231 | "tags": []
232 | },
233 | "outputs": [],
234 | "source": [
235 | "# Write your code below\n"
236 | ]
237 | },
238 | {
239 | "cell_type": "markdown",
240 | "id": "7be7e14c-0095-44e3-8c2a-13e5b3d68d43",
241 | "metadata": {},
242 | "source": [
243 | "It seems that things are working, at least in our example output from ChatGPT."
244 | ]
245 | },
246 | {
247 | "cell_type": "markdown",
248 | "id": "28f59c67-6803-43f1-8b3c-691eb4ac3f3e",
249 | "metadata": {},
250 | "source": [
251 | "### Writing good prompts\n",
252 | "\n",
253 | "Remember that you are talking with a machine in your prompts. So it is very important for you to provide prompts that are **clear** and have **clear objectives**. In your prompts, try to provide as much **context** and **instruction** as possible.\n",
254 | "\n",
255 | "Here's an example of a bad prompt:\n",
256 | "\n",
257 | "```\n",
258 | "A function to convert Kelvin to Newton\n",
259 | "```\n",
260 | "\n",
261 | "Response:"
262 | ]
263 | },
264 | {
265 | "cell_type": "code",
266 | "execution_count": null,
267 | "id": "2f05b1a8-7a29-40ad-b338-47744c04c279",
268 | "metadata": {},
269 | "outputs": [],
270 | "source": [
271 | "def kelvin_to_newton(mass_kg, acceleration_m_s2):\n",
272 | " force_newton = mass_kg * acceleration_m_s2\n",
273 | " return force_newton"
274 | ]
275 | },
276 | {
277 | "cell_type": "markdown",
278 | "id": "cdb4cbc6-f335-44db-a402-07d544db6019",
279 | "metadata": {},
280 | "source": [
281 | "This doesn't look correct, as we were aiming to convert from Kelvins to degrees Newton, as we did in the functions lesson.\n",
282 | "\n",
283 | "Let's write this better by adding more context:\n",
284 | "\n",
285 | "```\n",
286 | "A function to convert temperature in Kelvin to Newton\n",
287 | "```"
288 | ]
289 | },
290 | {
291 | "cell_type": "code",
292 | "execution_count": null,
293 | "id": "bb0f7e8d-f307-44fd-895c-881d7cb75bb6",
294 | "metadata": {},
295 | "outputs": [],
296 | "source": [
297 | "def kelvin_to_newton(kelvin_temperature):\n",
298 | " newton_temperature = kelvin_temperature * 0.33\n",
299 | " return newton_temperature"
300 | ]
301 | },
302 | {
303 | "cell_type": "markdown",
304 | "id": "8577b2da-1a5f-4955-8b7c-29d059d4f862",
305 | "metadata": {},
306 | "source": [
307 | "OK, so now we get a better looking function that at least has something about a temperature conversion."
308 | ]
309 | },
310 | {
311 | "cell_type": "markdown",
312 | "id": "85a5b706-5d07-425f-909a-ec56a500d2ca",
313 | "metadata": {},
314 | "source": [
315 | "#### Check your understanding\n",
316 | "\n",
317 | "This is a good example of a better prompt, but something is still not quite right about this output code. Can you identify any potential problems with the function created above?\n",
318 | "\n",
319 | "Enter your ideas and vote on the suggestions at [https://geo-python.github.io/poll/](https://geo-python.github.io/poll/)."
320 | ]
321 | },
322 | {
323 | "cell_type": "markdown",
324 | "id": "dec7e1d7-e592-4721-a017-b6d00a3487be",
325 | "metadata": {},
326 | "source": [
327 | "### Testing ChatGPT-generated code\n",
328 | "\n",
329 | "Now we can test out our temperature conversion function using a list of temperatures in Kelvins."
330 | ]
331 | },
332 | {
333 | "cell_type": "code",
334 | "execution_count": null,
335 | "id": "89a049c5-878d-49cb-90e4-fc3a6f0bb766",
336 | "metadata": {
337 | "tags": []
338 | },
339 | "outputs": [],
340 | "source": [
341 | "# Write your code below\n"
342 | ]
343 | },
344 | {
345 | "cell_type": "markdown",
346 | "id": "098a00df-8e1b-4e50-89c2-ccd652e0c0c8",
347 | "metadata": {
348 | "tags": []
349 | },
350 | "source": [
351 | "Oh, no! This results in an error as the function is not designed to receive a list as input.\n",
352 | "\n",
353 | "We can resolve this issue by writing our prompt more precisely:\n",
354 | "\n",
355 | "```\n",
356 | "Give me a python function to convert a list of temperatures in degrees Kelvin to Newton and print out the results\n",
357 | "```\n",
358 | "\n",
359 | "Response:"
360 | ]
361 | },
362 | {
363 | "cell_type": "code",
364 | "execution_count": null,
365 | "id": "32e76ad0-e522-4d22-bec0-8af742e1a713",
366 | "metadata": {
367 | "tags": []
368 | },
369 | "outputs": [],
370 | "source": [
371 | "def kelvin_to_newton(temperature_k):\n",
372 | " return temperature_k * 0.33\n",
373 | "\n",
374 | "def convert_and_print_temperatures(kelvin_temperatures):\n",
375 | " print(\"Kelvin to Newton Conversion:\")\n",
376 | " for temp_k in kelvin_temperatures:\n",
377 | " temp_n = kelvin_to_newton(temp_k)\n",
378 | " print(f\"{temp_k} K is equal to {temp_n:.2f} N\")"
379 | ]
380 | },
381 | {
382 | "cell_type": "markdown",
383 | "id": "b27ec500-426b-40fc-a595-1c1875f37668",
384 | "metadata": {},
385 | "source": [
386 | "Now let's test things out with the same list of temperatures in Kelvins from above."
387 | ]
388 | },
389 | {
390 | "cell_type": "code",
391 | "execution_count": null,
392 | "id": "cf6fcfcb-aaa4-42da-86e1-5c5d984f5973",
393 | "metadata": {
394 | "tags": []
395 | },
396 | "outputs": [],
397 | "source": [
398 | "# Write your code below\n"
399 | ]
400 | },
401 | {
402 | "cell_type": "markdown",
403 | "id": "466fef71-4b4f-497d-a4f9-3eb2666e2900",
404 | "metadata": {},
405 | "source": [
406 | "OK, so it seems the function now handles lists of temperatures without any trouble. Let's see if we can now create a new function that classifies temperatures into five classes. We can start with this prompt:\n",
407 | "\n",
408 | "```\n",
409 | "Create a python function that classifies an input temperature to five classes\n",
410 | "```\n",
411 | "\n",
412 | "Response:"
413 | ]
414 | },
415 | {
416 | "cell_type": "code",
417 | "execution_count": null,
418 | "id": "809da3c9-c2c1-45c2-bbe2-cfef385e11fa",
419 | "metadata": {
420 | "tags": []
421 | },
422 | "outputs": [],
423 | "source": [
424 | "def create_temperature_classifier():\n",
425 | " def classify_temperature(temperature):\n",
426 | " if temperature < 0:\n",
427 | " return \"Below Freezing\"\n",
428 | " elif 0 <= temperature < 20:\n",
429 | " return \"Cold\"\n",
430 | " elif 20 <= temperature < 40:\n",
431 | " return \"Cool\"\n",
432 | " elif 40 <= temperature < 60:\n",
433 | " return \"Moderate\"\n",
434 | " elif 60 <= temperature < 80:\n",
435 | " return \"Warm\"\n",
436 | " else:\n",
437 | " return \"Hot\"\n",
438 | " return classify_temperature"
439 | ]
440 | },
441 | {
442 | "cell_type": "markdown",
443 | "id": "3435c49d-33dd-429e-a8f4-d84b2db09cee",
444 | "metadata": {},
445 | "source": [
446 | "This works, but it is a vague prompt that does not specify the temperature unit or the classification logic needed. And ChatGPT provides a strange solution using an unnecesssary nested function for it. In addition, it is actually creating 6 classes instead of 5.\n",
447 | "\n",
448 | "Regardless, let's see how it works:"
449 | ]
450 | },
451 | {
452 | "cell_type": "code",
453 | "execution_count": null,
454 | "id": "8f2dc810-2a8e-4b86-9fbb-37a74ccc2dcf",
455 | "metadata": {
456 | "tags": []
457 | },
458 | "outputs": [],
459 | "source": [
460 | "classify_temp = create_temperature_classifier()\n",
461 | "temperature = 35\n",
462 | "category = classify_temp(temperature)\n",
463 | "print(f\"The temperature {temperature}°C is classified as {category}\")"
464 | ]
465 | },
466 | {
467 | "cell_type": "markdown",
468 | "id": "7d196b34-a864-40b3-84ec-e21bfb7e09ef",
469 | "metadata": {},
470 | "source": [
471 | "OK, so there are some issues... Perhaps we could do better by using the ChatGPT code as a starting place and modifying things to make more sense for our use:"
472 | ]
473 | },
474 | {
475 | "cell_type": "code",
476 | "execution_count": null,
477 | "id": "a4957565-eb19-4e7c-bfac-ea933c70a61c",
478 | "metadata": {},
479 | "outputs": [],
480 | "source": [
481 | "# Write your code below\n"
482 | ]
483 | },
484 | {
485 | "cell_type": "markdown",
486 | "id": "45168577-484c-4930-9bc7-3c40e6144cd0",
487 | "metadata": {},
488 | "source": [
489 | "Let's start with a basic test of the function with negative temperatures:"
490 | ]
491 | },
492 | {
493 | "cell_type": "code",
494 | "execution_count": null,
495 | "id": "24bb7725-6c7b-4f58-87ed-422c7329bb74",
496 | "metadata": {
497 | "tags": []
498 | },
499 | "outputs": [],
500 | "source": [
501 | "# Write your code below\n"
502 | ]
503 | },
504 | {
505 | "cell_type": "markdown",
506 | "id": "a73505d2-760b-4aae-a0e0-911b7bcf7fe1",
507 | "metadata": {
508 | "tags": []
509 | },
510 | "source": [
511 | "According to this function -60 and -1 are both in the same class!"
512 | ]
513 | },
514 | {
515 | "cell_type": "markdown",
516 | "id": "136db3c1-9e93-4b89-af56-cdc2c8cd1a46",
517 | "metadata": {},
518 | "source": [
519 | "## Privacy matters\n",
520 | "\n",
521 | "It's important to exercise caution when sharing personal or sensitive information with AI models like ChatGPT. While these models are designed to provide helpful information and engage in conversations, they don't have the ability to understand the implications of sharing sensitive data or guarantee complete privacy. Avoid sharing confidential information such as personal identification numbers, passwords, financial details, or any data that could compromise your privacy or security. Always remember that interactions with AI should be treated as public, and it's best to prioritize your privacy and safety online."
522 | ]
523 | },
524 | {
525 | "cell_type": "markdown",
526 | "id": "def3dade-6cc6-4612-a878-dbe504601e44",
527 | "metadata": {},
528 | "source": [
529 | "## Conclusion\n",
530 | "\n",
531 | "ChatGPT, as an example of a LLM, can be a useful tool for creating parts of code when used with clear and specific prompts. Good prompts may yield useful code, while vague or unclear prompts usually result in unhelpful or wrong responses. Remember to be aware of ChatGPT's limitations and thoroughly review the generated code to ensure it meets your requirements and standards.\n",
532 | "\n",
533 | "ChatGPT has limitations, and it is important to be aware of them:\n",
534 | "\n",
535 | "- Lack of Context: ChatGPT generates code based on the provided prompt, but it may not understand the broader context of your project or requirements.\n",
536 | "- Complex Logic: While ChatGPT can handle simple functions well, it may struggle with complex logic, intricate algorithms, or code that requires domain-specific knowledge.\n",
537 | "- Testing: Always thoroughly test the code generated by ChatGPT to ensure it functions as expected and doesn't have vulnerabilities.\n",
538 | "- Security: Be cautious when using ChatGPT to generate code for security-critical applications, as it may not consider all security risks.\n",
539 | "\n",
540 | "AI models can help you with certain tasks but be cautious with how you use them. The results can vary significantly depending on your prompt. Where there is nor information or context provided, the model makes assumptions which may not be accurate. And remember not to fully trust the result, AIs are smart but they DO make mistakes sometimes. "
541 | ]
542 | }
543 | ],
544 | "metadata": {
545 | "kernelspec": {
546 | "display_name": "Python 3 (ipykernel)",
547 | "language": "python",
548 | "name": "python3"
549 | },
550 | "language_info": {
551 | "codemirror_mode": {
552 | "name": "ipython",
553 | "version": 3
554 | },
555 | "file_extension": ".py",
556 | "mimetype": "text/x-python",
557 | "name": "python",
558 | "nbconvert_exporter": "python",
559 | "pygments_lexer": "ipython3",
560 | "version": "3.11.6"
561 | }
562 | },
563 | "nbformat": 4,
564 | "nbformat_minor": 5
565 | }
566 |
--------------------------------------------------------------------------------