diff --git a/_config.yml b/_config.yml index a40ece5..5eca94e 100644 --- a/_config.yml +++ b/_config.yml @@ -7,8 +7,28 @@ author: The Python in Chemistry Community # Force re-execution of notebooks on each build. # See https://jupyterbook.org/content/execute.html +only_build_toc_files: true execute: execute_notebooks: force + allow_errors: true + +launch_buttons: + notebook_interface: classic + binderhub_url: "https://mybinder.org" + colab_url: "https://colab.research.google.com" + +parse: + myst_enable_extensions: + - colon_fence + # - deflist + - dollarmath + # - html_admonition + - html_image + - linkify + # - replacements + # - smartquotes + - substitution + - tasklist # Define the name of the latex output file for PDF builds latex: @@ -42,4 +62,4 @@ sphinx: html_theme_options: logo: image_light: ./logo/logo-light.png - image_dark: ./logo/logo-dark.png \ No newline at end of file + image_dark: ./logo/logo-dark.png diff --git a/_toc.yml b/_toc.yml index 3a14203..9376e7d 100644 --- a/_toc.yml +++ b/_toc.yml @@ -10,6 +10,32 @@ parts: sections: - file: lessons/basics/write_run_python.md - file: lessons/basics/notebook.md + - file: lessons/manipulating_variables.md + sections: + - file: lessons/variables/Variable_data_types.ipynb + - file: lessons/variables/mathematical-operators.ipynb + - file: lessons/variables/Conditions.ipynb + - file: lessons/organising_code.md + sections: + - file: lessons/loops_functions/For_Loop_Lesson.ipynb + - file: lessons/loops_functions/PyinC_while_loops_WIP.ipynb + - file: lessons/loops_functions/functions_and_scope.ipynb + - file: lessons/files.md + sections: + - file: lessons/files/reading_files.ipynb + - file: lessons/files/writing_files.ipynb + - file: lessons/common_libraries.md + sections: + - file: lessons/common_libs/python-in-chem_pyplot_basics_SM.ipynb + - file: lessons/common_libs/Matplotlib_Documentation.ipynb + - file: lessons/common_libs/Introduction_to_NumPy.ipynb + - file: lessons/common_libs/NumPy_axes_operations.ipynb + - file: lessons/common_libs/intro_to_pandas.ipynb + - file: lessons/advanced_libraries.md + sections: + - file: lessons/advanced_libs/Intro_to_ASE.ipynb + - file: lessons/advanced_libs/PiC_scipy.ipynb + - file: lessons/advanced_libs/RDKit_drawing.ipynb - caption: Teaching Python in Chemistry Meeting Blogs chapters: - file: 2025-meeting diff --git a/lessons/advanced_libraries.md b/lessons/advanced_libraries.md new file mode 100644 index 0000000..704474d --- /dev/null +++ b/lessons/advanced_libraries.md @@ -0,0 +1,3 @@ +# Advanced Libraries + +This section gives a quick introduction to a few chemistry-specific libraries. diff --git a/lessons/advanced_libs/Asprin.mol b/lessons/advanced_libs/Asprin.mol new file mode 100644 index 0000000..856dab1 --- /dev/null +++ b/lessons/advanced_libs/Asprin.mol @@ -0,0 +1,47 @@ +2244 + -OEChem-04102511032D + + 21 21 0 0 0 0 0 0 0999 V2000 + 3.7320 -0.0600 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 1.4400 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 1.4400 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -1.5600 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -0.5600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.0600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.5600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -0.5600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -2.0600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -1.5600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.9400 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -0.5600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -0.0600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 -1.8700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -0.2500 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -2.6800 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -1.8700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3100 0.4769 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 0.2500 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6900 -0.5969 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 2.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 5 1 0 0 0 0 + 1 12 1 0 0 0 0 + 2 11 1 0 0 0 0 + 2 21 1 0 0 0 0 + 3 11 2 0 0 0 0 + 4 12 2 0 0 0 0 + 5 6 1 0 0 0 0 + 5 7 2 0 0 0 0 + 6 8 2 0 0 0 0 + 6 11 1 0 0 0 0 + 7 9 1 0 0 0 0 + 7 14 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 15 1 0 0 0 0 + 9 10 2 0 0 0 0 + 9 16 1 0 0 0 0 + 10 17 1 0 0 0 0 + 12 13 1 0 0 0 0 + 13 18 1 0 0 0 0 + 13 19 1 0 0 0 0 + 13 20 1 0 0 0 0 +M END diff --git a/lessons/advanced_libs/Intro_to_ASE.ipynb b/lessons/advanced_libs/Intro_to_ASE.ipynb new file mode 100644 index 0000000..3256896 --- /dev/null +++ b/lessons/advanced_libs/Intro_to_ASE.ipynb @@ -0,0 +1,402 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1639ee9d-703b-4c01-9a65-45d2a84cf4b0", + "metadata": {}, + "source": [ + "## The Atomic Simulation Environment, ASE" + ] + }, + { + "cell_type": "markdown", + "id": "24eee375-0afd-4f6c-9fca-1c5268956e59", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "- For loops\n", + "- Matplotlib" + ] + }, + { + "cell_type": "markdown", + "id": "4f7ccf4f-156f-4963-97e5-81191e2d07a6", + "metadata": {}, + "source": [ + "## Learning Outcomes" + ] + }, + { + "cell_type": "markdown", + "id": "4424d3e8-1280-45e1-97c8-86fb203059ce", + "metadata": {}, + "source": [ + "The Atomic Simulation Environment __[(ASE)](https://wiki.fysik.dtu.dk/ase/)__, is a python library containing tools to undertake, manipulate and analyse molecular simulations.\n", + "\n", + "The ASE supports several external **calculators**, including LAMMPS, QuantumEspresso and Orca (amongst many others) and also contains a number of inbuilt calculators. A list of external and inbuilt calculators is __[here](https://wiki.fysik.dtu.dk/ase/ase/calculators/calculators.html)__." + ] + }, + { + "cell_type": "markdown", + "id": "5c80ae51-0086-46bb-abe9-c4f2a9b8aefc", + "metadata": {}, + "source": [ + "### Atoms objects" + ] + }, + { + "attachments": { + "e1de4673-c8c2-46ca-ae6e-5f5ee70d7054.png": { + "image/png": "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" + } + }, + "cell_type": "markdown", + "id": "e522f21f-4b1a-4c0c-af05-feab7016aea9", + "metadata": {}, + "source": [ + "At the core of the ASE, is the **Atoms object**. Every _calculator_ operates on an _Atoms object_.\n", + "\n", + "An Atoms object is what we'd normally call a molecule. Makes sense right? A molecule is simply a list of atoms.\n", + "\n", + "Before we start, take a look at the below acetone molecule:\n", + "\n", + "![acetone.png](attachment:e1de4673-c8c2-46ca-ae6e-5f5ee70d7054.png)\n", + "\n", + "What information do I need to provide in order to distinguish each atom in the molecule? If you already have a background in molecular simulation, you may have already have some ideas from input files you have written.\n" + ] + }, + { + "cell_type": "markdown", + "id": "161f0dd0-c37c-44f8-bedc-5919fe9a8b48", + "metadata": {}, + "source": [ + "Some further questions to help you think about your answer:\n", + "- It is fairly straightforward to distiguish the single oxygen atom:\n", + " - Using **Atomic symbol / number**\n", + "- The central carbon is different from the two methyl carbons:\n", + " - Being bound to the electronegative oxygen atom, will cause the **partial charge** to be different\n", + "- While not shown, some atoms might be isotope labelled:\n", + " - The **atomic mass** is therefore useful\n", + "- It is otherwise difficult to distinguish the two methyl carbon atoms or the six hydrogens (assuming the methyl groups rotate freely)\n", + " - Therefore the **atom positions** are required to distinguish these atoms." + ] + }, + { + "cell_type": "markdown", + "id": "e34436a2-d7f4-4324-ac12-a30b74b76deb", + "metadata": {}, + "source": [ + "The ASE **Atom object** (for a single atom) provides the following information:\n", + "\n", + "- Atomic Symbol / Number\n", + "- Position\n", + "- Mass\n", + "- Momentum\n", + "- Magnetic moment\n", + "- Charge\n", + "- Tag (that can be used for special purposes)\n" + ] + }, + { + "cell_type": "markdown", + "id": "09b63bdf-36ea-4f96-a00d-6e687eedad0d", + "metadata": {}, + "source": [ + "Let's dive in.\n", + "\n", + "First import the Atoms class of the ASE:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c383410a-714a-458d-9c57-9858e5cf54bd", + "metadata": {}, + "outputs": [], + "source": [ + "from ase import Atoms" + ] + }, + { + "cell_type": "markdown", + "id": "e0c52455-f273-48c2-9d4b-d42d42839478", + "metadata": {}, + "source": [ + "Now let's make our first molecule (_ahem_ Atoms object), a simple CO molecule:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "648ce119-acd7-4b15-a684-2f6ffe458993", + "metadata": {}, + "outputs": [], + "source": [ + "d = 1.1\n", + "co = Atoms('CO', positions=[(0, 0, 0), (0, 0, d)])" + ] + }, + { + "cell_type": "markdown", + "id": "b3160912-4b27-448d-a703-aedd26f3a363", + "metadata": {}, + "source": [ + "The ASE also provides a variety of __[molecular viewers](https://wiki.fysik.dtu.dk/ase/ase/visualize/visualize.html)__, with different functionalities. For now, we'll use a simple but reliable viewer, that works directly in jupyter notebooks, called **x3d**:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a176851c-6c5f-4160-a94c-8f3d1614335f", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.visualize import view\n", + "view(co, viewer='x3d')" + ] + }, + { + "cell_type": "markdown", + "id": "ddb72a6c-651d-4d97-9fd9-499d6a8e9117", + "metadata": {}, + "source": [ + "In this simple viewer, we can use the mouse to rotate, zoom and translate the molecule within the viewer window." + ] + }, + { + "cell_type": "markdown", + "id": "21e1f876-bf93-4b94-9936-c11ad719122a", + "metadata": {}, + "source": [ + "Next we'll calculate the energy of our CO molecule. We will use the EMT calculator as it is quite fast and is implemented directly in ASE. To use a calculator, we need the following three steps:\n", + "1. Import the calculator\n", + "2. Setup the calculator\n", + "3. Attach the calculator to our Atoms object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76308f29-0f9a-44c3-ab9f-31b283e81bb4", + "metadata": {}, + "outputs": [], + "source": [ + "#1. Import\n", + "from ase.calculators.emt import EMT\n", + "#2. Setup calculator, EMT doesn't need any options, so we just need to define it\n", + "calc = EMT()\n", + "#3. Attach the calculator to our Atoms object\n", + "co.calc = calc" + ] + }, + { + "cell_type": "markdown", + "id": "748bdbf1-9e50-424d-9d9d-07939a47c2f5", + "metadata": {}, + "source": [ + "And now to calculate the energy of the molecule, we use the **get_potential_energy()** function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b21fcbc-8d48-4ed4-962d-30984488a953", + "metadata": {}, + "outputs": [], + "source": [ + "co.get_potential_energy()" + ] + }, + { + "cell_type": "markdown", + "id": "6d220b8b-63b3-4fe1-8d0b-f81184a7a79c", + "metadata": {}, + "source": [ + "Now let's manipulate our molecule, above we defined our bond length using the variable `d` to indicate our bond length. So we can easily change our bond length in a **for loop**.\n", + "\n", + "To set the CO bond length directly, we can use the __[**set_distance()** function](https://wiki.fysik.dtu.dk/ase/ase/atoms.html#ase.Atoms.set_distance)__. The set_distance function requires, at a minimum, three arguments; the index of _atom0_, index of _atom2_ (because our Atoms object is mostly going to be bigger than diatomic), and the _distance_ we want to set." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "642f800f-7478-4de4-a2ff-b2c50d04864a", + "metadata": {}, + "outputs": [], + "source": [ + "# Set up empty lists for distances and energies\n", + "distances = []\n", + "energies = []\n", + "\n", + "for distance in range(90,200): #loop variable must be an integer\n", + " co.calc = calc\n", + " co.set_distance(0, 1, distance * 0.01)\n", + " distances.append(distance * 0.01)\n", + " energies.append(co.get_total_energy())\n" + ] + }, + { + "cell_type": "markdown", + "id": "bf439b10-419d-4af0-9d34-70adbec59e7e", + "metadata": {}, + "source": [ + "We can use **matplotlib** to plot our results:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71b9cb8d-aac8-4e81-a60f-2e8e1f82ca8d", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "ax = plt.gca()\n", + "ax.plot(distances, energies)\n", + "ax.set_xlabel('Distance [Å]')\n", + "ax.set_ylabel('Total energy [eV]')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2c620673-38f4-4ba0-8d6b-498daf7e9393", + "metadata": {}, + "source": [ + "From our plot above, we can largely tell the equilibrium CO distance. We can also extract the minimum distance from the lists we created:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95f260c1-ab5b-4eb8-9bc3-338696ee3974", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Minimum energy =\", min(energies))\n", + "\n", + "print(\"Equilibrium bond length =\", distances[energies.index(min(energies))])\n" + ] + }, + { + "cell_type": "markdown", + "id": "f017ae4a-f850-4365-b7d2-bb75bb6f84f9", + "metadata": {}, + "source": [ + "## Getting and Setting\n", + "\n", + "We've just discovered two functions, `set_distance()` and `get_potential_energy()`. **get**ting and **set**ting are general concepts, with a fairly straighforward meaning:\n", + " - **get** will interrogate the Atoms object and return a value\n", + " - **set** will change the value in the Atoms object\n", + "Most things can be both **get**ted and **set**ted\n", + "\n", + "Common get functions include:\n", + " - get_distance(), get_angle(), get_dihedral()\n", + " - get_chemical_formula()\n", + " - get_dipole_moment()\n", + "\n", + "Common set functions include:\n", + " - set_distance(), set_angle(), set_dihedral()\n", + " - set_chemical_formula()\n", + " - set_cell() (for periodic systems)\n", + "\n", + "A complete list is in the __[Atoms object documentation](https://wiki.fysik.dtu.dk/ase/ase/atoms.html)__" + ] + }, + { + "cell_type": "markdown", + "id": "b1052def-b8fe-48f4-a2bf-ed97cdd144b1", + "metadata": {}, + "source": [ + "## Reading and writing molecule files\n", + "\n", + "We've just made our own CO Atoms object here, but for something more complicated, ASE has its own methods to both read and write many __[common molecule formats](https://wiki.fysik.dtu.dk/ase/ase/io/io.html)__.\n", + "\n", + "As always, we import:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99bb88b8-0bfc-4844-b7fd-71fe3195eca3", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.io import read, write" + ] + }, + { + "cell_type": "markdown", + "id": "aef470cb-e15a-4f19-82c7-1e9fcdd203d8", + "metadata": {}, + "source": [ + "Then to read a molecule, we simply specify the filename, ASE uses the file extension to automatically determine the format of the file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca3aa17a-52a4-4cc8-b3cd-67c469b57493", + "metadata": {}, + "outputs": [], + "source": [ + "ethane = read(\"ethane.xyz\")" + ] + }, + { + "cell_type": "markdown", + "id": "12a9c9ff-868b-49e4-a84c-e9ffced62f1e", + "metadata": {}, + "source": [ + "And to write, we similarly provide a filename, and ASE will use the extension we provide in order to determine the correct file format.\n", + "The write function takes two arguments:\n", + "`write(filename, Atoms_object)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "086a2c5e-2cfd-4a68-aec1-841121b847c4", + "metadata": {}, + "outputs": [], + "source": [ + "ethane_new = 'ethane_new.xyz'\n", + "write(ethane_new, ethane)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ed95dbd-d7f2-4611-adb9-21cdd3015686", + "metadata": {}, + "outputs": [], + "source": [ + "# TODO\n", + "# more examples" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/advanced_libs/PiC_scipy.ipynb b/lessons/advanced_libs/PiC_scipy.ipynb new file mode 100644 index 0000000..f3c0de9 --- /dev/null +++ b/lessons/advanced_libs/PiC_scipy.ipynb @@ -0,0 +1,330 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ad0fb146-f9ef-4d6c-9a04-ce3a7ad3ddd2", + "metadata": {}, + "source": [ + "# Scipy\n", + "A scientific Python library that among other functions provides:\n", + "- scientific constants\n", + "- advanced linear regression\n", + "- non-linear least squares curve fitting" + ] + }, + { + "cell_type": "markdown", + "id": "9242e111", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "- `matplotlib`\n", + "- `numpy`\n", + "- functions\n", + "- f-strings" + ] + }, + { + "cell_type": "markdown", + "id": "0dac4919", + "metadata": {}, + "source": [ + "## Learning outcomes\n", + "- Use physical and chemical constants from a library.\n", + "- Convert between common units of temperature, pressure and energy.\n", + "- Perform a linear regression and plot the result.\n", + "- Perform a non-linear least-squares fit and plot the result." + ] + }, + { + "cell_type": "markdown", + "id": "b33bf40b", + "metadata": {}, + "source": [ + "## Use of scientific constants\n", + "In any calculations or script you should define scientific constants with a clearly named variable rather than typing the number directly in your calculation. It is best practice to use the SI units for the constant." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1731cb41", + "metadata": {}, + "outputs": [], + "source": [ + "# Ideal gas constant\n", + "R = 8.314 # in J K^-1 mol^-1\n", + "# The volume of 1 mol of ideal gas at a pressure of 1 bar and a temperature of 298 K.\n", + "p = 100_000 # in Pa\n", + "n = 1 # in mol\n", + "T = 298 # in K\n", + "print(f\"The volume of 1 mol of ideal gas at standard pressure and 298 K is {1000*n*R*T/p:.2f} L.\")" + ] + }, + { + "cell_type": "markdown", + "id": "3e6b77c3", + "metadata": {}, + "source": [ + "## Scientific constants with `scipy.constants`\n", + "Import the `constants` package from the `scipy` library as a whole or import the specific constant(s) that you need." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec42bf6b", + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.constants import R,c\n", + "print(f\"The ideal gas constant is R = {R} J K^-1 mol^-1)\")\n", + "print(f\"The speed of light is c = {c} m s^-1\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62208e55", + "metadata": {}, + "outputs": [], + "source": [ + "import scipy.constants as c\n", + "print(f\"The Boltzmann constant is k_B = {c.k} J K^-1\")\n", + "print(f\"Planck's constant is h = {c.h} J s\")" + ] + }, + { + "cell_type": "markdown", + "id": "59a13bef", + "metadata": {}, + "source": [ + "## Exercise\n", + "Use the constants from the `scipy.constants` package to convert the energy of 1 kJ mol-1 into\n", + "- eV\n", + "- cm-1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b030b98", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "451e15a4", + "metadata": {}, + "source": [ + "You can find a whole list of physical constants included in the `scipy.constants` package at https://docs.scipy.org/doc/scipy/reference/constants.html" + ] + }, + { + "cell_type": "markdown", + "id": "c51b2c98", + "metadata": {}, + "source": [ + "## Advanced linear regression with `scipy.stats.linregress`\n", + "The linear regression method provided by the `scipy` package provides some advanced statistical parameters, those that users may be familiar with from Microsoft Excel's LINEST:\n", + "- Slope\n", + "- Intercept\n", + "- R value\n", + "- P value\n", + "- standard error in the slope\n", + "- standard error in the intercept" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36ba6ccf", + "metadata": {}, + "outputs": [], + "source": [ + "# Define an x,y dataset that follows a linear trend\n", + "import numpy as np\n", + "x = np.arange(0,10)\n", + "y = (3.0 + np.random.rand()) * (x + np.random.rand(10))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d715e20", + "metadata": {}, + "outputs": [], + "source": [ + "# Perform linear regression\n", + "from scipy.stats import linregress\n", + "result = linregress(x,y)\n", + "print(f\"The linear regression parameters are: {result}\")" + ] + }, + { + "cell_type": "markdown", + "id": "c0d7c80a", + "metadata": {}, + "source": [ + "### You can use `matplotlib` to visualise the data and the linear regression\n", + "Each of the elements of the fitting results can be accessed by appending the name, separated by a dot from the regression result e.g. `result.slope` for the slope or `result.intercept` for the intercept." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ad05330", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "plt.scatter(x,y,label=\"Data\")\n", + "plt.plot(x,x*result.slope + result.intercept,label=\"Regression\")\n", + "plt.title(\"Linear regression\")\n", + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"y\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4b1acf38", + "metadata": {}, + "source": [ + "## Exercise" + ] + }, + { + "cell_type": "markdown", + "id": "4df88078", + "metadata": {}, + "source": [ + "## Non-linear least-squares curve fitting with `scipy.optimize.curve_fit`\n", + "While as Chemists we like to linearise data to then apply a linear regression, sometimes data is in a format that cannot be easily linearised and requires to fit the non-linear data with the appropriate function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e9a5c00a", + "metadata": {}, + "outputs": [], + "source": [ + "# Import required packages\n", + "from scipy.optimize import curve_fit\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from scipy.constants import pi,k,u # Pi, Boltzmann constant, atomic mass unit" + ] + }, + { + "cell_type": "markdown", + "id": "4cbc0b5c", + "metadata": {}, + "source": [ + "### Maxwell-Boltzmann distribution\n", + "We will determine the mean speed and temperature of argon." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a3ecac55", + "metadata": {}, + "outputs": [], + "source": [ + "# Generate some data with noise\n", + "T = 300 # in K\n", + "steps = 50\n", + "v = np.linspace(1,1000,steps)\n", + "m = 39.948*u # mass of argon in atomic mass units\n", + "F = ((m/(2*pi*k*T)**1.5 * 4*pi*v**2 * np.exp(-m*v**2/(2*k*T)))/1e9+np.random.rand(steps))*1e9\n", + "plt.scatter(v,F)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5cb2ddc1", + "metadata": {}, + "outputs": [], + "source": [ + "def func(v,T,m):\n", + " \"\"\"Fitting function for a Maxwell-Boltzmann distribution.\"\"\"\n", + " return m/(2*pi*k*T)**1.5 * 4*pi*v**2 * np.exp(-m*v**2/(2*k*T))\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4030e65a", + "metadata": {}, + "outputs": [], + "source": [ + "# Performing the nonlinear fit which returns the fitting parameters \n", + "# as a tuple (which is like an immutable list) and the covariance matrix.\n", + "#\n", + "# In the fit we assume the element also as an unknown i.e. as a fitting parameter together with the temperature.\n", + "popt, pcov = curve_fit(func, v, F, p0=[250,1e-25])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33b4692d", + "metadata": {}, + "outputs": [], + "source": [ + "plt.scatter(v,F,label=\"data\")\n", + "plt.plot(v,func(v,*popt),label=f\"fit with T = {popt[0]:.1f} K, m = {popt[1]/u:.1f} amu\")\n", + "plt.title(\"Fitted Maxwell-Boltzmann distribution\")\n", + "plt.xlabel(r\"speed / m s$^{-1}$\")\n", + "plt.ylabel(\"Intensitiy / arb. units\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d39c119f", + "metadata": {}, + "source": [ + "# TODO\n", + "- Add example for unit conversion with `scipy.constants`\n", + "- Add exercises to all sections (unit conversion, linear regression possibly based on first linearising data, non-linear fit)\n", + "- add some questions / quizzes\n", + "- add a few debugging examples where suitable" + ] + }, + { + "cell_type": "markdown", + "id": "a73b7e31", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/advanced_libs/RDKit_drawing.ipynb b/lessons/advanced_libs/RDKit_drawing.ipynb new file mode 100644 index 0000000..4c0062e --- /dev/null +++ b/lessons/advanced_libs/RDKit_drawing.ipynb @@ -0,0 +1,254 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "Lck-NAtqxj-h" + }, + "source": [ + "# RDKit\n", + "\n", + "## Prerequisites:\n", + "- Importing libraries\n", + "- Molecule objects in RDKit\n", + "- SMILES molecular line notation\n", + "- Molecular file formats (MDF Mol)\n", + "\n", + "## Learning outcomes\n", + "- Become familiar with the use of RDKit to draw 2D molecular structures\n", + "- Use both SMILES strings and molecule files as input to create RDKit molecule objects" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1p_TIbTe1msT" + }, + "source": [ + "## Drawing molecules with RDKit\n", + "\n", + "RDKit is a free, massively powerful library of cheminformatics tools. An overview of the RDKit package (and its full documentation) can be accessed here https://www.rdkit.org/docs/Overview.html\n", + "\n", + "\n", + "The RDKit Python library makes drawing molecules simple. It requires two steps:\n", + "\n", + "\n", + "1. Create a molecule object that RDKit can operate on\n", + "2. Use the Draw function in RDKit's chemistry module to create the 2D image corresponding to the molecule object\n", + "\n", + "In order to do this, we must first tell Python to import the requires functionality from the RDKit package. Then, we are free to build our molecule and plot it.\n", + "\n", + "\n", + "## SMILES\n", + "Here we specify the molecular structure using the Simplified Molecular Input Line Entry System (SMILES) notation.\n", + "\n", + "Inspect, then run the following code to draw the benzene molecule." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mge9eDGA0A4M" + }, + "outputs": [], + "source": [ + "from rdkit import Chem\n", + "from rdkit.Chem import Draw\n", + "m1 = Chem.MolFromSmiles('c1ccccc1')\n", + "Draw.MolToImage(m1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1x0gHFxK64ui" + }, + "source": [ + "Here, we create a molecule object using the `MolFromSmiles()` function. RDKit reads the SMILES string and creates a 2D representation of the corresponding molecule. This is stored in the variable `m`.\n", + "\n", + "Once we have the `m` object, we can perform many different operations on it with RDKit, but here we focus on drawing the molecular structure. To do this we use the second import statement to give us access to RDKit's drawing functions.\n", + "\n", + "The function that we use in this example, `Draw.MolToImage()`, draws the molecular structure to the screen. However, you could also save your image to a file using the `Draw.MolToFile()` function for later insertion into e.g. Word documents." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "31Z-vyOx3Sf5" + }, + "source": [ + "By editing the SMILES string, we can alter the structure contained in the RDKit molecule object, and therefore the output image.\n", + "\n", + "Let's create toluene - you can do this by simply adding another carbon to the SMILES string.\n", + "\n", + "Note, however, that we are adding a capital C. In SMILES notation, capital \"C\" denotes an aliphatic sp$^3$ carbon and lowercase \"c\" gives an aromatic sp$^2$ carbon." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "S1Bo_RJb1VTP" + }, + "outputs": [], + "source": [ + "from rdkit import Chem\n", + "from rdkit.Chem import Draw\n", + "m = Chem.MolFromSmiles('c1ccccc1C')\n", + "Draw.MolToImage(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Kz6LvoP95fsF" + }, + "source": [ + "Similarly, we can change our benzene example to give us pyridine by replacing a ring carbon with nitrogen. We use a lowercase \"n\" because the pyridine nitrogen is also sp$^2$/aromatic." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UKNsFba00tcK" + }, + "outputs": [], + "source": [ + "from rdkit import Chem\n", + "from rdkit.Chem import Draw\n", + "m = Chem.MolFromSmiles('n1ccccc1')\n", + "Draw.MolToImage(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sFyadGUL9czn" + }, + "source": [ + "For simple molecules, editing the SMILES string by hand is fairly straightforward. This is not really the case for \"interesting\" molecules. In such cases, one alternative is to use an online source to obtain the SMILES for your molecule.\n", + "\n", + "The PubChem service (https://pubchem.ncbi.nlm.nih.gov/) is particularly useful for this. In the following example, PubChem was searched for \"asprin\". The entry for this compound contains the corresponding SMILES string, which was used here.\n", + "\n", + "NOTE: An alternative SMILES format is used here that explicitly shows the position of double bonds (denoted \"=\"), so no distinction is made between aromatic and aliphatic atoms. You can read more about SMILES grammar at http://opensmiles.org/opensmiles.html" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZlMpP_h-yG1f" + }, + "outputs": [], + "source": [ + "from rdkit import Chem\n", + "from rdkit.Chem import Draw\n", + "m = Chem.MolFromSmiles('CC(=O)OC1=CC=CC=C1C(=O)O')\n", + "Draw.MolToImage(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wO8MNehOGFQS" + }, + "source": [ + "## Molecular structure files\n", + "\n", + "An alternative input method to the SMILES strings, above, uses external files containing information on the elemental makup of the molecule in question and its geometry/bonding.\n", + "\n", + "RDKit can read several molecule file formats, but the next example uses a file in the \"MOL\" format." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "id": "yFv3z0gfxehq", + "jupyter": { + "outputs_hidden": true + } + }, + "outputs": [], + "source": [ + "from rdkit import Chem\n", + "from rdkit.Chem import Draw\n", + "m = Chem.MolFromMolFile('Asprin.mol')\n", + "Draw.MolToImage(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EydaQBa6TbGX" + }, + "source": [ + "NOTE: The orientation of the the asprin molecule is different because the order of the atoms in the .mol file is different from that given in the SMILES string. This means the \"starting\" atom for the drawing changes." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_0ed7gxW_sTz" + }, + "source": [ + "## Practice\n", + "Search PubChem for the data on paracetamol. Copy the SMILES string and edit/run the following Python code to draw the paracetamol molecule.\n", + "\n", + "Remember to put the SMILES string in quotes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "DwMYZFi4ACs-" + }, + "outputs": [], + "source": [ + "from rdkit import Chem\n", + "from rdkit.Chem import Draw\n", + "m = Chem.MolFromSmiles()\n", + "Draw.MolToImage(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "U93Gwk3V_Q9I" + }, + "source": [ + "# TODO:\n", + "- Clarify different SMILES formats (or simply tidy them up to use a single format - probably better for introductory students)\n", + "- Add examples using structure files as input instead of SMILES for more complex molecules to show limitations of 2D depiction (then add 3D images?)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/lessons/advanced_libs/ethane.xyz b/lessons/advanced_libs/ethane.xyz new file mode 100644 index 0000000..0942a40 --- /dev/null +++ b/lessons/advanced_libs/ethane.xyz @@ -0,0 +1,11 @@ +8 +GV ethane + C -2.12799175 0.80645143 0.00000001 + H -1.77133744 -0.20235861 -0.00000018 + H -1.77131892 1.31085006 -0.87365124 + H -3.19799175 0.80646453 0.00000003 + C -1.61464951 1.53240788 1.25740487 + H -0.54464951 1.53239477 1.25740486 + H -1.97130382 2.54121792 1.25740506 + H -1.97132234 1.02800925 2.13105613 + diff --git a/lessons/advanced_libs/ethane_new.xyz b/lessons/advanced_libs/ethane_new.xyz new file mode 100644 index 0000000..99a89e4 --- /dev/null +++ b/lessons/advanced_libs/ethane_new.xyz @@ -0,0 +1,10 @@ +8 +Properties=species:S:1:pos:R:3 GV=T ethane=T pbc="F F F" +C -2.12799175 0.80645143 0.00000001 +H -1.77133744 -0.20235861 -0.00000018 +H -1.77131892 1.31085006 -0.87365124 +H -3.19799175 0.80646453 0.00000003 +C -1.61464951 1.53240788 1.25740487 +H -0.54464951 1.53239477 1.25740486 +H -1.97130382 2.54121792 1.25740506 +H -1.97132234 1.02800925 2.13105613 diff --git a/lessons/basics.md b/lessons/basics.md index 80aa04f..ed1d235 100644 --- a/lessons/basics.md +++ b/lessons/basics.md @@ -1,3 +1,3 @@ -# Python Basics +# Getting Started with Python -Here, you will find some basic lessons in Python programming. +Here, you will find how to start running Python. diff --git a/lessons/common_libraries.md b/lessons/common_libraries.md new file mode 100644 index 0000000..79506dc --- /dev/null +++ b/lessons/common_libraries.md @@ -0,0 +1,3 @@ +# Common Libraries + +This section will introduce you to three commonly used libraries - NumPy, Matplotlib, and Pandas. It also covers how to read the documnetation of libraries like these. diff --git a/lessons/common_libs/Introduction_to_NumPy.ipynb b/lessons/common_libs/Introduction_to_NumPy.ipynb new file mode 100644 index 0000000..7976fe5 --- /dev/null +++ b/lessons/common_libs/Introduction_to_NumPy.ipynb @@ -0,0 +1,579 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "805c8bc8-6f38-481c-be9a-ce2ba998d686", + "metadata": {}, + "source": [ + "# An introduction to NumPy\n", + "\n", + "## Learning objectives\n", + "\n", + "- Create `numpy` arrays of one and two dimensions.\n", + "\n", + "- Perform mathematical operations on arrays.\n", + "\n", + "- Extract a single value from an array at a particular location.\n", + "\n", + "## Prerequisites\n", + "\n", + "- Variables\n", + "\n", + "- Lists" + ] + }, + { + "cell_type": "markdown", + "id": "5fc4dc12-62a2-42a0-af8d-451177ae071e", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "`numpy` is a Python library that enables us to create and use arrays of variables. `numpy` is very useful for working with data, as we will explore in the lesson.\n", + "\n", + "The basic object of `numpy` is an array, this is a collection of variables that looks much like a `list` but can be multi-dimensional. `numpy` typically uses numeric variables, with arrays of either integers of floats." + ] + }, + { + "cell_type": "markdown", + "id": "bec760b3-cc91-4655-a988-c50cbca27385", + "metadata": {}, + "source": [ + "### Import the NumPy library\n", + "\n", + "For `numpy`, the standard-practice alias is `np.`:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "928691dd-00ed-46db-83b9-85e480d48b52", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "2718fca7-d13e-4add-979e-2425b70078ac", + "metadata": {}, + "source": [ + "### Creating a NumPy array" + ] + }, + { + "cell_type": "markdown", + "id": "2b7964d7-a083-49c6-a701-99ecfc60bf60", + "metadata": {}, + "source": [ + "Imagine running an experiment in the lab in which the concentration of one product over time is measured. Mole fractions of our product of 0.0, 0.05, 0.08, 0.1, 0.11 are measured as a time series.\n", + "\n", + "In order to create a `numpy` array of our data, it's easiest to first create a list of the data. (Is this the best way to introduce this?)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "d20e8fd6-69c8-404d-91d6-27df5a5ce96a", + "metadata": {}, + "outputs": [], + "source": [ + "product_fraction_list = [0.0, 0.05, 0.08, 0.1, 0.11]" + ] + }, + { + "cell_type": "markdown", + "id": "a607f43f-b622-442b-8596-a00ce7c4147c", + "metadata": {}, + "source": [ + "The `numpy` `array()` function is used to create an array from a list." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "62d77504-91ae-43c9-8384-af3a3c950f1e", + "metadata": {}, + "outputs": [], + "source": [ + "product_fraction_array = np.array(product_fraction_list)" + ] + }, + { + "cell_type": "markdown", + "id": "8b0c94b4-5091-424a-90db-337957c94fa5", + "metadata": {}, + "source": [ + "Printing `product_fraction_array` allows inspection of the array." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "8319e6b9-a709-4c6c-8c24-47b21aba0b31", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0. 0.05 0.08 0.1 0.11]\n" + ] + } + ], + "source": [ + "print(product_fraction_array)" + ] + }, + { + "cell_type": "markdown", + "id": "1ed4c5cd-bcc1-4b9f-b1b1-bcd9b8009041", + "metadata": {}, + "source": [ + "`product_fraction_array` is a one dimensional array containing the experimental values of mole fraction." + ] + }, + { + "cell_type": "markdown", + "id": "f9a1237b-9e50-49de-b8b7-80c7c7874c0e", + "metadata": {}, + "source": [ + "`numpy` also has function for creating arrays filled with zeros or ones. These functions are fandily named `zeros` and `ones`! For one-dimensional arrays, you have to pass the length of array that you want to create to the function." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "4fbde00e-fca3-4e99-9ccf-1e825b2aaabc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0. 0. 0. 0. 0.]\n" + ] + } + ], + "source": [ + "array_of_zeros = np.zeros(5)\n", + "print(array_of_zeros)" + ] + }, + { + "cell_type": "markdown", + "id": "e831afad-609b-419f-ad71-17774439fa76", + "metadata": {}, + "source": [ + "### Excercise" + ] + }, + { + "cell_type": "markdown", + "id": "bdfcca73-0230-4110-869e-9351d54624da", + "metadata": {}, + "source": [ + "Create an array containing the following mole fractions of a reactant in a reaction: 0.5, 0.4, 0.37, 0.35, 0.34." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a68e2315-0776-4978-b7fa-0bc1780d0891", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "a3a1d2c8-84dd-4462-933c-7f57cf318d45", + "metadata": {}, + "source": [ + "Answer:\n", + "\n", + "```python\n", + "reactant_fraction_list = [0.5, 0.4, 0.37, 0.35, 0.34]\n", + "reactant_fraction_array = np.array(reactant_fraction_list)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "d41fd57d-c124-495b-8c37-9943b48c518f", + "metadata": {}, + "source": [ + "### Mathematical Operations" + ] + }, + { + "cell_type": "markdown", + "id": "004e8fd3-7b0d-4523-9d49-4a0d7de5e635", + "metadata": {}, + "source": [ + "`numpy` arrays allow mathematical operations. The most basic mathematical operations involve a single array and an integer. For instance, we might want to turn our mole fraction into a percentage by multiplying every value by 100. This is simple through `numpy` as mathematical operations are applied to each element." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "84535acd-cb25-4ba6-90ff-fdb5a4cfa694", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 0. 5. 8. 10. 11.]\n" + ] + } + ], + "source": [ + "product_percentage_array = product_fraction_array * 100\n", + "print(product_percentage_array)" + ] + }, + { + "cell_type": "markdown", + "id": "df8154d0-4b41-4a70-a3c2-e4a7b388066a", + "metadata": {}, + "source": [ + "All mathematical operators work in the same element by element way, including `+`, `-`, `/` and `**`." + ] + }, + { + "cell_type": "markdown", + "id": "bedf8b8c-224c-40ee-b6f4-bac5d7883f7f", + "metadata": {}, + "source": [ + "Mathematical operators also work between two arrays. For example, it would be possible to calculate the mole fraction of our known reactant and product at every time point by adding the two arrays together." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "56a09393-fe8a-4d67-b89d-10a626bbb655", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.5 0.45 0.45 0.45 0.45]\n" + ] + } + ], + "source": [ + "reactant_fraction_list = [0.5, 0.4, 0.37, 0.35, 0.34]\n", + "reactant_fraction_array = np.array(reactant_fraction_list)\n", + "\n", + "summed_fraction_array = product_fraction_array + reactant_fraction_array\n", + "print(summed_fraction_array)" + ] + }, + { + "cell_type": "markdown", + "id": "dd335fb5-c4eb-4256-9343-88863254adbd", + "metadata": {}, + "source": [ + "Mathematical operations between two arrays works in an element by element way too. The first element in one array is added to the first element in the other array, and so on.\n", + "\n", + "If an attempt is made to add two arrays of different size together, this gives an error!" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "39a68643-a5b7-4d83-838f-3cc8db6be500", + "metadata": {}, + "outputs": [], + "source": [ + "#array_four = np.array([1,2,3,4])\n", + "#array_five = np.array([1,2,3,4,5])\n", + "#array_four + array_five" + ] + }, + { + "cell_type": "markdown", + "id": "8107eb92-6022-42b9-9852-17f181f548db", + "metadata": {}, + "source": [ + "## Excercises" + ] + }, + { + "cell_type": "markdown", + "id": "f67fd141-122a-4573-b0b0-47ecdf151e85", + "metadata": {}, + "source": [ + "Using the mole fraction of products and reactants given above, create an array containing the mole fraction of other reactants and products in the reaction." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bccc39cb-1d02-4664-ab69-b0e6f2a8d7a9", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "7bdb72ca-d330-4f57-946f-d549882d821c", + "metadata": {}, + "source": [ + "Answer:\n", + "\n", + "```python\n", + "remaining_fraction_array = np.ones(5) - summed_fraction_array\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "14b0d8ba-f7e2-4c4e-b67d-48467a6c107d", + "metadata": {}, + "source": [ + "The reaction time was also collected during the experiment. This data in minutes is: [0, 2, 4, 6, 8]. Create an array for this time data and convert the values to seconds." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc444172-a526-4c9c-b554-519996ea8ee1", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "af6876e7-dee8-4938-bf9e-36fdccb17d9c", + "metadata": {}, + "source": [ + "Answer:\n", + "\n", + "```python\n", + "minutes = np.array((0, 2, 4, 6, 8))\n", + "seconds = minutes*60\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "1a780dbc-b9f2-4029-9faf-bbdb1ff66732", + "metadata": {}, + "source": [ + "## Indexing elements" + ] + }, + { + "cell_type": "markdown", + "id": "c92bd0ee-30bc-41f4-85bd-f506e5c0f164", + "metadata": {}, + "source": [ + "Individual elements in `numpy` arrays can be accessed very simply using indexing. Indexing of one-dimensional arrays works very similarly to lists, i.e. the number of the element is given in square brackets.\n", + "\n", + "For example, to take the first value of product mole fraction from `product_fraction_array`:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "bc94118d-66f3-426e-8c92-adeaa4bb40d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0\n" + ] + } + ], + "source": [ + "first_element = product_fraction_array[0]\n", + "print(first_element)" + ] + }, + { + "cell_type": "markdown", + "id": "1230059e-37f8-42cb-a123-bb31f07898f5", + "metadata": {}, + "source": [ + "As ever in Python, indexing starts at 0, i.e. the first element is at position `0` and so on." + ] + }, + { + "cell_type": "markdown", + "id": "26e8db32-f232-4ab1-a776-da17aea0becb", + "metadata": {}, + "source": [ + "### Excercise" + ] + }, + { + "cell_type": "markdown", + "id": "7e96792f-cc39-487b-9d65-44ae4ea1e3e7", + "metadata": {}, + "source": [ + "Calculate the difference in time (in seconds) between the second and fifth time points in the time series used in the previous excercises." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70c74f43-b1f7-4c38-9a77-402c9a20c7f5", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "02bf27af-222f-4263-8331-7401abbaa538", + "metadata": {}, + "source": [ + "Answer:\n", + "\n", + "```python\n", + "time_difference = seconds[4] - seconds[1]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "bd83ef73-062e-44b1-95e8-58c5c6f47fd4", + "metadata": {}, + "source": [ + "## Multi-dimensional Arrays" + ] + }, + { + "cell_type": "markdown", + "id": "0582dbb6-43ae-4f10-b5b7-b83aeb0e96dd", + "metadata": {}, + "source": [ + "`numpy` arrays are not just restricted to one dimension. There are no restrictions on the number of dimensions for numpy arrays.\n", + "\n", + "For example, it's possible to combine both the concentration and time data in a 2D `numpy` array. One possible way to do this is to combine two lists into a list of lists." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "4988bc0c-24ab-4003-b7ec-2f18cefaa81c", + "metadata": {}, + "outputs": [], + "source": [ + "combined_list = (product_fraction_list, (0, 120, 240, 360, 480))" + ] + }, + { + "cell_type": "markdown", + "id": "2610da37-4524-46a5-80de-f785ea1c25f4", + "metadata": {}, + "source": [ + "Using the `array` function on a list of lists creates a multi-dimensional array." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "393c10d1-ba47-4571-8bdf-0db5f7884259", + "metadata": {}, + "outputs": [], + "source": [ + "combined_array = np.array(combined_list)" + ] + }, + { + "cell_type": "markdown", + "id": "7a412030-2e7a-4713-8f6a-69547dcc6ca2", + "metadata": {}, + "source": [ + "Creating a 2D array also works from a list of 1D arrays. Note, the lists (or arrays) must be the same length for this to work." + ] + }, + { + "cell_type": "markdown", + "id": "c94bc156-6041-4007-a7f9-f1c3b92bfd92", + "metadata": {}, + "source": [ + "## Excercise" + ] + }, + { + "cell_type": "markdown", + "id": "9a8ea97d-81a7-47c2-adb5-b89e375a1cb6", + "metadata": {}, + "source": [ + "Create a two-dimensional array containing the `reactant_fraction_array` and `seconds`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e68cb100-2b43-4d7f-a839-fe095fb78f27", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "bd61c85b-1f73-4e6d-92b4-94ebe6a1fcfa", + "metadata": {}, + "source": [ + "Answer:\n", + "\n", + "```python\n", + "reaction_array = np.array((reactant_fraction_array,seconds))\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "48169543-098f-4cc8-84c1-7718b10b16f9", + "metadata": {}, + "source": [ + "## Learning Objectives\n", + "- Create numpy arrays of one and two dimensions.\n", + "\n", + "- Perform mathematical operations on arrays.\n", + "\n", + "- Understand how to index `numpy` arrays." + ] + }, + { + "cell_type": "markdown", + "id": "83d2a72b-a435-4178-aa90-c70bdcce28ab", + "metadata": {}, + "source": [ + "## TODO\n", + "\n", + "Check content vs other NumPy lesson.\n", + "\n", + "Add more complex excercises." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/common_libs/Matplotlib_Documentation.ipynb b/lessons/common_libs/Matplotlib_Documentation.ipynb new file mode 100644 index 0000000..4a5b7d6 --- /dev/null +++ b/lessons/common_libs/Matplotlib_Documentation.ipynb @@ -0,0 +1,162 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "4c983a83-8172-4b50-b428-b65eb006acc2", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "id": "9b6f0144-2402-43ec-927a-33517502a59d", + "metadata": {}, + "source": [ + "## Searching the Docs - Matplotlib!\n", + "#### Introduction:" + ] + }, + { + "cell_type": "markdown", + "id": "75ab876e-424b-4cd3-929e-f6d40c13cc67", + "metadata": {}, + "source": [ + "### Task 1: Finding argument options from the documentation page\n", + "\n", + "Here is some code to create a linear plot. The datapoints have been defined as a circle.\n", + "1. Can you change the markers to crosses?\n", + " > **Hint:** Where has the datapoint style been defined in the code?\n", + "2. Now can you change them to be squares?\n", + " > **Hint:** Take a look at the `matplotlib.markers` documentation page [here](matplotlib.org/stable/api/markers_api.html#module-matplotlib.markers).\n", + "3. Can you change the linestyle to be a dotted line?\n", + " > **Hint:** Find the linestyle menu on `matplotlib.pyplot.plot` documentation page [here](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html).\n", + "4. Now can you increase the data point size?\n", + " > **Hint:** What did you do in task 3?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab5c3579-d62e-4751-b182-682fc4c2531f", + "metadata": {}, + "outputs": [], + "source": [ + "T = np.array([700, 750, 800, 850, 900])\n", + "k = np.array([1.5e-5, 4.9e-4, 5.9e-3, 4.7e-2, 5.5e-1])\n", + "\n", + "plt.plot(1000/T, np.log(k), marker='o', linestyle='-')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "95dfea33-1642-4653-8a80-ba4dc8ff3d9e", + "metadata": {}, + "source": [ + "### Task 2: Exploring Pyplot Functions" + ] + }, + { + "cell_type": "markdown", + "id": "321ad4e0-b412-49af-b941-fdadf740617c", + "metadata": {}, + "source": [ + "1. Explore the [matplotlib.pyplot](https://matplotlib.org/stable/api/pyplot_summary.html#module-matplotlib.pyplot) documentation page to find the function to change the y axis scale to a log scale. Once found, ammend the code below accordingly.\n", + "2. Explore the [matplotlib.pyplot](https://matplotlib.org/stable/api/pyplot_summary.html#module-matplotlib.pyplot) documentation page to format the x and y axis major and minor ticks. Update the code accordingly. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "951f91a1-0e80-43ec-aaa0-97ac7976432d", + "metadata": {}, + "outputs": [], + "source": [ + "data = np.genfromtxt(\"Venus data.csv\", skip_header=1, delimiter=',', dtype=float, usecols=(1, 2, 3))\n", + "\n", + "plt.plot(data[:, 0], data[:, 1], color='b', label=\"SO\")\n", + "plt.plot(data[:, 0], data[:, 2], color='r', label=r\"SO$_2$\")\n", + "plt.xlabel('Altitude (km)', fontsize=14)\n", + "plt.ylabel('Mixing Ratio (ppm)', fontsize=14)\n", + "plt.xlim(0, 180)\n", + "plt.ylim(0, 1e-5)\n", + "plt.minorticks_on()\n", + "plt.legend(frameon=False, fontsize=14)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3ffabcd1-fc1e-4394-9524-3eb4271e6a34", + "metadata": {}, + "source": [ + "### Task 3: Different Plot Types\n", + "\n", + "1. Create a boxplot from data and format it appropriately\n", + "2. Create a violinplot (displaying mean values) and format it appropriately" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "510c0e66-df4f-42ea-91dd-c35a8710e6bc", + "metadata": {}, + "outputs": [], + "source": [ + "data = np.genfromtxt(\"Vit C in Orange Juice.csv\", skip_header=1, delimiter=',', dtype=float)\n", + "\n", + "plt.boxplot(data)\n", + "plt.show()\n", + "\n", + "plt.violinplot(data, showmeans=True)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b1bbdbd4-78ce-4010-a41d-0ac3dabe7d39", + "metadata": {}, + "source": [ + "# TODO:\n", + "\n", + "- Searching\n", + "- Understanding scary examples\n", + "- A non-coding activity (MCQ/Drag and drop style)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b204a3c2-ed09-4725-b712-acb6ffcd3645", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/common_libs/NumPy_axes_operations.ipynb b/lessons/common_libs/NumPy_axes_operations.ipynb new file mode 100644 index 0000000..fee8997 --- /dev/null +++ b/lessons/common_libs/NumPy_axes_operations.ipynb @@ -0,0 +1,738 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "28ce25bd-a7cc-45ba-b502-0b00f089facb", + "metadata": {}, + "source": [ + "# NumPy Operations" + ] + }, + { + "cell_type": "markdown", + "id": "5f375c7b-ea30-469e-a8c4-84f036ce200c", + "metadata": {}, + "source": [ + "In the _previous lesson_, you've seen how we can use NumPy to easily and efficiently apply operations to whole _arrays_ of numbers, rather than having to painstakinly apply them to each number individually.\n", + "\n", + "Now, we'll look in more detail on ways to control _how_ these operations are applied, especially in the (common) case of arrays having more than one dimension." + ] + }, + { + "cell_type": "markdown", + "id": "2280072b-353f-455b-a589-fc3685a505bb", + "metadata": {}, + "source": [ + "## Learning outcomes" + ] + }, + { + "cell_type": "markdown", + "id": "887b7149-cdc4-425b-8d09-8a6ee27d1df8", + "metadata": {}, + "source": [ + "- Build familiarity with multi-dimensional arrays of numbers in NumPy\n", + "- Be able to access \"slices\" of data from multi-dimensional arrays\n", + "- Perform basic statistical functions selectively along dimensions (\"axes\") of an array\n", + "- Find the indices of the maximum and minimum elements of an array" + ] + }, + { + "cell_type": "markdown", + "id": "0fcbb3e5-f4ed-4f3d-8531-170168118656", + "metadata": {}, + "source": [ + "Up until now, we've mostly been looking at _one-dimensional_ arrays -- basically, lists of numbers. You can access _elements_ of this array by _index_ -- an integer specifying the location of the data in the array.\n", + "\n", + "However, there are many reasons we might want to use _multiple indices_ to organize our data, especially when these indices represent something more than just position in a list. For instance, take the example from the previous lesson, where we have a list of temperatures of the same substance measured at several different times. Let's say that each of these measurements was taken at a different time of day. Therefore, your index also tells you what time of day the corresponding measurement was made.\n", + "\n", + "Now, let's say you repeat this experiment (repeated temperature measurements of the same substance) on several different days, but taking care to take the measurements at the same _times of day_ as your first series of measurements. You could just put all the measurements into one list, but then it wouldn't be very easy to figure out which index (position in the overall list) corresponds to which day of measurement and which time of day. It would be much nicer to have _two_ indices, one referring to the day of measurement and one referring to the time of day.\n", + "\n", + "In fact, NumPy arrays let you do exactly this -- you can create a _two-dimensional_ array with two indices specifying the location of a data point in the array.\n", + "\n", + "There are many ways we can create such an array, but let's just say for now that you've recorded your data by making lists of your observations from each day, and collected those lists into one overall list, like so:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e77cb01-1f98-40da-a3d3-b89d70b9f086", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24bf5d06-fb73-41a7-94a2-51e0799c9451", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "id": "f6e506d8-13de-45c8-9e4f-c1b86beb6bed", + "metadata": {}, + "source": [ + "(data generation code at the end of the notebook)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72a50b4d-2bba-4de4-ba0a-a424c6ca083c", + "metadata": {}, + "outputs": [], + "source": [ + "my_measurements = [[19.43, 20.84, 20.59, 21.51, 21.75, 22.08, 21.22], # Day 1\n", + " [19.63, 20.59, 20.96, 21.42, 21.69, 21.72, 21.33], # Day 2\n", + " [19.58, 20.63, 20.98, 21.23, 21.74, 21.59, 21.64], # Day 3\n", + " [19.19, 19.90, 20.66, 21.26, 21.28, 21.32, 20.71], # Day 4\n", + " [19.64, 20.20, 20.97, 21.32, 21.86, 21.80, 21.43]] # Day 5\n", + "times_of_day = [9.0, 11.0, 12.5, 13.5, 15.0, 17.0, 19.0]" + ] + }, + { + "cell_type": "markdown", + "id": "ac757314-83e1-4e2d-a320-8736222fc606", + "metadata": {}, + "source": [ + "**Note:** What is missing from the above cell? What additional information do we need to make this data meaningful and useful to other people?" + ] + }, + { + "cell_type": "markdown", + "id": "c2a83153-715a-4acd-b4e1-6fe1115518ae", + "metadata": {}, + "source": [ + "And now we'll convert these rather unwieldy data structures into NumPy arrays, the usual way:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ed53150-93af-49ed-811a-b071d4a6661d", + "metadata": {}, + "outputs": [], + "source": [ + "measurements = np.array(my_measurements)\n", + "times = np.array(times_of_day)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "590d8c7f-bee8-41b4-9056-f52353b7c08b", + "metadata": {}, + "outputs": [], + "source": [ + "measurements" + ] + }, + { + "cell_type": "markdown", + "id": "a30d5bf0-48c9-4e79-a618-3899f5cd4b2b", + "metadata": {}, + "source": [ + "Looks pretty similar. What happens when we try accessing an index of this array?" + ] + }, + { + "cell_type": "markdown", + "id": "347c4cc7-0562-42b0-972e-414fc2c5b66f", + "metadata": {}, + "source": [ + "## Accessing elements of an array" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7730e210-526d-401f-b119-006ac61c2638", + "metadata": {}, + "outputs": [], + "source": [ + "measurements[2]" + ] + }, + { + "cell_type": "markdown", + "id": "7e6ad026-239c-4515-aa32-5c9c6f89dee7", + "metadata": {}, + "source": [ + "We get a whole list of numbers! This is one of the _rows_ of the above array -- technically, what we've just done is called \"_slicing_\" (more on that later).\n", + "\n", + "So how do we access an individual element?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86fce8ad-5d53-4d78-9214-d8264c8ea167", + "metadata": {}, + "outputs": [], + "source": [ + "measurements[2, 0]" + ] + }, + { + "cell_type": "markdown", + "id": "d9a94c9c-0129-4f9a-b1c0-06a9bb03201d", + "metadata": {}, + "source": [ + "Notice how we now have _two indices_, separated by a comma. The first index selects the day and the second index selects the time of day. Note how in the printed representation above, the _first index_ corresponds to the _row_ and the _second index_ corresponds to the _column_. This is the same as the convention used in linear algebra for indexing elements of a matrix.\n", + "\n", + "On their own, however, these indices don't really tell us much. For instance, how do we know what time of day a `0` corresponds to? Luckily, we've already stored this information in another array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb638a6f-9a2b-474d-bb1b-0709d122e500", + "metadata": {}, + "outputs": [], + "source": [ + "times[0]" + ] + }, + { + "cell_type": "markdown", + "id": "7e387455-1af5-47eb-be0c-713c3914bdd5", + "metadata": {}, + "source": [ + "So 9am. Later on, you'll learn how to use the `pandas` library to label our data axes in a way that is both easier to use and to understand, but for now it's simpler if we just stick to NumPy arrays." + ] + }, + { + "cell_type": "markdown", + "id": "5ec84e8e-29d5-4542-b1db-49956b221fdb", + "metadata": {}, + "source": [ + "Now it's **your turn**. How would you access the measurement made at 11am on Day 4? How about 5pm on Day 1?" + ] + }, + { + "cell_type": "markdown", + "id": "1e6f33ef-5f0e-471a-a1f5-cc0f83c322ac", + "metadata": {}, + "source": [ + "## Slicing" + ] + }, + { + "cell_type": "markdown", + "id": "57e837d6-ed5d-44ee-a69b-9823fff20444", + "metadata": {}, + "source": [ + "Before, we saw that indexing our 2-d array with a single number actually gave us an entire _row_ of the array. This is a special case of a behaviour called \"slicing\", which is a way to access multiple elements of an array at once.\n", + "\n", + "What if we wanted to access an entire _column_ of the array instead? (In this case, all the measurements made at a given time of each day)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be7922fc-9ce8-454e-9c24-4e9b528308df", + "metadata": {}, + "outputs": [], + "source": [ + "measurements[:, 2]" + ] + }, + { + "cell_type": "markdown", + "id": "ecf40f14-3476-43a1-97e0-16b88cc47eaf", + "metadata": {}, + "source": [ + "This gives us all measurements made at 12:30 on each of the 5 days:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d481d7e8-e9b9-42b7-a65a-9374c4b429ac", + "metadata": {}, + "outputs": [], + "source": [ + "times[2]" + ] + }, + { + "cell_type": "markdown", + "id": "dc38e5e4-9731-46cc-b0e4-eb1c7c417ccd", + "metadata": {}, + "source": [ + "Notice this syntax looks very similar to the two-index form we used to access individual elements above. We've just replaced one of the indices (the row index) with a colon (`:`), which tells NumPy to give us _all_ the rows for the selected column index.\n", + "\n", + "We can also do this to select rows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07e07474-754a-4135-9960-de2d6a061e78", + "metadata": {}, + "outputs": [], + "source": [ + "measurements[2, :]" + ] + }, + { + "cell_type": "markdown", + "id": "09c24074-4720-46bc-966a-5cb2caf3c348", + "metadata": {}, + "source": [ + "which gives us the same result as the first indexing statement we tried (`measurements[2]`). In fact, if you specify fewer _indices_ than the array has _dimensions_, NumPy will automatically add colons (\"select all\") at the end, corresponding to the remaining (unspecified) dimensions." + ] + }, + { + "cell_type": "markdown", + "id": "13d273d9-4306-4ab8-84a0-b22a52d9ec82", + "metadata": {}, + "source": [ + "## Axes 🪓🪓🪓" + ] + }, + { + "cell_type": "markdown", + "id": "cb88caa0-6438-450f-9500-0e5d77b52931", + "metadata": {}, + "source": [ + "(no, not that kind...)" + ] + }, + { + "cell_type": "markdown", + "id": "322dbc96-33dc-4f1e-92f8-49df2642ac44", + "metadata": {}, + "source": [ + "We've used the term \"indices\" and \"dimensions\" somewhat interchangeably a few times, and now, unfortunately, we'll be introducing another near-synonym: **axes** (plural of \"axis\"). This is the term that the NumPy documentation formally uses to refer to each of the dimensions of an array. For instance, in a statement such as:\n", + "\n", + " measurements[2, 0]\n", + "\n", + "we say we're selecting _index_ 2 along _axis 0_ (remember, in Python we almost always start counting from zero...), and _index_ 0 along _axis_ 1. The array has two _dimensions_ in total. (Confused yet?)\n", + "\n", + "Why is all this terminology relevant? For one, you'll need it to understand the NumPy documentation. Let's take a look at the documentation for a simple function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b230b206-8cf6-4a53-a19c-b9cf67da3df3", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "?np.mean" + ] + }, + { + "cell_type": "markdown", + "id": "74e6aa53-491c-4595-949c-92c34222b3f3", + "metadata": {}, + "source": [ + "There's a lot of information there we still don't need, but let's focus on the paragraph:\n", + "\n", + " Returns the average of the array elements. The average is taken over\n", + " the flattened array by default, otherwise over the specified axis.\n", + " ...\n", + "\n", + "What does this mean? First, let's see what this default behaviour is:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11ab63d1-da2a-418a-ae05-019f021c7dc8", + "metadata": {}, + "outputs": [], + "source": [ + "np.mean(measurements)" + ] + }, + { + "cell_type": "markdown", + "id": "7ceca933-ef41-40f7-b299-d865462f8d32", + "metadata": {}, + "source": [ + "It's taken the average over the _whole array_, without regard to the structure or dimensions (hence, \"flattened\"). But part of the reason we organized it into two dimensions was that we could keep track of this structure, i.e. look at the day and time of measurement independently. What if we want to take the average only over the _time of day_, while keeping the _days_ themselves independent?" + ] + }, + { + "cell_type": "markdown", + "id": "41ff6400-3b7d-40f8-b306-8ef8caff5881", + "metadata": {}, + "source": [ + "In NumPy terminology, we'd be taking the average (mean) _along the axis_ representing time of day. Since we use the second axis to select the time of day, this means we're averaging along axis 1 (we count from zero, remember)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "863c882b-b930-4c2a-8031-e6aadaf7b760", + "metadata": {}, + "outputs": [], + "source": [ + "np.mean(measurements, axis=1)" + ] + }, + { + "cell_type": "markdown", + "id": "a8b1d4d5-7a4b-474a-b901-763ca2a50f85", + "metadata": {}, + "source": [ + "Other summary statistics functions work in a similar way:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "153ddfa9-adc5-40d8-ad24-7fe766157641", + "metadata": {}, + "outputs": [], + "source": [ + "# Standard deviation with the \"N-1\" correction for small sample sizes\n", + "# This is sometimes called \"Bessel's correction\"\n", + "np.std(measurements, axis=1, ddof=1)" + ] + }, + { + "cell_type": "markdown", + "id": "96cd2a4a-ec8a-4636-8278-2f86b8fe2739", + "metadata": {}, + "source": [ + "It seems there's a fair amount of variation over the course of a single day. What about between the individual days? And let's keep the times of day separate to see what that variation might look like." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c569f63c-1519-4f58-85ea-3b21e5088507", + "metadata": {}, + "outputs": [], + "source": [ + "measurement_time_means = np.mean(measurements, axis=0)\n", + "measurement_time_means" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b12f0812-f3c4-4b45-a8dc-ad207664dc97", + "metadata": {}, + "outputs": [], + "source": [ + "np.std(measurements, axis=0, ddof=1)" + ] + }, + { + "cell_type": "markdown", + "id": "d90b2491-d6e7-4118-bdb5-f2d936c808ab", + "metadata": {}, + "source": [ + "Now, given what you know about statistics: Is there a statistically significant difference between the temperatures measured at different times of day?\n", + "\n", + "Let's take the smallest and largest temperatures and do a classic hypothesis test. We'll use the _standard deviation of the mean_ here, since we're comparing two means:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3608c492-ae66-4422-a572-d042cbcc5022", + "metadata": {}, + "outputs": [], + "source": [ + "st_means = np.std(measurements, axis=0, ddof=1) / np.sqrt(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51e24f04-9210-4f5a-85ff-e93c765dfe76", + "metadata": {}, + "outputs": [], + "source": [ + "st_means" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "155f0b90-4af9-4f4b-bbc8-de590c34d84e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compute the \"t-score\"\n", + "(21.702 - 19.494) / np.sqrt(0.085**2 + 0.125**2)" + ] + }, + { + "cell_type": "markdown", + "id": "4d456d53-2cfa-46a1-99b5-8bc0b3fe7ed2", + "metadata": {}, + "source": [ + "This suggests the difference between the two values is more than 14 times the standard deviation of a hypothetical Gaussian distribution, if we assume that both values were drawn from the same distribution. This seems highly unlikely (it would definitely pass the \"$p$-test\", as the \"$p$-value\" is less than the magical value of 0.05 at differences larger than about 3 times the standard deviation). Therefore, we can reject the \"null hypothesis\" and say that the two values are clearly different.\n", + "\n", + "(There's a _lot_ of statistical rigor being glossed over here, not the least of which being that we're using _incredibly small_ sample sizes here, so the typical tools of statistics don't really work, and also we should be using a Student's $t$-test rather than assuming a Gaussian distribution -- the main point is, the two averages are _clearly_ different, and almost any rigorous statistical test you could apply here would tell you as much.)" + ] + }, + { + "cell_type": "markdown", + "id": "8a10beea-de5c-4e30-bf11-758d41ef43c9", + "metadata": {}, + "source": [ + "## Getting unknown indices" + ] + }, + { + "cell_type": "markdown", + "id": "2e5d0632-6ea3-4115-824d-44d6cc8d7a97", + "metadata": {}, + "source": [ + "In the test above, we just manually copied and pasted the largest and smallest array values from visual inspection. This is fine for a short demo, but in real applications with lots of data we'll need a more systematic, automatic approach.\n", + "\n", + "We already know how to get the maximum and minimum array _values_ -- there are NumPy functions that do this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f132ce4-3474-4c26-96d5-ac400f3679c1", + "metadata": {}, + "outputs": [], + "source": [ + "np.max(measurement_time_means)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "492ab1a5-de56-4a44-91f3-6d08d65120cf", + "metadata": {}, + "outputs": [], + "source": [ + "np.min(measurement_time_means)" + ] + }, + { + "cell_type": "markdown", + "id": "749ed217-4a26-4afb-807e-b4787fe2dc16", + "metadata": {}, + "source": [ + "But what if we want to find out _where_ in the array these values occur? Luckily, there are NumPy functions for this too, they just have less intuitive names:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9939b289-27eb-4416-b15e-dcab8c09086b", + "metadata": {}, + "outputs": [], + "source": [ + "# Think \"argument of the max\", as in \"index\" of maximum value\n", + "np.argmax(measurement_time_means)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f3a8264-9af6-4ce0-8204-48ac0dd0d934", + "metadata": {}, + "outputs": [], + "source": [ + "measurement_time_means[np.argmax(measurement_time_means)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fca0b382-e806-41e8-87d2-97ee86f0effc", + "metadata": {}, + "outputs": [], + "source": [ + "np.argmin(measurement_time_means)" + ] + }, + { + "cell_type": "markdown", + "id": "75560f1a-9bdd-41ce-8469-48390bdbddd5", + "metadata": {}, + "source": [ + "These functions are handy for finding the _corresponding value_ in an array of the same size, where the axis you're indexing represents the same thing. For instance: What if we want to know the time of day that the maximum and minimum (mean) temperature occurs?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "427c0f0b-e755-410f-9caa-3e7dd715e8f4", + "metadata": {}, + "outputs": [], + "source": [ + "times[np.argmax(measurement_time_means)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc2528da-6332-46db-919a-351c2bf3fa2b", + "metadata": {}, + "outputs": [], + "source": [ + "times[np.argmin(measurement_time_means)]" + ] + }, + { + "cell_type": "markdown", + "id": "640880a6-1bd3-4594-8d70-2c12f416cfe1", + "metadata": {}, + "source": [ + "So our highest (average) temperature is measured at 5pm and the lowest is at 9am." + ] + }, + { + "cell_type": "markdown", + "id": "ba892745-a144-41ba-8736-bb998fc5f2f2", + "metadata": {}, + "source": [ + "Now, it's **your turn**! Please _use_ the `argmin` and `argmax` functions to perform the hypothesis test above. As a reminder, we need to:\n", + "\n", + "- Find the maximum and minimum values of the `measurement_time_means` array\n", + "- Find the corresponding _standard deviation of the mean_ in the `st_means` array\n", + "- Compute the difference between the maximum and minimum (mean) temperatures\n", + "- Compute the standard deviation of the (hypothetical) Gaussian distribution of differences by adding the _variances_: $\\sigma_\\mathrm{diff} = \\sqrt{\\sigma_1^2 + \\sigma_2^2}$\n", + "- Divide the difference by the combined standard deviation to get the $t$-score. You should get the same answer (about 14.6) as above." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf89e6fd-f958-41d1-a7eb-150c3a273db2", + "metadata": {}, + "outputs": [], + "source": [ + "temp_max = np.max(measurement_time_means)\n", + "st_mean_max = st_means[np.argmax(measurement_time_means)]\n", + "\n", + "# Do the same for the min\n", + "\n", + "# Compute the temperature difference\n", + "\n", + "# Compute the combined standard deviation\n", + "\n", + "# Compute the t-score" + ] + }, + { + "cell_type": "markdown", + "id": "1eeb23a1-bb2b-4210-b78a-a36f0c726a64", + "metadata": {}, + "source": [ + "# TODO\n", + "\n", + "- Argmax/min in multiple dimensions?\n", + "- Application: Find the center of geometry (COG) of a molecule given its coordinates. Subtract this from the coordinates (broadcasting!) to get the coordinates in the COG frame.\n", + "- Introduce broadcasting!\n", + "- Application: Now compute the center of mass (COM) by weighting the coordinates by the corresponding atomic masses.\n", + "- Any other indexing / multi-D / slicing topics or applications?" + ] + }, + { + "cell_type": "markdown", + "id": "1236ae69-c1c3-40e0-bff9-7dcbe8fa3636", + "metadata": {}, + "source": [ + "# Data generation code (should stay hidden)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69d7d5c2-abce-492e-b7ee-04e9751d8dc2", + "metadata": {}, + "outputs": [], + "source": [ + "base_hours = np.array([9.0, 11.0, 12.5, 13.5, 15.0, 17.0, 19.0])\n", + "base_trend = np.cos((base_hours - 4) * np.pi / 12.) * -1.8 + 20.0\n", + "noise_points = np.random.normal(size=(5, 7)) * 0.15\n", + "meas_pts = base_trend + noise_points" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab92c71d-fa5c-4a9f-a7e2-1494a17cbd59", + "metadata": {}, + "outputs": [], + "source": [ + "# Simulate a systematic measurement error one one day only\n", + "meas_pts[3] -= 0.4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a96bf7d-e304-4cd1-a24f-56f297064f7d", + "metadata": {}, + "outputs": [], + "source": [ + "meas_pts" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52b74e6d-6744-4acd-a17f-9df9c76b1948", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(base_hours, base_trend)\n", + "for series in meas_pts:\n", + " plt.scatter(base_hours, series)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "358cf0e0-a197-4c38-b3bb-aad77748c1f3", + "metadata": {}, + "outputs": [], + "source": [ + "print(np.array2string(meas_pts, precision=2, separator=', ', floatmode='fixed'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b9a64e44-1823-440c-b02f-d70126b94a3d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/common_libs/Venus data.csv b/lessons/common_libs/Venus data.csv new file mode 100644 index 0000000..e205166 --- /dev/null +++ b/lessons/common_libs/Venus data.csv @@ -0,0 +1,79 @@ +,altitude,so,so2 +1,-0.704689723,6.10E-16,7.35E-06 +2,-0.665537672,6.14E-16,7.35E-06 +3,-0.560130865,6.38E-16,7.35E-06 +4,-0.338212662,7.16E-16,7.35E-06 +5,0.04588663,8.12E-16,7.35E-06 +6,0.636510833,9.03E-16,7.35E-06 +7,1.475348372,9.73E-16,7.35E-06 +8,2.600597941,1.02E-15,7.36E-06 +9,4.04613478,1.06E-15,7.39E-06 +10,5.840365711,1.07E-15,7.44E-06 +11,8.004692432,1.27E-15,7.56E-06 +12,10.55303372,5.92E-15,7.72E-06 +13,13.49135509,2.57E-14,7.91E-06 +14,16.81617891,6.80E-14,8.11E-06 +15,20.50942658,1.33E-13,8.27E-06 +16,24.44175194,2.84E-13,8.37E-06 +17,28.33343924,6.29E-13,8.43E-06 +18,32.01034726,1.10E-14,8.47E-06 +19,35.47595331,1.08E-15,8.47E-06 +20,38.71806531,3.80E-16,8.30E-06 +21,41.71853186,1.26E-16,7.90E-06 +22,44.50716306,1.23E-15,7.36E-06 +23,47.13362277,1.08E-14,6.92E-06 +24,49.60762433,7.45E-14,6.73E-06 +25,51.92295942,3.59E-13,6.65E-06 +26,54.07841631,1.55E-12,6.57E-06 +27,56.0802577,6.21E-12,6.45E-06 +28,57.93635846,1.46E-11,6.20E-06 +29,59.66297308,1.19E-11,5.58E-06 +30,61.28797567,2.49E-12,4.49E-06 +31,62.84736579,6.18E-12,3.25E-06 +32,64.41679145,1.61E-10,2.20E-06 +33,66.05791283,6.82E-10,1.38E-06 +34,67.75125984,1.32E-09,7.87E-07 +35,69.45006449,1.55E-09,4.30E-07 +36,71.12910436,1.55E-09,2.44E-07 +37,72.78328276,1.47E-09,1.60E-07 +38,74.42006023,1.41E-09,1.26E-07 +39,76.06583954,1.52E-09,1.11E-07 +40,77.73644499,1.69E-09,1.04E-07 +41,79.41103965,1.91E-09,9.87E-08 +42,81.05759183,2.12E-09,9.49E-08 +43,82.66393287,2.34E-09,9.35E-08 +44,84.22272226,2.64E-09,9.46E-08 +45,85.72809876,3.08E-09,9.58E-08 +46,87.39159687,3.76E-09,9.39E-08 +47,89.52876629,4.99E-09,9.01E-08 +48,92.09026302,7.54E-09,8.97E-08 +49,94.95427289,1.27E-08,8.88E-08 +50,98.00461311,1.46E-08,8.49E-08 +51,100.3931795,1.49E-08,7.89E-08 +52,102.2482482,1.41E-08,7.50E-08 +53,104.202286,1.75E-08,6.84E-08 +54,106.2514884,2.43E-08,6.02E-08 +55,108.367703,3.22E-08,5.17E-08 +56,110.5543634,4.18E-08,4.19E-08 +57,112.844633,5.25E-08,3.08E-08 +58,115.1585347,6.07E-08,2.19E-08 +59,117.3241908,6.62E-08,1.54E-08 +60,119.3478514,6.99E-08,1.07E-08 +61,121.2903091,7.22E-08,6.78E-09 +62,123.215108,7.30E-08,3.70E-09 +63,125.1267531,7.18E-08,1.84E-09 +64,126.9761047,6.87E-08,9.27E-10 +65,128.9080548,6.42E-08,4.77E-10 +66,130.9197488,5.87E-08,2.36E-10 +67,132.9497676,5.30E-08,1.16E-10 +68,135.0520522,4.72E-08,5.81E-11 +69,137.2246967,4.11E-08,2.41E-11 +70,139.5137683,3.48E-08,8.62E-12 +71,141.972967,2.87E-08,3.08E-12 +72,144.7091226,2.27E-08,1.22E-12 +73,147.8488383,1.70E-08,5.20E-13 +74,151.5060422,1.20E-08,2.54E-13 +75,155.7536542,7.86E-09,1.44E-13 +76,160.6153844,4.82E-09,8.65E-14 +77,166.0589755,2.81E-09,5.09E-14 +78,172.0472616,1.62E-09,2.61E-14 diff --git a/lessons/common_libs/Vit C in Orange Juice.csv b/lessons/common_libs/Vit C in Orange Juice.csv new file mode 100644 index 0000000..7bc1d85 --- /dev/null +++ b/lessons/common_libs/Vit C in Orange Juice.csv @@ -0,0 +1,121 @@ +Freshly squeezed orange juice - Unpasteurised (mg/100 mL),Premium (Industry pasteurised) juice (mg/100 mL),Budget (UHT) juice (mg/ 100 mL),Organic Juice (mg/ 100mL) +82.2016,62.0464,32.8016,63.232 +98.0096,62.244,36.556,56.316 +95.836,65.8008,31.8136,59.0824 +101.0724,64.22,41.5948,60.8608 +82.992,59.0824,34.7776,63.6272 +83.98,63.726,33.4932,59.8728 +77.064,64.3188,43.6696,76.9652 +101.27,64.714,32.604,65.9984 +82.004,60.0704,31.2208,64.0224 +97.6144,81.8064,36.556,73.112 +76.6688,67.5792,28.0592,56.316 +91.1924,54.5376,27.664,48.6096 +94.9468,63.4296,33.592,66.3936 +70.5432,51.376,51.5736,21.5384 +72.124,53.846,22.724,53.352 +87.1416,55.5256,27.664,50.5856 +99.788,64.22,35.568,65.6032 +91.884,63.726,34.58,61.256 +70.3456,60.8608,32.0112,60.0704 +78.8424,68.172,52.7592,44.46 +91.6864,55.7232,25.8856,52.7592 +87.0428,67.184,33.6908,45.6456 +98.8,65.9984,37.4452,61.256 +91.4888,56.7112,28.8496,50.388 +85.5608,67.184,33.7896,57.1064 +82.2016,63.4296,37.7416,63.0344 +78.2496,55.1304,17.1912,52.5616 +84.3752,61.6512,31.616,45.8432 +94.4528,63.8248,35.3704,64.0224 +94.848,53.352,17.784,56.6124 +91.4888,54.1424,13.4368,54.34 +95.836,59.28,29.146,64.22 +67.5792,57.5016,21.736,51.1784 +71.4324,45.2504,6.916,42.5828 +72.9144,57.304,30.5292,50.388 +99.1952,58.8848,28.2568,61.256 +113.62,51.376,8.892,47.424 +90.1056,52.364,21.5384,51.7712 +101.9616,64.8128,34.3824,62.4416 +82.2016,61.3548,34.1848,43.5708 +120.2396,74.4952,31.2208,81.3124 +70.3456,60.0704,35.3704,61.6512 +86.5488,61.1572,34.4812,53.0556 +85.7584,65.8996,33.8884,65.3068 +88.7224,60.268,25.2928,50.9808 +79.04,63.4296,33.7896,66.196 +79.2376,57.1064,38.1368,48.6096 +90.5008,64.0224,32.0112,64.6152 +86.944,60.268,30.628,54.34 +96.824,66.196,33.592,63.3308 +0,64.22,29.64,63.232 +70.3456,62.6392,34.58,65.208 +95.836,60.268,33.592,59.28 +71.63,57.4028,14.326,50.5856 +104.728,103.9376,28.652,64.22 +84.5728,59.0824,21.736,52.9568 +98.0096,67.184,39.52,61.256 +84.968,63.8248,33.3944,72.124 +82.004,52.7592,41.496,73.112 +96.824,41.496,23.712,58.292 +83.7824,62.244,33.3944,64.6152 +96.824,62.4416,30.8256,57.6992 +95.4408,63.6272,31.0232,58.9836 +94.0576,64.8128,36.4572,64.0224 +86.3512,62.0464,33.1968,64.0224 +83.98,64.4176,33.4932,60.268 +77.558,73.7048,42.6816,76.076 +98.8,65.208,34.086,65.208 +84.968,64.0224,32.0112,64.0224 +94.0576,79.4352,36.7536,65.702 +77.8544,66.3936,24.5024,56.9088 +89.1176,55.1304,22.9216,46.0408 +95.9348,63.6272,30.8256,66.196 +70.3456,51.7712,51.5736,20.9456 +75.088,53.9448,23.1192,53.352 +86.7464,55.822,28.158,51.376 +98.8,65.208,34.1848,62.244 +90.896,62.6392,35.4692,61.4536 +81.2136,64.4176,27.2688,64.4176 +80.4232,68.3696,47.0288,44.5588 +92.6744,57.304,25.2928,52.7592 +87.1416,64.22,33.9872,45.6456 +98.6024,66.196,37.544,62.244 +92.6744,57.304,30.628,51.1784 +88.7224,67.3816,33.9872,54.5376 +80.6208,61.256,41.496,57.5016 +82.3992,52.9568,17.784,56.316 +83.3872,55.328,32.604,45.448 +95.0456,64.8128,34.1848,0 +96.824,52.5616,15.808,52.7592 +92.872,53.1544,13.4368,54.5376 +90.402,51.376,29.64,60.268 +67.7768,58.0944,20.3528,52.364 +69.16,50.4868,7.904,42.3852 +72.7168,57.304,30.628,50.388 +99.5904,61.256,27.4664,61.0584 +123.5,59.28,11.856,46.436 +89.8092,52.2652,19.76,52.1664 +98.8,63.0344,36.1608,60.8608 +82.8932,60.6632,35.074,45.7444 +119.7456,78.9412,31.2208,79.534 +70.7408,59.0824,33.592,62.4416 +82.6956,60.762,33.2956,53.352 +85.6596,65.8996,33.6908,65.5044 +90.896,58.292,23.9096,48.8072 +84.2764,63.726,34.6788,64.22 +79.6328,56.9088,35.1728,46.436 +92.0816,64.4176,31.122,65.8008 +86.944,61.256,31.8136,53.7472 +91.884,64.22,33.098,63.0344 +79.6328,63.232,28.4544,57.6992 +72.9144,61.256,34.9752,66.7888 +94.7492,61.256,32.604,57.304 +72.618,58.1932,12.844,0 +95.0456,101.764,19.76,65.208 +84.5728,60.0704,20.3528,52.7592 +97.6144,65.208,37.544,59.0824 +84.968,64.22,36.7536,69.16 +87.932,61.256,37.544,68.172 +96.824,47.424,27.664,59.28 diff --git a/lessons/common_libs/intro_to_pandas.ipynb b/lessons/common_libs/intro_to_pandas.ipynb new file mode 100644 index 0000000..987d805 --- /dev/null +++ b/lessons/common_libs/intro_to_pandas.ipynb @@ -0,0 +1,327 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4e9376f84a7c26bc", + "metadata": {}, + "source": [ + "# Introduction to the Pandas Library" + ] + }, + { + "cell_type": "markdown", + "id": "16a10868d3450642", + "metadata": {}, + "source": [ + "*pandas* is a library within python that is designed to be used for data analysis. It is similar to Excel as it can handle large datasets, but with\n", + " the advantage of being able to manipulate the data in a programmable way.\n", + " You can\n", + "find the pandas documentation [here](https://pandas.pydata.org/docs/).\n", + "\n", + "\n", + "There is an [introductory video available](https://youtu.be/_T8LGqJtuGc) that tries to teach the basics of pands in just 10 minutes!" + ] + }, + { + "cell_type": "markdown", + "id": "5ddeb90892d82a5b", + "metadata": {}, + "source": [ + "### Prerequisites\n", + "- variables and data types\n", + "- libraries (not sure if this is needed)\n", + "- Boolean operators\n", + "- print\n", + "- f-strings" + ] + }, + { + "cell_type": "markdown", + "id": "a73114b516278ac5", + "metadata": {}, + "source": [ + "### Learning Outcomes\n", + "- Read and write files\n", + "- Understand what a dataframe is\n", + "- Check files are imported correctly\n", + "- Select a subset of a DataFrame\n", + "- Add new columns to a dataframe\n", + "- Calculate summary statistics\n" + ] + }, + { + "cell_type": "markdown", + "id": "5409de65537887d8", + "metadata": {}, + "source": [ + "The community standard alias for the pandas package is *pd*, which is assumed in the pandas documentation and in a lot of code you may see online." + ] + }, + { + "cell_type": "code", + "id": "705306f1027fa7e", + "metadata": {}, + "source": "import pandas as pd", + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "159944926f25cdc9", + "metadata": {}, + "source": "## Reading files" + }, + { + "cell_type": "markdown", + "id": "9f8ce7a24299e71c", + "metadata": {}, + "source": [ + "In pandas, it is useful to read data into a [**DataFrame**](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html#pandas.DataFrame),\n", + "which is similar to an Excel spreadsheet:\n", + "\n", + "![Pandas DataFrame](DataFrame.png)\n", + "\n", + "There are many ways to read data into pandas depending on the file type, but for regular delimited files,\n", + " the function [`read_csv`](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html) can be used." + ] + }, + { + "cell_type": "code", + "id": "6ef4f4222b561d3e", + "metadata": {}, + "source": [ + "data = pd.read_csv(\"periodic_table.csv\")\n", + "data" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "946227594d5d4492", + "metadata": {}, + "source": [ + "> This function assumes the data is comma separated, for other separators you can specify it using the delimiter parameter. If the separator is not a\n", + "regular character (e.g. a tab, multiple spaces), an internet search should tell you what string to use. E.g. for a *tab* separated file:\n", + ">\n", + "> ```data_tab = pd.read_csv(\"**need to get a file**\", delimiter=\"\\t\")```\n", + ">\n", + "> There are other parameters available, to specify the headers, the datatype etc. See [the documentation](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html) for full details.\n" + ] + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Viewing the data", + "id": "613367f256897f36" + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "Now that we have imported the data, it is important to view it is fully understand how it is formatted and ensure we imported it correctly. As you\n", + "may have noticed, when we try to display the dataframe, only some of the rows display. This is because only the first and last 5 rows will be shown\n", + " by default. There are functions we can use to display specific\n", + "parts of the\n", + "dataframe:\n", + "\n", + "- `data.head()` shows rows from the top of the file\n", + "- `data.tail()` shows rows from the bottom of the file\n", + "- `data.columns` shows the column names (header)\n", + "\n", + "If a number is given to `head` and `tail`, it will display that many rows.\n", + "\n", + "It can also be useful to check how pandas *interpreted* the data, and then change it if necessary. The data type can be checked using `.dtypes` and\n", + "it can be changed using `.astype()`.\n", + "\n", + "To display the datatype of all columns, we can run the function on the whole dataframe:" + ], + "id": "c00ce268787d2503" + }, + { + "metadata": {}, + "cell_type": "code", + "source": "data.dtypes", + "id": "de5e7c4b8c29071a", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "Or we can instead run the function on only one column:", + "id": "5d9551818a2553db" + }, + { + "metadata": {}, + "cell_type": "code", + "source": "data[\"AtomicNumber\"].dtype", + "id": "e4f7fa55f0ad8042", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "To change the data type, we need to reassign that column. E.g. to change the \"Name\" data to a string:", + "id": "b870cf77a1aea35f" + }, + { + "metadata": {}, + "cell_type": "code", + "source": [ + "print(f'Data type before change: {data[\"Name\"].dtype}')\n", + "data[\"Name\"] = data[\"Name\"].astype(\"string\")\n", + "print(f'Data type after change: {data[\"Name\"].dtype}')" + ], + "id": "d976fecb52130b29", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Exercise\n", + "\n", + "Display the first 8 elements." + ], + "id": "822ab5f3e84a6ff2" + }, + { + "metadata": {}, + "cell_type": "code", + "source": "# Add your answer here", + "id": "bce6df361acf974", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "code", + "source": [ + "# Answer\n", + "data.head(8)" + ], + "id": "ac14452b9f70836e", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "What element has atomic number 110? Hint: The table has 118 elements in it.", + "id": "ba7c9cb041afd40d" + }, + { + "metadata": {}, + "cell_type": "code", + "source": "# Add your answer here", + "id": "1c4beea42f5bb2d8", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "code", + "source": [ + "# Answer\n", + "data.tail(9)\n", + "\n", + "# The element with an atomic number of 110 is Darmstadtium." + ], + "id": "82f5627d2fea26b7", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "Change the \"Symbol\" data to strings. Check the data type of the column after.", + "id": "9885f5ed07d28703" + }, + { + "metadata": {}, + "cell_type": "code", + "source": "# Add your answer here", + "id": "7fa9904a9de0f284", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "code", + "source": [ + "# Answer\n", + "data[\"Symbol\"] = data[\"Symbol\"].astype(\"string\")\n", + "print(f'Data type after change: {data[\"Symbol\"].dtype}')" + ], + "id": "d6403b10cf05d3b9", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Writing files\n", + "\n", + "As with reading files, there are many ways to write data to a file depending on the file type wanted, but for regular delimited files,\n", + " the function [`to_csv`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_csv.html) can be used.\n", + "\n", + "As DataFrames have an index column, we have to decide if we want to keep this or not. We can do this using the `index` parameter. To **NOT**\n", + "include the index column, use `index=False`." + ], + "id": "420135f8853d1421" + }, + { + "metadata": {}, + "cell_type": "code", + "source": "data.to_csv(\"periodic_table_out.csv\", index=False)", + "id": "484f5eeecf6e9533", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "> As with reading files, we can specify what separator we want the data to be written using `sep`. There are many other useful parameters for\n", + "> specifying what data to save and how to save it. See [the documentation](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_csv.html) for more infromation." + ], + "id": "8cb03b854e801781" + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "# To Do\n", + "- select a subset of a df\n", + "- create new columns\n", + "- calculate statistics" + ], + "id": "73f5ded338418595" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/common_libs/periodic_table.csv b/lessons/common_libs/periodic_table.csv new file mode 100644 index 0000000..4185845 --- /dev/null +++ b/lessons/common_libs/periodic_table.csv @@ -0,0 +1,119 @@ +"AtomicNumber","Symbol","Name","AtomicMass","CPKHexColor","ElectronConfiguration","Electronegativity","AtomicRadius","IonizationEnergy","ElectronAffinity","OxidationStates","StandardState","MeltingPoint","BoilingPoint","Density","GroupBlock","YearDiscovered" +1,"H","Hydrogen",1.0080,"FFFFFF","1s1",2.2,120,13.598,0.754,"+1, -1","Gas",13.81,20.28,0.00008988,"Nonmetal",1766 +2,"He","Helium",4.00260,"D9FFFF","1s2","",140,24.587,"","0","Gas",0.95,4.22,0.0001785,"Noble gas",1868 +3,"Li","Lithium",7.0,"CC80FF","[He]2s1",0.98,182,5.392,0.618,"+1","Solid",453.65,1615,0.534,"Alkali metal",1817 +4,"Be","Beryllium",9.012183,"C2FF00","[He]2s2",1.57,153,9.323,"","+2","Solid",1560,2744,1.85,"Alkaline earth metal",1798 +5,"B","Boron",10.81,"FFB5B5","[He]2s2 2p1",2.04,192,8.298,0.277,"+3","Solid",2348,4273,2.37,"Metalloid",1808 +6,"C","Carbon",12.011,"909090","[He]2s2 2p2",2.55,170,11.260,1.263,"+4, +2, -4","Solid",3823,4098,2.2670,"Nonmetal","Ancient" +7,"N","Nitrogen",14.007,"3050F8","[He] 2s2 2p3",3.04,155,14.534,"","+5, +4, +3, +2, +1, -1, -2, -3","Gas",63.15,77.36,0.0012506,"Nonmetal",1772 +8,"O","Oxygen",15.999,"FF0D0D","[He]2s2 2p4",3.44,152,13.618,1.461,"-2","Gas",54.36,90.2,0.001429,"Nonmetal",1774 +9,"F","Fluorine",18.99840316,"90E050","[He]2s2 2p5",3.98,135,17.423,3.339,"-1","Gas",53.53,85.03,0.001696,"Halogen",1670 +10,"Ne","Neon",20.180,"B3E3F5","[He]2s2 2p6","",154,21.565,"","0","Gas",24.56,27.07,0.0008999,"Noble gas",1898 +11,"Na","Sodium",22.9897693,"AB5CF2","[Ne]3s1",0.93,227,5.139,0.548,"+1","Solid",370.95,1156,0.97,"Alkali metal",1807 +12,"Mg","Magnesium",24.305,"8AFF00","[Ne]3s2",1.31,173,7.646,"","+2","Solid",923,1363,1.74,"Alkaline earth metal",1808 +13,"Al","Aluminum",26.981538,"BFA6A6","[Ne]3s2 3p1",1.61,184,5.986,0.441,"+3","Solid",933.437,2792,2.70,"Post-transition metal","Ancient" +14,"Si","Silicon",28.085,"F0C8A0","[Ne]3s2 3p2",1.9,210,8.152,1.385,"+4, +2, -4","Solid",1687,3538,2.3296,"Metalloid",1854 +15,"P","Phosphorus",30.97376200,"FF8000","[Ne]3s2 3p3",2.19,180,10.487,0.746,"+5, +3, -3","Solid",317.3,553.65,1.82,"Nonmetal",1669 +16,"S","Sulfur",32.07,"FFFF30","[Ne]3s2 3p4",2.58,180,10.360,2.077,"+6, +4, -2","Solid",388.36,717.75,2.067,"Nonmetal","Ancient" +17,"Cl","Chlorine",35.45,"1FF01F","[Ne]3s2 3p5",3.16,175,12.968,3.617,"+7, +5, +1, -1","Gas",171.65,239.11,0.003214,"Halogen",1774 +18,"Ar","Argon",39.9,"80D1E3","[Ne]3s2 3p6","",188,15.760,"","0","Gas",83.8,87.3,0.0017837,"Noble gas",1894 +19,"K","Potassium",39.0983,"8F40D4","[Ar]4s1",0.82,275,4.341,0.501,"+1","Solid",336.53,1032,0.89,"Alkali metal",1807 +20,"Ca","Calcium",40.08,"3DFF00","[Ar]4s2",1,231,6.113,"","+2","Solid",1115,1757,1.54,"Alkaline earth metal","Ancient" +21,"Sc","Scandium",44.95591,"E6E6E6","[Ar]4s2 3d1",1.36,211,6.561,0.188,"+3","Solid",1814,3109,2.99,"Transition metal",1879 +22,"Ti","Titanium",47.867,"BFC2C7","[Ar]4s2 3d2",1.54,187,6.828,0.079,"+4, +3, +2","Solid",1941,3560,4.5,"Transition metal",1791 +23,"V","Vanadium",50.9415,"A6A6AB","[Ar]4s2 3d3",1.63,179,6.746,0.525,"+5, +4, +3, +2","Solid",2183,3680,6.0,"Transition metal",1801 +24,"Cr","Chromium",51.996,"8A99C7","[Ar]3d5 4s1",1.66,189,6.767,0.666,"+6, +3, +2","Solid",2180,2944,7.15,"Transition metal",1797 +25,"Mn","Manganese",54.93804,"9C7AC7","[Ar]4s2 3d5",1.55,197,7.434,"","+7, +4, +3, +2","Solid",1519,2334,7.3,"Transition metal",1774 +26,"Fe","Iron",55.84,"E06633","[Ar]4s2 3d6",1.83,194,7.902,0.163,"+3, +2","Solid",1811,3134,7.874,"Transition metal","Ancient" +27,"Co","Cobalt",58.93319,"F090A0","[Ar]4s2 3d7",1.88,192,7.881,0.661,"+3, +2","Solid",1768,3200,8.86,"Transition metal",1735 +28,"Ni","Nickel",58.693,"50D050","[Ar]4s2 3d8",1.91,163,7.640,1.156,"+3, +2","Solid",1728,3186,8.912,"Transition metal",1751 +29,"Cu","Copper",63.55,"C88033","[Ar]4s1 3d10",1.9,140,7.726,1.228,"+2, +1","Solid",1357.77,2835,8.933,"Transition metal","Ancient" +30,"Zn","Zinc",65.4,"7D80B0","[Ar]4s2 3d10",1.65,139,9.394,"","+2","Solid",692.68,1180,7.134,"Transition metal",1746 +31,"Ga","Gallium",69.723,"C28F8F","[Ar]4s2 3d10 4p1",1.81,187,5.999,0.3,"+3","Solid",302.91,2477,5.91,"Post-transition metal",1875 +32,"Ge","Germanium",72.63,"668F8F","[Ar]4s2 3d10 4p2",2.01,211,7.900,1.35,"+4, +2","Solid",1211.4,3106,5.323,"Metalloid",1886 +33,"As","Arsenic",74.92159,"BD80E3","[Ar]4s2 3d10 4p3",2.18,185,9.815,0.81,"+5, +3, -3","Solid",1090,887,5.776,"Metalloid","Ancient" +34,"Se","Selenium",78.97,"FFA100","[Ar]4s2 3d10 4p4",2.55,190,9.752,2.021,"+6, +4, -2","Solid",493.65,958,4.809,"Nonmetal",1817 +35,"Br","Bromine",79.90,"A62929","[Ar]4s2 3d10 4p5",2.96,183,11.814,3.365,"+5, +1, -1","Liquid",265.95,331.95,3.11,"Halogen",1826 +36,"Kr","Krypton",83.80,"5CB8D1","[Ar]4s2 3d10 4p6",3,202,14.000,"","0","Gas",115.79,119.93,0.003733,"Noble gas",1898 +37,"Rb","Rubidium",85.468,"702EB0","[Kr]5s1",0.82,303,4.177,0.468,"+1","Solid",312.46,961,1.53,"Alkali metal",1861 +38,"Sr","Strontium",87.62,"00FF00","[Kr]5s2",0.95,249,5.695,"","+2","Solid",1050,1655,2.64,"Alkaline earth metal",1790 +39,"Y","Yttrium",88.90584,"94FFFF","[Kr]5s2 4d1",1.22,219,6.217,0.307,"+3","Solid",1795,3618,4.47,"Transition metal",1794 +40,"Zr","Zirconium",91.22,"94E0E0","[Kr]5s2 4d2",1.33,186,6.634,0.426,"+4","Solid",2128,4682,6.52,"Transition metal",1789 +41,"Nb","Niobium",92.90637,"73C2C9","[Kr]5s1 4d4",1.6,207,6.759,0.893,"+5, +3","Solid",2750,5017,8.57,"Transition metal",1801 +42,"Mo","Molybdenum",95.95,"54B5B5","[Kr]5s1 4d5",2.16,209,7.092,0.746,"+6","Solid",2896,4912,10.2,"Transition metal",1778 +43,"Tc","Technetium",96.90636,"3B9E9E","[Kr]5s2 4d5",1.9,209,7.28,0.55,"+7, +6, +4","Solid",2430,4538,11,"Transition metal",1937 +44,"Ru","Ruthenium",101.1,"248F8F","[Kr]5s1 4d7",2.2,207,7.361,1.05,"+3","Solid",2607,4423,12.1,"Transition metal",1827 +45,"Rh","Rhodium",102.9055,"0A7D8C","[Kr]5s1 4d8",2.28,195,7.459,1.137,"+3","Solid",2237,3968,12.4,"Transition metal",1803 +46,"Pd","Palladium",106.42,"6985","[Kr]4d10",2.2,202,8.337,0.557,"+3, +2","Solid",1828.05,3236,12.0,"Transition metal",1803 +47,"Ag","Silver",107.868,"C0C0C0","[Kr]5s1 4d10",1.93,172,7.576,1.302,"+1","Solid",1234.93,2435,10.501,"Transition metal","Ancient" +48,"Cd","Cadmium",112.41,"FFD98F","[Kr]5s2 4d10",1.69,158,8.994,"","+2","Solid",594.22,1040,8.69,"Transition metal",1817 +49,"In","Indium",114.818,"A67573","[Kr]5s2 4d10 5p1",1.78,193,5.786,0.3,"+3","Solid",429.75,2345,7.31,"Post-transition metal",1863 +50,"Sn","Tin",118.71,"668080","[Kr]5s2 4d10 5p2",1.96,217,7.344,1.2,"+4, +2","Solid",505.08,2875,7.287,"Post-transition metal","Ancient" +51,"Sb","Antimony",121.760,"9E63B5","[Kr]5s2 4d10 5p3",2.05,206,8.64,1.07,"+5, +3, -3","Solid",903.78,1860,6.685,"Metalloid","Ancient" +52,"Te","Tellurium",127.6,"D47A00","[Kr]5s2 4d10 5p4",2.1,206,9.010,1.971,"+6, +4, -2","Solid",722.66,1261,6.232,"Metalloid",1782 +53,"I","Iodine",126.9045,"940094","[Kr]5s2 4d10 5p5",2.66,198,10.451,3.059,"+7, +5, +1, -1","Solid",386.85,457.55,4.93,"Halogen",1811 +54,"Xe","Xenon",131.29,"429EB0","[Kr]5s2 4d10 5p6",2.6,216,12.130,"","0","Gas",161.36,165.03,0.005887,"Noble gas",1898 +55,"Cs","Cesium",132.9054520,"57178F","[Xe]6s1",0.79,343,3.894,0.472,"+1","Solid",301.59,944,1.93,"Alkali metal",1860 +56,"Ba","Barium",137.33,"00C900","[Xe]6s2",0.89,268,5.212,"","+2","Solid",1000,2170,3.62,"Alkaline earth metal",1808 +57,"La","Lanthanum",138.9055,"70D4FF","[Xe]6s2 5d1",1.1,240,5.577,0.5,"+3","Solid",1191,3737,6.15,"Lanthanide",1839 +58,"Ce","Cerium",140.116,"FFFFC7","[Xe]6s2 4f1 5d1",1.12,235,5.539,0.5,"+4, +3","Solid",1071,3697,6.770,"Lanthanide",1803 +59,"Pr","Praseodymium",140.90766,"D9FFC7","[Xe]6s2 4f3",1.13,239,5.464,"","+3","Solid",1204,3793,6.77,"Lanthanide",1885 +60,"Nd","Neodymium",144.24,"C7FFC7","[Xe]6s2 4f4",1.14,229,5.525,"","+3","Solid",1294,3347,7.01,"Lanthanide",1885 +61,"Pm","Promethium",144.91276,"A3FFC7","[Xe]6s2 4f5","",236,5.55,"","+3","Solid",1315,3273,7.26,"Lanthanide",1945 +62,"Sm","Samarium",150.4,"8FFFC7","[Xe]6s2 4f6",1.17,229,5.644,"","+3, +2","Solid",1347,2067,7.52,"Lanthanide",1879 +63,"Eu","Europium",151.964,"61FFC7","[Xe]6s2 4f7","",233,5.670,"","+3, +2","Solid",1095,1802,5.24,"Lanthanide",1901 +64,"Gd","Gadolinium",157.25,"45FFC7","[Xe]6s2 4f7 5d1",1.2,237,6.150,"","+3","Solid",1586,3546,7.90,"Lanthanide",1880 +65,"Tb","Terbium",158.92535,"30FFC7","[Xe]6s2 4f9","",221,5.864,"","+3","Solid",1629,3503,8.23,"Lanthanide",1843 +66,"Dy","Dysprosium",162.500,"1FFFC7","[Xe]6s2 4f10",1.22,229,5.939,"","+3","Solid",1685,2840,8.55,"Lanthanide",1886 +67,"Ho","Holmium",164.93033,"00FF9C","[Xe]6s2 4f11",1.23,216,6.022,"","+3","Solid",1747,2973,8.80,"Lanthanide",1878 +68,"Er","Erbium",167.26,"","[Xe]6s2 4f12",1.24,235,6.108,"","+3","Solid",1802,3141,9.07,"Lanthanide",1843 +69,"Tm","Thulium",168.93422,"00D452","[Xe]6s2 4f13",1.25,227,6.184,"","+3","Solid",1818,2223,9.32,"Lanthanide",1879 +70,"Yb","Ytterbium",173.05,"00BF38","[Xe]6s2 4f14","",242,6.254,"","+3, +2","Solid",1092,1469,6.90,"Lanthanide",1878 +71,"Lu","Lutetium",174.9667,"00AB24","[Xe]6s2 4f14 5d1",1.27,221,5.426,"","+3","Solid",1936,3675,9.84,"Lanthanide",1907 +72,"Hf","Hafnium",178.49,"4DC2FF","[Xe]6s2 4f14 5d2",1.3,212,6.825,"","+4","Solid",2506,4876,13.3,"Transition metal",1923 +73,"Ta","Tantalum",180.9479,"4DA6FF","[Xe]6s2 4f14 5d3",1.5,217,7.89,0.322,"+5","Solid",3290,5731,16.4,"Transition metal",1802 +74,"W","Tungsten",183.84,"2194D6","[Xe]6s2 4f14 5d4",2.36,210,7.98,0.815,"+6","Solid",3695,5828,19.3,"Transition metal",1783 +75,"Re","Rhenium",186.207,"267DAB","[Xe]6s2 4f14 5d5",1.9,217,7.88,0.15,"+7, +6, +4","Solid",3459,5869,20.8,"Transition metal",1925 +76,"Os","Osmium",190.2,"266696","[Xe]6s2 4f14 5d6",2.2,216,8.7,1.1,"+4, +3","Solid",3306,5285,22.57,"Transition metal",1803 +77,"Ir","Iridium",192.22,"175487","[Xe]6s2 4f14 5d7",2.2,202,9.1,1.565,"+4, +3","Solid",2719,4701,22.42,"Transition metal",1803 +78,"Pt","Platinum",195.08,"D0D0E0","[Xe]6s1 4f14 5d9",2.28,209,9,2.128,"+4, +2","Solid",2041.55,4098,21.46,"Transition metal",1735 +79,"Au","Gold",196.96657,"FFD123","[Xe]6s1 4f14 5d10",2.54,166,9.226,2.309,"+3, +1","Solid",1337.33,3129,19.282,"Transition metal","Ancient" +80,"Hg","Mercury",200.59,"B8B8D0","[Xe]6s2 4f14 5d10",2,209,10.438,"","+2, +1","Liquid",234.32,629.88,13.5336,"Transition metal","Ancient" +81,"Tl","Thallium",204.383,"A6544D","[Xe]6s2 4f14 5d10 6p1",1.62,196,6.108,0.2,"+3, +1","Solid",577,1746,11.8,"Post-transition metal",1861 +82,"Pb","Lead",207,"575961","[Xe]6s2 4f14 5d10 6p2",2.33,202,7.417,0.36,"+4, +2","Solid",600.61,2022,11.342,"Post-transition metal","Ancient" +83,"Bi","Bismuth",208.98040,"9E4FB5","[Xe]6s2 4f14 5d10 6p3",2.02,207,7.289,0.946,"+5, +3","Solid",544.55,1837,9.807,"Post-transition metal",1753 +84,"Po","Polonium",208.98243,"AB5C00","[Xe]6s2 4f14 5d10 6p4",2,197,8.417,1.9,"+4, +2","Solid",527,1235,9.32,"Metalloid",1898 +85,"At","Astatine",209.98715,"754F45","[Xe]6s2 4f14 5d10 6p5",2.2,202,9.5,2.8,"7, 5, 3, 1, -1","Solid",575,"",7,"Halogen",1940 +86,"Rn","Radon",222.01758,"428296","[Xe]6s2 4f14 5d10 6p6","",220,10.745,"","0","Gas",202,211.45,0.00973,"Noble gas",1900 +87,"Fr","Francium",223.01973,"420066","[Rn]7s1",0.7,348,3.9,0.47,"+1","Solid",300,"","","Alkali metal",1939 +88,"Ra","Radium",226.02541,"007D00","[Rn]7s2",0.9,283,5.279,"","+2","Solid",973,1413,5,"Alkaline earth metal",1898 +89,"Ac","Actinium",227.02775,"70ABFA","[Rn]7s2 6d1",1.1,260,5.17,"","+3","Solid",1324,3471,10.07,"Actinide",1899 +90,"Th","Thorium",232.038,"00BAFF","[Rn]7s2 6d2",1.3,237,6.08,"","+4","Solid",2023,5061,11.72,"Actinide",1828 +91,"Pa","Protactinium",231.03588,"00A1FF","[Rn]7s2 5f2 6d1",1.5,243,5.89,"","+5, +4","Solid",1845,"",15.37,"Actinide",1913 +92,"U","Uranium",238.0289,"008FFF","[Rn]7s2 5f3 6d1",1.38,240,6.194,"","+6, +5, +4, +3","Solid",1408,4404,18.95,"Actinide",1789 +93,"Np","Neptunium",237.048172,"0080FF","[Rn]7s2 5f4 6d1",1.36,221,6.266,"","+6, +5, +4, +3","Solid",917,4175,20.25,"Actinide",1940 +94,"Pu","Plutonium",244.06420,"006BFF","[Rn]7s2 5f6",1.28,243,6.06,"","+6, +5, +4, +3","Solid",913,3501,19.84,"Actinide",1940 +95,"Am","Americium",243.061380,"545CF2","[Rn]7s2 5f7",1.3,244,5.993,"","+6, +5, +4, +3","Solid",1449,2284,13.69,"Actinide",1944 +96,"Cm","Curium",247.07035,"785CE3","[Rn]7s2 5f7 6d1",1.3,245,6.02,"","+3","Solid",1618,3400,13.51,"Actinide",1944 +97,"Bk","Berkelium",247.07031,"8A4FE3","[Rn]7s2 5f9",1.3,244,6.23,"","+4, +3","Solid",1323,"",14,"Actinide",1949 +98,"Cf","Californium",251.07959,"A136D4","[Rn]7s2 5f10",1.3,245,6.30,"","+3","Solid",1173,"","","Actinide",1950 +99,"Es","Einsteinium",252.0830,"B31FD4","[Rn]7s2 5f11",1.3,245,6.42,"","+3","Solid",1133,"","","Actinide",1952 +100,"Fm","Fermium",257.09511,"B31FBA","[Rn] 5f12 7s2",1.3,"",6.50,"","+3","Solid",1800,"","","Actinide",1952 +101,"Md","Mendelevium",258.09843,"B30DA6","[Rn]7s2 5f13",1.3,"",6.58,"","+3, +2","Solid",1100,"","","Actinide",1955 +102,"No","Nobelium",259.10100,"BD0D87","[Rn]7s2 5f14",1.3,"",6.65,"","+3, +2","Solid",1100,"","","Actinide",1957 +103,"Lr","Lawrencium",266.120,"C70066","[Rn]7s2 5f14 6d1",1.3,"","","","+3","Solid",1900,"","","Actinide",1961 +104,"Rf","Rutherfordium",267.122,"CC0059","[Rn]7s2 5f14 6d2","","","","","+4","Solid","","","","Transition metal",1964 +105,"Db","Dubnium",268.126,"D1004F","[Rn]7s2 5f14 6d3","","","","","5, 4, 3","Solid","","","","Transition metal",1967 +106,"Sg","Seaborgium",269.128,"D90045","[Rn]7s2 5f14 6d4","","","","","6, 5, 4, 3, 0","Solid","","","","Transition metal",1974 +107,"Bh","Bohrium",270.133,"E00038","[Rn]7s2 5f14 6d5","","","","","7, 5, 4, 3","Solid","","","","Transition metal",1976 +108,"Hs","Hassium",269.1336,"E6002E","[Rn]7s2 5f14 6d6","","","","","8, 6, 5, 4, 3, 2","Solid","","","","Transition metal",1984 +109,"Mt","Meitnerium",277.154,"EB0026","[Rn]7s2 5f14 6d7 (calculated)","","","","","9, 8, 6, 4, 3, 1","Solid","","","","Transition metal",1982 +110,"Ds","Darmstadtium",282.166,"","[Rn]7s2 5f14 6d8 (predicted)","","","","","8, 6, 4, 2, 0","Expected to be a Solid","","","","Transition metal",1994 +111,"Rg","Roentgenium",282.169,"","[Rn]7s2 5f14 6d9 (predicted)","","","","","5, 3, 1, -1","Expected to be a Solid","","","","Transition metal",1994 +112,"Cn","Copernicium",286.179,"","[Rn]7s2 5f14 6d10 (predicted)","","","","","2, 1, 0","Expected to be a Solid","","","","Transition metal",1996 +113,"Nh","Nihonium",286.182,"","[Rn]5f14 6d10 7s2 7p1 (predicted)","","","","","","Expected to be a Solid","","","","Post-transition metal",2004 +114,"Fl","Flerovium",290.192,"","[Rn]7s2 7p2 5f14 6d10 (predicted)","","","","","6, 4,2, 1, 0","Expected to be a Solid","","","","Post-transition metal",1998 +115,"Mc","Moscovium",290.196,"","[Rn]7s2 7p3 5f14 6d10 (predicted)","","","","","3, 1","Expected to be a Solid","","","","Post-transition metal",2003 +116,"Lv","Livermorium",293.205,"","[Rn]7s2 7p4 5f14 6d10 (predicted)","","","","","+4, +2, -2","Expected to be a Solid","","","","Post-transition metal",2000 +117,"Ts","Tennessine",294.211,"","[Rn]7s2 7p5 5f14 6d10 (predicted)","","","","","+5, +3, +1, -1","Expected to be a Solid","","","","Halogen",2010 +118,"Og","Oganesson",295.216,"","[Rn]7s2 7p6 5f14 6d10 (predicted)","","","","","+6, +4, +2, +1, 0, -1","Expected to be a Gas","","","","Noble gas",2006 diff --git a/lessons/common_libs/periodic_table_out.csv b/lessons/common_libs/periodic_table_out.csv new file mode 100644 index 0000000..3565424 --- /dev/null +++ b/lessons/common_libs/periodic_table_out.csv @@ -0,0 +1,119 @@ +AtomicNumber,Symbol,Name,AtomicMass,CPKHexColor,ElectronConfiguration,Electronegativity,AtomicRadius,IonizationEnergy,ElectronAffinity,OxidationStates,StandardState,MeltingPoint,BoilingPoint,Density,GroupBlock,YearDiscovered +1,H,Hydrogen,1.008,FFFFFF,1s1,2.2,120.0,13.598,0.754,"+1, -1",Gas,13.81,20.28,8.988e-05,Nonmetal,1766 +2,He,Helium,4.0026,D9FFFF,1s2,,140.0,24.587,,0,Gas,0.95,4.22,0.0001785,Noble gas,1868 +3,Li,Lithium,7.0,CC80FF,[He]2s1,0.98,182.0,5.392,0.618,+1,Solid,453.65,1615.0,0.534,Alkali metal,1817 +4,Be,Beryllium,9.012183,C2FF00,[He]2s2,1.57,153.0,9.323,,+2,Solid,1560.0,2744.0,1.85,Alkaline earth metal,1798 +5,B,Boron,10.81,FFB5B5,[He]2s2 2p1,2.04,192.0,8.298,0.277,+3,Solid,2348.0,4273.0,2.37,Metalloid,1808 +6,C,Carbon,12.011,909090,[He]2s2 2p2,2.55,170.0,11.26,1.263,"+4, +2, -4",Solid,3823.0,4098.0,2.267,Nonmetal,Ancient +7,N,Nitrogen,14.007,3050F8,[He] 2s2 2p3,3.04,155.0,14.534,,"+5, +4, +3, +2, +1, -1, -2, -3",Gas,63.15,77.36,0.0012506,Nonmetal,1772 +8,O,Oxygen,15.999,FF0D0D,[He]2s2 2p4,3.44,152.0,13.618,1.461,-2,Gas,54.36,90.2,0.001429,Nonmetal,1774 +9,F,Fluorine,18.99840316,90E050,[He]2s2 2p5,3.98,135.0,17.423,3.339,-1,Gas,53.53,85.03,0.001696,Halogen,1670 +10,Ne,Neon,20.18,B3E3F5,[He]2s2 2p6,,154.0,21.565,,0,Gas,24.56,27.07,0.0008999,Noble gas,1898 +11,Na,Sodium,22.9897693,AB5CF2,[Ne]3s1,0.93,227.0,5.139,0.548,+1,Solid,370.95,1156.0,0.97,Alkali metal,1807 +12,Mg,Magnesium,24.305,8AFF00,[Ne]3s2,1.31,173.0,7.646,,+2,Solid,923.0,1363.0,1.74,Alkaline earth metal,1808 +13,Al,Aluminum,26.981538,BFA6A6,[Ne]3s2 3p1,1.61,184.0,5.986,0.441,+3,Solid,933.437,2792.0,2.7,Post-transition metal,Ancient +14,Si,Silicon,28.085,F0C8A0,[Ne]3s2 3p2,1.9,210.0,8.152,1.385,"+4, +2, -4",Solid,1687.0,3538.0,2.3296,Metalloid,1854 +15,P,Phosphorus,30.973762,FF8000,[Ne]3s2 3p3,2.19,180.0,10.487,0.746,"+5, +3, -3",Solid,317.3,553.65,1.82,Nonmetal,1669 +16,S,Sulfur,32.07,FFFF30,[Ne]3s2 3p4,2.58,180.0,10.36,2.077,"+6, +4, -2",Solid,388.36,717.75,2.067,Nonmetal,Ancient +17,Cl,Chlorine,35.45,1FF01F,[Ne]3s2 3p5,3.16,175.0,12.968,3.617,"+7, +5, +1, -1",Gas,171.65,239.11,0.003214,Halogen,1774 +18,Ar,Argon,39.9,80D1E3,[Ne]3s2 3p6,,188.0,15.76,,0,Gas,83.8,87.3,0.0017837,Noble gas,1894 +19,K,Potassium,39.0983,8F40D4,[Ar]4s1,0.82,275.0,4.341,0.501,+1,Solid,336.53,1032.0,0.89,Alkali metal,1807 +20,Ca,Calcium,40.08,3DFF00,[Ar]4s2,1.0,231.0,6.113,,+2,Solid,1115.0,1757.0,1.54,Alkaline earth metal,Ancient +21,Sc,Scandium,44.95591,E6E6E6,[Ar]4s2 3d1,1.36,211.0,6.561,0.188,+3,Solid,1814.0,3109.0,2.99,Transition metal,1879 +22,Ti,Titanium,47.867,BFC2C7,[Ar]4s2 3d2,1.54,187.0,6.828,0.079,"+4, +3, +2",Solid,1941.0,3560.0,4.5,Transition metal,1791 +23,V,Vanadium,50.9415,A6A6AB,[Ar]4s2 3d3,1.63,179.0,6.746,0.525,"+5, +4, +3, +2",Solid,2183.0,3680.0,6.0,Transition metal,1801 +24,Cr,Chromium,51.996,8A99C7,[Ar]3d5 4s1,1.66,189.0,6.767,0.666,"+6, +3, +2",Solid,2180.0,2944.0,7.15,Transition metal,1797 +25,Mn,Manganese,54.93804,9C7AC7,[Ar]4s2 3d5,1.55,197.0,7.434,,"+7, +4, +3, +2",Solid,1519.0,2334.0,7.3,Transition metal,1774 +26,Fe,Iron,55.84,E06633,[Ar]4s2 3d6,1.83,194.0,7.902,0.163,"+3, +2",Solid,1811.0,3134.0,7.874,Transition metal,Ancient +27,Co,Cobalt,58.93319,F090A0,[Ar]4s2 3d7,1.88,192.0,7.881,0.661,"+3, +2",Solid,1768.0,3200.0,8.86,Transition metal,1735 +28,Ni,Nickel,58.693,50D050,[Ar]4s2 3d8,1.91,163.0,7.64,1.156,"+3, +2",Solid,1728.0,3186.0,8.912,Transition metal,1751 +29,Cu,Copper,63.55,C88033,[Ar]4s1 3d10,1.9,140.0,7.726,1.228,"+2, +1",Solid,1357.77,2835.0,8.933,Transition metal,Ancient +30,Zn,Zinc,65.4,7D80B0,[Ar]4s2 3d10,1.65,139.0,9.394,,+2,Solid,692.68,1180.0,7.134,Transition metal,1746 +31,Ga,Gallium,69.723,C28F8F,[Ar]4s2 3d10 4p1,1.81,187.0,5.999,0.3,+3,Solid,302.91,2477.0,5.91,Post-transition metal,1875 +32,Ge,Germanium,72.63,668F8F,[Ar]4s2 3d10 4p2,2.01,211.0,7.9,1.35,"+4, +2",Solid,1211.4,3106.0,5.323,Metalloid,1886 +33,As,Arsenic,74.92159,BD80E3,[Ar]4s2 3d10 4p3,2.18,185.0,9.815,0.81,"+5, +3, -3",Solid,1090.0,887.0,5.776,Metalloid,Ancient +34,Se,Selenium,78.97,FFA100,[Ar]4s2 3d10 4p4,2.55,190.0,9.752,2.021,"+6, +4, -2",Solid,493.65,958.0,4.809,Nonmetal,1817 +35,Br,Bromine,79.9,A62929,[Ar]4s2 3d10 4p5,2.96,183.0,11.814,3.365,"+5, +1, -1",Liquid,265.95,331.95,3.11,Halogen,1826 +36,Kr,Krypton,83.8,5CB8D1,[Ar]4s2 3d10 4p6,3.0,202.0,14.0,,0,Gas,115.79,119.93,0.003733,Noble gas,1898 +37,Rb,Rubidium,85.468,702EB0,[Kr]5s1,0.82,303.0,4.177,0.468,+1,Solid,312.46,961.0,1.53,Alkali metal,1861 +38,Sr,Strontium,87.62,00FF00,[Kr]5s2,0.95,249.0,5.695,,+2,Solid,1050.0,1655.0,2.64,Alkaline earth metal,1790 +39,Y,Yttrium,88.90584,94FFFF,[Kr]5s2 4d1,1.22,219.0,6.217,0.307,+3,Solid,1795.0,3618.0,4.47,Transition metal,1794 +40,Zr,Zirconium,91.22,94E0E0,[Kr]5s2 4d2,1.33,186.0,6.634,0.426,+4,Solid,2128.0,4682.0,6.52,Transition metal,1789 +41,Nb,Niobium,92.90637,73C2C9,[Kr]5s1 4d4,1.6,207.0,6.759,0.893,"+5, +3",Solid,2750.0,5017.0,8.57,Transition metal,1801 +42,Mo,Molybdenum,95.95,54B5B5,[Kr]5s1 4d5,2.16,209.0,7.092,0.746,+6,Solid,2896.0,4912.0,10.2,Transition metal,1778 +43,Tc,Technetium,96.90636,3B9E9E,[Kr]5s2 4d5,1.9,209.0,7.28,0.55,"+7, +6, +4",Solid,2430.0,4538.0,11.0,Transition metal,1937 +44,Ru,Ruthenium,101.1,248F8F,[Kr]5s1 4d7,2.2,207.0,7.361,1.05,+3,Solid,2607.0,4423.0,12.1,Transition metal,1827 +45,Rh,Rhodium,102.9055,0A7D8C,[Kr]5s1 4d8,2.28,195.0,7.459,1.137,+3,Solid,2237.0,3968.0,12.4,Transition metal,1803 +46,Pd,Palladium,106.42,6985,[Kr]4d10,2.2,202.0,8.337,0.557,"+3, +2",Solid,1828.05,3236.0,12.0,Transition metal,1803 +47,Ag,Silver,107.868,C0C0C0,[Kr]5s1 4d10,1.93,172.0,7.576,1.302,+1,Solid,1234.93,2435.0,10.501,Transition metal,Ancient +48,Cd,Cadmium,112.41,FFD98F,[Kr]5s2 4d10,1.69,158.0,8.994,,+2,Solid,594.22,1040.0,8.69,Transition metal,1817 +49,In,Indium,114.818,A67573,[Kr]5s2 4d10 5p1,1.78,193.0,5.786,0.3,+3,Solid,429.75,2345.0,7.31,Post-transition metal,1863 +50,Sn,Tin,118.71,668080,[Kr]5s2 4d10 5p2,1.96,217.0,7.344,1.2,"+4, +2",Solid,505.08,2875.0,7.287,Post-transition metal,Ancient +51,Sb,Antimony,121.76,9E63B5,[Kr]5s2 4d10 5p3,2.05,206.0,8.64,1.07,"+5, +3, -3",Solid,903.78,1860.0,6.685,Metalloid,Ancient +52,Te,Tellurium,127.6,D47A00,[Kr]5s2 4d10 5p4,2.1,206.0,9.01,1.971,"+6, +4, -2",Solid,722.66,1261.0,6.232,Metalloid,1782 +53,I,Iodine,126.9045,940094,[Kr]5s2 4d10 5p5,2.66,198.0,10.451,3.059,"+7, +5, +1, -1",Solid,386.85,457.55,4.93,Halogen,1811 +54,Xe,Xenon,131.29,429EB0,[Kr]5s2 4d10 5p6,2.6,216.0,12.13,,0,Gas,161.36,165.03,0.005887,Noble gas,1898 +55,Cs,Cesium,132.905452,57178F,[Xe]6s1,0.79,343.0,3.894,0.472,+1,Solid,301.59,944.0,1.93,Alkali metal,1860 +56,Ba,Barium,137.33,00C900,[Xe]6s2,0.89,268.0,5.212,,+2,Solid,1000.0,2170.0,3.62,Alkaline earth metal,1808 +57,La,Lanthanum,138.9055,70D4FF,[Xe]6s2 5d1,1.1,240.0,5.577,0.5,+3,Solid,1191.0,3737.0,6.15,Lanthanide,1839 +58,Ce,Cerium,140.116,FFFFC7,[Xe]6s2 4f1 5d1,1.12,235.0,5.539,0.5,"+4, +3",Solid,1071.0,3697.0,6.77,Lanthanide,1803 +59,Pr,Praseodymium,140.90766,D9FFC7,[Xe]6s2 4f3,1.13,239.0,5.464,,+3,Solid,1204.0,3793.0,6.77,Lanthanide,1885 +60,Nd,Neodymium,144.24,C7FFC7,[Xe]6s2 4f4,1.14,229.0,5.525,,+3,Solid,1294.0,3347.0,7.01,Lanthanide,1885 +61,Pm,Promethium,144.91276,A3FFC7,[Xe]6s2 4f5,,236.0,5.55,,+3,Solid,1315.0,3273.0,7.26,Lanthanide,1945 +62,Sm,Samarium,150.4,8FFFC7,[Xe]6s2 4f6,1.17,229.0,5.644,,"+3, +2",Solid,1347.0,2067.0,7.52,Lanthanide,1879 +63,Eu,Europium,151.964,61FFC7,[Xe]6s2 4f7,,233.0,5.67,,"+3, +2",Solid,1095.0,1802.0,5.24,Lanthanide,1901 +64,Gd,Gadolinium,157.25,45FFC7,[Xe]6s2 4f7 5d1,1.2,237.0,6.15,,+3,Solid,1586.0,3546.0,7.9,Lanthanide,1880 +65,Tb,Terbium,158.92535,30FFC7,[Xe]6s2 4f9,,221.0,5.864,,+3,Solid,1629.0,3503.0,8.23,Lanthanide,1843 +66,Dy,Dysprosium,162.5,1FFFC7,[Xe]6s2 4f10,1.22,229.0,5.939,,+3,Solid,1685.0,2840.0,8.55,Lanthanide,1886 +67,Ho,Holmium,164.93033,00FF9C,[Xe]6s2 4f11,1.23,216.0,6.022,,+3,Solid,1747.0,2973.0,8.8,Lanthanide,1878 +68,Er,Erbium,167.26,,[Xe]6s2 4f12,1.24,235.0,6.108,,+3,Solid,1802.0,3141.0,9.07,Lanthanide,1843 +69,Tm,Thulium,168.93422,00D452,[Xe]6s2 4f13,1.25,227.0,6.184,,+3,Solid,1818.0,2223.0,9.32,Lanthanide,1879 +70,Yb,Ytterbium,173.05,00BF38,[Xe]6s2 4f14,,242.0,6.254,,"+3, +2",Solid,1092.0,1469.0,6.9,Lanthanide,1878 +71,Lu,Lutetium,174.9667,00AB24,[Xe]6s2 4f14 5d1,1.27,221.0,5.426,,+3,Solid,1936.0,3675.0,9.84,Lanthanide,1907 +72,Hf,Hafnium,178.49,4DC2FF,[Xe]6s2 4f14 5d2,1.3,212.0,6.825,,+4,Solid,2506.0,4876.0,13.3,Transition metal,1923 +73,Ta,Tantalum,180.9479,4DA6FF,[Xe]6s2 4f14 5d3,1.5,217.0,7.89,0.322,+5,Solid,3290.0,5731.0,16.4,Transition metal,1802 +74,W,Tungsten,183.84,2194D6,[Xe]6s2 4f14 5d4,2.36,210.0,7.98,0.815,+6,Solid,3695.0,5828.0,19.3,Transition metal,1783 +75,Re,Rhenium,186.207,267DAB,[Xe]6s2 4f14 5d5,1.9,217.0,7.88,0.15,"+7, +6, +4",Solid,3459.0,5869.0,20.8,Transition metal,1925 +76,Os,Osmium,190.2,266696,[Xe]6s2 4f14 5d6,2.2,216.0,8.7,1.1,"+4, +3",Solid,3306.0,5285.0,22.57,Transition metal,1803 +77,Ir,Iridium,192.22,175487,[Xe]6s2 4f14 5d7,2.2,202.0,9.1,1.565,"+4, +3",Solid,2719.0,4701.0,22.42,Transition metal,1803 +78,Pt,Platinum,195.08,D0D0E0,[Xe]6s1 4f14 5d9,2.28,209.0,9.0,2.128,"+4, +2",Solid,2041.55,4098.0,21.46,Transition metal,1735 +79,Au,Gold,196.96657,FFD123,[Xe]6s1 4f14 5d10,2.54,166.0,9.226,2.309,"+3, +1",Solid,1337.33,3129.0,19.282,Transition metal,Ancient +80,Hg,Mercury,200.59,B8B8D0,[Xe]6s2 4f14 5d10,2.0,209.0,10.438,,"+2, +1",Liquid,234.32,629.88,13.5336,Transition metal,Ancient +81,Tl,Thallium,204.383,A6544D,[Xe]6s2 4f14 5d10 6p1,1.62,196.0,6.108,0.2,"+3, +1",Solid,577.0,1746.0,11.8,Post-transition metal,1861 +82,Pb,Lead,207.0,575961,[Xe]6s2 4f14 5d10 6p2,2.33,202.0,7.417,0.36,"+4, +2",Solid,600.61,2022.0,11.342,Post-transition metal,Ancient +83,Bi,Bismuth,208.9804,9E4FB5,[Xe]6s2 4f14 5d10 6p3,2.02,207.0,7.289,0.946,"+5, +3",Solid,544.55,1837.0,9.807,Post-transition metal,1753 +84,Po,Polonium,208.98243,AB5C00,[Xe]6s2 4f14 5d10 6p4,2.0,197.0,8.417,1.9,"+4, +2",Solid,527.0,1235.0,9.32,Metalloid,1898 +85,At,Astatine,209.98715,754F45,[Xe]6s2 4f14 5d10 6p5,2.2,202.0,9.5,2.8,"7, 5, 3, 1, -1",Solid,575.0,,7.0,Halogen,1940 +86,Rn,Radon,222.01758,428296,[Xe]6s2 4f14 5d10 6p6,,220.0,10.745,,0,Gas,202.0,211.45,0.00973,Noble gas,1900 +87,Fr,Francium,223.01973,420066,[Rn]7s1,0.7,348.0,3.9,0.47,+1,Solid,300.0,,,Alkali metal,1939 +88,Ra,Radium,226.02541,007D00,[Rn]7s2,0.9,283.0,5.279,,+2,Solid,973.0,1413.0,5.0,Alkaline earth metal,1898 +89,Ac,Actinium,227.02775,70ABFA,[Rn]7s2 6d1,1.1,260.0,5.17,,+3,Solid,1324.0,3471.0,10.07,Actinide,1899 +90,Th,Thorium,232.038,00BAFF,[Rn]7s2 6d2,1.3,237.0,6.08,,+4,Solid,2023.0,5061.0,11.72,Actinide,1828 +91,Pa,Protactinium,231.03588,00A1FF,[Rn]7s2 5f2 6d1,1.5,243.0,5.89,,"+5, +4",Solid,1845.0,,15.37,Actinide,1913 +92,U,Uranium,238.0289,008FFF,[Rn]7s2 5f3 6d1,1.38,240.0,6.194,,"+6, +5, +4, +3",Solid,1408.0,4404.0,18.95,Actinide,1789 +93,Np,Neptunium,237.048172,0080FF,[Rn]7s2 5f4 6d1,1.36,221.0,6.266,,"+6, +5, +4, +3",Solid,917.0,4175.0,20.25,Actinide,1940 +94,Pu,Plutonium,244.0642,006BFF,[Rn]7s2 5f6,1.28,243.0,6.06,,"+6, +5, +4, +3",Solid,913.0,3501.0,19.84,Actinide,1940 +95,Am,Americium,243.06138,545CF2,[Rn]7s2 5f7,1.3,244.0,5.993,,"+6, +5, +4, +3",Solid,1449.0,2284.0,13.69,Actinide,1944 +96,Cm,Curium,247.07035,785CE3,[Rn]7s2 5f7 6d1,1.3,245.0,6.02,,+3,Solid,1618.0,3400.0,13.51,Actinide,1944 +97,Bk,Berkelium,247.07031,8A4FE3,[Rn]7s2 5f9,1.3,244.0,6.23,,"+4, +3",Solid,1323.0,,14.0,Actinide,1949 +98,Cf,Californium,251.07959,A136D4,[Rn]7s2 5f10,1.3,245.0,6.3,,+3,Solid,1173.0,,,Actinide,1950 +99,Es,Einsteinium,252.083,B31FD4,[Rn]7s2 5f11,1.3,245.0,6.42,,+3,Solid,1133.0,,,Actinide,1952 +100,Fm,Fermium,257.09511,B31FBA,[Rn] 5f12 7s2,1.3,,6.5,,+3,Solid,1800.0,,,Actinide,1952 +101,Md,Mendelevium,258.09843,B30DA6,[Rn]7s2 5f13,1.3,,6.58,,"+3, +2",Solid,1100.0,,,Actinide,1955 +102,No,Nobelium,259.101,BD0D87,[Rn]7s2 5f14,1.3,,6.65,,"+3, +2",Solid,1100.0,,,Actinide,1957 +103,Lr,Lawrencium,266.12,C70066,[Rn]7s2 5f14 6d1,1.3,,,,+3,Solid,1900.0,,,Actinide,1961 +104,Rf,Rutherfordium,267.122,CC0059,[Rn]7s2 5f14 6d2,,,,,+4,Solid,,,,Transition metal,1964 +105,Db,Dubnium,268.126,D1004F,[Rn]7s2 5f14 6d3,,,,,"5, 4, 3",Solid,,,,Transition metal,1967 +106,Sg,Seaborgium,269.128,D90045,[Rn]7s2 5f14 6d4,,,,,"6, 5, 4, 3, 0",Solid,,,,Transition metal,1974 +107,Bh,Bohrium,270.133,E00038,[Rn]7s2 5f14 6d5,,,,,"7, 5, 4, 3",Solid,,,,Transition metal,1976 +108,Hs,Hassium,269.1336,E6002E,[Rn]7s2 5f14 6d6,,,,,"8, 6, 5, 4, 3, 2",Solid,,,,Transition metal,1984 +109,Mt,Meitnerium,277.154,EB0026,[Rn]7s2 5f14 6d7 (calculated),,,,,"9, 8, 6, 4, 3, 1",Solid,,,,Transition metal,1982 +110,Ds,Darmstadtium,282.166,,[Rn]7s2 5f14 6d8 (predicted),,,,,"8, 6, 4, 2, 0",Expected to be a Solid,,,,Transition metal,1994 +111,Rg,Roentgenium,282.169,,[Rn]7s2 5f14 6d9 (predicted),,,,,"5, 3, 1, -1",Expected to be a Solid,,,,Transition metal,1994 +112,Cn,Copernicium,286.179,,[Rn]7s2 5f14 6d10 (predicted),,,,,"2, 1, 0",Expected to be a Solid,,,,Transition metal,1996 +113,Nh,Nihonium,286.182,,[Rn]5f14 6d10 7s2 7p1 (predicted),,,,,,Expected to be a Solid,,,,Post-transition metal,2004 +114,Fl,Flerovium,290.192,,[Rn]7s2 7p2 5f14 6d10 (predicted),,,,,"6, 4,2, 1, 0",Expected to be a Solid,,,,Post-transition metal,1998 +115,Mc,Moscovium,290.196,,[Rn]7s2 7p3 5f14 6d10 (predicted),,,,,"3, 1",Expected to be a Solid,,,,Post-transition metal,2003 +116,Lv,Livermorium,293.205,,[Rn]7s2 7p4 5f14 6d10 (predicted),,,,,"+4, +2, -2",Expected to be a Solid,,,,Post-transition metal,2000 +117,Ts,Tennessine,294.211,,[Rn]7s2 7p5 5f14 6d10 (predicted),,,,,"+5, +3, +1, -1",Expected to be a Solid,,,,Halogen,2010 +118,Og,Oganesson,295.216,,[Rn]7s2 7p6 5f14 6d10 (predicted),,,,,"+6, +4, +2, +1, 0, -1",Expected to be a Gas,,,,Noble gas,2006 diff --git a/lessons/common_libs/python-in-chem_pyplot_basics_SM.ipynb b/lessons/common_libs/python-in-chem_pyplot_basics_SM.ipynb new file mode 100644 index 0000000..84c169b --- /dev/null +++ b/lessons/common_libs/python-in-chem_pyplot_basics_SM.ipynb @@ -0,0 +1,466 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8ea296df-22a6-4136-a2fc-901fb1c81f68", + "metadata": { + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "# Matplotlib Pyplot Basics\n", + "\n", + "
\n", + "\n", + "## Pre-requesites\n", + "\n", + "- Variables\n", + "- Lists\n", + "- ...\n", + "- ...\n", + "- ...\n", + "- ...\n", + "\n", + "
\n", + "\n", + "## Learning Outcomes\n", + "\n", + "- Plot scatter and line plots of lists of data\n", + "- Include axis labels\n", + "- Modify some of the appearances of the plots \n", + "\n", + "One of the important applications of Python is to create high quality visualisations such as graphs. There are multiple libraries that can be used for producing plots, but one of the most widely used in chemistry is `matplotlib`.\n", + "\n", + "\n", + "The library `matplotlib` has multiple modules. The module of interest here is `pyplot` and its primary function is generating data plots. This module has a widely used alias, `plt`, and can be imported using the following line of code.\n", + "\n", + "`import matplotlib.pyplot as plt`\n", + "\n", + "### Line plots\n", + "\n", + "One of the most important commands within the `pyplot` module is `plt.plot(x,y)`. This command instructs Python to produce a line plot of data contained in the list with variable name `x` on the horizontal axis and the data contained in the list with variable name `y` on the vertical axis." + ] + }, + { + "cell_type": "markdown", + "id": "b22a7fac-c022-4a7f-93d1-9995bb2b488e", + "metadata": { + "slideshow": { + "slide_type": "" + }, + "tags": [ + "instructions" + ] + }, + "source": [ + "### Activity 1 Plotting a line graph\n", + "\n", + "\n", + "The code below implements `pyplot` to plot a line graph of the following atomic radii data.\n", + "\n", + "**Table 1** Atomic radii of second row p-block elements\n", + "\n", + "|Element |\tGroup\t| Atomic radii / pm |\n", + "|---|---|---|\n", + "|$\\text{B}$|\t13\t|$88$|\n", + "|$\\text{C}$|\t14\t|$77$|\n", + "|$\\text{N}$|\t15\t|$74$|\n", + "|$\\text{O}$|\t16\t|$73$|\n", + "|$\\text{F}$|\t17\t|$71$|\n", + "|$\\text{Ne}$|\t18\t|$71$|\n", + "\n", + "(a)\tEnter the data contained in the list for the `radii` variable.\n", + "\n", + "(b)\tChange the strings contained in the `plt.xlabel(\"horizontal axis title\")` and `plt.ylabel(\"vertical axis title\")` to reflect the data that is being plotted\n", + "\n", + "Possible (c) ..... discuss the trend?????\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "33eee2a9-87e2-4392-b5ac-17b797f550d9", + "metadata": { + "slideshow": { + "slide_type": "" + }, + "tags": [ + "student" + ] + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "group = [13,14,15,16,17,18]\n", + "#radii = []\n", + "radii = [88,77,74,73,71,71]\n", + "\n", + "plt.plot(group,radii)\n", + "plt.xlabel(\"horizontal axis title\")\n", + "plt.ylabel(\"vertical axis title\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cfd6e7b2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "#Possible answer to Activity 1\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "group = [13,14,15,16,17,18]\n", + "radii = [88,77,74,73,71,71]\n", + "\n", + "plt.plot(group,radii)\n", + "plt.xlabel(\"Group\")\n", + "plt.ylabel(\"Atomic radii / pm\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "217704b2-aeb8-4db0-9a1d-f3e81947b2de", + "metadata": { + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The commands `plt.xlabel(\" \")` and `plt.ylabel(\" \")` are used to include labels for the horizontal and vertical axes, respectively.\n", + "\n", + "The axis labels are strings inside the brackets, between speech marks in the same way strings are used in the `print()` function.\n", + "\n", + "The command `plt.show()` instructs pyplot to display the graph. \n", + "(SM: this seems overly simplistic but i'm not completely sure the difference between interactive and non-interactive mode. \n", + "\n", + "
\n", + "\n", + "### Scatter plots\n", + "\n", + "Another option instead if a line plot is to use a scatter plot. For this option in `pyplot` use the command:\n", + "`plt.scatter(x,y)`\n", + "where `x` is the variable name of the list to be plotted on the x-axis, and `y` the variable name of the list to be plotted on the y-axis.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "a641c831-ce97-4bd8-9c8b-1169eb369b71", + "metadata": { + "slideshow": { + "slide_type": "" + }, + "tags": [ + "instructions" + ] + }, + "source": [ + "### Activity 2 Plotting a scatter graph\n", + "\n", + "\n", + "Table 2 contains the atomic radii of the third row p-block elements. hydrides. In the cell below complete the code to generate a sactter plot of this data, with group on the horizontal axis and atomic radii on the vertical axis.\n", + "\n", + "(*Hint:* it is not necessary to write the code from scratch. It is perfectly acceptable to copy the code from activity 1 and modify it to include the desired data.)\n", + "\n", + "
\n", + "\n", + "**Table 2** Atomic radii of third row p-block elements\n", + "\n", + "|Element |\tGroup\t| Atomic radii / pm |\n", + "|---|---|---|\n", + "|$\\text{Al}$|\t13\t|$125$|\n", + "|$\\text{Si}$|\t14\t|$118$|\n", + "|$\\text{P}$|\t15\t|$110$|\n", + "|$\\text{S}$|\t16\t|$104$|\n", + "|$\\text{Cl}$|\t17\t|$99$|\n", + "|$\\text{Ar}$|\t18\t|$98$|\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89b96bae-bc5a-43f3-a966-9385d2ee8fbc", + "metadata": { + "slideshow": { + "slide_type": "" + }, + "tags": [ + "student" + ] + }, + "outputs": [], + "source": [ + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "group = \n", + "radii = \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "abe65330", + "metadata": { + "slideshow": { + "slide_type": "" + }, + "tags": [ + "solution" + ] + }, + "outputs": [], + "source": [ + "#Possible answer to Activity 2\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "group = [13,14,15,16,17,18]\n", + "radii = [125,118,110,104,99,98]\n", + "\n", + "plt.scatter(group,radii)\n", + "plt.xlabel(\"Group\")\n", + "plt.ylabel(\"Atomic radii / pm\")\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "a55c01ba-3804-4026-b278-faf4fdad8b78", + "metadata": {}, + "source": [ + "### Plotting multiple datasets\n", + "\n", + "To plot multiple sets of data on the same graph you include more than one of the\n", + "`plt.plot()` and/or the `plt.scatter()` commands.\n", + "\n", + "As multiple sets of data are to be plotted a legend should be included to indicate what each series represents. In the same `plt.plot()`/`plt.scatter()` code, after specifying the data to be plotted on the horizontal and vertical axes within the brackets, a comma should be added and then the option `label=(\"\")` used. A string can then be included between the speech marks to label the series. For example, to plot a scatter graph of the lists `x` and `y` with the series name `Series A` you would use the following command:\n", + "`plt.scatter(x,y, label=\"Series A\")`\n", + "\n", + "By default, the legend option is not turned on in `matplotlib`. To show the legends you need to include the line `plt.legend()`.\n" + ] + }, + { + "cell_type": "markdown", + "id": "221e04f1", + "metadata": {}, + "source": [ + "### Activity 3 Plotting multiple datasets and including legends\n", + "\n", + "Complete the code in the cell below to generate a plot that contains the atomic radii of both the second and third row elements of the p-block. Remember to include a legend." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67345c3a", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "group = []\n", + "radii_2nd_period = []\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b45182f", + "metadata": {}, + "outputs": [], + "source": [ + "#Possible answer to Activity 3\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "group = [13,14,15,16,17,18]\n", + "radii_2nd_period = [88,77,74,73,71,71]\n", + "radii_3rd_period = [125,118,110,104,99,98]\n", + "\n", + "plt.scatter(group,radii_2nd_period, label=\"2nd Period\")\n", + "plt.scatter(group,radii_3rd_period, label=\"3rd Period\")\n", + "plt.xlabel(\"Group\")\n", + "plt.ylabel(\"Atomic radii / pm\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "abef10e9", + "metadata": {}, + "source": [ + "## Further customisation options\n", + "\n", + "### Changing the colour on graphs\n", + "\n", + "There are many options within `matplotlib` to modify the appearance of a graph. For example, you may want to change the colour of lines or data points in your graph. This may not seem like an important aspect of coding, but this can be useful if you are creating multiple charts for reports or presentations, and you want consistency between them. \n", + "\n", + "`matplotlib` chooses default colours for plotting multiple datasets: the first `plt.plot()`/`plt.scatter()` routine will use the colour blue, the second will use orange, and so on.\n", + "\n", + "Changing the colour of a line or scatter plot in Python requires a small addition to the `plt.plot()` or `plt.scatter()` routines. This command is `color=\"\"`, where this is preceeded by a comma (similar to the syntax of `label=\"\"` command in the same routines). \n", + "\n", + "Note that the syntax in `matplotlib` uses the American spelling for ‘color’, instead of the British spelling.\n", + "\n", + "For example, if you wanted to change the colour of the scatter plots from blue to red in the completed code from activity 3 the entire line would appear\n", + "\n", + " plt.scatter(group,radii_2nd_period, label=\"2nd Period\", color=\"red\")\n", + "\n", + "This will however change the next plotted series to the default blue, so to fully customise you would need to state the colours for each series.\n", + "\n", + "Most of the common colours you could name are included in `matplotlib`. For a full list of the available colours you can check the [Matplotlib documentation]." + ] + }, + { + "cell_type": "markdown", + "id": "d4c09ef7", + "metadata": {}, + "source": [ + "### Gridlines\n", + "\n", + "There are instances where you may want to include gridlines on your plots. To include major grdilines you would use the command:\n", + "\n", + " plt.grid()\n", + "\n", + "If you wanted to include major gridlines and minor gridlines you would use the two following commands:\n", + "\n", + " plt.grid(which='both')\n", + " plt.minorticks_on()\n", + "\n", + "Where the second command turns on the minor ticks on both axis, and the 'both' in the plt.grid() command indicates both the minor and major gridlines. Further options can be found in the Matplotlib documentation (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.grid.html)" + ] + }, + { + "cell_type": "markdown", + "id": "630154f7", + "metadata": {}, + "source": [ + "### Exporting plots as image files\n", + "\n", + "Once you have successfully created graphs with Python, you need to be able to save them for later use or to include them in other resources.\n", + "\n", + "`matplotlib` has a command for this:\n", + "\n", + " plt.savefig(\"file_name.png\")\n", + "\n", + "Here, `\"file_name.png\"` is the name you choose for the saved graph. It will be saved in a `.png` format.\n", + "\n", + "It is also possible to save a graph as a PDF file by changing the `.png` extension in the previous routine to `.pdf`.\n", + "\n", + "Note the `plt.savefig()` command must go *before* the `plt.show()` command. If you do not do this, then the saved image file will be blank. \n", + "\n", + "\n", + "\n", + "$$$$ SM - This is how it works in jupyterlab, not sure how it will work elsewhere $$$$" + ] + }, + { + "cell_type": "markdown", + "id": "d6857f12", + "metadata": {}, + "source": [ + "### Superscripts and subscripts on plots\n", + "\n", + "When displaying strings on plots generated using `matplotlib`, such as axis labels and legends, you may noticed that you have not been able to add formatting, this is because strings are displayed in plain text. This missing formatting is important when labelling scientific data, particularly for units or chemical formulas.\n", + "\n", + "As an example, consider the line of code to label the y axis with the concentration of some reaction species, [A], with the incorrect formatting for the units of concentration. \n", + "\n", + " plt.ylabel(\"[A]/mol dm-3\")\n", + " \n", + "To correctly display the ‘$-3$’ in superscript you can use the `pyplot` command:\n", + "\n", + " plt.ylabel(\"[A]/mol dm$^{-3}$\")\n", + " \n", + "In this command:\n", + "\n", + "* the first and second dollar signs, `$`, indicate the start and end of and the text you want to be formatted (the dollar signs will not be printed in the string)\n", + "* the curly braces, `{}`, contain the text you want to apply formatting to\n", + "* the caret, `^`, goes before the curly braces and is used to indicate the text within the braces should be a superscript.\n", + "\n", + "\n", + "Another common formatting option you will need is subscript. This is especially important when it comes to chemical formulas. Subscripts use a similar syntax to superscripts, but use an underscore `_` instead of the caret `^`.\n", + "\n", + "So if in the above example A was nitrogen dioxide you would use the following command:\n", + " \n", + " plt.ylabel(\"[NO$_{2}$]/mol dm$^{-3}$\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "0f09f12d", + "metadata": {}, + "source": [ + "## Todo\n", + "\n", + "Add more activities related to the options above" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c96f413a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/files.md b/lessons/files.md new file mode 100644 index 0000000..78632af --- /dev/null +++ b/lessons/files.md @@ -0,0 +1,3 @@ +# Using Files + +This section will teach you how to read and write files using Python. diff --git a/lessons/files/elements.csv b/lessons/files/elements.csv new file mode 100644 index 0000000..7eafedb --- /dev/null +++ b/lessons/files/elements.csv @@ -0,0 +1,4 @@ +Name,Symbol,Number +Hydrogen,H,1 +Helium,He,2 +Lithium,Li,3 diff --git a/lessons/files/gas_const.txt b/lessons/files/gas_const.txt new file mode 100644 index 0000000..7afce3e --- /dev/null +++ b/lessons/files/gas_const.txt @@ -0,0 +1 @@ +The gas constant is: 8.314 J/K.mol \ No newline at end of file diff --git a/lessons/files/molecule.txt b/lessons/files/molecule.txt new file mode 100644 index 0000000..0e18691 --- /dev/null +++ b/lessons/files/molecule.txt @@ -0,0 +1 @@ +C2H6 diff --git a/lessons/files/reading_files.ipynb b/lessons/files/reading_files.ipynb new file mode 100644 index 0000000..7da41c0 --- /dev/null +++ b/lessons/files/reading_files.ipynb @@ -0,0 +1,291 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1767a58b", + "metadata": {}, + "source": [ + "# **Reading Files**\n", + "\n", + "## Prerequisites:\n", + "- Variables\n", + "- Iterables(?)\n", + "- Loops\n", + "\n", + "## Learning Outcomes:\n", + "- Open files using Python's built-in functions and extract their contents to variables\n", + "- Use the CSV module to read data from CSV files" + ] + }, + { + "cell_type": "markdown", + "id": "f4882898", + "metadata": {}, + "source": [ + "## Reading Files\n", + "\n", + "One of the common uses of Python in chemistry is to analyse large amounts of data. \n", + "This might be data gathered during an experiment that has been stored in a number of files, and Python has a number of built-in functions to read (and write) files. \n", + "In this section, we will explore how to read different types of files, including text files and CSV files, using Python's built-in capabilities.\n", + "\n", + "Let's start with a opening a simple text file and reading its contents:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ff6944a", + "metadata": {}, + "outputs": [], + "source": [ + "file = open('molecule.txt', 'r')\n", + "contents = file.read()\n", + "file.close()\n", + "print(contents)" + ] + }, + { + "cell_type": "markdown", + "id": "6d821f38", + "metadata": {}, + "source": [ + "After running the cell above, you should see the contents of the `molecule.txt` file in the cell output. \n", + "If you don't see the output, make sure that the file is in the same directory as this notebook. \n", + "You can also verify the output by checking the file's contents in a text editor.\n", + "\n", + "The first line of the code cell above opens the file `molecule.txt` using the `open()` function and saves it to a special file-reading Python *object* we have called `file`.\n", + "The `open()` function takes at least one argument which is either the file name (if in the same working directory) or the full filepath of the file.\n", + "It can also take a second argument to specify the mode in which the file is opened (e.g., `'r'` for reading, `'w'` for writing, etc.).\n", + "If you don't specify a mode, the file is opened in read mode by default.\n", + "\n", + "The second line of the code cell reads the entire contents of the file using the `read()` method of the file object and stores it in a variable called `contents`. \n", + "\n", + "The third line closes the file using the `close()` method and is considered good practice.\n", + "Otherwise we might leave it open, which can lead to various issues (e.g., file access errors).\n", + "\n", + "Finally, the last line prints the contents of the `contents` variable." + ] + }, + { + "cell_type": "markdown", + "id": "900f642e", + "metadata": {}, + "source": [ + "### Reading Files with `with`\n", + "We can also use the `with` statement to open files, which will automatically close the file for us when we are done with it.\n", + "This is a more \"Pythonic\" way to handle files and is generally recommended.\n", + "\n", + "Let's take a look at the same example using the `with` statement:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f63f3d19", + "metadata": {}, + "outputs": [], + "source": [ + "with open('molecule.txt', 'r') as file:\n", + " contents = file.read()\n", + "\n", + "print(contents)" + ] + }, + { + "cell_type": "markdown", + "id": "06bbb57c", + "metadata": {}, + "source": [ + "As before, we open the `molecule.txt` file and read its contents.\n", + "The difference is that we use the `with` statement to open the file, which automatically closes it when we are done with it (i.e., when we exit the `with` block).\n", + "\n", + "We now have a way to read files in Python, and use their contents as *variables* in our code." + ] + }, + { + "cell_type": "markdown", + "id": "8ec1d24a", + "metadata": {}, + "source": [ + "## Reading CSV Files\n", + "CSV (Comma Separated Values) files are a common format for storing tabular data, such as data from experiments or simulations.\n", + "Each line in a CSV file represents a row of data, and each value in the row is separated by a comma (you can easily verify this by opening up a CSV file in a text editor).\n", + "Python has a built-in module called `csv` that makes it easy to read (and write) CSV files.\n", + "\n", + "Let's take a look at how to read a CSV file using the `csv` module:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ca51d4d", + "metadata": {}, + "outputs": [], + "source": [ + "import csv\n", + "\n", + "with open('elements.csv') as file:\n", + " csv_reader = csv.reader(file)\n", + " for row in csv_reader:\n", + " print(row)" + ] + }, + { + "cell_type": "markdown", + "id": "7ae13696", + "metadata": {}, + "source": [ + "Here, we first import the built-in `csv` module to allow us to easily parse CSV files.\n", + "\n", + "Next we open the `elements.csv` file using the `with` statement as we have seen before.\n", + "Note that we are opening the file in read mode without needing to specify it explicitly.\n", + "\n", + "The `csv.reader()` function takes the file object as an argument and returns a CSV reader object that can be used to *iterate* over the rows in the CSV file.\n", + "\n", + "Finally, we use a `for` loop to iterate over the rows in the CSV file and print the contents of each row.\n", + "The csv_reader object allows us to access each row as a list of values, making it easy to work with the data." + ] + }, + { + "cell_type": "markdown", + "id": "760dcb9a", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "### Manipulate data\n", + "Use f-strings to print the contents of the `elements.csv` file in a more readable format.\n", + "Don't forget about the header row!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53a6fb7d", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "633a2836", + "metadata": {}, + "source": [ + "Example answer (skipping the header entirely):\n", + "```python\n", + "import csv\n", + "\n", + "with open('elements.csv') as csvfile:\n", + " csv_reader = csv.reader(csvfile)\n", + " next(csv_reader) # Skip the header row\n", + " for row in csv_reader:\n", + " print(f\"Name: {row[0]}, Symbol: {row[1]}, Atomic Number: {row[2]}\")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "c67c1875", + "metadata": {}, + "source": [ + "### Using the file path\n", + "Try to open a file that is not in the same directory as this notebook and print its contents." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de2abab4", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "3430cc73", + "metadata": {}, + "source": [ + "TODO: Example answer" + ] + }, + { + "cell_type": "markdown", + "id": "10c5379d", + "metadata": {}, + "source": [ + "### Loop through multiple files\n", + "TODO: Task involving looping through multiple files with a predictable filename (e.g. `001.csv`) and reading their contents." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "002dbb28", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "c1114d99", + "metadata": {}, + "source": [ + "TODO: Example answer" + ] + }, + { + "cell_type": "markdown", + "id": "619f5799", + "metadata": {}, + "source": [ + "## Debugging\n", + "The code below contains a bug and will not run.\n", + "See if you can fix it by reading the error message and using the information it provides." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "818250af", + "metadata": {}, + "outputs": [], + "source": [ + "with open('molecule.csv', 'r') as file:\n", + " text = file.read()\n", + "\n", + "print(text)" + ] + }, + { + "cell_type": "markdown", + "id": "f58d91db", + "metadata": {}, + "source": [ + "## TODO\n", + "- Discuss carriage returns and other special characters?\n", + "- Explain the distinction between text and binary files?" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/files/spectrum.dat b/lessons/files/spectrum.dat new file mode 100644 index 0000000..e0c2c25 --- /dev/null +++ b/lessons/files/spectrum.dat @@ -0,0 +1,9 @@ +nm abs +240 0.123 +250 0.132 +260 0.346 +270 0.563 +280 0.998 +290 0.377 +300 0.007 +310 0.002 diff --git a/lessons/files/writing_files.ipynb b/lessons/files/writing_files.ipynb new file mode 100644 index 0000000..1ce922f --- /dev/null +++ b/lessons/files/writing_files.ipynb @@ -0,0 +1,257 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "R-GtgLtZw1hH" + }, + "source": [ + "# **Writing files**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yjL3LTCFJXwl" + }, + "source": [ + "## **Learning Outcomes**\n", + "\n", + "The learning outcomes are as follows:\n", + "\n", + "1. Saving data into a newly created file\n", + "2. Add additional data into an existing file." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hTDYb2QRyNeb" + }, + "source": [ + "## **Pre-requisites**\n", + "\n", + "Variables
\n", + "FOR loops
\n", + "Writing f-strings
\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xUUYv_pr1Sbt" + }, + "source": [ + "## **Writing files**\n", + "\n", + "It is often necesary to be able to save chemical data, images etc. that have been generated as a result of data processing etc., which is known as 'writing'. You can choose either to write your data to:\n", + "(i) a new file, or\n", + "(ii) alternatively add (aka append) to an existing file.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BVX99iEZ3Rc2" + }, + "source": [ + "## **Writing data to a new file**\n", + "\n", + "In Python, in order to write data to a new file, you must first create a file, ensuring the the file is writable.\n", + "\n", + "In order to create a file, you can use the command 'open'. Following this (in ( ) brackets) you need to supply the name of the file that you want to create, followed by a comma and then indicate that you wish to *write* to the new file (as opposed to e.g. read) by using the tag 'w'.\n", + "\n", + "A simple example of this is given below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JRvmRpz13Q0o" + }, + "outputs": [], + "source": [ + "file = open('gas_const.txt', 'w')\n", + "\n", + "R = 8.314\n", + "file.write(\"The gas constant is: \" + str(R) + \" J/K.mol\")\n", + "\n", + "file.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KI8esPi893AW" + }, + "source": [ + "We have defined and assigned a value to the variable, R, that is to be written to the file. R will be written to the file as a string. We have constructed the string \"The gas constant is: 8.314 J/K.mol\" which is then written to the file using the file.write command. Lastly the file is closed.\n", + "\n", + "Google Colab: This simple file has been saved in a temporary storage location. You can have a look at the file that's just been written by selecting the File icon on the left-hand side of the Colab page. Within the 'Content' file, you should be able to see the file. Click on the three vertical dots on the right-hand side of the file, download and open.\n", + "**NOTICE FOR EDITORS: we appreciate that we've written this for Google Colab - so we will need to re-write this section**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JQCkCjKt9UI7" + }, + "source": [ + "The way in which data is written to a file depends on the type of data itself. So... FINISH" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uYsrtUqF_k_s" + }, + "source": [ + "## **Writing a simple structured file**\n", + "\n", + "XXXX\n", + "\n", + "In the example below, the ... FINISH" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VPqkYrd1zla5" + }, + "outputs": [], + "source": [ + "file = open('spectrum.dat', 'w')\n", + "\n", + "wavelength = [240, 250, 260, 270, 280, 290]\n", + "absorbance = [0.123, 0.132, 0.346, 0.563, 0.998, 0.377, 0.021]\n", + "file.write(f\"nm \\t abs \\n\")\n", + "for i in range(len(wavelength)):\n", + " file.write(f\"{wavelength[i]} \\t {absorbance[i]}\\n\")\n", + "\n", + "file.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YBO7aNknGp7b" + }, + "source": [ + "In the above example, the 'f' in the file.write commands... FINISH" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RscVg7DyHKVW" + }, + "source": [ + "## **Appending to an existing file**\n", + "\n", + "You can also write data to an existsing file. In the example below, we will append some more data to the spectrum data created above.\n", + "\n", + "... FINISH" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xpAqJWu2_ss2" + }, + "outputs": [], + "source": [ + "file = open('spectrum.dat', 'a')\n", + "\n", + "wavelength = [300, 310]\n", + "absorbance = [0.007, 0.002]\n", + "\n", + "for i in range(len(wavelength)):\n", + " file.write(f\"{wavelength[i]} \\t {absorbance[i]}\\n\")\n", + "\n", + "file.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Kp8OJXO1_oMr" + }, + "source": [ + "## **User-defined save paths**\n", + "\n", + "Although a library is required for a file dialogue pop-up, ... FINISH ALSO NEED TO CREATE CODE FOR THIS." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aVs6rJBuJLiw" + }, + "source": [ + "## **Exercises**\n", + "\n", + "**Exercise 1**
\n", + "Take the day of the month in which you were born (e.g. this would be 10 is you were born on the 10th April), and create a file named 'element.txt' and write the name of the element corresponding to the atomic number matching this.

\n", + "\n", + "**Exercise 2**
\n", + "Determine the group that the element identified in Exercise 1 below to and append this information to your file.

\n", + "\n", + "**Exercise 3:**
\n", + "Three moles of an ideal gas are ontained within a frictionless piston at 298.15 K. Use Python to calculate the volume of the gas at the following four different pressures:
\n", + "1.00 kPa
\n", + "10.00 kPa
\n", + "50.00 kPa
\n", + "100.00 kPa
\n", + "and output the results in a file, formatted as two columns of numbers (to two d.p.), with the first column being pressure and the second being volume." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tOX5HbrsJR02" + }, + "source": [ + "## **Learning Outcomes**\n", + "\n", + "FINISH" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyOe2vlSb52narfb6tegF/97", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/lessons/loops_functions/For_Loop_Lesson.ipynb b/lessons/loops_functions/For_Loop_Lesson.ipynb new file mode 100644 index 0000000..0306d38 --- /dev/null +++ b/lessons/loops_functions/For_Loop_Lesson.ipynb @@ -0,0 +1,387 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "UuDCTZ7LX9Jb" + }, + "source": [ + "# The `for` loop\n", + "\n", + "## Learning Outcomes\n", + "- TODO\n", + "- \n", + "\n", + "## Prerequisites:\n", + "- Variables\n", + "- Comparison\n", + "- `if` statements" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2VRPDborAVn1" + }, + "source": [ + "

The `for` loop

\n", + "\n", + "

One area where you will see a real benefit of using programming in your work is when you need to repeat a task over and over again. For example maybe you need to process the same type of data file many times with different values. This can take a lot of time one by one but Python can really come to your rescue and save you hours. Plus all your lab colleagues will be beating a path to your door to ask for your help. You will soon become the lab data processing guru!

\n", + "\n", + "\n", + "

One of the easiest way to perform this task is to use a 'for' loop.

\n", + "\n", + "

Here is an example:

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VSiGQd0PKsxD" + }, + "outputs": [], + "source": [ + "molecules = [\"H2\", \"H2O\", \"CH4\"]\n", + "\n", + "for mol in molecules:\n", + " print(mol)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hwu0INZoK5FO" + }, + "source": [ + "The cell above results in three strings printed despite having only used a single `print()` statement. Let's look at this closer.\n", + "\n", + "We begin by defining a list of strings:\n", + "```python\n", + "molecules = [\"H2\", \"H2O\", \"CH4\"]\n", + "```\n", + "\n", + "We then start a loop with the line:\n", + "```python\n", + "for mol in molecules:\n", + "```\n", + "- `for` indicates that we will begin a loop.\n", + "- `mol` is a new variable that we are currently defining.\n", + "- `in molecules` indicates that we want to do repeat the operations in the loop once per element in the list `molecules`.\n", + "- `:` signals the beginning of the loop.\n", + "\n", + "After this line of code, `print(mol)` prints the contents of the `mol` variable to the screen.\n", + "\n", + "Notice how `print(mol)` has some leading spaces (typed with the `` key). We say that this piece of code is _indented_, meaning that it has one `` space separating it from the left side of the screen.\n", + "\n", + "When we write indented code below a line starting with `for`, all of that code gets repeated within the `for` loop. Conveniently, while we are within this loop, the variable `mol` will adopt the values of each element of `molecules` in sequence, changing at every loop.\n", + "\n", + "This is why `print(mol)` is effectively becoming `print(\"H2\")`, `print(\"H2O\")`, and `print(\"CH4\")` one after another.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Task 1: Understanding a Loop \n", + "\n", + " Take a look at this code block and answer the questions\n", + "\n", + ">``` Python\n", + ">\n", + ">gas_list = ['Nitrogen', 'Oxygen', 'Fluorine']\n", + ">for gas in gas_list:\n", + "> print(gas)\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "MULTIPLE CHOICE QUESTION\n", + "\n", + "- The loop will print the name of each gas on a separate line (correct)\n", + "- the loop will print one line with all the gas names\n", + "- The variable gas takes on each value of gas list in turn (correct)\n", + "- the variable gas_list changes each time around the loop" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Task 2: Understanding a Loop \n", + "\n", + "In the lab you are making methyl benzoate according using the scheme below. Five scientists repeat the reaction starting with 3 g of benzoic acid and get of 2.5 g, 2.7 g, 3.1 g, 1.6 g and 4 g of product.\n", + "\n", + "\"The\n", + "\n", + "Lets use your understanding of for loops to quickly work out the yields for each person.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product_mass_list = [2.5,2.7,3.1,1.6,4]\n", + "starting_mass = 3 # grams\n", + "theoretical_yield = (_______/_______)*136\n", + "for product_mass in product_mass_list:\n", + " percent_yield = (_________ / ___________) * 100\n", + " # the print statement uses the f-string syntax to format the output of the calculation\n", + " print(f\"Percent yield: {percent_yield:.0f}%\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Click here to see a possible solution to Task 2 \n", + "\n", + ">```Python\n", + ">product_mass_list = [2.5,2.7,3.1,1.6,4]\n", + ">starting_mass = 3 # grams\n", + ">theoretical_yield = (starting_mass/122)*136\n", + ">for product_mass in product_mass_list:\n", + "> percent_yield = (product_mass / theoretical_yield) * 100\n", + "> # the print statement uses the f-string syntax to format the output of the calculation\n", + "> print(f\"Percent yield: {percent_yield:.0f}%\")\n", + ">```\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Task 3: Understanding a Loop \n", + "\n", + "You have a series of temperature measurements from your reaction in degrees C and you want to print them out in Kelvin. One of your colleagues writes some code but it doesn't seem to be working. Can you identify the problem and fix the code?\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Your colleagues code\n", + "\n", + "# The code below is not working. Can you fix it?\n", + "temperature_measurements = [27.3, 28.1, 26.9, 27.5, 28.0]\n", + "for temperature_C in temperature_measurements:\n", + " temperature_K = temperature_C + 273.15\n", + "print(f\"Temperature: {temperature_K}°C\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Click here to see a possible solution to Task 3 \n", + "\n", + "Remember that the items that you want to do each time you go around the for loop need to be indented. In the example the print statement is not indented and so is not executed each time, but only one the loop is finished. You can fix the code just by indenting the print statement with the tab key.\n", + ">```Python\n", + ">temperature_measurements = [27.3, 28.1, 26.9, 27.5, 28.0]\n", + ">for temperature_C in temperature_measurements:\n", + "> temperature_K = temperature_C + 273.15\n", + "> print(f\"Temperature: {temperature_K}°C\")\n", + ">```\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When we are talking about for loops more generally we use the following terms:\n", + "- **Discrete variable**: a variable changes its value at every iteration of the loop (e.g. `temperature_C` in task 3).\n", + "- **Iterator**: a variable containing multiple values, which can be used in a `for` loop (e.g. `temperature_measurements` in the last example). Lists are the most common kind of operator in Python.\n", + "\n", + "The general syntax of a `for` loop is:\n", + "```python\n", + "for discrete_variable in iterator:\n", + " # some\n", + " # indented\n", + " # code\n", + "```\n", + "\n", + "You can write Python code of any length inside a `for` loop as long as you respect its indentation. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Task 4: Understanding a Loop \n", + "\n", + "Run the code below and answer the following questions to check your understanding\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "natural_amino_acids = [\"Alanine\", \"Arginine\", \"Asparagine\", \"Aspartic acid\", \"Cysteine\", \"Glutamic acid\", \"Glutamine\", \"Glycine\", \"Histidine\", \"Isoleucine\", \"Leucine\", \"Lysine\", \"Methionine\", \"Phenylalanine\", \"Proline\", \"Serine\", \"Threonine\", \"Tryptophan\", \"Tyrosine\", \"Valine\"]\n", + "for amino_acid in natural_amino_acids:\n", + " print(f\"Amino acid: {amino_acid}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "MULTIPLE CHOICE QUESTION\n", + "\n", + "- The discrete variable is amino acid (correct)\n", + "- The discrete variable is amino acids\n", + "- The iterator is amino_acid\n", + "- The iterator is amino_acids (correct)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Task 5: Using loops \n", + "\n", + "When chemists want to store molecular structures in a computer they can store in an encoded way called SMILES strings. You don't need to understand what these mean but just to know that libraries that can handle chemical structure can read them. The code below will import the RDKit package and print the structure of benzoic acid. The code works by passing the SMILES string for benzoic acid to the function display_molecule that then draws it out. Check out the functions lesson if you want to understand more about functions, but for this lesson all you just need to follow the syntax below.\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from rdkit import Chem\n", + "\n", + "def display_molecule (smiles_string):\n", + " mol = Chem.MolFromSmiles(smiles_string)\n", + " img=Chem.Draw.MolToImage(mol)\n", + " display(img)\n", + "\n", + "# display the structure of benzoic acid\n", + "benzoic_acid_miles = \"C1=CC=C(C=C1)C(=O)O\"\n", + "display_molecule(benzoic_acid_miles)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Task 5 continued \n", + "Now you know how to print one molecule lets use your understanding of for loops to print lots of molecules. Here are a list of the smiles strings of some interesting molecules, Dolutegravir a very important anti-hiv drug, Atovaquone which treats malaria and paclitaxol that is an important \n", + "\n", + "\n", + "
\n", + "
    \n", + "
  • C[C@@H]1CCO[C@@H]2N1C(=O)C3=C(C(=O)C(=CN3C2)C(=O)NCC4=C(C=C(C=C4)F)F)O\n", + "
  • C1CC(CCC1C2=CC=C(C=C2)Cl)C3=C(C4=CC=CC=C4C(=O)C3=O)O\n", + "
  • CC1=C2[C@H](C(=O)[C@@]3([C@H](C[C@@H]4[C@]([C@H]3[C@@H]([C@@](C2(C)C)(C[C@@H]1OC(=O)[C@@H]([C@H](C5=CC=CC=C5)NC(=O)C6=CC=CC=C6)O)O)OC(=O)C7=CC=CC=C7)(CO4)OC(=O)C)O)C)OC(=O)C\n", + "
\n", + " \n", + "
\n", + "\n", + "Using the concepts above write a for loop that will draw the structrue of all 3 molecules. You need to:\n", + "- define a list of the smiles strings\n", + "- loop through the list and display each molecule using the display_molecule function\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "molecule_smiles = ['C[C@@H]1CCO[C@@H]2N1C(=O)C3=C(C(=O)C(=CN3C2)C(=O)NCC4=C(C=C(C=C4)F)F)O',\n", + "'C1CC(CCC1C2=CC=C(C=C2)Cl)C3=C(C4=CC=CC=C4C(=O)C3=O)O',\n", + "'CC1=C2[C@H](C(=O)[C@@]3([C@H](C[C@@H]4[C@]([C@H]3[C@@H]([C@@](C2(C)C)(C[C@@H]1OC(=O)[C@@H]([C@H](C5=CC=CC=C5)NC(=O)C6=CC=CC=C6)O)O)OC(=O)C7=CC=CC=C7)(CO4)OC(=O)C)O)C)OC(=O)C']\n", + "\n", + "for smiles in molecule_smiles:\n", + " display_molecule(smiles)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Click here to see a possible solution to Task 5 \n", + "\n", + "\n", + "```Python\n", + "molecule_smiles = ['C[C@@H]1CCO[C@@H]2N1C(=O)C3=C(C(=O)C(=CN3C2)C(=O)NCC4=C(C=C(C=C4)F)F)O',\n", + "'C1CC(CCC1C2=CC=C(C=C2)Cl)C3=C(C4=CC=CC=C4C(=O)C3=O)O',\n", + "'CC1=C2[C@H](C(=O)[C@@]3([C@H](C[C@@H]4[C@]([C@H]3[C@@H]([C@@](C2(C)C)(C[C@@H]1OC(=O)[C@@H]([C@H]>>>(C5=CC=CC=C5)NC(=O)C6=CC=CC=C6)O)O)OC(=O)C7=CC=CC=C7)(CO4)OC(=O)C)O)C)OC(=O)C']\n", + "\n", + "for smiles in molecule_smiles:\n", + " display_molecule(smiles)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ejspk6fFPXFK" + }, + "source": [ + "## TODO\n", + "A second part of the lesson covering the following.\n", + "- Iterating with `range()`\n", + "- Using a loop to generate a list (and maybe list comprehension)\n", + "- Nesting loops\n", + "- Continue statements" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tpuihpop9duI" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/lessons/loops_functions/PyinC_while_loops_WIP.ipynb b/lessons/loops_functions/PyinC_while_loops_WIP.ipynb new file mode 100644 index 0000000..40512e6 --- /dev/null +++ b/lessons/loops_functions/PyinC_while_loops_WIP.ipynb @@ -0,0 +1,260 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The ```while``` Loop" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Learning Objectives\n", + "- Understand how to construct and use `while` loops\n", + "- Know how to control iterations in a loop\n", + "\n", + "## Prerequisites: \n", + "- boolean variables\n", + "- comparisons\n", + "- `if` statements\n", + "- `for` loops\n", + "\n", + "## The `while` Loop\n", + "\n", + "A `while` loop is similar to a [`for` loop](./For_Loop_Lesson.ipynb), with the main difference being it will continuosly loop while a condition is `True`. As as example see the cell below which prints integers from 1 to 5." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "count=0\n", + "while count<5:\n", + " count+=1\n", + " print(count)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first line defines our starting value and the second initiates our loop.\n", + "```python\n", + "while(count<5):\n", + "```\n", + "Looking closer at this line we have:\n", + "\n", + "- `while` which signals we want to start a loop\n", + "- `count<5` is our conditional statement\n", + "- `:` signals the start of the loop\n", + "\n", + "If our conditional statement is `True` the code prints out the current value of `count` and increases it by one for each iteration using the `+=` operator.\n", + "\n", + "In general `while` loops take the structure:\n", + "\n", + "```python\n", + "while condition:\n", + " code \n", + " block\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise\n", + "Create a `while` loop to print even integers from zero to 10." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Answer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "count = 0\n", + "while count < 11:\n", + " print(count)\n", + " count += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Infinite `while` loops\n", + "\n", + "When constructing `while` loops be wary of your condition, one common bug can be creating a loop that runs forever:\n", + "\n", + "```python\n", + "count = 5\n", + "while count != 0:\n", + " print(count)\n", + " count -= 2\n", + "```\n", + "\n", + "In this example, `count` is never exactly equal to 0: it counts down from 5, to 3, 1, -1, and continues down. As a result, the condition `count != 0` is never `False`, and the loop will run forever." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Break Clauses\n", + "\n", + "In order to avoid infinite loops, we might want to add a second condition under which the code should stop looping. We can do this using the keyword `break`.\n", + "\n", + "For example, the code below is the same as the code above, but with an additional `break` added, which will be executed once `count < 9`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "count = 5\n", + "while count != 0:\n", + " print(count)\n", + " count -= 2\n", + " if count < -9:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Password Example\n", + "\n", + "The code below pulls together everything we've seen in this lesson. It continuially asks the user for a password, while counting the number of attempts the user has made. If the user either enters the password correctly, or reaches the maximum permitted number of attempts, we use `break` to exit the loop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Password example\n", + "\n", + "MAX_ATTEMPTS = 3\n", + "\n", + "correct_password = \"secret123\"\n", + "attempts = 0\n", + "\n", + "while True:\n", + " password = input(\"Password: \").strip()\n", + " attempts += 1\n", + "\n", + " if password == correct_password:\n", + " print(\"Login successful! Welcome!\")\n", + " break\n", + "\n", + " if attempts >= MAX_ATTEMPTS:\n", + " print(\"Too many failed attempts.\")\n", + " break\n", + " else:\n", + " print(f\"Incorrect password. {MAX_ATTEMPTS - attempts} attempts left.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise\n", + "\n", + "The code above uses two `break` statements, and begins `while True:`. Rewrite the code so that only one `break` statement is used." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Answer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "MAX_ATTEMPTS = 3\n", + "\n", + "correct_password = \"secret123\"\n", + "attempts = 0\n", + "\n", + "while attempts < MAX_ATTEMPTS:\n", + " password = input(\"Password: \").strip()\n", + " attempts += 1\n", + "\n", + " if password == correct_password:\n", + " print(\"Login successful! Welcome!\")\n", + " break\n", + " else:\n", + " if attempts < MAX_ATTEMPTS:\n", + " print(f\"Incorrect password. {MAX_ATTEMPTS - attempts} attempts left.\")\n", + " else:\n", + " print(\"Too many failed attempts.\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TO DO\n", + "- Useful chemical applications/exercises?\n", + "- applications? -> program inputs, chemical?" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/lessons/loops_functions/StockCake-Overwhelmed with paperwork_1744290627.jpg b/lessons/loops_functions/StockCake-Overwhelmed with paperwork_1744290627.jpg new file mode 100644 index 0000000..8453085 Binary files /dev/null and b/lessons/loops_functions/StockCake-Overwhelmed with paperwork_1744290627.jpg differ diff --git a/lessons/loops_functions/functions_and_scope.ipynb b/lessons/loops_functions/functions_and_scope.ipynb new file mode 100644 index 0000000..ad79c3f --- /dev/null +++ b/lessons/loops_functions/functions_and_scope.ipynb @@ -0,0 +1,508 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "db5b621f-285a-4035-86f8-129e4382a8b5", + "metadata": {}, + "source": [ + "# Functions and Scope\n", + "\n", + "## Prerequisites\n", + "\n", + "- Variables and Data Types\n", + "- Mathematical Operators\n", + "- Conditional Execution\n", + "- Loops\n", + "- Lists, Dictionaries and Tuples\n", + "\n", + "## Learning Objectives\n", + "\n", + "- To learn how to re-run code which will be used often\n", + "- To understand the purpose of a function\n", + "- Understanding how function arguments work\n", + "- Understanding how functions return information\n", + " \n", + " \n" + ] + }, + { + "cell_type": "markdown", + "id": "1c23b1aa-bffb-4400-867c-042a065273f8", + "metadata": {}, + "source": [ + "## Functions\n", + "\n", + "When running Python scripts, we often use statements to change or read the values of variables. Take the following code, for instance, which looks to add up the values of numbers in a list. Here we can use it to generate the molecular mass of a molecule from a list of atomic masses. The script iterates through the list, and adds each element in it to a running total variable, which at the end of the function contains the total:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c60cd53-dab9-4810-9ec2-6bc1914af9a9", + "metadata": {}, + "outputs": [], + "source": [ + "m1 = [12,12,1,1,1,1]\n", + "\n", + "m1_total = 0\n", + "\n", + "for i in m1:\n", + " m1_total += i\n", + "\n", + "print(m1_total)\n" + ] + }, + { + "cell_type": "markdown", + "id": "6603b780-f776-4993-9f61-b02a303b5e8a", + "metadata": {}, + "source": [ + "This code works well to add up the numbers in the list, and if we're just doing this once, will serve perfectly fine. However, if you had another list you needed to add up this way, you would need to write this expression again. If you had 100 lists, you could be spending a lot of time writing just these few lines of code." + ] + }, + { + "cell_type": "markdown", + "id": "c4c048a9-fa8c-43d2-ae91-48939a2d02c2", + "metadata": {}, + "source": [ + "## Defining functions\n", + "\n", + "Functions offer a way for us to package up frequently-run code into an easily callable package, which we can call repeatedly. Let's now wrap the code used to add up the list elements in a function, called `add_up`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d03d38ba-1447-4f03-86b0-6c09c09297af", + "metadata": {}, + "outputs": [], + "source": [ + "def add_up(list_of_atoms):\n", + " total = 0\n", + " for atom in list_of_atoms:\n", + " total += atom\n", + " return total" + ] + }, + { + "cell_type": "markdown", + "id": "59c43866-bfd3-4e98-abe9-fa41d97cf5b1", + "metadata": {}, + "source": [ + "To turn our adding up into a function, we have added a line before and after. To start with, we have added the statement `def add_up(list_of_atoms):` to the start of our code. This tells Python that we are defining a function, and that it is to treat all indented lines following the colon as part of the function. The `list_of_atoms` contained within the brackets is the function's _argument_. Functions can take many arguments (or none) but this function just takes one: the list which we are asking it to add up. \n", + "\n", + "Following the iterative adding process, the function is asked to `return` the total. Whatever is on the return line of the function is what the function will give back once it has finished running. Occasionally functions will just end in `return`, but generally something should be put on this line so that the function can pass it back. \n", + "\n", + "Below, we use our new `add_up` function to perform the same operation on `l1` and store the result in variable `x`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a30ee2e-a69a-4495-b681-cb58d2437655", + "metadata": {}, + "outputs": [], + "source": [ + "x = add_up(m1)\n", + "\n", + "print(x)" + ] + }, + { + "cell_type": "markdown", + "id": "d417fb60-282e-4457-a911-c0f94b50afb4", + "metadata": {}, + "source": [ + "## Scope\n", + "\n", + "You may be asking why we need the `return total` statement in our code, as we should be able to reference just the variable `total` which is used as our running total during the function's loop. However, if we try that, we will get an error:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8a25e4c-cd67-4ae3-a784-a8f7b1078cd2", + "metadata": {}, + "outputs": [], + "source": [ + "x = add_up(m1)\n", + "\n", + "print(total)" + ] + }, + { + "cell_type": "markdown", + "id": "fc7bffb4-2db8-4ab2-9570-49a13975ec9b", + "metadata": {}, + "source": [ + "Here, Python is telling us that the variable named `total` is not defined. This highlights an important feature of functions in Python. Functions only return what is included on the return line, and any other variables which we define in them which are not returned are lost once the function has finished executing. However, this is not true the other way around. Functions *do* have access to variables which have not been given to them explicitly as arguments.\n", + "\n", + "In the code below we can initialise a variable `string` which contains the string `\"benzene\"`. We can then also initialise the function `read_string()` which takes no arguments, but prints the value of the variable `string`. If we call this function, we can see that the string `benzene` is printed to the console." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8cdf670-d3e8-4504-8240-4628fd65ec6a", + "metadata": {}, + "outputs": [], + "source": [ + "string = \"benzene\"\n", + "\n", + "def read_string():\n", + " print(string)\n", + " return\n", + "\n", + "read_string() # Note here that even though readfoo takes no arguments, we still need to supply some empty brackets after it when calling it.\n" + ] + }, + { + "cell_type": "markdown", + "id": "ba898076-1abc-45bc-9198-2d22fc0bbdb3", + "metadata": {}, + "source": [ + "This shows us that even though we did not explicitly pass the variable `string` to our function, it was able to find the value of the variable `string` and output it to the console anyway. This is because `string`, having been declared in our main script, is a *global* variable, in contrast to function variables, which are *local* variables. Remember that if you see an error like this in your code output, it is likely because you have referenced a value which only exists inside the function from outside of it. " + ] + }, + { + "cell_type": "markdown", + "id": "10374f42-0a0f-4457-b3b7-b0f8443d3fd9", + "metadata": {}, + "source": [ + "## Calling functions\n", + "\n", + "Now that we've written our `add_up` function, we can use it to add up lots of lists very quickly. Here, we can define a list of lists, then iterate through that to provide a list of their totals. To call the function, all we need to do is write the name of the function, and enclose the list on which we would like it to operate in the brackets:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f554c80-ff5a-4db0-b432-ef087b1d5252", + "metadata": {}, + "outputs": [], + "source": [ + "many_sets_of_atoms = [\n", + " [12,12,1,1,1,1],\n", + " [12,1,1,1,12,16,12,1,1,1],\n", + " [12,12,12,12,12,12]\n", + "]\n", + "\n", + "totals = []\n", + "\n", + "for atom_set in many_sets_of_atoms:\n", + " set_total = add_up(atom_set)\n", + " totals.append(set_total)\n", + "\n", + "\n", + "print(totals)" + ] + }, + { + "cell_type": "markdown", + "id": "8e0c9d02-ebf4-4467-83d5-d281a3cb6132", + "metadata": {}, + "source": [ + "In the example above, we are actually calling two functions every time our loop executes. The first is the `add_up` function which we have written, but the second is a the function we use to add the number to the list. We call the `list.append()` function, which adds its argument to the end of the list. Note here that the dot `.` between the `totals` and `append` is a signifier that this append function is specifically associated with the list stored in the `totals` variable. This type of function is quite common in Python, but is outside the scope of this lesson. For now, the important thing to note from this is that due to this association, we do not need to pass the `totals` list as an argument to the `append` function. \n", + "\n", + "Python has a huge amount of pre-set functions, many of which you may have already used. The `print()` and `type()` functions are ubiquitious in Python programming, but there are also many other functions which are pre-packaged in Python. In fact, the function that we have been looking at so far, `add_up()` replicates the behaviour of the inbuilt Python `sum()` function. " + ] + }, + { + "cell_type": "markdown", + "id": "cfd2f639-6eb3-4378-8c31-8f391d7ddbdf", + "metadata": {}, + "source": [ + "## More complex functions\n", + "\n", + "So far we have covered basic functions, but there are more complex facets to function writing. In this section, we will look at a few of these facets, although there are many more which will not be covered here.\n", + "\n", + "### Multiple return conditions\n", + "\n", + "Functions we have written so far have only had one return statment right at the end. This is not, however, a requirement. A function can have a return statement anywhere, and it can have more than one. This is usually encountered when functions contain if statements, where depending on whether the conditions are met, different sections of the function may execute. Take the below function, for example, which checks if a supplied letter is a vowel or not:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03ea1aea-92db-4b13-97cd-ef159f206cd3", + "metadata": {}, + "outputs": [], + "source": [ + "def is_vowel(letter):\n", + " if type(letter) is not str or len(letter) != 1:\n", + " print(\"Input must be a single character\")\n", + " return None\n", + " elif letter in [\"a\",\"e\",\"i\",\"o\",\"u\"]:\n", + " return True\n", + " else:\n", + " return False" + ] + }, + { + "cell_type": "markdown", + "id": "354eeb4c-e5bf-42ed-b877-9550d7f30730", + "metadata": {}, + "source": [ + "There are three different possible return values in the above function, depending on the input. The first conditional statement confirms that a single character has been passed to the function as a string. There are more sophisticated ways to handle this sort of error, but these are outside the scope of this lesson. The key thing here is that based on this test, we can have the function return `None` rather than true or false, if an incompatible input is supplied. Following this, the function uses if/else tests to check if the single character is a vowel or not. \n", + "\n", + "We can also return more than one thing on the return line. If we define a function which uses the inbuilt python `min()` and `max()` functions to return the largest and smallest items in a list, we can return both in one statement:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1fcf6271-7e69-43c3-bd14-de1e1aff0c7f", + "metadata": {}, + "outputs": [], + "source": [ + "def minmax(number_list):\n", + " min_value = min(number_list)\n", + " max_value = max(number_list)\n", + " return min_value, max_value" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "279c982c-39a6-4414-a5e9-487979d3eb8e", + "metadata": {}, + "outputs": [], + "source": [ + "x = minmax([1,2,3])\n", + "\n", + "print(x)\n", + "\n", + "y,z = minmax([1,2,3])\n", + "\n", + "print(y)\n", + "print(z)" + ] + }, + { + "cell_type": "markdown", + "id": "9db49603-743b-43ee-9351-3a3cf09a8008", + "metadata": {}, + "source": [ + "As you can see above, when we store the output of this function in a single variable, it is returned as a tuple containing both values. However, in Python we can also use the multiple assignment variable unpacking feature to assign the minimum and maximum values in a single statement. This sort of behaviour is useful when we want to return and assign multiple variables from a single function." + ] + }, + { + "cell_type": "markdown", + "id": "14e8bd90-4002-4b5e-92eb-8a6fe0eee1a1", + "metadata": {}, + "source": [ + "# Debugging\n", + "\n", + "The following code chunks have errors in them. See what output the chunk is currently providing, and fix the errors to give the expected output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47374f25-3d38-4b46-9442-acdff2dddd26", + "metadata": {}, + "outputs": [], + "source": [ + "def say_hello():\n", + " print(\"Hello World\")\n", + "\n", + "\n", + "say_hello\n", + "\n", + "#Expected output: Hello World" + ] + }, + { + "cell_type": "markdown", + "id": "4b8b293d-cc79-40e9-a83c-b8f75454aec2", + "metadata": {}, + "source": [ + "# Exercises\n", + "\n", + "1. Write a function that takes one argument, `num`, and returns `True` if it is even and `False` if it is odd. \n", + "\n", + "Remember that the modulo operator (%) returns the remainder of the left hand quantity when divided by the right hand quantity." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "950d5308-327c-41f4-9560-3d96e72996e2", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "abf7a379-edaf-4d04-857a-ba5ffccd01c4", + "metadata": {}, + "source": [ + "2. Using your function above, write a function which takes a list of integers, and returns only the even integers of this list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cbe33a5b-28c9-433f-a8ab-8bea29570d2b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "7a07fe93-0917-4b8b-ac26-cc6c13353b25", + "metadata": {}, + "source": [ + "3. The function `add_up()` defined earlier in the document accepted a list of atomic masses. However, molecules are more generally referred to using formulae rather than lists of masses. Write a series of functions as directed in the comments in the cell below to allow calculation of molecular masses from molecular formulae for simple organic molecules:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "991aba54-f8ab-4431-aba2-36fd3843401a", + "metadata": {}, + "outputs": [], + "source": [ + "# The dictionary below can be used to look up an atom's mass from its symbol. \n", + "# Don't worry about other elements for the time being, you can assume that these are the only elements that matter.\n", + "# (pretend you're an organic chemist)\n", + "\n", + "atom_masses = {\n", + " \"C\" : 12,\n", + " \"H\" : 1,\n", + " \"O\" : 16,\n", + " \"N\" : 14\n", + "}\n", + "\n", + "# Write a function to take a list of atomic symbols, and return a list of masses.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "203732e1-509b-436a-a2b3-fcde5b3d8ab6", + "metadata": {}, + "source": [ + "# Answers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e14aea88-2dd4-486a-8a11-ba69db8256c5", + "metadata": {}, + "outputs": [], + "source": [ + "# Exercise 1:\n", + "\n", + "def is_even(num):\n", + " if num%2 == 0:\n", + " return True\n", + " else:\n", + " return False\n", + "\n", + "print(is_even(9))\n", + "print(is_even(42))\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df8b0816-99f6-4f09-8636-96c14fa153e7", + "metadata": {}, + "outputs": [], + "source": [ + "# Exercise 2:\n", + "\n", + "def keep_evens(num_list):\n", + " evens = []\n", + " for num in num_list:\n", + " if is_even(num):\n", + " evens.append(num)\n", + "\n", + " return evens\n", + "\n", + "print(keep_evens([1,2,3,4,5,6,7,8,9,10]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22ba62ed-7af1-42f9-90dd-93cf68f828ca", + "metadata": {}, + "outputs": [], + "source": [ + "# Exercise 3:\n", + "\n", + "# The dictionary below can be used to look up an atom's mass from its symbol. \n", + "# Don't worry about other elements for the time being, you can assume that these are the only elements that matter.\n", + "# (pretend you're an organic chemist)\n", + "\n", + "atom_masses = {\n", + " \"C\" : 12,\n", + " \"H\" : 1,\n", + " \"O\" : 16,\n", + " \"N\" : 14\n", + "}\n", + "\n", + "# Write a function to take a list of atomic symbols, and return a list of masses.\n", + "\n", + "def get_masses(atoms):\n", + " mass_list = []\n", + " for atom in atoms:\n", + " mass_list.append(atom_masses[atom])\n", + " return mass_list\n", + "\n", + "print(get_masses([\"C\",\"H\",\"H\",\"H\",\"C\",\"H\",\"O\"]))\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "26af8885-0ec8-4421-8947-89f371cf350f", + "metadata": {}, + "source": [ + "## TODO:\n", + "\n", + "- More relation to chemistry in some of the later examples?\n", + "- Discussion of use of *args etc to supply multiple arguments to a function\n", + "- More debugging exercises\n", + "- Default arguments\n", + "\n", + "Please email theo.tanner@chem.ox.ac.uk with any questions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a571c19b-6970-4073-8a24-4cf10d757fb8", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/loops_functions/methyl_benzoate.png b/lessons/loops_functions/methyl_benzoate.png new file mode 100644 index 0000000..0fffd08 Binary files /dev/null and b/lessons/loops_functions/methyl_benzoate.png differ diff --git a/lessons/manipulating_variables.md b/lessons/manipulating_variables.md new file mode 100644 index 0000000..f588326 --- /dev/null +++ b/lessons/manipulating_variables.md @@ -0,0 +1,3 @@ +# Manipulating Variables + +These lessons introduce different types of variables used in Python, and how to manipulate them. diff --git a/lessons/organising_code.md b/lessons/organising_code.md new file mode 100644 index 0000000..b461946 --- /dev/null +++ b/lessons/organising_code.md @@ -0,0 +1,3 @@ +# Organising Code + +These lessons describe how to write code efficiently by organising it into loops and functions. diff --git a/lessons/variables/Conditions.ipynb b/lessons/variables/Conditions.ipynb new file mode 100644 index 0000000..748ae87 --- /dev/null +++ b/lessons/variables/Conditions.ipynb @@ -0,0 +1,598 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bfa23092-475a-4e40-a6cd-538bb1b16a5a", + "metadata": {}, + "source": [ + "# Comparisons and Conditional Statements" + ] + }, + { + "cell_type": "markdown", + "id": "63b3ed50-5670-4fcb-9cd4-81e0a50b650e", + "metadata": {}, + "source": [ + "## Learning Objectives" + ] + }, + { + "cell_type": "markdown", + "id": "b56ee7b2-cffc-471e-9fd2-bf2b2c437894", + "metadata": {}, + "source": [ + "- To understand how in python we can compare objects to each other.\n", + "- To understand how we can use logical statements to perform multiple comparisons or assessments at once.\n", + "- To see how we can build statements and scripts that can do different tasks when we give them different inputs." + ] + }, + { + "cell_type": "markdown", + "id": "27111d44-d8ba-4393-bb63-7a1521a4a956", + "metadata": {}, + "source": [ + "## Prerequisites" + ] + }, + { + "cell_type": "markdown", + "id": "98242630-1ee1-4fe6-bd25-d3bd5805c7ee", + "metadata": {}, + "source": [ + "- Variables\n", + "- Mathematical Operations\n", + "- Data types" + ] + }, + { + "cell_type": "markdown", + "id": "dad9b34b-ec0d-4b52-abd3-07b9efc631f4", + "metadata": {}, + "source": [ + "## Comparisons in Python" + ] + }, + { + "cell_type": "markdown", + "id": "423201d6-956c-4bab-973f-981ee93188ef", + "metadata": {}, + "source": [ + "Python has a number of different ways in which it can compare objects, variables or just numbers. These can be very useful both for simple tasks (\"Does H$_2$O have a higher molecular weight than NH$_3$\") and more complex ones (\"How do we know that this peak in an IR spectrum is above the baseline noise?\")\n", + "\n", + "Let us start by looking at a few of the [mathematical operators](./mathematical-operators.ipynb) (either identical or very similar to how you would write them down on paper) that can be used directly in Python:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de0e669c-1c14-47ad-b12d-5b25c0fdaf35", + "metadata": {}, + "outputs": [], + "source": [ + "4 > 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e5d946d4-36d2-4114-bb2f-326975ee0ee5", + "metadata": {}, + "outputs": [], + "source": [ + "5.1 < 3.6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dd5f7b7d-b9c7-411c-bef6-af9154332c9f", + "metadata": {}, + "outputs": [], + "source": [ + "4 >= 4" + ] + }, + { + "cell_type": "markdown", + "id": "732d5859-56b9-48d6-beff-8ee521d46879", + "metadata": {}, + "source": [ + "We can see a few things from the above statements: firstly that running a comparison between two numbers in a code block outputs either 'True' if the expression is indeed true, or 'False' if not. Second, both the greater than (\">\") and less than (\"<\") mathematical operations are identical to those used in standard mathematical notation, while the greater than or equal to operation (\"≥\") is written with two characters, ```>=```. Finally, we can see that this works for both numerical [data types](Variable_data_types.ipynb) in Python - integers and floats. \n", + "\n", + "What if we wanted to check if two values were exactly equal? **Be careful!** We know from our understanding of [variables](Variable_data_types.ipynb) that ```=``` is used in python to assign values to variable names, we cannot use it here. Instead:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962ed8f3-bb4e-4826-a348-4d42fb38025e", + "metadata": {}, + "outputs": [], + "source": [ + "4 == 4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39103a6d-8149-4a1e-9be8-f59f8231d729", + "metadata": {}, + "outputs": [], + "source": [ + "4 != 4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff5c56d6-1170-4464-8ce3-23395e43a744", + "metadata": {}, + "outputs": [], + "source": [ + "3.1 != 3" + ] + }, + { + "cell_type": "markdown", + "id": "c42c319f-594f-47a2-bb2f-535840c1ed80", + "metadata": {}, + "source": [ + "The double equals ```==``` is used to compare if two values are equal, while ```!=``` checks if two values are not equal.\n", + "\n", + "What about other data types? Let us try some examples of strings or lists using the ```==``` comparison operator." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2331f446-16b9-4b2a-82ca-c7c90aeaa15c", + "metadata": {}, + "outputs": [], + "source": [ + "\"CH4\" == \"CH4\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c29dc42-ae21-4a7f-beff-8c115fb3a7c8", + "metadata": {}, + "outputs": [], + "source": [ + "\"NH3\" == \"H3N\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be03a6a0-eff3-4135-9258-97c46a00221d", + "metadata": {}, + "outputs": [], + "source": [ + "[2, 3, 5] == [2, 3, 4]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6d6ecf7-38ca-4fc8-998f-9987dbd5a024", + "metadata": {}, + "outputs": [], + "source": [ + "[\"helium\", \"neon\", \"argon\"] == [\"helium\", \"neon\", \"argon\"]" + ] + }, + { + "cell_type": "markdown", + "id": "4239a695-24f5-4120-b42e-f7ca60b27aa2", + "metadata": {}, + "source": [ + "Note that all parts of the string (including order of characters) and all parts of a list (including the order of elements) need to be identical for the ```==``` operation to return 'True'. There is an alternative comparison that can be made - what if we wanted to distinguish if those two lists not only contained the same elements in the same order but if they are exactly the same object (i.e. rather than just looking the same, they are actually the same 'behind the scenes')? For this, we can use ```is```:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ebfdf09-34fa-4919-a6f1-ba1b6fc12e1f", + "metadata": {}, + "outputs": [], + "source": [ + "x = [\"N\", \"H\", 3] \n", + "y = [\"N\", \"H\", 3]\n", + "x is y" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "462ee079-297b-4e00-bd37-6ff3df21a4af", + "metadata": {}, + "outputs": [], + "source": [ + "x = y\n", + "x is y" + ] + }, + { + "cell_type": "markdown", + "id": "be246bfa-183d-42a7-8388-b0af6bbba21c", + "metadata": {}, + "source": [ + "In the first example, despite the two variables, ```x``` and ```y```, containing the same chemical formula (in the same order), the two are currently two separate instances of variables according to python, so ```x is y``` is False. In the second block, we specifically reassign the variable x to the value of y, and so now the ```is``` comparison is true." + ] + }, + { + "cell_type": "markdown", + "id": "0deade2a-b54e-4888-8b22-48f902c82ce4", + "metadata": {}, + "source": [ + "## Boolean logic operators" + ] + }, + { + "cell_type": "markdown", + "id": "b818dedf-8ad5-4926-89d1-76e721599366", + "metadata": {}, + "source": [ + "Can we make multiple comparisons at the same time - i.e. to check if a number is within a certain range of bounds? Yes, and for this, we need Boolean operators - these are written as words but still make comparisons between two things: ```and```, ```or``` and ```not```. \n", + "\n", + "What does these mean? Let's start with ```and```:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "302f094f-766c-47fb-95ae-a89eed39ffac", + "metadata": {}, + "outputs": [], + "source": [ + "z = 2\n", + "z < 3 and z > 0.1 " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b15d57e0-6260-42a5-94d8-f1e45743a40b", + "metadata": {}, + "outputs": [], + "source": [ + "z < 3 and z != 2" + ] + }, + { + "cell_type": "markdown", + "id": "06f35439-6217-4e18-a68b-e4375fa3025a", + "metadata": {}, + "source": [ + "When using ```and```, the statement before the ```and``` and after the ```and``` must **both** be 'True' for the overall result to be 'True', hence why the two code blocks give different answers: while z is less than 3 in both statements, z is equal to 2, and so the second statement in the second code block is 'False'. I.e. if Statement A is True AND Statement B is False, ```A and B``` is False.\n", + "\n", + "What about ```or```?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f016ee5a-25b4-4547-857f-b10f5c1d48d3", + "metadata": {}, + "outputs": [], + "source": [ + "z < 3 or z > 0.1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e97f2cdf-8e79-4eba-b5dc-2cbca8710b8e", + "metadata": {}, + "outputs": [], + "source": [ + "z < 3 or z !=2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d14bcff8-6b5f-4055-88bb-e18e3a0cafd0", + "metadata": {}, + "outputs": [], + "source": [ + "z > 3 or z != 2" + ] + }, + { + "cell_type": "markdown", + "id": "448d821c-a069-455f-9992-a6320bbea6fb", + "metadata": {}, + "source": [ + "With ```or``` - only one of the two comparisons must be true for the whole statement to be 'True', though the whole statement will still be False if neither of the two are True. \n", + "\n", + "You don't need to just have two comparisons!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87ab9e7d-dc0d-44d5-9f0e-94b79f728f5f", + "metadata": {}, + "outputs": [], + "source": [ + "w = \"tungsten\"\n", + "w == \"neon\" or w == \"iron\" or w == \"bismuth\" or w == \"tungsten\"" + ] + }, + { + "cell_type": "markdown", + "id": "1c2b08e0-672b-46e7-b92e-ee9a35d69907", + "metadata": {}, + "source": [ + "Finally, not is slightly different - first it should be used before a statement, i.e. (```not a == 2```). \n", + "It can then be used to 'invert' the True or False behaviour of a statement: if a statement was true, not will make it False, or vice versa:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f30a769e-184f-4d13-aab3-ccd2a91f6470", + "metadata": {}, + "outputs": [], + "source": [ + "z = 2\n", + "not z <= 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40c9c586-0376-48c5-97cb-279d31c26067", + "metadata": {}, + "outputs": [], + "source": [ + "not z > 3 or z != 2" + ] + }, + { + "cell_type": "markdown", + "id": "79597a1c-5dd4-4426-b21e-eb8135a9bfd0", + "metadata": {}, + "source": [ + "## Conditional Statements" + ] + }, + { + "cell_type": "markdown", + "id": "feabf370-ad13-48b3-aba6-16dc7c74d5c0", + "metadata": {}, + "source": [ + "How are these comparisons useful for working with actual chemical data though? Writing a comparison for every data point would get very tiring! This is where conditional statements come in -- we can use the comparisons we have learned about to check **if** certain relationships are true as part of our data sets, and then either do something or not, or even doing a different operation...\n", + "\n", + "We do this, sensibly, through what are called **if statements**. If statements require specific syntax:\n", + "```\n", + "if :\n", + " what to do if statement is true\n", + "```\n", + "\n", + "When the statement after ```if``` is True, then Python will go on to do whatever is **indented**. Indentation is the way for python to know what is part of the ```if``` statement and what is the rest of the code. As a standard, indenting is 4 spaces before the code line -- many programs like jupyter notebook will automatically do this indenting after the if statement is written and you move to a new line with the Return key.\n", + "\n", + "If what comes after the ```if``` is False, then the indented code is ignored, and Python continues onto whatever the next non-indented lines are. As with loops, there must be a colon after the if statement, and there must be an indentation -- not having these can cause errors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3761936a-7163-41de-813b-580ce83a8857", + "metadata": {}, + "outputs": [], + "source": [ + "x = \"CH4\"\n", + "y = \"CH4\"\n", + "if x == y:\n", + " ### This part of the code is indented, and is run if the if statement is true\n", + " print(\"Yes, these are the same formula\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34f11095-cd62-40a6-ad7a-9df4dd7eae14", + "metadata": {}, + "outputs": [], + "source": [ + "x = \"CH4\"\n", + "y = \"NH3\"\n", + "if x == y:\n", + " print(\"Yes, these are the same formula\")\n", + "\n", + "print(\"The indentation has ended, this code line is always run\")" + ] + }, + { + "cell_type": "markdown", + "id": "f5e0ff28-eec5-4508-b5dd-ccea6d24c9e9", + "metadata": {}, + "source": [ + "What if we want to have multiple different categories and multiple different outcomes? We can use ```if... else``` if we want to do one thing if a statement is true, and then something else for for all other possibilities. Alternatively, if we have more than two decisions to make, we can use ```if... elif... else```, with ```elif``` statements acting like other if statements that are checked if they are true. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b66d509c-f888-45a0-ab94-7c8115852979", + "metadata": {}, + "outputs": [], + "source": [ + "x = \"CH4\"\n", + "if x == \"CH4\":\n", + " print(\"This compound is methane\")\n", + "elif x != \"NH3\":\n", + " print(\"This compound is not ammonia\")\n", + "else:\n", + " print(\"This compound is ammonia\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64bfd426-81a8-44f3-a54e-cd2f9e99687e", + "metadata": {}, + "outputs": [], + "source": [ + "x = \"H2O\"\n", + "if x == \"CH4\":\n", + " print(\"This compound is methane\")\n", + "elif x != \"NH3\":\n", + " print(\"This compound is not ammonia\")\n", + "else:\n", + " print(\"This compound is ammonia\")" + ] + }, + { + "cell_type": "markdown", + "id": "46cb8ac8-5627-4ddb-a169-552794c2099d", + "metadata": {}, + "source": [ + "Be careful, conditional statements are strictly ordered - if the first ```if``` is True, then that code block will be run, while all further ```elif``` statements are ignored, even if they would also be true!" + ] + }, + { + "cell_type": "markdown", + "id": "0a5899ad-5b4a-439b-bd47-df83a6d37136", + "metadata": {}, + "source": [ + "### Exercise" + ] + }, + { + "cell_type": "markdown", + "id": "116cf3f9-74fa-46fe-a06c-ee5cec0a4b9b", + "metadata": {}, + "source": [ + "The following commented code needs the correct operator to replace the **BLANK** in the code to output True - what replaces the **BLANK**?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a827cfbe-7726-4a42-bb38-0468a752b998", + "metadata": {}, + "outputs": [], + "source": [ + "atomic_no_of_nitrogen = 7\n", + "atomic_no_of_fluorine = 9" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81dd9a93-9296-4da8-be41-3f4fc6989d56", + "metadata": {}, + "outputs": [], + "source": [ + "atomic_no_of_nitrogen BLANK atomic_no_of_fluorine" + ] + }, + { + "cell_type": "markdown", + "id": "bcdbba50-4cb5-4556-bc5c-39a16db6113f", + "metadata": {}, + "source": [ + "### Answer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3f749e9-674c-428c-8eeb-df549473ebae", + "metadata": {}, + "outputs": [], + "source": [ + "### ANSWER: <, <= or != all work\n", + "atomic_no_of_nitrogen < atomic_no_of_fluorine" + ] + }, + { + "cell_type": "markdown", + "id": "120f98bf-03ef-49b8-8338-6081411d2b10", + "metadata": {}, + "source": [ + "## Learning Outcomes" + ] + }, + { + "cell_type": "markdown", + "id": "b2a04b94-1118-422a-a616-ff6983cb92fa", + "metadata": {}, + "source": [ + "In this lesson we have learned:\n", + "- How to make comparisons between numbers and variables of different data types, getting Boolean True or False statements\n", + "- How we can use Boolean operators such as ```and``` and ```or``` to compare if multiple statements are True or False in different combinations.\n", + "- How to use ```if...elif...else``` statements to do certain tasks only if certain comparisons or statements are the case." + ] + }, + { + "cell_type": "markdown", + "id": "be1e9adf-0c99-47e1-8fe7-01f461d77e08", + "metadata": {}, + "source": [ + "## TODO" + ] + }, + { + "cell_type": "markdown", + "id": "4163f470-0898-4966-aa1e-fa2f9644278b", + "metadata": {}, + "source": [ + "- add comments to code?\n", + "- note different data type behaviour for is (e.g. strings versus lists as given)\n", + "- Think of more imaginative chemistry related examples for the lessons\n", + "- More tasks!\n", + "- Debugging task - maybe using a complex if not statement giving the wrong output?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "105d0079-c0ae-4a11-bccd-4d84b32ad1b5", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32454202-0869-460c-b3e6-ed6fec5bf114", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73a9fb7c-8930-4091-89a3-283eda6f41c1", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/variables/Variable_data_types.ipynb b/lessons/variables/Variable_data_types.ipynb new file mode 100644 index 0000000..1c14824 --- /dev/null +++ b/lessons/variables/Variable_data_types.ipynb @@ -0,0 +1,454 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "VqjrEzS2z1Ol" + }, + "source": [ + "# Variable Data Types\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_iRikrUPUjs3" + }, + "source": [ + "## Learning Outcomes\n", + "\n", + "* Understand how to print basic outputs in python\n", + "* Understand how to identify and verify the type of variable\n", + "* Create and print lists in python\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Kup00O1iVHOG" + }, + "source": [ + "## Prerequisites\n", + "\n", + "- Be able to access Google Colab, Jupyter Notebooks or another piece of software that will allow you to interact and practice the code" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IxCxV-mv0Svg" + }, + "source": [ + "## What is a variable?\n", + "\n", + "A variable is a place where some information can be held in the code, and can be used in many different ways within a programme." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pNvKKeprCw5V" + }, + "source": [ + "## Print Function\n", + "\n", + "Python can specified message using the print function. This is one of the main ways you will output information." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VQe62zJIDd2s" + }, + "outputs": [], + "source": [ + "message = \"we love chemistry\"\n", + "print(message)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6K3DweS-SJkH" + }, + "source": [ + "You can use the print function to print answers to calculations. Remember to use brackets for more complex calculations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mIQZr9GdSSzy" + }, + "outputs": [], + "source": [ + "print(54*145)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E7oo-rhqLU4O" + }, + "source": [ + "## Type Function\n", + "\n", + "This is a built-in function that is used to return the type of data stored in the objects or variables in the program. For example, if a variable contains a value of 45.5 then the type of that variable is float.\n", + "\n", + "This is useful as when you have multiple data a variable types you check that they are functioning in the correct way." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "e1jc0uR_LXLE" + }, + "outputs": [], + "source": [ + "number = 45.5\n", + "print(type(number))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0LF3cDZLBe82" + }, + "source": [ + "## Naming of variables\n", + "\n", + "There are few strict conventions with the naming of variables, and Python can be incredibly tolerant in this regard. However, for the ease of legibility of code, and minimise mistakes caused by referring to the wrong variable, it is sensible to try and use a clear and consistent approach to variable naming.\n", + "\n", + "### Variable name requirements\n", + "\n", + "\n", + "* Variables can only start with a letter or underscore (_) character and may not start with a number.\n", + "* Can only include upper and lowercase letters, numbers and the underscore character (A-z, 0-9, and _ )\n", + "* Names must not be a [Python keyword](https://docs.python.org/3/reference/lexical_analysis.html#keywords)\n", + "\n", + "### Choosing variable names\n", + "Short, descriptive names are generally a good choice, as variables will often need to be entered in multiple places in code. Names can consist of multiple words, but cannot use spaces. Commonly 'camelCase' is used with variable naming, where a multiple word variable name is closed together (ie the spaces are removed), and the initial letter of each word being capitalised (excluding the first word).\n", + "\n", + "Examples might include:\n", + "\n", + "\n", + "* elementMass\n", + "* elementAtomicNumber\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VPhG17aLAcBb" + }, + "source": [ + "## Types of variables\n", + "Some of the common variable types are outlined below. For each type of variable, there is a code snippet which shows how the variable is set, the variable is printed, and the type of variable is printed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PwADJGso9G7-" + }, + "source": [ + "### Strings\n", + "\n", + "These store a sequence of characters. You signify what characters are stored by writing quotation marks either side of the characters. These can be double or single quotation marks, as long as they are consistent - it will still work either way." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ObD5OOJswqse" + }, + "outputs": [], + "source": [ + "subject = \"Chemistry\"\n", + "print(subject)\n", + "print(type(subject))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "j2LOZu_qAnti" + }, + "source": [ + "### Numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5Zzn3B9OAq1d" + }, + "source": [ + "#### Integer\n", + "\n", + "This is any whole number, e.g. 5. If your variable type is an integer you will see the class output will be displayed as 'int'." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FV_SqZ4N3edG" + }, + "outputs": [], + "source": [ + "number = 5\n", + "print(number)\n", + "print(type(number))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tmlYe2PTAzCN" + }, + "source": [ + "#### Float\n", + "\n", + "This is any number where you have a decimal place. If your variable type is a float, you will see the class output will be displayed at 'float'." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "axf-MWdz4V03" + }, + "outputs": [], + "source": [ + "number = 5.3\n", + "print(number)\n", + "print(type(number))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xuiU47q1A3qV" + }, + "source": [ + "#### Standard form\n", + "\n", + "When writing standard form, as is the case for Avogadro's constant you would use the letter `e` to show that the number is multipled by 10 to the power of 23." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SF6MngZc5LTw" + }, + "outputs": [], + "source": [ + "avogadro = 6.023e23\n", + "print(avogadro)\n", + "print(type(avogadro))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aWRMOW5M7b2D" + }, + "source": [ + "#### Complex Numbers\n", + "\n", + "You may not use complex numbers when you first start out on your chemistry course, but you will come across them later in maths and potentially when you study crystallography, quantum mechanics and other areas of science.\n", + "\n", + "When using complex numbers in python, you cannot use $i$ for imaginary numbers, you must use $j$ instead. In the example below we have the complex number $3+3i$, which is written as $3+3j$ in the code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "v6df_hB45r-3" + }, + "outputs": [], + "source": [ + "complexNumber=(3+3j)\n", + "print(complexNumber)\n", + "print(type(complexNumber))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "K6aMfYCUAjMn" + }, + "source": [ + "#### Boolean\n", + "\n", + "Boolean variables are the simplest type of variable - they can be equal to either `True` or `False`, and nothing else." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gkuDEMKq4csJ" + }, + "outputs": [], + "source": [ + "Answer = True\n", + "print(Answer)\n", + "print(type(Answer))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HFWW4cO9BlLI" + }, + "source": [ + "#### Lists\n", + "\n", + "Lists are collections of variables, separated by commas. These variables do not have to be of the same type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "3C66kNgC4sQf" + }, + "outputs": [], + "source": [ + "elements = ['H', 'He', 'Li']\n", + "print(elements)\n", + "print(type(elements))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Individual entries within a list can be accessed by writing the variable name, followed by the index of the entry within the list inside square brackets. Note that Python starts counting at 0, not 1, so the first entry in a list has index 0." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(elements[0])\n", + "print(elements[2])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "98-HGTmIBoSl" + }, + "source": [ + "#### Dictionaries\n", + "\n", + "Dictionaries are similar to lists, in that they are a collection of information. However, instead of using numbers to index each entry in a dictionary, we use \"keys\" instead, which are usually strings. In the example below the names of the elements are the keys. Using dictionaries usually leads to more human readable code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lAOQrm4LE24D" + }, + "outputs": [], + "source": [ + "ram = {\n", + " \"Hydrogen\": 1.008,\n", + " \"Helium\": 4.002602,\n", + " \"Lithium\": 6.941\n", + "}\n", + "\n", + "print(ram)\n", + "print(ram['Hydrogen'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise** Can you work out what the data dictionary below is describing?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "EbJjHF7fD0Ea" + }, + "outputs": [], + "source": [ + "data = {\n", + "'H' : [(1.007825, 99.9885),(2.014102, 0.0115)],\n", + "'C' : [ (12.0, 98.93),(13.003355, 1.07)],\n", + "'N' : [(14.003074, 99.632),(15.000109, 0.368)],\n", + "'O' : [(15.994915 , 99.757),(16.999132, 0.038),(17.999160,0.205)]\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5Rep96srUZeM" + }, + "source": [ + "### TO-DO List\n", + "\n", + "Add in an introduction to f-strings.\n", + "\n", + "Add in some more worked examples\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/lessons/variables/mathematical-operators.ipynb b/lessons/variables/mathematical-operators.ipynb new file mode 100644 index 0000000..2504642 --- /dev/null +++ b/lessons/variables/mathematical-operators.ipynb @@ -0,0 +1,231 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Mathematical Operators \n", + "\n", + "## Learning outcomes\n", + "\n", + "- Develop familiarity with basic mathematical operations in Python\n", + "- Understand how to access some additional, more complex mathematical operations. \n", + "\n", + "## Prerequisites\n", + "\n", + "- Variables and their data types\n", + "- Printing f-strings\n", + "\n", + "## Arithmetic\n", + "\n", + "Python is extremely relevant to numerical computing. Therefore, it is useful to outline some of the mathematical operations that can be natively performed with Python. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python natively supports some mathematical operations. \n", + "\n", + "| Operation | Mathematical Notation | Pythonic Notation |\n", + "| -------- | ------- | ------- |\n", + "| Addition | $a + b$ | `a + b` |\n", + "| Subtraction | $a - b$ | `a - b` |\n", + "| Multiplication | $a \\times b$ | `a * b` |\n", + "| Division | $a \\div b$ | `a / b` |\n", + "| Exponent | $a ^ b$ | `a ** b` |\n", + "| Modulo | $a \\textrm{ mod } b$ | `a % b` |\n", + "\n", + "The modulo operation may be new to you, you may know it as the remainder from the division of two numbers. Other, more complicated operations are typically also available through Python libraries such as [NumPy](https://numpy.org/), which is introduced below.\n", + "\n", + "A single line of code may have many mathematical operations. In this event, Python will follow the standard order of operations for mathematical operations: you might know this as [BODMAS](https://en.wikipedia.org/wiki/Order_of_operations#Mnemonics)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example: The Quadratic Formula\n", + "\n", + "The quadratic formula is an expression to solve quadratic equations with the form, \n", + "\n", + "$$\n", + "ax^2 + bx + c = 0,\n", + "$$\n", + "\n", + "where, $x$ is an unknown value that we want to find, and $a$, $b$, and $c$ are fixed parameters. \n", + "The quadratic formula states that the value of $x$ is, \n", + "\n", + "$$\n", + "x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}.\n", + "$$\n", + "\n", + "This becomes relevant in chemistry if we consider the following problem: Formic acid is a weak acid with a dissociation constant $K_a$ of 1.8×10-4. \n", + "The dissociation constant relates the concentration of the H+ ions and the amount of acid dissolve, $N$, by the equation: \n", + "\n", + "$$\n", + "K_a = \\frac{[\\textrm{H}^+]^2}{N - [\\textrm{H}^+]}.\n", + "$$\n", + "\n", + "This equation can be reformulated as a quadratic equation, \n", + "\n", + "$$\n", + "[\\textrm{H}^+]^2 + K_a[\\textrm{H}^+] - K_aN = 0.\n", + "$$\n", + "\n", + "Therefore, we can use the quadratic formula to solve for the concentration of hydrogen ions for a given amount of dissolved acid, where $a = 1$, $b=K_a$ and $c=-K_aN$. \n", + "\n", + "We can write Python code to compute [H+] for 0.1 moles of dissolved acid. \n", + "Don't worry if some of the code specifics below are a bit new, the table below explains each of the operators. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "K_a = 1.8e-4\n", + "N = 0.1\n", + "\n", + "a = 1.\n", + "b = K_a\n", + "c = -K_a * N\n", + "\n", + "H_conc_plus = (-b + (b ** 2 - 4 * a * c) ** (1 / 2)) / (2 * a)\n", + "H_conc_minus = (-b - (b ** 2 - 4 * a * c) ** (1 / 2)) / (2 * a)\n", + "\n", + "print(f'H_conc_plus = {H_conc_plus:.5f} M')\n", + "print(f'H_conc_minus = {H_conc_minus:.5f} M')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Clearly, the variable `H_conc_minus` should be disregarded as it is not possible to have a negative concentration. \n", + "This means that the concentration of [H+] is 0.00415 (to 5 decimal places). " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## More Complex Mathematical Operations\n", + "\n", + "The `numpy` module provides access to a range of advanced mathematical functions. \n", + "Information about all of the functions that the `numpy` module has can be found in the [numpy lesson](../common_libs/Introduction_to_NumPy.ipynb). To access a given function, we must `import` it `from` the module. Below, we import the base 10 logarithm function, `log10`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import log10" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can then use this with the result from above to compute the pH of the solution. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pH = log10(H_conc_plus)\n", + "print(f'pH = {pH:.2f}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise\n", + "Find the velocity, $v$, of a N2 molecule with a mass, $m$ of 4.6×10-26 kg at a temperature, $T$, of 293 K, given the following equation,\n", + "\n", + "$$\n", + "v = \\sqrt{\\frac{3k_bT}{m}}, \n", + "$$\n", + "\n", + "where, $k_b$ is 1.38×10−23 J/K." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Answer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "k_b = 1.38e-23\n", + "T = 293\n", + "m = 4.6e-26\n", + "\n", + "v = ((3 * k_b * T) / m) ** (1 / 2)\n", + "print(f'velocity = {v:.1f} m/s')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Learning outcomes\n", + "\n", + "- Develop familiarity with basic mathematical operations in Python, including `+`, `-`, `*`, `/`. \n", + "- Understand how to access some additiona, more complex mathematical function using the `numpy` module. \n", + "\n", + "## TODO \n", + "\n", + "- Add more exercises for other operations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}