diff --git a/.gitignore b/.gitignore index f494b1b..0f5c155 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +.ipynb_checkpoints/ .tool-versions diff --git a/CITATION.cff b/CITATION.cff index ada6ffb..8178183 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -25,5 +25,5 @@ abstract: >- attention is given to best practices in coding, e.g.: writing clean code and documentation. license: BSD-3-Clause -version: 3.0.2 -date-released: '2024-02-01' +version: 2024-11 +date-released: '2024-09-26' diff --git a/lessons/00 index.ipynb b/lessons/00 index.ipynb index 24aa28d..01a3ed2 100644 --- a/lessons/00 index.ipynb +++ b/lessons/00 index.ipynb @@ -2,69 +2,66 @@ "cells": [ { "cell_type": "markdown", + "id": "b3f7b25f", "metadata": { "id": "RQ73d6XVysi7" }, "source": [ "# CDH course \"Programming in Python\"\n", "\n", - "**April 2023 edition**\n", + "**November 2023 edition**\n", "\n", "[Teams channel](https://teams.microsoft.com/l/channel/19%3aAlr7pcE2v9p1eaKT1BtNxJAB9mgCFT1olZvAMswJlEc1%40thread.tacv2/Algemeen?groupId=7fd15384-6748-4852-8646-d96963fb3524&tenantId=d72758a0-a446-4e0f-a0aa-4bf95a4a10e7)\n", "\n", "## Course modules\n", "\n", - "1. [Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)\n", - "2. [Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)\n", - "3. [Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)\n", - "4. [Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)\n", - "5. [Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM)\n", - "6. [Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)\n", - "7. [Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) \n", - "8. [Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)\n", - "9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n", - "10. [Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9)\n", - "11. [Working with files](https://colab.research.google.com/drive/17G-gA5-GNKX-mQglrPVNtndV2YhBafBr)\n", + "1. [Introduction](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B)\n", + "2. [Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA)\n", + "3. [Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR)\n", + "4. [Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt)\n", + "5. [Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH)\n", + "6. [Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ)\n", + "7. [Functions](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg)\n", + "8. [Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS)\n", + "9. [String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm)\n", + "10. [Dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj)\n", + "11. [Working with files](https://colab.research.google.com/drive/1_mDpeRCHzrGJstcxEWZgq5YMhHujPdfe)\n", "\n", "## Extra material\n", "\n", - "- [Functions exercises](https://colab.research.google.com/drive/13__hh18xcBeZ5xjeby21KXUBYjRpb_Vy)\n", - "- [Tips](https://colab.research.google.com/drive/1qKVaI3Ct1hOsM_06mZdXpx0sLSR_O-6R)\n", + "- [Functions exercises](https://colab.research.google.com/drive/1gdZjsBNSfDzJAl-Z-mdt3ui2w5uGldAi)\n", + "- [Tips](https://colab.research.google.com/drive/1-Qm1VYjy-c8H6gIyW92QPxDJ4fQeZnZh)\n", + "- [Life after the course](https://colab.research.google.com/drive/1Uf6IzqaKprwMXRP_vsyTKXbmVdYON9vl)\n", "\n", "## Projects\n", - "- [Text analysis](https://colab.research.google.com/drive/1wUgaVE70dzjIlIEuZtgQalqYI2UcfiXK)\n", + "\n", + "- [Text analysis](https://colab.research.google.com/drive/1Iq_s0AtiqCMHnP1SdqecwLxYUiaBwxvD)\n", "\n", "## Exercise solutions\n", "\n", - "1. [Introduction](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd)\n", - "2. [Values and expressions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy)\n", - "3. [Conditionals](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI)\n", - "4. [Datastructures](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn)\n", - "5. [Assertions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK)\n", - "6. [Loops](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-)\n", - "7. [Functions](https://colab.research.google.com/drive/13M4t34msJvudQTN-Im24w4iRfctOuNCv)\n", - "8. Debugging - no solutions\n", - "9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n", - "10. [Dictionaries](https://colab.research.google.com/drive/1Qw7vgiyRVg1E-BkUEvCxX0e6QZQ9y_k6)\n", - "11. [Working with files](https://colab.research.google.com/drive/1Nz3lhsntgw4yAI6257drrp-EtEQEVogF)" + "1. [Introduction](https://colab.research.google.com/drive/13q9wUC6QRT3hDuvLOuctuph9yZYeEvVu)\n", + "2. [Values and expressions](https://colab.research.google.com/drive/1R0_DYzufN6PSKpot8iBmn1Gh6mNSOCy5)\n", + "3. [Conditionals](https://colab.research.google.com/drive/1-vBxja7MWudomSKEg1NU5D3JO0SEQc3J)\n", + "4. [Datastructures](https://colab.research.google.com/drive/1mf_sQpAVxz8oRbsZnA548cC3TtVaQxmU)\n", + "5. [Assertions](https://colab.research.google.com/drive/1yrrB0EYglFKJ0_zhb91xY7FGgJosSUFw)\n", + "6. [Loops](https://colab.research.google.com/drive/19mQnQILY7oRREvwUsAD2SmqQHiOffgx_)\n", + "7. [Functions](https://colab.research.google.com/drive/1BdBQeTKBvEPn1CTIJC5sDDY6VBMs32Sq)\n", + "8. Debugging has no solutions notebook\n", + "9. [String manipulation](https://colab.research.google.com/drive/1wOUWUUP4KdYUFzp5-fALi5t_k_4ovow1)\n", + "10. [Dictionaries](https://colab.research.google.com/drive/1j1hikQCS3Px0_q4hguuIqWSXSmyKcAiN)\n", + "11. [Working with files](https://colab.research.google.com/drive/1a5fHoa8eY_ABZereDQLcSnV5w3AUYSav)" ] } ], "metadata": { - "colab": { - "provenance": [] - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "name": "python3" - }, - "language_info": { - "name": "python" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 5 } diff --git a/lessons/00 index.py b/lessons/00 index.py index d06c8bd..2d00e95 100644 --- a/lessons/00 index.py +++ b/lessons/00 index.py @@ -14,42 +14,44 @@ # %% [markdown] id="RQ73d6XVysi7" # # CDH course "Programming in Python" # -# **April 2023 edition** +# **November 2023 edition** # # [Teams channel](https://teams.microsoft.com/l/channel/19%3aAlr7pcE2v9p1eaKT1BtNxJAB9mgCFT1olZvAMswJlEc1%40thread.tacv2/Algemeen?groupId=7fd15384-6748-4852-8646-d96963fb3524&tenantId=d72758a0-a446-4e0f-a0aa-4bf95a4a10e7) # # ## Course modules # -# 1. [Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) -# 2. [Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) -# 3. [Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) -# 4. [Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) -# 5. [Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) -# 6. [Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) -# 7. [Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) -# 8. [Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi) -# 9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) -# 10. [Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9) -# 11. [Working with files](https://colab.research.google.com/drive/17G-gA5-GNKX-mQglrPVNtndV2YhBafBr) +# 1. [Introduction](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B) +# 2. [Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA) +# 3. [Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR) +# 4. [Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt) +# 5. [Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH) +# 6. [Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ) +# 7. [Functions](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg) +# 8. [Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS) +# 9. [String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm) +# 10. [Dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj) +# 11. [Working with files](https://colab.research.google.com/drive/1_mDpeRCHzrGJstcxEWZgq5YMhHujPdfe) # # ## Extra material # -# - [Functions exercises](https://colab.research.google.com/drive/13__hh18xcBeZ5xjeby21KXUBYjRpb_Vy) -# - [Tips](https://colab.research.google.com/drive/1qKVaI3Ct1hOsM_06mZdXpx0sLSR_O-6R) +# - [Functions exercises](https://colab.research.google.com/drive/1gdZjsBNSfDzJAl-Z-mdt3ui2w5uGldAi) +# - [Tips](https://colab.research.google.com/drive/1-Qm1VYjy-c8H6gIyW92QPxDJ4fQeZnZh) +# - [Life after the course](https://colab.research.google.com/drive/1Uf6IzqaKprwMXRP_vsyTKXbmVdYON9vl) # # ## Projects -# - [Text analysis](https://colab.research.google.com/drive/1wUgaVE70dzjIlIEuZtgQalqYI2UcfiXK) +# +# - [Text analysis](https://colab.research.google.com/drive/1Iq_s0AtiqCMHnP1SdqecwLxYUiaBwxvD) # # ## Exercise solutions # -# 1. [Introduction](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd) -# 2. [Values and expressions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy) -# 3. [Conditionals](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI) -# 4. [Datastructures](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn) -# 5. [Assertions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK) -# 6. [Loops](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-) -# 7. [Functions](https://colab.research.google.com/drive/13M4t34msJvudQTN-Im24w4iRfctOuNCv) -# 8. Debugging - no solutions -# 9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) -# 10. [Dictionaries](https://colab.research.google.com/drive/1Qw7vgiyRVg1E-BkUEvCxX0e6QZQ9y_k6) -# 11. [Working with files](https://colab.research.google.com/drive/1Nz3lhsntgw4yAI6257drrp-EtEQEVogF) +# 1. [Introduction](https://colab.research.google.com/drive/13q9wUC6QRT3hDuvLOuctuph9yZYeEvVu) +# 2. [Values and expressions](https://colab.research.google.com/drive/1R0_DYzufN6PSKpot8iBmn1Gh6mNSOCy5) +# 3. [Conditionals](https://colab.research.google.com/drive/1-vBxja7MWudomSKEg1NU5D3JO0SEQc3J) +# 4. [Datastructures](https://colab.research.google.com/drive/1mf_sQpAVxz8oRbsZnA548cC3TtVaQxmU) +# 5. [Assertions](https://colab.research.google.com/drive/1yrrB0EYglFKJ0_zhb91xY7FGgJosSUFw) +# 6. [Loops](https://colab.research.google.com/drive/19mQnQILY7oRREvwUsAD2SmqQHiOffgx_) +# 7. [Functions](https://colab.research.google.com/drive/1BdBQeTKBvEPn1CTIJC5sDDY6VBMs32Sq) +# 8. Debugging has no solutions notebook +# 9. [String manipulation](https://colab.research.google.com/drive/1wOUWUUP4KdYUFzp5-fALi5t_k_4ovow1) +# 10. [Dictionaries](https://colab.research.google.com/drive/1j1hikQCS3Px0_q4hguuIqWSXSmyKcAiN) +# 11. [Working with files](https://colab.research.google.com/drive/1a5fHoa8eY_ABZereDQLcSnV5w3AUYSav) diff --git a/lessons/01 introduction.ipynb b/lessons/01 introduction.ipynb index 05268de..02e5f7d 100644 --- a/lessons/01 introduction.ipynb +++ b/lessons/01 introduction.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "bbeebfbd", "metadata": { "id": "Buvh9v-iYeOO" }, @@ -9,13 +10,13 @@ "# Module 1: Introduction\n", "## CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", "## Welcome!\n", "\n", "## Who are we?\n", "\n", - "- Julian Gonggrijp, Sheean Spoel, Jelte van Boheemen, Luka van der Plas, Mees van Stiphout\n", + "- Julian Gonggrijp, Sheean Spoel\n", "- Developers at the Digital Humanities Lab\n", "\n", "## Who are you?\n", @@ -40,6 +41,7 @@ }, { "cell_type": "markdown", + "id": "5084c1d0", "metadata": { "id": "STQf1EROV-4I" }, @@ -57,6 +59,7 @@ }, { "cell_type": "markdown", + "id": "72aadb03", "metadata": { "id": "rC4AwjL06k0q" }, @@ -72,6 +75,7 @@ }, { "cell_type": "markdown", + "id": "2f43eee4", "metadata": { "id": "xyNb4mPMcQCd" }, @@ -83,6 +87,7 @@ }, { "cell_type": "markdown", + "id": "d3a2216b", "metadata": { "id": "4lc8nYpP9mtf" }, @@ -98,6 +103,7 @@ }, { "cell_type": "markdown", + "id": "a7bb272a", "metadata": { "id": "8zoSNQtC_6_v" }, @@ -114,6 +120,7 @@ }, { "cell_type": "markdown", + "id": "da9831b7", "metadata": { "id": "qtRYKOWnB5Wl" }, @@ -128,6 +135,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c4debb8a", "metadata": { "id": "MdA4_KyrDAsf" }, @@ -139,6 +147,7 @@ }, { "cell_type": "markdown", + "id": "50c86069", "metadata": { "id": "AoBoZqIeFEru" }, @@ -148,6 +157,7 @@ }, { "cell_type": "markdown", + "id": "550a4f5f", "metadata": { "id": "ZOqgk7VAFKnJ" }, @@ -159,6 +169,7 @@ }, { "cell_type": "markdown", + "id": "9a44b836", "metadata": { "id": "NOtudpGlFe78" }, @@ -171,6 +182,7 @@ { "cell_type": "code", "execution_count": null, + "id": "596123d9", "metadata": { "id": "in8WoUafGEyK" }, @@ -181,6 +193,7 @@ }, { "cell_type": "markdown", + "id": "5990a98d", "metadata": { "id": "BHQPB7LuQ1mf" }, @@ -190,6 +203,7 @@ }, { "cell_type": "markdown", + "id": "7cbe5d65", "metadata": { "id": "Src0ZdRKGHtl" }, @@ -202,6 +216,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7a41eca6", "metadata": { "id": "ssP4GWUOIxWS" }, @@ -214,6 +229,7 @@ }, { "cell_type": "markdown", + "id": "1bc17adc", "metadata": { "id": "YxXeF4u5I2c_" }, @@ -224,6 +240,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8587e51c", "metadata": { "id": "nMTlvlbbJUWu" }, @@ -243,6 +260,7 @@ }, { "cell_type": "markdown", + "id": "7ddbb6ef", "metadata": { "id": "ZhgCPUNTLIZX" }, @@ -254,7 +272,8 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, + "id": "3af649f9", "metadata": { "id": "oS7kfP3OLtJn" }, @@ -265,6 +284,7 @@ }, { "cell_type": "markdown", + "id": "7f09e362", "metadata": { "id": "pKIEfocbMaIR" }, @@ -279,6 +299,7 @@ { "cell_type": "code", "execution_count": null, + "id": "759c57f8", "metadata": { "id": "6hlNxRNNM1fV" }, @@ -291,6 +312,7 @@ { "cell_type": "code", "execution_count": null, + "id": "810d5fc4", "metadata": { "id": "VIm6FdRIM6OE" }, @@ -303,6 +325,7 @@ { "cell_type": "code", "execution_count": null, + "id": "681f5165", "metadata": { "id": "_MN48xz5NAya" }, @@ -314,6 +337,7 @@ { "cell_type": "code", "execution_count": null, + "id": "fdc42ba2", "metadata": { "id": "g9WKeIA2NVIA" }, @@ -327,6 +351,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a121a8ca", "metadata": { "id": "NCbCfbHaNafJ" }, @@ -339,7 +364,8 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, + "id": "74711629", "metadata": { "id": "-ZuiJ92yNqpi" }, @@ -353,6 +379,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0a95202c", "metadata": { "id": "b9bTbrNSNwwn" }, @@ -366,6 +393,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2505f05f", "metadata": { "id": "GYmcSm6iOAiA" }, @@ -379,6 +407,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e4eb5608", "metadata": { "id": "rbjHe9KbOzFb" }, @@ -392,6 +421,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0ed721f0", "metadata": { "id": "V1GiIP_ZNK8H" }, @@ -403,6 +433,7 @@ { "cell_type": "code", "execution_count": null, + "id": "712ab94f", "metadata": { "id": "sNNoSfndOSiw" }, @@ -414,6 +445,7 @@ { "cell_type": "code", "execution_count": null, + "id": "6c87244c", "metadata": { "id": "6IZS1NUuTdOX" }, @@ -424,6 +456,7 @@ }, { "cell_type": "markdown", + "id": "2594eb1a", "metadata": { "id": "Tr3wPlHMeBCI" }, @@ -438,6 +471,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3dc44475", "metadata": { "id": "jN7WTVOOSq2C" }, @@ -448,42 +482,26 @@ }, { "cell_type": "markdown", + "id": "70d55e26", "metadata": { "id": "zI0ohEpPUwpC" }, "source": [ "## Next module\n", "\n", - "[2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)" + "[2. Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA)" ] } ], "metadata": { - "colab": { - "authorship_tag": "ABX9TyOqiugh/2SmC5zeIHsVl+q8", - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "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.6" } }, "nbformat": 4, - "nbformat_minor": 4 + "nbformat_minor": 5 } diff --git a/lessons/01 introduction.py b/lessons/01 introduction.py index e1a1b99..e49fd02 100644 --- a/lessons/01 introduction.py +++ b/lessons/01 introduction.py @@ -15,13 +15,13 @@ # # Module 1: Introduction # ## CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # # ## Welcome! # # ## Who are we? # -# - Julian Gonggrijp, Sheean Spoel, Jelte van Boheemen, Luka van der Plas, Mees van Stiphout +# - Julian Gonggrijp, Sheean Spoel # - Developers at the Digital Humanities Lab # # ## Who are you? @@ -221,4 +221,4 @@ # %% [markdown] id="zI0ohEpPUwpC" # ## Next module # -# [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) +# [2. Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA) diff --git a/lessons/02 values and expressions.ipynb b/lessons/02 values and expressions.ipynb index 9f6dfa1..bea0c28 100644 --- a/lessons/02 values and expressions.ipynb +++ b/lessons/02 values and expressions.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "8f9b978d", "metadata": { "id": "fqMJHzNk5yXQ" }, @@ -10,9 +11,9 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)\n", + "Previous module: [1. Introduction](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B)\n", "\n", "### This module\n", "\n", @@ -23,6 +24,7 @@ }, { "cell_type": "markdown", + "id": "11c0192d", "metadata": { "id": "AqGUR1POYVNL" }, @@ -37,11 +39,12 @@ "- floating point - `float`\n", "- string - `str`\n", "- boolean - `bool`\n", - "- none - `None`\n" + "- none - `NoneType`\n" ] }, { "cell_type": "markdown", + "id": "fdf30577", "metadata": { "id": "8sSOrG7FdMT5" }, @@ -53,6 +56,7 @@ { "cell_type": "code", "execution_count": null, + "id": "71c926bf", "metadata": { "id": "wTlfkN-Sa_MG" }, @@ -67,6 +71,7 @@ }, { "cell_type": "markdown", + "id": "b8602048", "metadata": { "id": "iBCZZWfDdS7o" }, @@ -78,6 +83,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4ff819b4", "metadata": { "id": "2JbaI2-pbTF5" }, @@ -92,6 +98,7 @@ }, { "cell_type": "markdown", + "id": "f76481c5", "metadata": { "id": "om0zZYCBdd8F" }, @@ -103,6 +110,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9c47f5f9", "metadata": { "id": "DB6KZC5Sblwy" }, @@ -114,7 +122,7 @@ "\n", "\"I don't want to do this\"\n", "\n", - "# escaping difficult characters with \\ \n", + "# escaping difficult characters with \\\n", "\"I won't say \\\"banana\\\"\\\\\"\n", "\n", "# line breaks are preserved\n", @@ -128,6 +136,7 @@ }, { "cell_type": "markdown", + "id": "34dc9605", "metadata": { "id": "dQ6u3Syk4fS4" }, @@ -138,6 +147,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3b3acd68", "metadata": { "id": "7QCMtj3S4d6E" }, @@ -150,6 +160,7 @@ }, { "cell_type": "markdown", + "id": "65245758", "metadata": { "id": "ouf6r2zmdjY9" }, @@ -161,6 +172,7 @@ { "cell_type": "code", "execution_count": null, + "id": "fda18cad", "metadata": { "id": "8n81rHXFb9cl" }, @@ -175,6 +187,7 @@ }, { "cell_type": "markdown", + "id": "0b7a000a", "metadata": { "id": "xS0efw6fdoW9" }, @@ -186,6 +199,7 @@ { "cell_type": "code", "execution_count": null, + "id": "571494c8", "metadata": { "id": "fOZdR0YUcFRb" }, @@ -198,6 +212,7 @@ }, { "cell_type": "markdown", + "id": "758f2ed3", "metadata": { "id": "jockLUXXd2Ad" }, @@ -209,7 +224,8 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, + "id": "e65f1829", "metadata": { "id": "lHtfczHxd89N" }, @@ -225,6 +241,7 @@ }, { "cell_type": "markdown", + "id": "067d0c1c", "metadata": { "id": "kZffa20rXLOQ" }, @@ -235,6 +252,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2639d5d9", "metadata": { "id": "-Zv10HJFXUW8" }, @@ -246,6 +264,7 @@ }, { "cell_type": "markdown", + "id": "a437bc9b", "metadata": { "id": "rJ_mNXXAYIpy" }, @@ -256,6 +275,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7523b4f0", "metadata": { "id": "PN0FTHz2YRPM" }, @@ -266,6 +286,7 @@ }, { "cell_type": "markdown", + "id": "51041ce4", "metadata": { "id": "-0p3SmmgWdif" }, @@ -276,6 +297,7 @@ { "cell_type": "code", "execution_count": null, + "id": "675a981c", "metadata": { "id": "FPeklTLXWuXl" }, @@ -291,6 +313,7 @@ }, { "cell_type": "markdown", + "id": "9e39e5e4", "metadata": { "id": "YGU7CclEYz2y" }, @@ -301,6 +324,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c5f4580b", "metadata": { "id": "U_63E6jfab6N" }, @@ -315,6 +339,7 @@ }, { "cell_type": "markdown", + "id": "bee16848", "metadata": { "id": "0dh3t4uZa9AT" }, @@ -325,6 +350,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7c414fe8", "metadata": { "id": "ffIkwHahcGv9" }, @@ -339,6 +365,7 @@ }, { "cell_type": "markdown", + "id": "3f8edf00", "metadata": { "id": "InNZNIOpezlG" }, @@ -359,6 +386,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a077423f", "metadata": { "id": "wgaSYh4yfGCx" }, @@ -374,6 +402,7 @@ }, { "cell_type": "markdown", + "id": "327d1719", "metadata": { "id": "BD2s2EqKfQtx" }, @@ -384,6 +413,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9ea52775", "metadata": { "id": "pJ-N_rc8fZia" }, @@ -396,6 +426,7 @@ }, { "cell_type": "markdown", + "id": "8439dd2b", "metadata": { "id": "uADEckPZgGt_" }, @@ -406,6 +437,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5207d9a4", "metadata": { "id": "ehEig-sCgL48" }, @@ -420,6 +452,7 @@ }, { "cell_type": "markdown", + "id": "0d3af7bd", "metadata": { "id": "TaiYZ3R6ghq0" }, @@ -431,6 +464,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a36ec0ad", "metadata": { "id": "pIIq1la9g1Qr", "lines_to_next_cell": 2 @@ -451,6 +485,7 @@ }, { "cell_type": "markdown", + "id": "2db8430c", "metadata": { "id": "nMn_xu6Ih3hJ" }, @@ -461,7 +496,7 @@ "\n", "- must start with a letter or underscore (`_`)\n", "- can only contain letters (`a-z`, `A-Z`), numbers (`0-9`), and underscore (`_`)\n", - "- variable names are case sensitive. `name` is a different variable from `Name` \n", + "- variable names are case sensitive. `name` is a different variable from `Name`\n", "\n", "#### Conventions\n", "\n", @@ -491,6 +526,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2f4a7169", "metadata": { "id": "m4pTI2BEn-Z-" }, @@ -499,6 +535,7 @@ }, { "cell_type": "markdown", + "id": "209caedc", "metadata": { "id": "pDU1uK2Igmki" }, @@ -511,6 +548,7 @@ { "cell_type": "code", "execution_count": null, + "id": "b318da5a", "metadata": { "id": "FydkKnx_hUPq" }, @@ -523,6 +561,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5bfb1172", "metadata": { "id": "LTWods50h-Ij" }, @@ -535,6 +574,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3667b843", "metadata": { "id": "l2vT4L7piGRf" }, @@ -548,6 +588,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d2c30586", "metadata": { "id": "k1Z_yWLXiWy7" }, @@ -563,6 +604,7 @@ }, { "cell_type": "markdown", + "id": "6c612065", "metadata": { "id": "BZ50KykuAYPs" }, @@ -573,6 +615,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d08e4cae", "metadata": { "id": "X3xHg6K4Aicn" }, @@ -586,6 +629,7 @@ }, { "cell_type": "markdown", + "id": "9d1623b1", "metadata": { "id": "0az3tNK7WD3G" }, @@ -596,6 +640,7 @@ { "cell_type": "code", "execution_count": null, + "id": "840971fb", "metadata": { "id": "lY-M8mfSXDfG" }, @@ -609,6 +654,7 @@ }, { "cell_type": "markdown", + "id": "5aef5a59", "metadata": { "id": "dvNIQh7KYuJb" }, @@ -618,6 +664,7 @@ }, { "cell_type": "markdown", + "id": "6d2df9aa", "metadata": { "id": "GI9fyUO8XOcp" }, @@ -630,6 +677,7 @@ { "cell_type": "code", "execution_count": null, + "id": "6d512ea5", "metadata": { "id": "Om6z53RXYBoS" }, @@ -638,6 +686,7 @@ }, { "cell_type": "markdown", + "id": "5a05e6c2", "metadata": { "id": "h6UKIMXCj3w9" }, @@ -648,6 +697,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a663b2ea", "metadata": { "id": "3MI6_DY7ku30" }, @@ -665,19 +715,21 @@ }, { "cell_type": "markdown", + "id": "d18285ec", "metadata": { "id": "ZXd6jCn90CA_" }, "source": [ "## Expressions\n", "\n", - "- An expression is a combination between *operands* and *operators*. \n", + "- An expression is a combination between *operands* and *operators*.\n", "- Think of arithmetic: `1 + 2`\n", "\n" ] }, { "cell_type": "markdown", + "id": "ba164354", "metadata": { "id": "s8uJy_kI9C8S" }, @@ -688,6 +740,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e786fe18", "metadata": { "id": "f9ACtc0e13i-" }, @@ -708,6 +761,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1337b52c", "metadata": { "id": "w9cfOflP8DEJ" }, @@ -720,6 +774,7 @@ }, { "cell_type": "markdown", + "id": "d50d0917", "metadata": { "id": "zkt9aNKm28Lc" }, @@ -730,6 +785,7 @@ { "cell_type": "code", "execution_count": null, + "id": "43026531", "metadata": { "id": "6os4-rt43ThF" }, @@ -745,6 +801,7 @@ { "cell_type": "code", "execution_count": null, + "id": "534c3f0b", "metadata": { "id": "SyrelaMu42UZ" }, @@ -756,6 +813,7 @@ }, { "cell_type": "markdown", + "id": "1c8a578b", "metadata": { "id": "DPbfP65xXlfb" }, @@ -768,6 +826,7 @@ { "cell_type": "code", "execution_count": null, + "id": "16f12279", "metadata": { "id": "ogL2Iw3-Xs15" }, @@ -787,6 +846,7 @@ }, { "cell_type": "markdown", + "id": "a76aaab4", "metadata": { "id": "z_bXvnya5J2_" }, @@ -797,6 +857,7 @@ }, { "cell_type": "markdown", + "id": "278f5c93", "metadata": { "id": "j9xhznQlbCBf" }, @@ -807,6 +868,7 @@ { "cell_type": "code", "execution_count": null, + "id": "26844be1", "metadata": { "id": "0QcMB4xwbSfL" }, @@ -818,6 +880,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c338bd1c", "metadata": { "id": "eHKN9kP9bWkm" }, @@ -829,6 +892,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ee38b6c6", "metadata": { "id": "uINoRNNbbXwJ" }, @@ -842,6 +906,7 @@ { "cell_type": "code", "execution_count": null, + "id": "b175ad4c", "metadata": { "id": "4xyWkYlnbc_8" }, @@ -854,6 +919,7 @@ { "cell_type": "code", "execution_count": null, + "id": "63f9fb49", "metadata": { "id": "7tW2T4mebljv" }, @@ -865,6 +931,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a8a92ee1", "metadata": { "id": "uEdPHIhBb2Mw" }, @@ -877,6 +944,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a2017101", "metadata": { "id": "-D7BB50Qceo2" }, @@ -888,6 +956,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2a7882ce", "metadata": { "id": "OQUGr5rGck3m" }, @@ -899,6 +968,7 @@ { "cell_type": "code", "execution_count": null, + "id": "02d53c54", "metadata": { "id": "Jc8fawaIdCD5" }, @@ -913,6 +983,7 @@ }, { "cell_type": "markdown", + "id": "cd9db204", "metadata": { "id": "TlLkcRv-droI" }, @@ -923,13 +994,14 @@ { "cell_type": "code", "execution_count": null, + "id": "19764272", "metadata": { "id": "pYZUkeDJenob" }, "outputs": [], "source": [ - "character = 'o'\n", - "multiplier = 5\n", + "character = 'x'\n", + "multiplier = 0\n", "\n", "begin = 'G'\n", "middle = character * multiplier\n", @@ -940,6 +1012,7 @@ }, { "cell_type": "markdown", + "id": "31f38e9a", "metadata": { "id": "G8X4n_a8a5tl" }, @@ -952,6 +1025,7 @@ { "cell_type": "code", "execution_count": null, + "id": "89188cfa", "metadata": { "id": "nDUVvhDEfIVI" }, @@ -960,6 +1034,7 @@ }, { "cell_type": "markdown", + "id": "e562c3ff", "metadata": { "id": "3HCqdTj2fVPK" }, @@ -969,6 +1044,7 @@ }, { "cell_type": "markdown", + "id": "0dc29704", "metadata": { "id": "EKFdkLkWa8EY" }, @@ -983,6 +1059,7 @@ { "cell_type": "code", "execution_count": null, + "id": "852674d2", "metadata": { "id": "V59a7vpDfzO2" }, @@ -991,6 +1068,7 @@ }, { "cell_type": "markdown", + "id": "9fc1da91", "metadata": { "id": "o0WYIAUla-AX" }, @@ -1001,6 +1079,7 @@ { "cell_type": "code", "execution_count": null, + "id": "bd9c0a40", "metadata": { "id": "Z9Vd9pdIUzT5" }, @@ -1020,6 +1099,7 @@ }, { "cell_type": "markdown", + "id": "8d73aa72", "metadata": { "id": "DRObBQZHgsIG" }, @@ -1030,6 +1110,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8fba4694", "metadata": { "id": "uLuiUk10gqwM" }, @@ -1038,6 +1119,7 @@ }, { "cell_type": "markdown", + "id": "066088a3", "metadata": { "id": "QaamMzJY6ISR" }, @@ -1049,6 +1131,7 @@ { "cell_type": "code", "execution_count": null, + "id": "b4746c45", "metadata": { "id": "kLjya4Au6Ucu" }, @@ -1063,6 +1146,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9875485c", "metadata": { "id": "tYNp6nUp7ixW" }, @@ -1077,6 +1161,7 @@ { "cell_type": "code", "execution_count": null, + "id": "70288312", "metadata": { "id": "oixxKDHV7s5J" }, @@ -1096,6 +1181,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8b6cb185", "metadata": { "id": "Z198kfCf75sU" }, @@ -1113,6 +1199,7 @@ }, { "cell_type": "markdown", + "id": "441a972a", "metadata": { "id": "pvrcgKU18OrJ" }, @@ -1122,6 +1209,7 @@ }, { "cell_type": "markdown", + "id": "bcee304c", "metadata": { "id": "sWdnjezoil9j" }, @@ -1132,6 +1220,7 @@ { "cell_type": "code", "execution_count": null, + "id": "46b1c5c2", "metadata": { "id": "RpSvrom3Z8fQ" }, @@ -1151,6 +1240,7 @@ }, { "cell_type": "markdown", + "id": "77ebce29", "metadata": { "id": "zJiaGIlZBN_P" }, @@ -1161,6 +1251,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7ab62d95", "metadata": { "id": "_PrnFf2lioMB" }, @@ -1171,6 +1262,7 @@ }, { "cell_type": "markdown", + "id": "44df26b2", "metadata": { "id": "q8QviA70kdQE" }, @@ -1181,6 +1273,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a1fe97a2", "metadata": { "id": "qDQ9Ob5Zkrqm" }, @@ -1194,6 +1287,7 @@ }, { "cell_type": "markdown", + "id": "cde3daa5", "metadata": { "id": "YbdV7SQVmDVV" }, @@ -1204,6 +1298,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d43f28dd", "metadata": { "id": "hzjXIwkAmChv" }, @@ -1216,6 +1311,7 @@ }, { "cell_type": "markdown", + "id": "10aac3a0", "metadata": { "id": "SwyAMDzDn2_X" }, @@ -1226,6 +1322,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9b7f6bb8", "metadata": { "id": "lmCGfx_DqMWe" }, @@ -1238,40 +1335,26 @@ }, { "cell_type": "markdown", + "id": "05dd98bf", "metadata": { "id": "jXSxbjf4q6q5" }, "source": [ "## Next module\n", "\n", - "[3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)" + "[3. Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR)" ] } ], "metadata": { - "colab": { - "provenance": [] - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "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.6" } }, "nbformat": 4, - "nbformat_minor": 4 + "nbformat_minor": 5 } diff --git a/lessons/02 values and expressions.py b/lessons/02 values and expressions.py index 83ec85f..9882dd2 100644 --- a/lessons/02 values and expressions.py +++ b/lessons/02 values and expressions.py @@ -16,9 +16,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) +# Previous module: [1. Introduction](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B) # # ### This module # @@ -37,7 +37,7 @@ # - floating point - `float` # - string - `str` # - boolean - `bool` -# - none - `None` +# - none - `NoneType` # # %% [markdown] id="8sSOrG7FdMT5" @@ -73,7 +73,7 @@ "I don't want to do this" -# escaping difficult characters with \ +# escaping difficult characters with \ "I won't say \"banana\"\\" # line breaks are preserved @@ -233,7 +233,7 @@ # # - must start with a letter or underscore (`_`) # - can only contain letters (`a-z`, `A-Z`), numbers (`0-9`), and underscore (`_`) -# - variable names are case sensitive. `name` is a different variable from `Name` +# - variable names are case sensitive. `name` is a different variable from `Name` # # #### Conventions # @@ -331,7 +331,7 @@ # %% [markdown] id="ZXd6jCn90CA_" # ## Expressions # -# - An expression is a combination between *operands* and *operators*. +# - An expression is a combination between *operands* and *operators*. # - Think of arithmetic: `1 + 2` # # @@ -434,8 +434,8 @@ # 2. In the code block below, change the values of `character` and `multiplier` so that the output becomes `'Goooood!'`. # %% id="pYZUkeDJenob" -character = 'o' -multiplier = 5 +character = 'x' +multiplier = 0 begin = 'G' middle = character * multiplier @@ -572,4 +572,4 @@ # %% [markdown] id="jXSxbjf4q6q5" # ## Next module # -# [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) +# [3. Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR) diff --git a/lessons/03 conditionals.ipynb b/lessons/03 conditionals.ipynb index 3676ceb..c2838fb 100644 --- a/lessons/03 conditionals.ipynb +++ b/lessons/03 conditionals.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "1c5b818b", "metadata": { "id": "fqMJHzNk5yXQ" }, @@ -10,9 +11,9 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)\n", + "Previous module: [2. Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA)\n", "\n", "### This module\n", "\n", @@ -21,6 +22,7 @@ }, { "cell_type": "markdown", + "id": "a518c602", "metadata": { "id": "SshSsbtF8ldm" }, @@ -30,12 +32,13 @@ "- Only evaluate part of the code if some condition is met\n", "- Syntax: `if :`\n", "- `` can be any expression that evaluates to `True` or `False`\n", - "- Introducing *indented blocks* " + "- Introducing *indented blocks*" ] }, { "cell_type": "code", "execution_count": null, + "id": "bea5449d", "metadata": { "id": "Va6X8kIz9ey0" }, @@ -50,6 +53,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5dd17719", "metadata": { "id": "ADXv-2u090ql" }, @@ -66,16 +70,18 @@ }, { "cell_type": "markdown", + "id": "9ba226ed", "metadata": { "id": "5gqn2ac_sg7Y" }, "source": [ - "- Indented blocks are the reason we sometimes need `pass` as a placeholder (remember [1. introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)). It lets us create an indented block, without defining any behavior for it yet." + "- Indented blocks are the reason we sometimes need `pass` as a placeholder (remember [1. introduction](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B)). It lets us create an indented block, without defining any behavior for it yet." ] }, { "cell_type": "code", "execution_count": null, + "id": "c77086fe", "metadata": { "id": "ZdH7HnL6tDpS" }, @@ -90,6 +96,7 @@ }, { "cell_type": "markdown", + "id": "534406d9", "metadata": { "id": "sQxxBZwm-FYm" }, @@ -102,6 +109,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c80847d0", "metadata": { "id": "atRhlkGq-Mh1" }, @@ -112,7 +120,7 @@ "if letter == 'a':\n", " print('we found the letter a')\n", "\n", - "if letter == 'b': \n", + "if letter == 'b':\n", " print('this is not a')\n", "\n", "if letter == 'c':\n", @@ -121,6 +129,7 @@ }, { "cell_type": "markdown", + "id": "4d56fd85", "metadata": { "id": "O1repWSS-3Y0" }, @@ -131,6 +140,7 @@ { "cell_type": "code", "execution_count": null, + "id": "bee181f4", "metadata": { "id": "4n9ksUuH-8kE" }, @@ -146,6 +156,7 @@ }, { "cell_type": "markdown", + "id": "a2118aa8", "metadata": { "id": "Rg1ohowkAGFh" }, @@ -159,6 +170,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a8a0d962", "metadata": { "id": "JbiihBgdANIM" }, @@ -176,6 +188,7 @@ }, { "cell_type": "markdown", + "id": "3c4556a6", "metadata": { "id": "lVOu8HvVIEj6" }, @@ -187,6 +200,7 @@ { "cell_type": "code", "execution_count": null, + "id": "99358d0f", "metadata": { "id": "MH_RrrBRIPh4" }, @@ -197,7 +211,7 @@ "if number > 2:\n", " if number < 10:\n", " print('between 2 and 10')\n", - " print('larger than 2, but not smaller than 10')\n", + " print('greater than 2, but not less than 10')\n", "\n", "# There is a mistake in the code above, can you find\n", "# and fix it?" @@ -205,6 +219,7 @@ }, { "cell_type": "markdown", + "id": "8bae5e2a", "metadata": { "id": "8Lvam7rpIms6" }, @@ -216,6 +231,7 @@ { "cell_type": "code", "execution_count": null, + "id": "365c1f69", "metadata": { "id": "zJ8rBgcfIw4I" }, @@ -235,6 +251,7 @@ }, { "cell_type": "markdown", + "id": "34b3ddb6", "metadata": { "id": "SmsIZBLEtg9r" }, @@ -245,6 +262,7 @@ { "cell_type": "code", "execution_count": null, + "id": "6a6fe5fc", "metadata": { "id": "XMWJeaujt2lj" }, @@ -262,6 +280,7 @@ }, { "cell_type": "markdown", + "id": "3512604b", "metadata": { "id": "tvXa9KWXAwge" }, @@ -271,6 +290,7 @@ }, { "cell_type": "markdown", + "id": "b7c5f5a1", "metadata": { "id": "pKBuViM9u7ZC" }, @@ -281,6 +301,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5e299b96", "metadata": { "id": "sEQyw2xJvzQN" }, @@ -294,6 +315,7 @@ { "cell_type": "code", "execution_count": null, + "id": "eec84109", "metadata": { "id": "FnCFfEb_v-0Y" }, @@ -307,6 +329,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f645aaef", "metadata": { "id": "arta4ZQ0vDSC" }, @@ -324,6 +347,7 @@ }, { "cell_type": "markdown", + "id": "56478e72", "metadata": { "id": "4SVSiqHSu4WX" }, @@ -334,6 +358,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5d59b496", "metadata": { "id": "czmdFacjeUaL" }, @@ -351,6 +376,7 @@ }, { "cell_type": "markdown", + "id": "dd935e5d", "metadata": { "id": "sEOwMi04e6WW" }, @@ -361,6 +387,7 @@ { "cell_type": "code", "execution_count": null, + "id": "b4bc610d", "metadata": { "id": "8HeaY6l9f9iA" }, @@ -375,6 +402,7 @@ }, { "cell_type": "markdown", + "id": "2753aef2", "metadata": { "id": "TWzbez_2RUh4" }, @@ -385,6 +413,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a9267c36", "metadata": { "id": "XsIg1MD4JrPX" }, @@ -399,6 +428,7 @@ }, { "cell_type": "markdown", + "id": "75179bb9", "metadata": { "id": "-XEYQZJ1ya1j" }, @@ -408,6 +438,7 @@ }, { "cell_type": "markdown", + "id": "5238dc47", "metadata": { "id": "POVFwRu_f91I" }, @@ -420,12 +451,13 @@ "- if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`;\n", "- in all remaining cases, print `value`.\n", "\n", - "Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)!" + "Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA#scrollTo=Exercise_2_4_Bonus)!" ] }, { "cell_type": "code", "execution_count": null, + "id": "15f5fb73", "metadata": { "id": "SZGeQtqEhiAK" }, @@ -438,33 +470,26 @@ }, { "cell_type": "markdown", + "id": "16547959", "metadata": { "id": "YBC4OfihzFho" }, "source": [ "## Next module\n", "\n", - "[4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)" + "[4. Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt)" ] } ], "metadata": { - "colab": { - "authorship_tag": "ABX9TyOwt+JcPyfyBfscI/sF+CKo", - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "name": "python3" - }, - "language_info": { - "name": "python" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 5 } diff --git a/lessons/03 conditionals.py b/lessons/03 conditionals.py index 68bac63..010a554 100644 --- a/lessons/03 conditionals.py +++ b/lessons/03 conditionals.py @@ -16,9 +16,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) +# Previous module: [2. Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA) # # ### This module # @@ -30,7 +30,7 @@ # - Only evaluate part of the code if some condition is met # - Syntax: `if :` # - `` can be any expression that evaluates to `True` or `False` -# - Introducing *indented blocks* +# - Introducing *indented blocks* # %% id="Va6X8kIz9ey0" a = 12 @@ -48,7 +48,7 @@ print('but not this!') # %% [markdown] id="5gqn2ac_sg7Y" -# - Indented blocks are the reason we sometimes need `pass` as a placeholder (remember [1. introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)). It lets us create an indented block, without defining any behavior for it yet. +# - Indented blocks are the reason we sometimes need `pass` as a placeholder (remember [1. introduction](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B)). It lets us create an indented block, without defining any behavior for it yet. # %% id="ZdH7HnL6tDpS" earth = 'round' @@ -68,7 +68,7 @@ if letter == 'a': print('we found the letter a') -if letter == 'b': +if letter == 'b': print('this is not a') if letter == 'c': @@ -112,7 +112,7 @@ if number > 2: if number < 10: print('between 2 and 10') - print('larger than 2, but not smaller than 10') + print('greater than 2, but not less than 10') # There is a mistake in the code above, can you find # and fix it? @@ -218,7 +218,7 @@ # - if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`; # - in all remaining cases, print `value`. # -# Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)! +# Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA#scrollTo=Exercise_2_4_Bonus)! # %% id="SZGeQtqEhiAK" value = 9 @@ -228,4 +228,4 @@ # %% [markdown] id="YBC4OfihzFho" # ## Next module # -# [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) +# [4. Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt) diff --git a/lessons/04 datastructures.ipynb b/lessons/04 datastructures.ipynb index 493b559..0d5896a 100644 --- a/lessons/04 datastructures.ipynb +++ b/lessons/04 datastructures.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "dab1ccb5", "metadata": { "id": "fqMJHzNk5yXQ" }, @@ -10,9 +11,9 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)\n", + "Previous module: [3. Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR)\n", "\n", "### This module\n", "\n", @@ -21,6 +22,7 @@ }, { "cell_type": "markdown", + "id": "5b25ab3e", "metadata": { "id": "rDdBkbX5kmUD" }, @@ -35,6 +37,7 @@ { "cell_type": "code", "execution_count": null, + "id": "eab24efb", "metadata": { "id": "bmAaWSOPm87H" }, @@ -48,6 +51,7 @@ }, { "cell_type": "markdown", + "id": "cbae0644", "metadata": { "id": "8Vl5wbRunR82" }, @@ -55,7 +59,7 @@ "## Lists\n", "\n", "- `list`: an ordered collection of values\n", - "- One type of *iterable*, a collection you that allows iteration over its elements\n", + "- One type of *iterable*, a collection that allows iteration over its elements\n", "- Syntax: `[element1, element2, ...]`\n", "- Empty list also exists: `[]`" ] @@ -63,6 +67,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5d5b238d", "metadata": { "id": "sewvhM8JnhJZ" }, @@ -75,6 +80,7 @@ }, { "cell_type": "markdown", + "id": "3a7dadc3", "metadata": { "id": "zmKyJPoQnwmK" }, @@ -85,6 +91,7 @@ { "cell_type": "code", "execution_count": null, + "id": "55f5537e", "metadata": { "id": "Bp6_Mev2nzZV" }, @@ -95,6 +102,7 @@ }, { "cell_type": "markdown", + "id": "e8194c9a", "metadata": { "id": "dt_IOpu_rqbk" }, @@ -105,6 +113,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1de01395", "metadata": { "id": "TTCPoO7QrtVy" }, @@ -117,6 +126,7 @@ }, { "cell_type": "markdown", + "id": "28d30849", "metadata": { "id": "uYR5FBUEoR0P" }, @@ -130,6 +140,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d7f02d69", "metadata": { "id": "2eL0BOUJodLK" }, @@ -144,6 +155,7 @@ }, { "cell_type": "markdown", + "id": "93da8a02", "metadata": { "id": "cyX0YcO5uZRa" }, @@ -154,6 +166,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2881eb8a", "metadata": { "id": "m6ETvPPXuc4z" }, @@ -165,6 +178,7 @@ }, { "cell_type": "markdown", + "id": "18912c92", "metadata": { "id": "KvOEQrqRrS0T" }, @@ -176,6 +190,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a55fb882", "metadata": { "id": "wFyceuSArcEB" }, @@ -192,6 +207,7 @@ }, { "cell_type": "markdown", + "id": "cf10c022", "metadata": { "id": "DixopwTyr6gN" }, @@ -204,6 +220,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1ce840b6", "metadata": { "id": "nPyn0UHcsDbG" }, @@ -219,6 +236,7 @@ { "cell_type": "code", "execution_count": null, + "id": "531a3326", "metadata": { "id": "Y2B57KQRsO2a" }, @@ -231,12 +249,13 @@ "\n", "students.remove('john')\n", "# or by index:\n", - "# del students[2]\n", + "del students[2]\n", "print(students)" ] }, { "cell_type": "markdown", + "id": "b42a6fad", "metadata": { "id": "kUUwwkDVtXOC" }, @@ -248,6 +267,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2a912f03", "metadata": { "id": "EPxrRcL0tbpN" }, @@ -265,6 +285,7 @@ }, { "cell_type": "markdown", + "id": "3ae45d81", "metadata": { "id": "1HDqXMbWwmbk" }, @@ -278,6 +299,7 @@ { "cell_type": "code", "execution_count": null, + "id": "bc3f847f", "metadata": { "id": "wIS3jCYlw2P6", "lines_to_next_cell": 2 @@ -292,6 +314,7 @@ }, { "cell_type": "markdown", + "id": "38ee6f6e", "metadata": { "id": "BblECQZfw7Uy" }, @@ -302,6 +325,7 @@ { "cell_type": "code", "execution_count": null, + "id": "af086b82", "metadata": { "id": "6fIsX2VvxEq9", "lines_to_next_cell": 2 @@ -316,6 +340,7 @@ }, { "cell_type": "markdown", + "id": "3b0d4ba4", "metadata": { "id": "n9lZQ72ExR4Z" }, @@ -327,6 +352,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4a991284", "metadata": { "id": "C5AIMJEHxWnX" }, @@ -341,6 +367,7 @@ }, { "cell_type": "markdown", + "id": "515b9ccc", "metadata": { "id": "CPHEdywi-IuC" }, @@ -351,6 +378,7 @@ { "cell_type": "code", "execution_count": null, + "id": "76cb774b", "metadata": { "id": "Tzhdcojp-TTn" }, @@ -367,6 +395,7 @@ }, { "cell_type": "markdown", + "id": "ad5f28cd", "metadata": { "id": "nfpm1orRO34Q" }, @@ -378,6 +407,7 @@ { "cell_type": "code", "execution_count": null, + "id": "db03b982", "metadata": { "id": "0A9JACKJPCYt" }, @@ -391,6 +421,7 @@ }, { "cell_type": "markdown", + "id": "a57ee627", "metadata": { "id": "2NX28b3sZscv" }, @@ -402,6 +433,7 @@ { "cell_type": "code", "execution_count": null, + "id": "230e95b4", "metadata": { "id": "A7UHSeTtZ2nw" }, @@ -413,6 +445,7 @@ }, { "cell_type": "markdown", + "id": "79097897", "metadata": { "id": "cO6hX3FBZ6cC" }, @@ -423,6 +456,7 @@ { "cell_type": "code", "execution_count": null, + "id": "72a92a9a", "metadata": { "id": "VPmLssc7aByj" }, @@ -434,6 +468,7 @@ }, { "cell_type": "markdown", + "id": "396234f8", "metadata": { "id": "ZyOZeS2SuRJ6" }, @@ -448,6 +483,7 @@ { "cell_type": "code", "execution_count": null, + "id": "192339b7", "metadata": { "id": "etiZVM_puu4Z" }, @@ -459,6 +495,7 @@ }, { "cell_type": "markdown", + "id": "8ffa8364", "metadata": { "id": "70aMsClGPRy9" }, @@ -471,6 +508,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8ae7bd9d", "metadata": { "id": "KMUxwcSqPlU1" }, @@ -491,17 +529,19 @@ }, { "cell_type": "markdown", + "id": "2f44b00d", "metadata": { "id": "TyebsOIpU6hv" }, "source": [ - "2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. \n", + "2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, + "id": "f798f46f", "metadata": { "id": "H8o6vsHKVKoq" }, @@ -512,6 +552,7 @@ }, { "cell_type": "markdown", + "id": "65fa2d86", "metadata": { "id": "HMU5X7XFWbCw" }, @@ -522,6 +563,7 @@ { "cell_type": "code", "execution_count": null, + "id": "81ca851a", "metadata": { "id": "u_RWc8wBWgMT" }, @@ -544,6 +586,7 @@ }, { "cell_type": "markdown", + "id": "2cb2ef9c", "metadata": { "id": "3BfUO-jKS_u1" }, @@ -555,7 +598,8 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, + "id": "209d241b", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -573,18 +617,7 @@ "id": "Y9oxyQb7TIPI", "outputId": "158288d5-94e0-4068-d13e-af2a5c85177f" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n", - "['japan', 'maldives', 'bhutan']\n", - "['bhutan', 'gabon', 'maldives', 'hungary', 'japan']\n", - "['bhutan', 'maldives', 'japan']\n" - ] - } - ], + "outputs": [], "source": [ "countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n", "\n", @@ -596,6 +629,7 @@ }, { "cell_type": "markdown", + "id": "925bfc02", "metadata": { "id": "Mb6CvHt3CaA0" }, @@ -610,6 +644,7 @@ { "cell_type": "code", "execution_count": null, + "id": "fe41d273", "metadata": { "id": "QQyGzsqCCe3o" }, @@ -631,41 +666,26 @@ }, { "cell_type": "markdown", + "id": "c1df1ecd", "metadata": { "id": "HiEWGB1V1W4U" }, "source": [ "## Next module\n", "\n", - "[5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM)" + "[5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH)" ] } ], "metadata": { - "colab": { - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "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": 0 + "nbformat_minor": 5 } diff --git a/lessons/04 datastructures.py b/lessons/04 datastructures.py index c78bf62..e3bfccd 100644 --- a/lessons/04 datastructures.py +++ b/lessons/04 datastructures.py @@ -16,9 +16,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) +# Previous module: [3. Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR) # # ### This module # @@ -41,7 +41,7 @@ # ## Lists # # - `list`: an ordered collection of values -# - One type of *iterable*, a collection you that allows iteration over its elements +# - One type of *iterable*, a collection that allows iteration over its elements # - Syntax: `[element1, element2, ...]` # - Empty list also exists: `[]` @@ -117,7 +117,7 @@ students.remove('john') # or by index: -# del students[2] +del students[2] print(students) # %% [markdown] id="kUUwwkDVtXOC" @@ -236,7 +236,7 @@ print(countries) # %% [markdown] id="TyebsOIpU6hv" -# 2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. +# 2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. # # @@ -298,4 +298,4 @@ # %% [markdown] id="HiEWGB1V1W4U" # ## Next module # -# [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) +# [5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH) diff --git a/lessons/05 assertions.ipynb b/lessons/05 assertions.ipynb index 8d62cb8..a738e00 100644 --- a/lessons/05 assertions.ipynb +++ b/lessons/05 assertions.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "d1e288dd", "metadata": { "id": "fqMJHzNk5yXQ" }, @@ -12,9 +13,9 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)\n", + "Previous module: [4. Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt)\n", "\n", "### This module\n", "\n", @@ -23,6 +24,7 @@ }, { "cell_type": "markdown", + "id": "5733668f", "metadata": { "id": "bxJaE7DE4ig4" }, @@ -35,6 +37,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ccbed536", "metadata": { "id": "PzRUSOI54_Zq" }, @@ -45,6 +48,7 @@ }, { "cell_type": "markdown", + "id": "c91fb5c1", "metadata": { "id": "tPPNPy2q5hAU" }, @@ -55,6 +59,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d592292d", "metadata": { "id": "58Pa2nRq5x8R" }, @@ -67,6 +72,7 @@ }, { "cell_type": "markdown", + "id": "ddbea610", "metadata": { "id": "aLDwedXecSLp" }, @@ -78,6 +84,7 @@ }, { "cell_type": "markdown", + "id": "5e733cff", "metadata": { "id": "m_FYfvXbbZXe" }, @@ -89,6 +96,7 @@ { "cell_type": "code", "execution_count": null, + "id": "48bac916", "metadata": { "id": "ztDylwg9biL5" }, @@ -100,6 +108,7 @@ { "cell_type": "code", "execution_count": null, + "id": "bd02bdd8", "metadata": { "id": "0Uk4w2DBbxfD" }, @@ -111,6 +120,7 @@ { "cell_type": "code", "execution_count": null, + "id": "72ac17b4", "metadata": { "id": "orOWCpWVbzKf" }, @@ -122,6 +132,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8adc5e1e", "metadata": { "id": "F6NjZ7gOb05u" }, @@ -133,6 +144,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7eb92f3e", "metadata": { "id": "KB_YkNSIb2KT" }, @@ -144,6 +156,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5e924dc4", "metadata": { "id": "1iUK81Nvb3Ri" }, @@ -155,6 +168,7 @@ { "cell_type": "code", "execution_count": null, + "id": "45e4b599", "metadata": { "id": "Tje6e-Jgb4rn" }, @@ -165,12 +179,13 @@ }, { "cell_type": "markdown", + "id": "18b221d7", "metadata": { "id": "xgqh3r7Bcj_F" }, "source": [ "## Exercise 5.2: Bonus - Test-driven development\n", - "[Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) is a proccess where you **first** write some test conditions, and then write the code that should satisfy these conditions. \n", + "[Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) is a proccess where you **first** write some test conditions, and then write the code that should satisfy these conditions.\n", "Implement the following programs in these steps:\n", "1. Create multiple test conditions using `assert`\n", "2. Run the cell, the tests should fail (output `AssertionError`)\n", @@ -182,6 +197,7 @@ }, { "cell_type": "markdown", + "id": "45e33773", "metadata": { "id": "TMWSMWg7dQqB" }, @@ -192,24 +208,28 @@ { "cell_type": "code", "execution_count": null, + "id": "38a894f2", "metadata": { "id": "Q_yIUKSRdVjF" }, "outputs": [], "source": [ "a = 12\n", + "\n", + "# Change the value for b\n", "b = 0\n", "\n", "# Write an expression for c using a and b\n", "c = 0\n", "\n", - "assert a < b, 'a should be smaller than b'\n", + "assert a < b, 'a should be less than b'\n", "assert a != b, 'a and b should not be equal'\n", "assert c == 18, 'c should be 18'" ] }, { "cell_type": "markdown", + "id": "fdff394a", "metadata": { "id": "1u_bBUpSfQr5" }, @@ -220,6 +240,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f2b9aa4b", "metadata": { "id": "UOp8NFVOfR6Z" }, @@ -233,41 +254,26 @@ }, { "cell_type": "markdown", + "id": "ff59d4d7", "metadata": { "id": "JaaguG-D3k_i" }, "source": [ "## Next module\n", "\n", - "[6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)" + "[6. Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ)" ] } ], "metadata": { - "colab": { - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "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": 0 + "nbformat_minor": 5 } diff --git a/lessons/05 assertions.py b/lessons/05 assertions.py index abfaf3a..fab576c 100644 --- a/lessons/05 assertions.py +++ b/lessons/05 assertions.py @@ -18,9 +18,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) +# Previous module: [4. Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt) # # ### This module # @@ -74,7 +74,7 @@ # %% [markdown] id="xgqh3r7Bcj_F" # ## Exercise 5.2: Bonus - Test-driven development -# [Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) is a proccess where you **first** write some test conditions, and then write the code that should satisfy these conditions. +# [Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) is a proccess where you **first** write some test conditions, and then write the code that should satisfy these conditions. # Implement the following programs in these steps: # 1. Create multiple test conditions using `assert` # 2. Run the cell, the tests should fail (output `AssertionError`) @@ -89,12 +89,14 @@ # %% id="Q_yIUKSRdVjF" a = 12 + +# Change the value for b b = 0 # Write an expression for c using a and b c = 0 -assert a < b, 'a should be smaller than b' +assert a < b, 'a should be less than b' assert a != b, 'a and b should not be equal' assert c == 18, 'c should be 18' @@ -110,4 +112,4 @@ # %% [markdown] id="JaaguG-D3k_i" # ## Next module # -# [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) +# [6. Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ) diff --git a/lessons/06 Loops - Legacy.ipynb b/lessons/06 Loops - Legacy.ipynb index a251347..4a7c5e2 100644 --- a/lessons/06 Loops - Legacy.ipynb +++ b/lessons/06 Loops - Legacy.ipynb @@ -3,6 +3,7 @@ { "cell_type": "code", "execution_count": null, + "id": "89e422cf", "metadata": { "id": "ZGnlpVha4WwJ" }, @@ -11,6 +12,7 @@ }, { "cell_type": "markdown", + "id": "81bf43e5", "metadata": { "id": "fqMJHzNk5yXQ" }, @@ -22,6 +24,7 @@ }, { "cell_type": "markdown", + "id": "295e7211", "metadata": { "id": "mzWET4w4lAr4" }, @@ -39,6 +42,7 @@ }, { "cell_type": "markdown", + "id": "1dff2016", "metadata": { "id": "d7GJLYfEXwfs" }, @@ -50,7 +54,8 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, + "id": "7d600781", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -68,15 +73,7 @@ "id": "Fnut4-DIYmb8", "outputId": "91a840be-aa92-47c9-c07f-bb52cb0447bc" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sheean!!!!\n" - ] - } - ], + "outputs": [], "source": [ "name = 'Sheean'\n", "while len(name) < 10:\n", @@ -86,6 +83,7 @@ }, { "cell_type": "markdown", + "id": "babda674", "metadata": { "id": "p9-je4X0zkhq" }, @@ -95,6 +93,7 @@ }, { "cell_type": "markdown", + "id": "89d88845", "metadata": { "id": "BDCe1ux90B7r" }, @@ -107,6 +106,7 @@ { "cell_type": "code", "execution_count": null, + "id": "03788d39", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -124,17 +124,7 @@ "id": "O0XEpRMI0WG2", "outputId": "41b9437b-26e9-45c4-ffb6-48e2d0510e0b" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "hello\n", - "True\n" - ] - } - ], + "outputs": [], "source": [ "the_list = [1, 'hello', True]\n", "\n", @@ -144,6 +134,7 @@ }, { "cell_type": "markdown", + "id": "67b01e94", "metadata": { "id": "gdd6v6LU1DlK" }, @@ -154,6 +145,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5bc26640", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -171,22 +163,7 @@ "id": "dpqb0qFc1u0s", "outputId": "9311ae9c-066d-4dbf-da9a-7c440473dac3" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "A\n", - "A \n", - "very\n", - "A very \n", - "short\n", - "A very short \n", - "sentence\n", - "A very short sentence \n" - ] - } - ], + "outputs": [], "source": [ "words = ['A', 'very', 'short', 'sentence']\n", "full_text = ''\n", @@ -199,6 +176,7 @@ }, { "cell_type": "markdown", + "id": "61e8920e", "metadata": { "id": "vDU53qkB2zo4" }, @@ -209,6 +187,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c512b35a", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -226,15 +205,7 @@ "id": "q6ucnoqeUnlI", "outputId": "461dfdab-3387-4d9e-d59e-ad73fb8993cd" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "35\n" - ] - } - ], + "outputs": [], "source": [ "numbers = [9, 9, 4, 7, 6]\n", "sum = 0\n", @@ -246,6 +217,7 @@ }, { "cell_type": "markdown", + "id": "543b0dc6", "metadata": { "id": "0Gun_3cX1ey8" }, @@ -258,6 +230,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2c3ce752", "metadata": { "id": "nMF8WE3F19HC" }, @@ -274,6 +247,7 @@ }, { "cell_type": "markdown", + "id": "67daf6fe", "metadata": { "id": "8zB10pLC2ZaT" }, @@ -284,6 +258,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3e1db262", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -302,19 +277,7 @@ "lines_to_next_cell": 2, "outputId": "6d1d0526-c780-4eaa-d9e4-2262de2691c2" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "apricot ['apricot']\n", - "banana ['banana', 'apricot']\n", - "cherry ['cherry', 'banana', 'apricot']\n", - "date ['date', 'cherry', 'banana', 'apricot']\n", - "['date', 'cherry', 'banana', 'apricot']\n" - ] - } - ], + "outputs": [], "source": [ "fruits = ['apricot', 'banana', 'cherry', 'date']\n", "\n", @@ -361,6 +324,7 @@ }, { "cell_type": "markdown", + "id": "bbeefa47", "metadata": { "id": "DATxv0pM2gQc" }, @@ -372,6 +336,7 @@ }, { "cell_type": "markdown", + "id": "4befa592", "metadata": { "id": "MVcUZD4T7j4h" }, @@ -382,6 +347,7 @@ { "cell_type": "code", "execution_count": null, + "id": "15f429cd", "metadata": { "id": "l40DJzDx2nCz" }, @@ -393,6 +359,7 @@ }, { "cell_type": "markdown", + "id": "1105ef95", "metadata": { "id": "0axR682t-ub4" }, @@ -402,6 +369,7 @@ }, { "cell_type": "markdown", + "id": "d4d424c4", "metadata": { "id": "2xzzyBX43Rbq" }, @@ -412,6 +380,7 @@ { "cell_type": "code", "execution_count": null, + "id": "652d8740", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -429,19 +398,7 @@ "id": "kbbKDHwd4Eiq", "outputId": "6ff54d8a-dfa6-4995-c469-1b3177bce245" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "apricot\n", - "banana\n", - "cherry\n", - "date\n", - "Aww no elderberry. 😞\n" - ] - } - ], + "outputs": [], "source": [ "basket = ['apricot', 'banana', 'cherry', 'date']\n", "\n", @@ -456,6 +413,7 @@ }, { "cell_type": "markdown", + "id": "6720806e", "metadata": { "id": "3C9WlDSd-8zw" }, @@ -465,6 +423,7 @@ }, { "cell_type": "markdown", + "id": "1386825b", "metadata": { "id": "ZZGIvGNg673Z" }, @@ -475,6 +434,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2a866e7d", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -492,16 +452,7 @@ "id": "AyVZVJtL7nMJ", "outputId": "2df5b782-b01c-4bea-812f-4f17ab255e5e" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "dishwashing\n", - "laundry\n" - ] - } - ], + "outputs": [], "source": [ "chores = ['dishwashing', 'vacuum cleaning', 'laundry']\n", "\n", @@ -514,6 +465,7 @@ }, { "cell_type": "markdown", + "id": "35fd0237", "metadata": { "id": "3fs83n3H_J_j" }, @@ -523,6 +475,7 @@ }, { "cell_type": "markdown", + "id": "5dd0eb8f", "metadata": { "id": "FHf46PtqBvVb" }, @@ -537,6 +490,7 @@ { "cell_type": "code", "execution_count": null, + "id": "70762c31", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -554,20 +508,7 @@ "id": "WffnwizrCvPw", "outputId": "64cc2f10-38da-4276-a401-26eeffd95890" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "new row:\n", - "bread; fluffy; light brown; \n", - "new row:\n", - "olives; savory; dark green; \n", - "new row:\n", - "grapes; sweet; shiny red; \n" - ] - } - ], + "outputs": [], "source": [ "table = [\n", " ['bread', 'fluffy', 'light brown'],\n", @@ -584,6 +525,7 @@ }, { "cell_type": "markdown", + "id": "87867218", "metadata": { "id": "DZVVVSYy8HGd" }, @@ -593,7 +535,8 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, + "id": "76e3ba42", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -611,15 +554,7 @@ "id": "14DRs2jK8Po7", "outputId": "bd49b199-0647-4e5b-86fd-19c7d09497ee" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "107 lowercase, 11 uppercase and 60 other.\n" - ] - } - ], + "outputs": [], "source": [ "invitation = '''\n", " Dear Sheean,\n", @@ -647,6 +582,7 @@ }, { "cell_type": "markdown", + "id": "5ebd49f7", "metadata": { "id": "HrfN3OhbELuM" }, @@ -661,6 +597,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7b83f22d", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -678,19 +615,7 @@ "id": "iqNQLZVcHYRX", "outputId": "40591d82-d9d1-4c9c-c887-042daa2f9a81" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0\n", - "1\n", - "2\n", - "3\n", - "4\n" - ] - } - ], + "outputs": [], "source": [ "for number in range(5):\n", " print(number)" @@ -698,6 +623,7 @@ }, { "cell_type": "markdown", + "id": "eff4e0e6", "metadata": { "id": "9GySTLd_Hq9l" }, @@ -708,6 +634,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2a333650", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -725,18 +652,7 @@ "id": "MIyn99IxH74D", "outputId": "0c2f832c-c245-49ef-c51c-da9903e163e5" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fruit number 0 is apricot\n", - "Fruit number 1 is banana\n", - "Fruit number 2 is cherry\n", - "Fruit number 3 is date\n" - ] - } - ], + "outputs": [], "source": [ "basket = ['apricot', 'banana', 'cherry', 'date']\n", "\n", @@ -746,6 +662,7 @@ }, { "cell_type": "markdown", + "id": "7847ddf9", "metadata": { "id": "WGlaXiuxISRX" }, @@ -755,6 +672,7 @@ }, { "cell_type": "markdown", + "id": "fd7c1309", "metadata": { "id": "-K0CvNkOLEYE" }, @@ -769,6 +687,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5cec1b7c", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -786,20 +705,7 @@ "id": "7gappYcLOrsu", "outputId": "6fff15b1-41c9-4798-bbb6-43c9ef12c85d" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "0\n", - "1\n", - "9\n", - "length: 10\n" - ] - } - ], + "outputs": [], "source": [ "basket = ['apricot', 'banana', 'cherry', 'date']\n", "\n", @@ -828,6 +734,7 @@ }, { "cell_type": "markdown", + "id": "9655ec32", "metadata": { "id": "2Haq4E95bN6T" }, @@ -838,6 +745,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3c2f7f3b", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -855,18 +763,7 @@ "id": "0k_YQbBccyC_", "outputId": "15b0c654-f047-4036-b95c-36a42d112d6b" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Please give me a word: charming\n", - "you wrote charming\n", - "the length is 8\n", - "Please give me a word: stop\n" - ] - } - ], + "outputs": [], "source": [ "# word = input('Please give me a word: ')\n", "# print('you wrote', word)\n", @@ -891,6 +788,7 @@ }, { "cell_type": "markdown", + "id": "66919c10", "metadata": { "id": "uyqbuhKsUlhG" }, @@ -901,6 +799,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f2dfcdac", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -918,18 +817,7 @@ "id": "BUeMXIQXaKna", "outputId": "2852de82-cad6-4441-8f47-5555c83f08a4" }, - "outputs": [ - { - "data": { - "text/plain": [ - "10" - ] - }, - "execution_count": 74, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# the end=', ' argument will help you:\n", "print('this prints with a comma instead of a newline', end=', ')\n", @@ -974,31 +862,14 @@ } ], "metadata": { - "colab": { - "authorship_tag": "ABX9TyO/OJ7mLjXRcw3YDskGFU1J", - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "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": 0 + "nbformat_minor": 5 } diff --git a/lessons/06 Loops.ipynb b/lessons/06 Loops.ipynb index ddc5281..00bd5c1 100644 --- a/lessons/06 Loops.ipynb +++ b/lessons/06 Loops.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "6c514565", "metadata": { "id": "fqMJHzNk5yXQ" }, @@ -10,13 +11,14 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) " + "Previous module: [5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH)" ] }, { "cell_type": "markdown", + "id": "4bd33073", "metadata": { "id": "mzWET4w4lAr4" }, @@ -34,6 +36,7 @@ }, { "cell_type": "markdown", + "id": "afc41fc6", "metadata": { "id": "d7GJLYfEXwfs" }, @@ -46,6 +49,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8c1c7641", "metadata": { "id": "Fnut4-DIYmb8" }, @@ -59,6 +63,7 @@ }, { "cell_type": "markdown", + "id": "66da3b4e", "metadata": { "id": "p9-je4X0zkhq" }, @@ -68,6 +73,7 @@ }, { "cell_type": "markdown", + "id": "f4641fa4", "metadata": { "id": "BDCe1ux90B7r" }, @@ -80,6 +86,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4e195ba2", "metadata": { "id": "O0XEpRMI0WG2" }, @@ -93,6 +100,7 @@ }, { "cell_type": "markdown", + "id": "f5ed9d5a", "metadata": { "id": "gdd6v6LU1DlK" }, @@ -103,6 +111,7 @@ { "cell_type": "code", "execution_count": null, + "id": "45703111", "metadata": { "id": "dpqb0qFc1u0s" }, @@ -119,6 +128,7 @@ }, { "cell_type": "markdown", + "id": "3b4694ba", "metadata": { "id": "vDU53qkB2zo4" }, @@ -129,32 +139,11 @@ { "cell_type": "code", "execution_count": null, + "id": "4acb40f2", "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 550, - "status": "ok", - "timestamp": 1667900533282, - "user": { - "displayName": "Julian Gonggrijp", - "userId": "06467962548183964912" - }, - "user_tz": -60 - }, - "id": "q6ucnoqeUnlI", - "outputId": "461dfdab-3387-4d9e-d59e-ad73fb8993cd" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "35\n" - ] - } - ], + "id": "q6ucnoqeUnlI" + }, + "outputs": [], "source": [ "numbers = [9, 9, 4, 7, 6]\n", "sum = 0\n", @@ -166,6 +155,7 @@ }, { "cell_type": "markdown", + "id": "c297fed9", "metadata": { "id": "0Gun_3cX1ey8" }, @@ -178,6 +168,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f77efa79", "metadata": { "id": "nMF8WE3F19HC" }, @@ -194,6 +185,7 @@ }, { "cell_type": "markdown", + "id": "5eee8d4d", "metadata": { "id": "8zB10pLC2ZaT" }, @@ -204,6 +196,7 @@ { "cell_type": "code", "execution_count": null, + "id": "093b4751", "metadata": { "id": "bAwZ_ipU28AY", "lines_to_next_cell": 2 @@ -218,6 +211,7 @@ }, { "cell_type": "markdown", + "id": "70f3cf5f", "metadata": { "id": "DATxv0pM2gQc" }, @@ -229,6 +223,7 @@ }, { "cell_type": "markdown", + "id": "23104208", "metadata": { "id": "MVcUZD4T7j4h" }, @@ -239,6 +234,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a985d813", "metadata": { "id": "l40DJzDx2nCz" }, @@ -250,6 +246,7 @@ }, { "cell_type": "markdown", + "id": "32078d6f", "metadata": { "id": "0axR682t-ub4" }, @@ -259,6 +256,7 @@ }, { "cell_type": "markdown", + "id": "0e274cd1", "metadata": { "id": "2xzzyBX43Rbq" }, @@ -270,6 +268,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ac7a35ef", "metadata": { "id": "kbbKDHwd4Eiq" }, @@ -288,6 +287,7 @@ }, { "cell_type": "markdown", + "id": "f952b572", "metadata": { "id": "3C9WlDSd-8zw" }, @@ -297,6 +297,7 @@ }, { "cell_type": "markdown", + "id": "51c4a515", "metadata": { "id": "ZZGIvGNg673Z" }, @@ -307,6 +308,7 @@ { "cell_type": "code", "execution_count": null, + "id": "32a275de", "metadata": { "id": "AyVZVJtL7nMJ" }, @@ -323,6 +325,7 @@ }, { "cell_type": "markdown", + "id": "09d47105", "metadata": { "id": "3fs83n3H_J_j" }, @@ -332,6 +335,7 @@ }, { "cell_type": "markdown", + "id": "1f2f888a", "metadata": { "id": "FHf46PtqBvVb" }, @@ -346,6 +350,7 @@ { "cell_type": "code", "execution_count": null, + "id": "6794768f", "metadata": { "id": "WffnwizrCvPw" }, @@ -366,6 +371,7 @@ }, { "cell_type": "markdown", + "id": "fa82b3d2", "metadata": { "id": "DZVVVSYy8HGd" }, @@ -377,6 +383,7 @@ { "cell_type": "code", "execution_count": null, + "id": "50af873d", "metadata": { "id": "14DRs2jK8Po7" }, @@ -417,6 +424,7 @@ }, { "cell_type": "markdown", + "id": "a083ce22", "metadata": { "id": "HrfN3OhbELuM" }, @@ -431,6 +439,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9fe0d873", "metadata": { "id": "iqNQLZVcHYRX" }, @@ -442,6 +451,7 @@ }, { "cell_type": "markdown", + "id": "57019322", "metadata": { "id": "9GySTLd_Hq9l" }, @@ -452,6 +462,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9c0b077a", "metadata": { "id": "MIyn99IxH74D" }, @@ -465,6 +476,7 @@ }, { "cell_type": "markdown", + "id": "1792d20e", "metadata": { "id": "WGlaXiuxISRX" }, @@ -474,6 +486,7 @@ }, { "cell_type": "markdown", + "id": "5d569f6b", "metadata": { "id": "-K0CvNkOLEYE" }, @@ -488,6 +501,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2fff61dc", "metadata": { "id": "7gappYcLOrsu" }, @@ -520,6 +534,7 @@ }, { "cell_type": "markdown", + "id": "12b68a98", "metadata": { "id": "2Haq4E95bN6T" }, @@ -530,6 +545,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d47a9856", "metadata": { "id": "0k_YQbBccyC_", "lines_to_next_cell": 2 @@ -542,16 +558,18 @@ }, { "cell_type": "markdown", + "id": "2984039c", "metadata": { "id": "uyqbuhKsUlhG" }, "source": [ - "4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 3.2 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `." + "4. *FizzBuzz part 2* (advanced). Look back at your solution to [exercise 3.2](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR#scrollTo=Exercise_3_2_Bonus) (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `." ] }, { "cell_type": "code", "execution_count": null, + "id": "fbf61822", "metadata": { "id": "BUeMXIQXaKna", "lines_to_next_cell": 2 @@ -568,41 +586,26 @@ }, { "cell_type": "markdown", + "id": "305ae53b", "metadata": { "id": "0eGibfk04LI0" }, "source": [ "## Next module\n", "\n", - "[7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr)" + "[7. Functions](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg)" ] } ], "metadata": { - "colab": { - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "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": 0 + "nbformat_minor": 5 } diff --git a/lessons/06 Loops.py b/lessons/06 Loops.py index bc58df7..da33b6d 100644 --- a/lessons/06 Loops.py +++ b/lessons/06 Loops.py @@ -16,9 +16,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) +# Previous module: [5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH) # %% [markdown] id="mzWET4w4lAr4" # # 1. Loops @@ -71,7 +71,7 @@ # %% [markdown] id="vDU53qkB2zo4" # The loop ends when the indentation ends. Here, we calculate the sum of a list of numbers, and print the sum only at the end: -# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 550, "status": "ok", "timestamp": 1667900533282, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="q6ucnoqeUnlI" outputId="461dfdab-3387-4d9e-d59e-ad73fb8993cd" +# %% id="q6ucnoqeUnlI" numbers = [9, 9, 4, 7, 6] sum = 0 @@ -272,7 +272,7 @@ # %% [markdown] id="uyqbuhKsUlhG" -# 4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 3.2 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `. +# 4. *FizzBuzz part 2* (advanced). Look back at your solution to [exercise 3.2](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR#scrollTo=Exercise_3_2_Bonus) (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `. # %% id="BUeMXIQXaKna" # the end=', ' argument will help you: @@ -286,4 +286,4 @@ # %% [markdown] id="0eGibfk04LI0" # ## Next module # -# [7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) +# [7. Functions](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg) diff --git a/lessons/07 Functions.ipynb b/lessons/07 Functions.ipynb index 12a3fba..08e9c1e 100644 --- a/lessons/07 Functions.ipynb +++ b/lessons/07 Functions.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "7ef4bb3d", "metadata": { "id": "fqMJHzNk5yXQ" }, @@ -10,9 +11,9 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)\n", + "Previous module: [6. Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ)\n", "\n", "### This module\n", "\n", @@ -22,6 +23,7 @@ }, { "cell_type": "markdown", + "id": "bfbecacc", "metadata": { "id": "mnc8HshNPLte" }, @@ -34,6 +36,7 @@ { "cell_type": "code", "execution_count": null, + "id": "26cbac9a", "metadata": { "id": "IP2mOWYgR-QA" }, @@ -51,6 +54,7 @@ }, { "cell_type": "markdown", + "id": "17475452", "metadata": { "id": "u_YUDiccZKwh" }, @@ -61,6 +65,7 @@ { "cell_type": "code", "execution_count": null, + "id": "693c105d", "metadata": { "id": "Mct6Hy3HZa5H" }, @@ -87,6 +92,7 @@ }, { "cell_type": "markdown", + "id": "997e0c1e", "metadata": { "id": "hBgqVL7sSyFx" }, @@ -97,6 +103,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e1f3c94e", "metadata": { "id": "wb98z1kBS9tm" }, @@ -108,6 +115,7 @@ }, { "cell_type": "markdown", + "id": "cb919d2a", "metadata": { "id": "Byutz190TEH-" }, @@ -118,6 +126,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d2e3bbf3", "metadata": { "id": "N7YW9_X_UOuB" }, @@ -129,6 +138,7 @@ }, { "cell_type": "markdown", + "id": "473fd45c", "metadata": { "id": "H0SocqBbXQ6f" }, @@ -139,6 +149,7 @@ { "cell_type": "code", "execution_count": null, + "id": "089b5b9b", "metadata": { "id": "uTk4InjdXg5g" }, @@ -150,6 +161,7 @@ }, { "cell_type": "markdown", + "id": "b077caa1", "metadata": { "id": "dCUHGgepctcp" }, @@ -158,12 +170,13 @@ "\n", "In our example of `max(3, 4, 5)` above, `3`, `4`, and `5` are positional arguments. In this case, the order of the numbers does not actually matter (the maximum is the same, no matter what).\n", "\n", - "However, for most functions, this is not the case! An example you have already seen is `range`: the first argument is the _start_ and the second argument is the _stop_." + "However, for most functions, order does matter! An example you have already seen is `range`: the first argument is the _start_ and the second argument is the _stop_." ] }, { "cell_type": "code", "execution_count": null, + "id": "d39807ca", "metadata": { "id": "yCyPbsvAd5PB" }, @@ -176,6 +189,7 @@ }, { "cell_type": "markdown", + "id": "f57e1316", "metadata": { "id": "ECGJttnRUX_J" }, @@ -186,6 +200,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c1ad6e1a", "metadata": { "id": "gAMO-QD4Uuxe" }, @@ -197,6 +212,7 @@ }, { "cell_type": "markdown", + "id": "30c3a6c3", "metadata": { "id": "Bj4diwbcU1Q7" }, @@ -207,6 +223,7 @@ { "cell_type": "code", "execution_count": null, + "id": "eac6375d", "metadata": { "id": "yYRQIGnoVHWA" }, @@ -221,6 +238,7 @@ }, { "cell_type": "markdown", + "id": "9e0cf320", "metadata": { "id": "xMHYhvPhVXet" }, @@ -231,6 +249,7 @@ { "cell_type": "code", "execution_count": null, + "id": "474d9ff2", "metadata": { "id": "OKEha-rwVo99" }, @@ -244,6 +263,7 @@ }, { "cell_type": "markdown", + "id": "f6d8d9cd", "metadata": { "id": "emAs76qOagVF" }, @@ -256,6 +276,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4e525baf", "metadata": { "id": "n0UOjklcbEHQ" }, @@ -268,6 +289,7 @@ }, { "cell_type": "markdown", + "id": "57c11c3e", "metadata": { "id": "vAt_0PfecAGK" }, @@ -284,6 +306,7 @@ { "cell_type": "code", "execution_count": null, + "id": "b8b4acaf", "metadata": { "id": "MeUnr3KNcaVZ" }, @@ -304,6 +327,7 @@ }, { "cell_type": "markdown", + "id": "c5d585d9", "metadata": { "id": "FL0w8VMkeLm7" }, @@ -316,6 +340,7 @@ }, { "cell_type": "markdown", + "id": "1d52c384", "metadata": { "id": "bBiz8La8jv81" }, @@ -326,6 +351,7 @@ { "cell_type": "code", "execution_count": null, + "id": "79e4ea5d", "metadata": { "id": "HOjpPJBNiSvr" }, @@ -349,6 +375,7 @@ }, { "cell_type": "markdown", + "id": "0515caad", "metadata": { "id": "y77LwKKjk-2D" }, @@ -361,6 +388,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e863579e", "metadata": { "id": "Ph3RsaBbllvB" }, @@ -377,6 +405,7 @@ }, { "cell_type": "markdown", + "id": "d855f610", "metadata": { "id": "6Esxqtn0y1Ai" }, @@ -387,6 +416,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4d56a7ca", "metadata": { "id": "vBfiX0-WzWQg" }, @@ -402,6 +432,7 @@ }, { "cell_type": "markdown", + "id": "06d60a1c", "metadata": { "id": "S3cY2mRgl9z8" }, @@ -414,6 +445,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f5bce85d", "metadata": { "id": "fWAzMGqfnC8p" }, @@ -438,6 +470,7 @@ }, { "cell_type": "markdown", + "id": "9b578065", "metadata": { "id": "J5Pd_TjZqnwj" }, @@ -448,6 +481,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5df84dc6", "metadata": { "id": "48ARWWmvrgjz" }, @@ -475,6 +509,7 @@ }, { "cell_type": "markdown", + "id": "2880a62b", "metadata": { "id": "kOlF8tonzwZr" }, @@ -485,6 +520,7 @@ { "cell_type": "code", "execution_count": null, + "id": "96e3f1d5", "metadata": { "id": "EHX1V-E71EkI" }, @@ -513,6 +549,7 @@ }, { "cell_type": "markdown", + "id": "34abfa63", "metadata": { "id": "spLYBkqtvDmF" }, @@ -527,6 +564,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1f36a7f6", "metadata": { "id": "DR6t3PCCvSEX" }, @@ -554,6 +592,7 @@ }, { "cell_type": "markdown", + "id": "1499789a", "metadata": { "id": "aPFGhEVz40JP" }, @@ -563,6 +602,7 @@ }, { "cell_type": "markdown", + "id": "68437417", "metadata": { "id": "hfcz-cSEKZWW" }, @@ -572,6 +612,7 @@ }, { "cell_type": "markdown", + "id": "ec608c6a", "metadata": { "id": "BUnMsiUzKbws" }, @@ -582,6 +623,7 @@ { "cell_type": "code", "execution_count": null, + "id": "be5d14b3", "metadata": { "id": "_3va9jT5O0H7" }, @@ -596,6 +638,7 @@ { "cell_type": "code", "execution_count": null, + "id": "099b3c37", "metadata": { "id": "vdTIwoGxM_JV" }, @@ -610,6 +653,7 @@ { "cell_type": "code", "execution_count": null, + "id": "eed178f4", "metadata": { "id": "30fv8SAMOblV" }, @@ -624,6 +668,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5e5a3440", "metadata": { "id": "ongrNaZFNNmV" }, @@ -641,6 +686,7 @@ { "cell_type": "code", "execution_count": null, + "id": "b3d0f9d7", "metadata": { "id": "MSkOCMMyNoUO" }, @@ -655,6 +701,7 @@ { "cell_type": "code", "execution_count": null, + "id": "64a5721a", "metadata": { "id": "72DDRhD5OQ0g" }, @@ -669,6 +716,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0e071c7c", "metadata": { "id": "L0GlJecf8ntf" }, @@ -695,6 +743,7 @@ }, { "cell_type": "markdown", + "id": "b11d180c", "metadata": { "id": "Rwvwlpp0-Hrt" }, @@ -705,6 +754,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8d927c60", "metadata": { "id": "ajcRnvzQQ9c5" }, @@ -717,6 +767,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2299c11a", "metadata": { "id": "giU_bIKrRME4" }, @@ -733,6 +784,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c8957be5", "metadata": { "id": "pAChbRWn-SKS" }, @@ -748,6 +800,7 @@ }, { "cell_type": "markdown", + "id": "36f5df19", "metadata": { "id": "mc9RtAeATiHw" }, @@ -758,6 +811,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2682588f", "metadata": { "id": "Ap33-rF-UbsB" }, @@ -777,6 +831,7 @@ }, { "cell_type": "markdown", + "id": "b6a4832a", "metadata": { "id": "apA7o120TYRl" }, @@ -786,6 +841,7 @@ }, { "cell_type": "markdown", + "id": "6fc69624", "metadata": { "id": "nM43w3VlB3-O" }, @@ -796,6 +852,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f9d01f5f", "metadata": { "id": "I5s4_a53ENJC" }, @@ -810,6 +867,7 @@ }, { "cell_type": "markdown", + "id": "7484dff6", "metadata": { "id": "z4z3-dOaVROx" }, @@ -820,6 +878,7 @@ { "cell_type": "code", "execution_count": null, + "id": "db1bc544", "metadata": { "id": "kyQwfz-mYvfW" }, @@ -834,6 +893,7 @@ }, { "cell_type": "markdown", + "id": "4c28344b", "metadata": { "id": "3Lx61L5B0Zqe" }, @@ -843,6 +903,7 @@ }, { "cell_type": "markdown", + "id": "226154d8", "metadata": { "id": "gRae5PaguhV9" }, @@ -853,6 +914,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0d823d80", "metadata": { "id": "MJqHyAI31FLZ" }, @@ -873,6 +935,7 @@ }, { "cell_type": "markdown", + "id": "b9d46cac", "metadata": { "id": "1ORLASkU2Uga" }, @@ -885,6 +948,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1772b766", "metadata": { "id": "IuDsXgrK3rK8" }, @@ -907,6 +971,7 @@ }, { "cell_type": "markdown", + "id": "3427f6b8", "metadata": { "id": "2gPmuYUe1nyO" }, @@ -919,6 +984,7 @@ { "cell_type": "code", "execution_count": null, + "id": "48f6299f", "metadata": { "id": "IqabfNzi2-XV" }, @@ -953,6 +1019,7 @@ }, { "cell_type": "markdown", + "id": "b152fb06", "metadata": { "id": "_mTHAoog6glc" }, @@ -965,6 +1032,7 @@ { "cell_type": "code", "execution_count": null, + "id": "6a1c3167", "metadata": { "id": "ETGRDi8J7cyP" }, @@ -988,18 +1056,20 @@ }, { "cell_type": "markdown", + "id": "f740f8fb", "metadata": { "id": "1jWzKu5omWUV" }, "source": [ "## Functions as first-class citizens\n", "\n", - "As mentioned previously, a function is just a variable. That means you can do the same things with it as with other values. For example, you can pass a function as an argument to another function:" + "As mentioned previously, a function is just a value. That means you can do the same things with it as with other values. For example, you can pass a function as an argument to another function:" ] }, { "cell_type": "code", "execution_count": null, + "id": "5ad91e96", "metadata": { "id": "ukVglqcwsAdT" }, @@ -1019,6 +1089,7 @@ }, { "cell_type": "markdown", + "id": "403ef5cb", "metadata": { "id": "l4dAFklEvdhZ" }, @@ -1029,6 +1100,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d4b26453", "metadata": { "id": "V2-kY5O6xHRo" }, @@ -1043,6 +1115,7 @@ }, { "cell_type": "markdown", + "id": "9a56f2c3", "metadata": { "id": "TZXA2q46taZi" }, @@ -1053,6 +1126,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2af0bcac", "metadata": { "id": "fG6-q8EatpRz" }, @@ -1076,6 +1150,7 @@ }, { "cell_type": "markdown", + "id": "94e09dd5", "metadata": { "id": "1xUD2ZNjcqEZ" }, @@ -1085,6 +1160,7 @@ }, { "cell_type": "markdown", + "id": "0ff27a16", "metadata": { "id": "hxRC5Nx-cyM4" }, @@ -1095,6 +1171,7 @@ { "cell_type": "code", "execution_count": null, + "id": "745408a4", "metadata": { "id": "HPxio0q6c-M9" }, @@ -1112,6 +1189,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ebfaa0b6", "metadata": { "id": "NxXqK4ROdxva" }, @@ -1128,6 +1206,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5f09cfeb", "metadata": { "id": "EVCf3w9qfBCU" }, @@ -1143,6 +1222,7 @@ { "cell_type": "code", "execution_count": null, + "id": "323ad959", "metadata": { "id": "OUSpe-hPl6-G" }, @@ -1163,6 +1243,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a1f1c865", "metadata": { "id": "uSN17qnvmoWv" }, @@ -1185,6 +1266,7 @@ }, { "cell_type": "markdown", + "id": "91aad969", "metadata": { "id": "CKma4p6Egkwb" }, @@ -1195,6 +1277,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f4b9e958", "metadata": { "id": "SaygnjMygwNF" }, @@ -1212,6 +1295,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ba8dffda", "metadata": { "id": "GqIgTcG6hMuG" }, @@ -1230,6 +1314,7 @@ { "cell_type": "code", "execution_count": null, + "id": "76dc71c5", "metadata": { "id": "nH77gJdOkS5b" }, @@ -1244,6 +1329,7 @@ }, { "cell_type": "markdown", + "id": "9aae5c87", "metadata": { "id": "YG5XrO1PoIJx" }, @@ -1254,24 +1340,26 @@ { "cell_type": "code", "execution_count": null, + "id": "bee865ee", "metadata": { "id": "TnuU_I0Tq9wQ" }, "outputs": [], "source": [ - "# You may pretend that it is forever April\n", - "current_month = 4\n", + "# You may pretend that it is forever November\n", + "current_month = 11\n", "\n", "# Your definition of month here\n", "\n", "assert month(3) == 'March'\n", "assert month(4) == 'April'\n", "assert month(11) == 'November'\n", - "assert month() == 'April'" + "assert month() == 'November'" ] }, { "cell_type": "markdown", + "id": "0aa13173", "metadata": { "id": "WuRrElhUsD40" }, @@ -1282,17 +1370,18 @@ { "cell_type": "code", "execution_count": null, + "id": "9162cf92", "metadata": { "id": "WUGQqmJysrqS" }, "outputs": [], "source": [ - "# You may pretend it is forever Tuesday\n", - "current_weekday = 2\n", + "# You may pretend it is forever Wednesday\n", + "current_weekday = 3\n", "\n", "# Your definition of weekday here\n", "\n", - "assert weekday() == 'Tuesday'\n", + "assert weekday() == 'Wednesday'\n", "assert weekday(0) == 'Sunday'\n", "assert weekday(7) == 'Sunday'\n", "assert weekday(4) == 'Thursday'" @@ -1300,6 +1389,7 @@ }, { "cell_type": "markdown", + "id": "528a25ae", "metadata": { "id": "ZvfEq3NctoOo" }, @@ -1309,6 +1399,7 @@ }, { "cell_type": "markdown", + "id": "52f4370b", "metadata": { "id": "8au2fNRutw8i" }, @@ -1319,6 +1410,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f88ff676", "metadata": { "id": "JFhOX_Z5uVfC" }, @@ -1327,6 +1419,7 @@ }, { "cell_type": "markdown", + "id": "decdbb41", "metadata": { "id": "Gx54DrgJuWKg" }, @@ -1337,6 +1430,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e1eda263", "metadata": { "id": "0KyXFHEWwZ45" }, @@ -1356,6 +1450,7 @@ }, { "cell_type": "markdown", + "id": "d4a16fdb", "metadata": { "id": "b-p6Ct5_0I-x" }, @@ -1368,6 +1463,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1def7f03", "metadata": { "id": "407dPPK966R9" }, @@ -1387,6 +1483,7 @@ }, { "cell_type": "markdown", + "id": "5790aaa4", "metadata": { "id": "QCKc52-r9DrX" }, @@ -1401,6 +1498,7 @@ { "cell_type": "code", "execution_count": null, + "id": "50dc3ade", "metadata": { "id": "NFADyIW3-7qt" }, @@ -1420,6 +1518,7 @@ }, { "cell_type": "markdown", + "id": "97d6fe94", "metadata": { "id": "o_3wq4agCCZH" }, @@ -1430,6 +1529,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ed2af6ca", "metadata": { "id": "4c0A4kMfDdvt" }, @@ -1448,6 +1548,7 @@ }, { "cell_type": "markdown", + "id": "8fede2b3", "metadata": { "id": "HBA4z4yVIhsn" }, @@ -1458,6 +1559,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a3197599", "metadata": { "id": "5g4BRdpLJLIc" }, @@ -1477,17 +1579,19 @@ }, { "cell_type": "markdown", + "id": "add10079", "metadata": { "id": "BXzFYNGmPeO6" }, "source": [ "5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

\n", - "Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can." + "Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ#scrollTo=uyqbuhKsUlhG), in as little code as you can." ] }, { "cell_type": "code", "execution_count": null, + "id": "01d87452", "metadata": { "id": "nDGUS26IMApD" }, @@ -1506,41 +1610,26 @@ }, { "cell_type": "markdown", + "id": "97473e99", "metadata": { "id": "Dntbbioh29xm" }, "source": [ "## Next module\n", "\n", - "[8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)" + "[8. Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS)" ] } ], "metadata": { - "colab": { - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "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": 0 + "nbformat_minor": 5 } diff --git a/lessons/07 Functions.py b/lessons/07 Functions.py index bc261b6..b645449 100644 --- a/lessons/07 Functions.py +++ b/lessons/07 Functions.py @@ -16,9 +16,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) +# Previous module: [6. Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ) # # ### This module # @@ -88,7 +88,7 @@ # # In our example of `max(3, 4, 5)` above, `3`, `4`, and `5` are positional arguments. In this case, the order of the numbers does not actually matter (the maximum is the same, no matter what). # -# However, for most functions, this is not the case! An example you have already seen is `range`: the first argument is the _start_ and the second argument is the _stop_. +# However, for most functions, order does matter! An example you have already seen is `range`: the first argument is the _start_ and the second argument is the _stop_. # %% id="yCyPbsvAd5PB" assert max(3, 4, 5) is max(4, 3, 5) @@ -542,7 +542,7 @@ def tax_rate(income, entrepreneur): # %% [markdown] id="1jWzKu5omWUV" # ## Functions as first-class citizens # -# As mentioned previously, a function is just a variable. That means you can do the same things with it as with other values. For example, you can pass a function as an argument to another function: +# As mentioned previously, a function is just a value. That means you can do the same things with it as with other values. For example, you can pass a function as an argument to another function: # %% id="ukVglqcwsAdT" def exclaim(text): @@ -685,26 +685,26 @@ def echo(value): # 3. In the following code block, write a function `month`, which returns a string name for given numeric month. Month `1` is `'January'` and month `12` is `December`. When no argument is passed, it should default to the current month. **Tip:** you can avoid writing a big `if`/`elif`/`else` tree by using a list of month names and using the month number as a list index. # %% id="TnuU_I0Tq9wQ" -# You may pretend that it is forever April -current_month = 4 +# You may pretend that it is forever November +current_month = 11 # Your definition of month here assert month(3) == 'March' assert month(4) == 'April' assert month(11) == 'November' -assert month() == 'April' +assert month() == 'November' # %% [markdown] id="WuRrElhUsD40" # 4. In the following code block, write a function `weekday`, which is analogous to `month` in the previous exercise. Day `1` is `'Monday'` while day `0` and day `7` are both `'Sunday'`. Can you avoid writing the string `'Sunday'` twice in your code? # %% id="WUGQqmJysrqS" -# You may pretend it is forever Tuesday -current_weekday = 2 +# You may pretend it is forever Wednesday +current_weekday = 3 # Your definition of weekday here -assert weekday() == 'Tuesday' +assert weekday() == 'Wednesday' assert weekday(0) == 'Sunday' assert weekday(7) == 'Sunday' assert weekday(4) == 'Thursday' @@ -799,7 +799,7 @@ def echo(value): # %% [markdown] id="BXzFYNGmPeO6" # 5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

-# Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can. +# Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ#scrollTo=uyqbuhKsUlhG), in as little code as you can. # %% id="nDGUS26IMApD" # The following code is just for illustration. @@ -815,4 +815,4 @@ def echo(value): # %% [markdown] id="Dntbbioh29xm" # ## Next module # -# [8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi) +# [8. Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS) diff --git a/lessons/07a Functions (extra exercises).ipynb b/lessons/07a Functions (extra exercises).ipynb index 78090a8..2c97442 100644 --- a/lessons/07a Functions (extra exercises).ipynb +++ b/lessons/07a Functions (extra exercises).ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "0ed7eab1", "metadata": { "id": "q2_7oxCuwonl" }, @@ -15,6 +16,7 @@ }, { "cell_type": "markdown", + "id": "713c9b78", "metadata": { "id": "KoEzBrcFxk8T" }, @@ -26,6 +28,7 @@ }, { "cell_type": "markdown", + "id": "3867759f", "metadata": { "id": "DxKNUR11yfvL" }, @@ -38,6 +41,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8044e752", "metadata": { "id": "YtJmU-K0woBC" }, @@ -53,6 +57,7 @@ }, { "cell_type": "markdown", + "id": "c9d2a678", "metadata": { "id": "jgxg9ecn0GkZ" }, @@ -63,6 +68,7 @@ { "cell_type": "code", "execution_count": null, + "id": "797d52e9", "metadata": { "id": "Z2Xa8C6z0BrI" }, @@ -78,6 +84,7 @@ }, { "cell_type": "markdown", + "id": "ca51c01e", "metadata": { "id": "Zx9RsFoY2o5M" }, @@ -88,6 +95,7 @@ { "cell_type": "code", "execution_count": null, + "id": "90732060", "metadata": { "id": "Y8M_CCuCcVZm" }, @@ -104,6 +112,7 @@ }, { "cell_type": "markdown", + "id": "d4fd2315", "metadata": { "id": "YKRA2X94dmfm" }, @@ -114,6 +123,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d7e52250", "metadata": { "id": "CFukYZRFeMMr" }, @@ -130,6 +140,7 @@ }, { "cell_type": "markdown", + "id": "acdfae1a", "metadata": { "id": "9C8cDTS3e62p" }, @@ -140,6 +151,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3caf1e72", "metadata": { "id": "X9kXxwe3fD7P" }, @@ -157,6 +169,7 @@ }, { "cell_type": "markdown", + "id": "e921b5de", "metadata": { "id": "lXc43yNgicoA" }, @@ -167,6 +180,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8616d2d3", "metadata": { "id": "YOqKnf7rjpvW" }, @@ -182,6 +196,7 @@ }, { "cell_type": "markdown", + "id": "5435b8d8", "metadata": { "id": "mzKQAJxNLUgV" }, @@ -203,6 +218,7 @@ { "cell_type": "code", "execution_count": null, + "id": "b5ec1d0d", "metadata": { "id": "XDVlkfHyMIfE" }, @@ -218,6 +234,7 @@ }, { "cell_type": "markdown", + "id": "9256e274", "metadata": { "id": "SQHPePrYxdnG" }, @@ -230,6 +247,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9f854789", "metadata": { "id": "5cdwImkNUGcc" }, @@ -245,6 +263,7 @@ }, { "cell_type": "markdown", + "id": "8c8a4cb4", "metadata": { "id": "shtLjnOGxen9" }, @@ -255,6 +274,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4e4fa3bb", "metadata": { "id": "xJ_XTebzxfCs" }, @@ -270,6 +290,7 @@ }, { "cell_type": "markdown", + "id": "72488451", "metadata": { "id": "p1NF-Tbpxfs1" }, @@ -286,6 +307,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4b52d8c7", "metadata": { "id": "LsA_B5iPxgEE" }, @@ -302,6 +324,7 @@ }, { "cell_type": "markdown", + "id": "bbeb32c5", "metadata": { "id": "o55Hp9SomntG" }, @@ -318,6 +341,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5086339a", "metadata": { "id": "aqngxSucow39" }, @@ -341,6 +365,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1dc6c3b2", "metadata": { "id": "khwUSdMWn1CR" }, @@ -366,6 +391,7 @@ { "cell_type": "code", "execution_count": null, + "id": "beec0a48", "metadata": { "id": "nkpyPnDgoxKf" }, @@ -393,6 +419,7 @@ { "cell_type": "code", "execution_count": null, + "id": "03b6d542", "metadata": { "id": "yQ_-_xnuoyZ9" }, @@ -419,6 +446,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a16021e4", "metadata": { "id": "ArwDSoguox2j" }, @@ -447,6 +475,7 @@ { "cell_type": "code", "execution_count": null, + "id": "24450d61", "metadata": { "id": "hMIpfW_lMK0l" }, @@ -471,6 +500,7 @@ }, { "cell_type": "markdown", + "id": "7eb3efbb", "metadata": { "id": "aKgZVXZXoF3g" }, @@ -492,6 +522,7 @@ }, { "cell_type": "markdown", + "id": "0c0f86ba", "metadata": { "id": "EXgk0_mjoukf" }, @@ -502,6 +533,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a49ace30", "metadata": { "id": "ExH64Sbgovw0" }, @@ -510,6 +542,7 @@ }, { "cell_type": "markdown", + "id": "aad7ea7a", "metadata": { "id": "uA4PQxchxUrT" }, @@ -524,6 +557,7 @@ { "cell_type": "code", "execution_count": null, + "id": "de3579e3", "metadata": { "id": "VIYiW1OCxVEo" }, @@ -532,6 +566,7 @@ }, { "cell_type": "markdown", + "id": "89fd889b", "metadata": { "id": "QzewFsCJomVB" }, @@ -546,6 +581,7 @@ { "cell_type": "code", "execution_count": null, + "id": "dd767961", "metadata": { "id": "oCID8txRotb-" }, @@ -554,6 +590,7 @@ }, { "cell_type": "markdown", + "id": "ba62c8dd", "metadata": { "id": "SoO0C4pyxSjO" }, @@ -569,6 +606,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a539d146", "metadata": { "id": "ddWh7EWCxS6M" }, @@ -577,6 +615,7 @@ }, { "cell_type": "markdown", + "id": "168a0b20", "metadata": { "id": "m1bBl0RvxXe-" }, @@ -587,6 +626,7 @@ { "cell_type": "code", "execution_count": null, + "id": "686a5a52", "metadata": { "id": "GKRKIpKgxXwV" }, @@ -595,6 +635,7 @@ }, { "cell_type": "markdown", + "id": "3600e798", "metadata": { "id": "NPXFJd95o3Ei" }, @@ -616,6 +657,7 @@ { "cell_type": "code", "execution_count": null, + "id": "87183539", "metadata": { "id": "JFc_E5sF5rt1" }, @@ -648,6 +690,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7c474033", "metadata": { "id": "lirYtOcWpJ6m" }, @@ -677,6 +720,7 @@ { "cell_type": "code", "execution_count": null, + "id": "354f695c", "metadata": { "id": "mdNug4ct5645", "lines_to_next_cell": 2 @@ -768,21 +812,14 @@ } ], "metadata": { - "colab": { - "authorship_tag": "ABX9TyMaLbZUxFMzVIjab/8j3smI", - "provenance": [] - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "name": "python3" - }, - "language_info": { - "name": "python" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 5 } diff --git a/lessons/08 debugging.ipynb b/lessons/08 debugging.ipynb index 60287bf..61e6b7e 100644 --- a/lessons/08 debugging.ipynb +++ b/lessons/08 debugging.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "df9f86d0", "metadata": { "id": "fBBVyNmi8Fo0" }, @@ -10,9 +11,9 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr)\n", + "Previous module: [7. Functions](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg)\n", "\n", "### This module\n", "\n", @@ -22,6 +23,7 @@ }, { "cell_type": "markdown", + "id": "c5bbc4c9", "metadata": { "id": "y0FX-JxubjAb" }, @@ -55,6 +57,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4b7eb5bb", "metadata": { "id": "rDx9b5MJhR7X" }, @@ -75,6 +78,7 @@ }, { "cell_type": "markdown", + "id": "d69ac43d", "metadata": { "id": "V5a4AtnAAMc2" }, @@ -84,6 +88,7 @@ }, { "cell_type": "markdown", + "id": "bb17f738", "metadata": { "id": "5fI43xswHUXJ" }, @@ -95,6 +100,7 @@ }, { "cell_type": "markdown", + "id": "5c4cdd94", "metadata": { "id": "6OXyDh8tASN_" }, @@ -112,6 +118,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3e6fba6d", "metadata": { "id": "Z_oXfbLeI6mK" }, @@ -132,6 +139,7 @@ { "cell_type": "code", "execution_count": null, + "id": "251ae90a", "metadata": { "id": "mxcdaX6SNFdl" }, @@ -157,6 +165,7 @@ }, { "cell_type": "markdown", + "id": "db1c9290", "metadata": { "id": "CqsSkFb_H7Sk" }, @@ -178,6 +187,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e0383d79", "metadata": { "id": "6Nm0bcBINyNk", "lines_to_next_cell": 2 @@ -200,6 +210,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9917e908", "metadata": { "id": "vooRS00TQNSP" }, @@ -220,6 +231,7 @@ }, { "cell_type": "markdown", + "id": "3b35d769", "metadata": { "id": "O0Piet3Y-T6B" }, @@ -229,6 +241,7 @@ }, { "cell_type": "markdown", + "id": "7fab6f47", "metadata": { "id": "833NNleFrsro" }, @@ -295,6 +308,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3cb015a3", "metadata": { "id": "TcfxzJ_Bn971" }, @@ -324,6 +338,7 @@ }, { "cell_type": "markdown", + "id": "0463e8dc", "metadata": { "id": "bDpSj4jmTe-G" }, @@ -348,6 +363,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1b616166", "metadata": { "id": "TjsG6HmVanqq" }, @@ -362,40 +378,26 @@ }, { "cell_type": "markdown", + "id": "004a87ff", "metadata": { "id": "QBPQVbY_aoLt" }, "source": [ "## Next module\n", "\n", - "[9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)" + "[9. String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm)" ] } ], "metadata": { - "colab": { - "provenance": [] - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "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": 0 + "nbformat_minor": 5 } diff --git a/lessons/08 debugging.py b/lessons/08 debugging.py index 56cc339..0fe6fa3 100644 --- a/lessons/08 debugging.py +++ b/lessons/08 debugging.py @@ -16,9 +16,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) +# Previous module: [7. Functions](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg) # # ### This module # @@ -266,4 +266,4 @@ def gnome_sort(items): # %% [markdown] id="QBPQVbY_aoLt" # ## Next module # -# [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) +# [9. String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm) diff --git a/lessons/09 string manipulation.ipynb b/lessons/09 string manipulation.ipynb index 6307690..dcbb8b5 100644 --- a/lessons/09 string manipulation.ipynb +++ b/lessons/09 string manipulation.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "523bb362", "metadata": { "id": "7_SeGArbg7Ou" }, @@ -10,9 +11,9 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)\n", + "Previous module: [8. Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS)\n", "\n", "### This module\n", "\n", @@ -22,6 +23,7 @@ }, { "cell_type": "markdown", + "id": "25b48abe", "metadata": { "id": "oadzHVRYh1PN" }, @@ -32,6 +34,7 @@ }, { "cell_type": "markdown", + "id": "ce14c90a", "metadata": { "id": "3KZGnQuWiM1X" }, @@ -45,6 +48,7 @@ { "cell_type": "code", "execution_count": null, + "id": "51166e0b", "metadata": { "id": "u-gFos5wii5k" }, @@ -56,6 +60,7 @@ }, { "cell_type": "markdown", + "id": "0ab91fc4", "metadata": { "id": "r0ZiGq36ipEo" }, @@ -68,6 +73,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f51b741a", "metadata": { "id": "yOOOzh3WjCGc" }, @@ -80,6 +86,7 @@ }, { "cell_type": "markdown", + "id": "9f13c0b5", "metadata": { "id": "A4Wfxuj_jRAW" }, @@ -93,6 +100,7 @@ { "cell_type": "code", "execution_count": null, + "id": "61bbf69a", "metadata": { "id": "uErzYzBHjbND" }, @@ -104,6 +112,7 @@ }, { "cell_type": "markdown", + "id": "9868a860", "metadata": { "id": "AsVvV-dtjxSD" }, @@ -117,6 +126,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c1d8b56e", "metadata": { "id": "2iIwX0phj4Wt" }, @@ -133,6 +143,7 @@ }, { "cell_type": "markdown", + "id": "ce89a761", "metadata": { "id": "OQ07Frl4j_H_" }, @@ -148,6 +159,7 @@ { "cell_type": "code", "execution_count": null, + "id": "fb104979", "metadata": { "id": "xtsuwTrikrpk" }, @@ -176,6 +188,7 @@ }, { "cell_type": "markdown", + "id": "4a6f5c1c", "metadata": { "id": "UM5FtDPBlBGx" }, @@ -189,6 +202,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1574c5c8", "metadata": { "id": "CqxglgPOlDfe" }, @@ -205,6 +219,7 @@ { "cell_type": "code", "execution_count": null, + "id": "6765e95b", "metadata": { "id": "z7p9kwjBlakE" }, @@ -220,6 +235,7 @@ }, { "cell_type": "markdown", + "id": "286dff38", "metadata": { "id": "5hIj-tbVleEq" }, @@ -231,6 +247,7 @@ { "cell_type": "code", "execution_count": null, + "id": "948ca300", "metadata": { "id": "02q-FgvVlxEj" }, @@ -242,6 +259,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0957c7fb", "metadata": { "id": "cvCrnnn9l-oH" }, @@ -253,6 +271,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1422585a", "metadata": { "id": "UlWWF0k7mA62" }, @@ -264,6 +283,7 @@ { "cell_type": "code", "execution_count": null, + "id": "90bf1a93", "metadata": { "id": "Ui3gmvCNmHfB" }, @@ -275,6 +295,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7ece757c", "metadata": { "id": "1tDEnzrumNdO" }, @@ -287,6 +308,7 @@ { "cell_type": "code", "execution_count": null, + "id": "38da65f4", "metadata": { "id": "BUpf6LglmZ4n" }, @@ -299,6 +321,7 @@ { "cell_type": "code", "execution_count": null, + "id": "52eaa4da", "metadata": { "id": "sgfEH2jImlwz" }, @@ -310,6 +333,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0a088984", "metadata": { "id": "BEE94VVBmf7T" }, @@ -321,6 +345,7 @@ { "cell_type": "code", "execution_count": null, + "id": "09e9b106", "metadata": { "id": "BVKc0bQAnGYq" }, @@ -335,6 +360,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9d7bda7e", "metadata": { "id": "R4U_8kVtmpHE" }, @@ -348,6 +374,7 @@ }, { "cell_type": "markdown", + "id": "8b002302", "metadata": { "id": "mRATbQdclrcX" }, @@ -365,8 +392,19 @@ "\n" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "c513fe24", + "metadata": { + "id": "NyrzV4VGUJWK" + }, + "outputs": [], + "source": [] + }, { "cell_type": "markdown", + "id": "b01c867b", "metadata": { "id": "8YAcMdHpnuKw" }, @@ -378,6 +416,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c62dc7c4", "metadata": { "id": "pl5hfOYmnzF2" }, @@ -390,6 +429,7 @@ }, { "cell_type": "markdown", + "id": "34c4b7f5", "metadata": { "id": "SjWVj2R5n1XR" }, @@ -402,6 +442,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ac85a38c", "metadata": { "id": "EQ9u1CX6n_TN" }, @@ -420,6 +461,7 @@ }, { "cell_type": "markdown", + "id": "65320f6c", "metadata": { "id": "B1_gS-UaoEy0" }, @@ -431,6 +473,7 @@ { "cell_type": "code", "execution_count": null, + "id": "378d77fb", "metadata": { "id": "8KMQfVXGoHq5" }, @@ -447,6 +490,7 @@ }, { "cell_type": "markdown", + "id": "d9205bf7", "metadata": { "id": "3lMa3IDjoMZP" }, @@ -458,6 +502,7 @@ { "cell_type": "code", "execution_count": null, + "id": "957fe756", "metadata": { "id": "re9x0TNLoM4Q" }, @@ -474,16 +519,22 @@ }, { "cell_type": "markdown", - "metadata": {}, + "id": "7d93a3d4", + "metadata": { + "id": "lxJqWxlRfIxd" + }, "source": [ "### F-strings\n", - "Similar to placeholders, expressions an also be directly combined within a string by putting `f` in front of a string:" + "Similar to placeholders, expressions can also be directly combined within a string by putting `f` in front of a string:" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "id": "b3581033", + "metadata": { + "id": "ThZE6YnXfIxd" + }, "outputs": [], "source": [ "name = 'Sheean'\n", @@ -495,6 +546,7 @@ }, { "cell_type": "markdown", + "id": "c9e10330", "metadata": { "id": "avcqgDAAoq-w" }, @@ -506,6 +558,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1c4b0ceb", "metadata": { "id": "BIa0VOX_owF4" }, @@ -517,6 +570,7 @@ { "cell_type": "code", "execution_count": null, + "id": "89cbedde", "metadata": { "id": "81GUitOZo0l2" }, @@ -528,6 +582,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f73d3149", "metadata": { "id": "96Dq4eSCpAji" }, @@ -539,6 +594,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ee7b0f95", "metadata": { "id": "h43fjzPco4V_" }, @@ -550,6 +606,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8e9e11d4", "metadata": { "id": "wA18AIPKpFAH" }, @@ -560,41 +617,26 @@ }, { "cell_type": "markdown", + "id": "c0bdb200", "metadata": { "id": "y5FcFvgypMfE" }, "source": [ "## Next module\n", "\n", - "[10 - Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9)" + "[10 - Dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj)" ] } ], "metadata": { - "colab": { - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "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": 0 + "nbformat_minor": 5 } diff --git a/lessons/09 string manipulation.py b/lessons/09 string manipulation.py index ba0247f..950f543 100644 --- a/lessons/09 string manipulation.py +++ b/lessons/09 string manipulation.py @@ -16,9 +16,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi) +# Previous module: [8. Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS) # # ### This module # @@ -181,6 +181,8 @@ # # +# %% id="NyrzV4VGUJWK" + # %% [markdown] id="8YAcMdHpnuKw" # ## String formatting # We have seen a (not very convenient) way of building strings: @@ -232,11 +234,11 @@ print(shoutout_template.format(followup=question, name=julian)) print(shoutout_template.format(followup=julian, name=question)) -# %% [markdown] +# %% [markdown] id="lxJqWxlRfIxd" # ### F-strings -# Similar to placeholders, expressions an also be directly combined within a string by putting `f` in front of a string: +# Similar to placeholders, expressions can also be directly combined within a string by putting `f` in front of a string: -# %% +# %% id="ThZE6YnXfIxd" name = 'Sheean' weeks = 2 text = f'Hi {name}, your next appointment is in {weeks * 7} days.' @@ -265,4 +267,4 @@ # %% [markdown] id="y5FcFvgypMfE" # ## Next module # -# [10 - Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9) +# [10 - Dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj) diff --git a/lessons/10 - Dictionaries.ipynb b/lessons/10 - Dictionaries.ipynb index 7e37450..d77b1d5 100644 --- a/lessons/10 - Dictionaries.ipynb +++ b/lessons/10 - Dictionaries.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "5df420eb", "metadata": { "id": "kjdcNtg3k8Aa" }, @@ -10,9 +11,9 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n", + "Previous module: [9. String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm)\n", "\n", "### This module\n", "\n", @@ -21,6 +22,7 @@ }, { "cell_type": "markdown", + "id": "714284d5", "metadata": { "id": "CtgPyDQ6lkT4" }, @@ -33,6 +35,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e292ae96", "metadata": { "id": "OKs8GP5zk9bM" }, @@ -47,6 +50,7 @@ }, { "cell_type": "markdown", + "id": "b3e8fc68", "metadata": { "id": "OOyTxS6_mv2o" }, @@ -57,6 +61,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5a42f5d4", "metadata": { "id": "d0SFY-fil7-l" }, @@ -67,6 +72,7 @@ }, { "cell_type": "markdown", + "id": "811e5b98", "metadata": { "id": "RiNPElB7nj2F" }, @@ -79,6 +85,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9fe7850e", "metadata": { "id": "ao56gCSxnhkS" }, @@ -98,18 +105,20 @@ { "cell_type": "code", "execution_count": null, + "id": "1effe690", "metadata": { "id": "JgfCNNoHn3uR" }, "outputs": [], "source": [ "# this will be rejected\n", - "shared_interests = {['Julian', 'Jelte']: ['cats', 'programming'], \n", + "shared_interests = {['Julian', 'Jelte']: ['cats', 'programming'],\n", " ['Jelte', 'Berit']: 'music'}" ] }, { "cell_type": "markdown", + "id": "14ccef3d", "metadata": { "id": "1tYs_3xBqx2e" }, @@ -120,6 +129,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1e1d97a1", "metadata": { "id": "TykfEB8iq4MQ" }, @@ -134,6 +144,7 @@ }, { "cell_type": "markdown", + "id": "f9dc6c78", "metadata": { "id": "_3J4oaBAsTGa" }, @@ -144,6 +155,7 @@ { "cell_type": "code", "execution_count": null, + "id": "06ef20ec", "metadata": { "id": "pK0eYwsasBJ9" }, @@ -157,6 +169,7 @@ }, { "cell_type": "markdown", + "id": "da5685da", "metadata": { "id": "L56hdp03r9Q6" }, @@ -167,6 +180,7 @@ { "cell_type": "code", "execution_count": null, + "id": "55c1f99b", "metadata": { "id": "da-Re3sPsyYS" }, @@ -179,6 +193,7 @@ }, { "cell_type": "markdown", + "id": "5c0180f5", "metadata": { "id": "nksJpi6mqgXY" }, @@ -191,6 +206,7 @@ { "cell_type": "code", "execution_count": null, + "id": "11ce46d1", "metadata": { "id": "SzxfzTANsjGN" }, @@ -205,6 +221,7 @@ }, { "cell_type": "markdown", + "id": "d06c3abe", "metadata": { "id": "yL7NrEzwtm_r" }, @@ -217,6 +234,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d4f648b3", "metadata": { "id": "HEOgD62xtPQj" }, @@ -236,6 +254,7 @@ }, { "cell_type": "markdown", + "id": "d337ac2e", "metadata": { "id": "zeJU8wt9uY2V" }, @@ -250,6 +269,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0fc0e67c", "metadata": { "id": "pyxjqy31uWEs" }, @@ -264,6 +284,7 @@ }, { "cell_type": "markdown", + "id": "f4bf0b2a", "metadata": { "id": "FvGfwcVjuwIY" }, @@ -275,6 +296,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e70f18de", "metadata": { "id": "72V_U4tUupnw" }, @@ -287,6 +309,7 @@ }, { "cell_type": "markdown", + "id": "93f1a90a", "metadata": { "id": "-kH7Vw3WvgFj" }, @@ -301,6 +324,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7453c1d6", "metadata": { "id": "sNwzKYOAvk-d" }, @@ -320,6 +344,7 @@ }, { "cell_type": "markdown", + "id": "ac2dd77b", "metadata": { "id": "3oBW4bxgv5JV" }, @@ -331,6 +356,7 @@ { "cell_type": "code", "execution_count": null, + "id": "225a775e", "metadata": { "id": "ESzsmJCXv7pl" }, @@ -350,6 +376,7 @@ }, { "cell_type": "markdown", + "id": "d0b1b75e", "metadata": { "id": "x2nJsta6wRGQ" }, @@ -366,6 +393,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e1aeed41", "metadata": { "id": "11lfiYMiwTct" }, @@ -384,6 +412,7 @@ }, { "cell_type": "markdown", + "id": "4ce000da", "metadata": { "id": "1ezL-XhlvHBq" }, @@ -393,6 +422,7 @@ }, { "cell_type": "markdown", + "id": "0076cc18", "metadata": { "id": "rxcz60KQximW" }, @@ -403,6 +433,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7deddaac", "metadata": { "id": "yn89oAAZu33C" }, @@ -416,16 +447,18 @@ }, { "cell_type": "markdown", + "id": "3da12964", "metadata": { "id": "Gtp5V9dE0LxK" }, "source": [ - "2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`)." + "2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`)." ] }, { "cell_type": "code", "execution_count": null, + "id": "0ac37b44", "metadata": { "id": "S7gCNyLCxdrO" }, @@ -441,6 +474,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f5f6eea5", "metadata": { "id": "nScDQipK35qN" }, @@ -456,6 +490,7 @@ }, { "cell_type": "markdown", + "id": "c1c1c4d8", "metadata": { "id": "-Qp6R3Kp3GId" }, @@ -466,6 +501,7 @@ { "cell_type": "code", "execution_count": null, + "id": "07d4bc7e", "metadata": { "id": "awf-lLQO3N1U" }, @@ -480,6 +516,7 @@ { "cell_type": "code", "execution_count": null, + "id": "91a289c8", "metadata": { "id": "MDpIZpbm3-BG", "lines_to_next_cell": 2 @@ -495,6 +532,7 @@ }, { "cell_type": "markdown", + "id": "41b46311", "metadata": { "id": "h-kJhTO951pc" }, @@ -507,6 +545,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9fac6702", "metadata": { "id": "mdNug4ct5645" }, @@ -514,19 +553,20 @@ "source": [ "# the variable sent0 contains the first sentence of The Catcher in the Rye\n", "# split into single words\n", - "sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', \n", - " 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', \n", - " 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', \n", - " 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', \n", - " 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', \n", - " 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', \n", - " 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', \n", + "sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the',\n", + " 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is',\n", + " 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood',\n", + " 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied',\n", + " 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that',\n", + " 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t',\n", + " 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want',\n", " 'to', 'know', 'the', 'truth.']" ] }, { "cell_type": "code", "execution_count": null, + "id": "d24e9125", "metadata": { "id": "XGY3qSEk6B9j" }, @@ -534,23 +574,29 @@ "source": [ "# your code here..." ] + }, + { + "cell_type": "markdown", + "id": "bd614245", + "metadata": { + "id": "y5FcFvgypMfE" + }, + "source": [ + "## Next module\n", + "\n", + "[11 - Working with files](https://colab.research.google.com/drive/1_mDpeRCHzrGJstcxEWZgq5YMhHujPdfe)" + ] } ], "metadata": { - "colab": { - "provenance": [] - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "name": "python3" - }, - "language_info": { - "name": "python" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 5 } diff --git a/lessons/10 - Dictionaries.py b/lessons/10 - Dictionaries.py index 3ff4590..1bb9c07 100644 --- a/lessons/10 - Dictionaries.py +++ b/lessons/10 - Dictionaries.py @@ -16,9 +16,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) +# Previous module: [9. String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm) # # ### This module # @@ -60,7 +60,7 @@ # %% id="JgfCNNoHn3uR" # this will be rejected -shared_interests = {['Julian', 'Jelte']: ['cats', 'programming'], +shared_interests = {['Julian', 'Jelte']: ['cats', 'programming'], ['Jelte', 'Berit']: 'music'} # %% [markdown] id="1tYs_3xBqx2e" @@ -210,7 +210,7 @@ # your code here... # %% [markdown] id="Gtp5V9dE0LxK" -# 2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`). +# 2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`). # %% id="S7gCNyLCxdrO" lots_of_fruit = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange', @@ -252,14 +252,19 @@ # %% id="mdNug4ct5645" # the variable sent0 contains the first sentence of The Catcher in the Rye # split into single words -sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', - 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', - 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', - 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', - 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', - 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', - 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', +sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', + 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', + 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', + 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', + 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', + 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', + 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', 'to', 'know', 'the', 'truth.'] # %% id="XGY3qSEk6B9j" # your code here... + +# %% [markdown] id="y5FcFvgypMfE" +# ## Next module +# +# [11 - Working with files](https://colab.research.google.com/drive/1_mDpeRCHzrGJstcxEWZgq5YMhHujPdfe) diff --git a/lessons/11 working with files.ipynb b/lessons/11 working with files.ipynb index b9e630e..44c7e44 100644 --- a/lessons/11 working with files.ipynb +++ b/lessons/11 working with files.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "2c9766ab", "metadata": { "id": "uHYIr4R9dWyQ" }, @@ -10,9 +11,9 @@ "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [10. dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9)\n", + "Previous module: [10. dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj)\n", "\n", "### This module\n", "\n", @@ -23,6 +24,7 @@ }, { "cell_type": "markdown", + "id": "ccdf1db9", "metadata": { "id": "MgaAMPDbdiTr" }, @@ -35,6 +37,7 @@ }, { "cell_type": "markdown", + "id": "0058c2f9", "metadata": { "id": "p9157kmNeAfI" }, @@ -48,6 +51,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8978a4f1", "metadata": { "id": "kup_j23UeW71", "lines_to_next_cell": 2 @@ -72,21 +76,23 @@ }, { "cell_type": "markdown", + "id": "c688896d", "metadata": { "id": "96NQljy2ez-E" }, "source": [ "### Sidenote: constants\n", - "Isn't the convention that variables are lowercased? \n", + "Isn't the convention that variables are lowercased?\n", "Not if they are *constants*:\n", "- Variables that do not change during the progam\n", "- Often represent some 'setting' or 'constant value'\n", " - `PATH = 'countries.csv'`\n", - " - `PI = 3.14159265358979323846264338327950288419716939937510582097` \n" + " - `PI = 3.14159265358979323846264338327950288419716939937510582097`\n" ] }, { "cell_type": "markdown", + "id": "7dd3bae9", "metadata": { "id": "ZlbuEqiKftk-" }, @@ -100,6 +106,7 @@ { "cell_type": "code", "execution_count": null, + "id": "94a18f57", "metadata": { "id": "SzytvDuCgBiE" }, @@ -113,6 +120,7 @@ }, { "cell_type": "markdown", + "id": "b9401eba", "metadata": { "id": "9V_7uhwdgDXA" }, @@ -127,6 +135,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8fcc4351", "metadata": { "id": "a-Q7R8x4gby6" }, @@ -140,6 +149,7 @@ }, { "cell_type": "markdown", + "id": "4d02b144", "metadata": { "id": "bbtncTLHhUaq" }, @@ -155,6 +165,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e8010e3b", "metadata": { "id": "lSmlpO2-hn6j" }, @@ -173,6 +184,7 @@ }, { "cell_type": "markdown", + "id": "51646207", "metadata": { "id": "QVQvj4ztkhmj" }, @@ -183,6 +195,7 @@ { "cell_type": "code", "execution_count": null, + "id": "dc381616", "metadata": { "id": "NhD03dHgklBT" }, @@ -194,6 +207,7 @@ }, { "cell_type": "markdown", + "id": "9342fc3f", "metadata": { "id": "YC0q3Z4HiM5Z" }, @@ -205,6 +219,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7ceb86cd", "metadata": { "id": "vZtR4360i7kh" }, @@ -221,6 +236,7 @@ }, { "cell_type": "markdown", + "id": "31a3b9a1", "metadata": { "id": "BXOkW2J7ldYK" }, @@ -233,6 +249,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9489bf92", "metadata": { "id": "eP_TfHcmlsce" }, @@ -241,6 +258,7 @@ }, { "cell_type": "markdown", + "id": "d046e8e3", "metadata": { "id": "Phv4ave0mh5e" }, @@ -256,6 +274,7 @@ }, { "cell_type": "markdown", + "id": "3bee335a", "metadata": { "id": "TBgRxECDnTKW" }, @@ -267,6 +286,7 @@ { "cell_type": "code", "execution_count": null, + "id": "793b66e5", "metadata": { "id": "fXIBTOFDnVFU" }, @@ -278,6 +298,7 @@ }, { "cell_type": "markdown", + "id": "46d91009", "metadata": { "id": "NxBU-xkZneT6" }, @@ -288,6 +309,7 @@ { "cell_type": "code", "execution_count": null, + "id": "bc7afd51", "metadata": { "id": "Ykrw__BXni5z" }, @@ -299,6 +321,7 @@ }, { "cell_type": "markdown", + "id": "637f8bbb", "metadata": { "id": "dMIZr2o_nlch" }, @@ -310,6 +333,7 @@ { "cell_type": "code", "execution_count": null, + "id": "96701f9d", "metadata": { "id": "05Smz2DbntnT" }, @@ -330,6 +354,7 @@ }, { "cell_type": "markdown", + "id": "4ddd70e5", "metadata": { "id": "TRHR32Bfn9tO" }, @@ -340,6 +365,7 @@ { "cell_type": "code", "execution_count": null, + "id": "b0fe8a6b", "metadata": { "id": "T67PXssOoICa" }, @@ -360,6 +386,7 @@ }, { "cell_type": "markdown", + "id": "a8b9e850", "metadata": { "id": "h-mOS3pwoc1O" }, @@ -379,22 +406,14 @@ } ], "metadata": { - "colab": { - "authorship_tag": "ABX9TyMB+UscaVxkKRr/LCtKDZnL", - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "name": "python3" - }, - "language_info": { - "name": "python" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 5 } diff --git a/lessons/11 working with files.py b/lessons/11 working with files.py index c4a70ad..57ab5d8 100644 --- a/lessons/11 working with files.py +++ b/lessons/11 working with files.py @@ -16,9 +16,9 @@ # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [10. dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9) +# Previous module: [10. dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj) # # ### This module # @@ -57,12 +57,12 @@ # %% [markdown] id="96NQljy2ez-E" # ### Sidenote: constants -# Isn't the convention that variables are lowercased? +# Isn't the convention that variables are lowercased? # Not if they are *constants*: # - Variables that do not change during the progam # - Often represent some 'setting' or 'constant value' # - `PATH = 'countries.csv'` -# - `PI = 3.14159265358979323846264338327950288419716939937510582097` +# - `PI = 3.14159265358979323846264338327950288419716939937510582097` # # %% [markdown] id="ZlbuEqiKftk-" diff --git a/lessons/Bonus Exercise data.ipynb b/lessons/Bonus Exercise data.ipynb index 66dbf00..ca17926 100644 --- a/lessons/Bonus Exercise data.ipynb +++ b/lessons/Bonus Exercise data.ipynb @@ -3,6 +3,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ecc41afc", "metadata": { "id": "7gksh6WLMy2q" }, @@ -531,21 +532,14 @@ } ], "metadata": { - "colab": { - "authorship_tag": "ABX9TyPlTZNDb2yTw7vB3bqNYG48", - "provenance": [] - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "name": "python3" - }, - "language_info": { - "name": "python" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 5 } diff --git a/lessons/Extra exercises day 1.ipynb b/lessons/Extra exercises day 1.ipynb index c156f9c..0c19419 100644 --- a/lessons/Extra exercises day 1.ipynb +++ b/lessons/Extra exercises day 1.ipynb @@ -1,16 +1,16 @@ { "cells": [ { - "attachments": {}, "cell_type": "markdown", + "id": "a1452008", "metadata": {}, "source": [ "Hierbij wat extra opdrachtjes voor de modules van dag 1!" ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "560bb4d1", "metadata": {}, "source": [ "# Types\n", @@ -26,7 +26,8 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, + "id": "39e41f18", "metadata": {}, "outputs": [], "source": [ @@ -37,8 +38,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "8eb4c6d9", "metadata": {}, "source": [ "Did you know that you can check the type of a variable in Python? If you are ever unsure of the type of a variable, use the `type()` function to check, e.g.:" @@ -46,20 +47,10 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "\n", - "\n" - ] - } - ], + "execution_count": null, + "id": "0feefa75", + "metadata": {}, + "outputs": [], "source": [ "print(type(1))\n", "print(type(1.0))\n", @@ -68,8 +59,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "04db6ba5", "metadata": {}, "source": [ "2. Now try and check the types of the examples you just declared using the method above:" @@ -77,7 +68,8 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, + "id": "8dd1756b", "metadata": {}, "outputs": [], "source": [ @@ -85,8 +77,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "2938e3bb", "metadata": {}, "source": [ "Python is a 'dynamically typed' language, which means that the type of a variable can vary based on its context or user coercion. For example, you can coerce an integer to a float by declaring:" @@ -94,18 +86,10 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2 \n", - "2.0 \n" - ] - } - ], + "execution_count": null, + "id": "ee9757e5", + "metadata": {}, + "outputs": [], "source": [ "example_integer = 2 # declare example integer\n", "print(example_integer, type(example_integer)) #print the integer and its type\n", @@ -115,8 +99,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "96920f51", "metadata": {}, "source": [ "You can coerce these types manually or Python sometimes does it automatically, for example when you try and execute operations on a variable that requires a certain type. For instance, if you try to add the integer and float above, the result of that operation will be a float. Python automatically coerces the integer to a float to complete the addition.\n", @@ -126,17 +110,10 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3.0 \n" - ] - } - ], + "execution_count": null, + "id": "1eb2eaca", + "metadata": {}, + "outputs": [], "source": [ "ex_int = 1\n", "ex_float = 2.0\n", @@ -148,8 +125,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "f789aa58", "metadata": {}, "source": [ "# Lists\n", @@ -160,7 +137,8 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, + "id": "12ce6d82", "metadata": {}, "outputs": [], "source": [ @@ -168,8 +146,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "0781d4b1", "metadata": {}, "source": [ "You can check the length of the list with the `len()` function:" @@ -177,28 +155,17 @@ }, { "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "object of type 'NoneType' has no len()", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[20], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mlen\u001b[39;49m(my_list))\n", - "\u001b[0;31mTypeError\u001b[0m: object of type 'NoneType' has no len()" - ] - } - ], + "execution_count": null, + "id": "30162fa3", + "metadata": {}, + "outputs": [], "source": [ "print(len(my_list))" ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "43d04710", "metadata": {}, "source": [ "2. Now, add some elements to your list. Then check the length of the list again!" @@ -206,7 +173,8 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, + "id": "71a28165", "metadata": {}, "outputs": [], "source": [ @@ -214,8 +182,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "08f854c5", "metadata": {}, "source": [ "# Loops\n", @@ -223,8 +191,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "4f6afa9c", "metadata": {}, "source": [ "1. Now that you have your list, let's walk through it and print every element in it. Hint: use the `for` loop." @@ -232,7 +200,8 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, + "id": "acb95a74", "metadata": {}, "outputs": [], "source": [ @@ -240,8 +209,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "a8abd9a4", "metadata": {}, "source": [ "2. Instead of simply printing the elements, see if you can print the result of an operation with each element (for example, print the element + 1). Alternatively, see if you can print the type of each element in the list." @@ -249,7 +218,8 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, + "id": "997dd7fd", "metadata": {}, "outputs": [], "source": [ @@ -257,8 +227,8 @@ ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "6b2f04a4", "metadata": {}, "source": [ "# Logical Operators\n", @@ -268,7 +238,8 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, + "id": "6ff79c35", "metadata": {}, "outputs": [], "source": [ @@ -287,28 +258,18 @@ }, { "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "b8bf098d", + "metadata": {}, + "outputs": [], "source": [ "#Example 1\n", "n1 == n2" ] }, { - "attachments": {}, "cell_type": "markdown", + "id": "3c33bb8e", "metadata": {}, "source": [ "True or False?" @@ -316,20 +277,10 @@ }, { "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "e938649b", + "metadata": {}, + "outputs": [], "source": [ "#Example 2\n", "n1 + n2 == 3" @@ -337,20 +288,10 @@ }, { "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "0c52dfe1", + "metadata": {}, + "outputs": [], "source": [ "#Example 3\n", "n1 + n3 != 3.4" @@ -358,6 +299,7 @@ }, { "cell_type": "markdown", + "id": "fe7a2b5f", "metadata": {}, "source": [ "True or False?" @@ -365,20 +307,10 @@ }, { "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "6c537a48", + "metadata": {}, + "outputs": [], "source": [ "#Example 4\n", "s1 + s2 == \"Hello World\"" @@ -386,6 +318,7 @@ }, { "cell_type": "markdown", + "id": "5f4edbf8", "metadata": {}, "source": [ "True or False?" @@ -393,20 +326,10 @@ }, { "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "b5de5047", + "metadata": {}, + "outputs": [], "source": [ "#Example 5\n", "s3 == \"hallo\"" @@ -414,6 +337,7 @@ }, { "cell_type": "markdown", + "id": "f80a8075", "metadata": {}, "source": [ "True or False?" @@ -421,20 +345,10 @@ }, { "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "873dfb89", + "metadata": {}, + "outputs": [], "source": [ "#Example 6\n", "len(l1) == len(l2)" @@ -442,6 +356,7 @@ }, { "cell_type": "markdown", + "id": "1e5aa506", "metadata": {}, "source": [ "True or False?" @@ -449,20 +364,10 @@ }, { "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "17b9ab0e", + "metadata": {}, + "outputs": [], "source": [ "#Example 7\n", "len(s3) == len(s1)" @@ -470,20 +375,10 @@ }, { "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n", - "True\n", - "True\n", - "False\n" - ] - } - ], + "execution_count": null, + "id": "32ed9d09", + "metadata": {}, + "outputs": [], "source": [ "##Example 8\n", "for item in l2:\n", @@ -496,25 +391,8 @@ "display_name": "Python 3", "language": "python", "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.2" - }, - "vscode": { - "interpreter": { - "hash": "2cc67e3a9982c67c9491f847535e93961a1a3bd6725cdcb43113c9880de5dac3" - } } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 5 } diff --git a/lessons/Life-after-the-course.ipynb b/lessons/Life-after-the-course.ipynb new file mode 100644 index 0000000..27f0ea3 --- /dev/null +++ b/lessons/Life-after-the-course.ipynb @@ -0,0 +1,783 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8538127d", + "metadata": { + "id": "QMJDG-UsDngf" + }, + "source": [ + "# Life after the course\n", + "\n", + "Entry level programming in Python - CDH\n", + "\n", + "Congratulations, you finished the course. During the course, you learned basic Python techniques as well as important refactoring and debugging skills. Finally, you learned how to import CSV files and obtained leads on how to conduct your own analysis.\n", + "\n", + "Due to the limited time, we could not cover all the basic skills that a programmer needs in order to be economic with her development time. Also, while online notebooks are a great teaching tool and we would not have been able to cover as much ground without them, you will be better able to reuse your own code if you learn how to run standalone Python programs on your own computer.\n", + "\n", + "In order to help you with the next steps on your learning journey, we list the concepts below and provide links to web pages where you can learn more about the topic in question. We recommend that you scroll through the entire notebook; all principles and techniques discussed here are essential best practices." + ] + }, + { + "cell_type": "markdown", + "id": "6b1c51ff", + "metadata": { + "id": "F2I_ZaA9J496" + }, + "source": [ + "## Troubleshooting\n", + "\n", + "Whether you are trying to install something and it is refusing or your own code is throwing errors at you, more often than not a well-chosen web search will lead you to a solution. Good queries generally include the name of the program or package that you use, the name of the feature or function that you use, and the name of the error that you get (or a concise description of the problem: \"no output\", \"lower value than expected\", etcetera). For example [`pandas read_csv EncodingError`](https://duckduckgo.com/?q=pandas+read_csv+EncodingError).\n", + "\n", + "In many cases, a web search will lead you to [Stack Overflow](https://stackoverflow.com) (or a related question-and-answer site). You can also post your own questions about code-related problems here. There are some [tips](https://stackoverflow.com/help/how-to-ask) to help you maximize the change that you'll get a useful answer. If somebody answers your question adequately, be sure to [accept the answer](https://stackoverflow.com/help/someone-answers)." + ] + }, + { + "cell_type": "markdown", + "id": "9258116b", + "metadata": { + "id": "cCxnaLPZVblw" + }, + "source": [ + "## Command line interface (CLI, shell)\n", + "\n", + "Command line interfaces (CLIs) let you control your computer by typing commands instead of by clicking with your mouse. Every PC comes with a CLI by default (including macs). On Windows, the default CLI is called Command Prompt (`cmd.exe`) while on other platforms, it is usually called Terminal. In both cases, the text-based interface running within the window is called the *shell*. Since the distinction is not so important most of the time, we often use the word \"shell\" or \"terminal\" to refer to either.\n", + "\n", + "You don't need to be a shell expert, but being able to navigate your file system and to run basic commands can be very useful when programming on your own PC. There are many [introductory tutorials online](https://duckduckgo.com/?q=basic+command+line+interface+tutorial). https://ss64.com is a useful cross-platform reference of standard commands and syntax." + ] + }, + { + "cell_type": "markdown", + "id": "c63c2024", + "metadata": { + "id": "E343lYdIZvPB" + }, + "source": [ + "## Software needed for editing and running Python on your PC\n", + "\n", + "Running standalone Python on your PC is a bit different from using notebooks. At the very least, you will need a code editor and a Python interpreter. As the name suggests, a Python interpreter is a program that reads Python code and executes the program that is described in the code.\n", + "\n", + "Over time, programmers usually end up installing a lot of other software as well. For this reason, it is generally worthwhile to install a package manager." + ] + }, + { + "cell_type": "markdown", + "id": "5d3a3ca8", + "metadata": { + "id": "TsJgFTy7hiPv" + }, + "source": [ + "### Editor\n", + "\n", + "While there are more options, we will mention a few general-purpose code editors that are both widely used and actively maintained:\n", + "\n", + "- [BBEdit](https://www.barebones.com/products/bbedit/index.html) (macOS)\n", + "- [Notepad++](https://notepad-plus-plus.org) (Windows)\n", + "- [Sublime Text](https://www.sublimetext.com) (cross-platform)\n", + "- [TextMate](https://macromates.com) (macOS)\n", + "- [Visual Studio Code](https://code.visualstudio.com) (cross-platform)\n", + "\n", + "There are also \"integrated development environments\" (IDEs) specifically targeted at Python programming, such as [Spyder](https://www.spyder-ide.org) for data scientists and [PyCharm](https://www.jetbrains.com/pycharm/) for application developers. These have lots of bells and whistles. Some people find them very convenient. We mention them here for completeness, but we recommend learning how to work without one first." + ] + }, + { + "cell_type": "markdown", + "id": "0272c7be", + "metadata": { + "id": "agGDd9aQk_or" + }, + "source": [ + "### Python and a package manager\n", + "\n", + "macOS and Linux come with Python preinstalled by default, though this is usually an outdated version of Python. In principle, you *could* also just [download Python from the website](https://www.python.org/downloads/). However, since it is likely that you'll be installing more software alongside Python, we recommend investing some additional time to install a package manager instead, which will give you a central workflow for installing Python as well as any related software. Especially on Windows, using a package manager will likely provide a smoother experience as well.\n", + "\n", + "One approach to installing a package manager is [Anaconda](https://www.anaconda.com). It is specifically targeted at data scientists working with Python and it ships with many commonly used Python packages by default, as well as the Spyder IDE that we mentioned in the editor section. For a much smaller download, you can install [miniconda](https://docs.conda.io/en/latest/miniconda.html) instead, to get just Python, Anaconda's package manager (`conda`) and a small amount of supporting software. While conventient, `conda` is not a complete solution like the general purpose package managers that we discuss next.\n", + "\n", + "Linux distribution generally ship with a general purpose package manager by default, such as `apt-get` or `yum`. If you work with Linux, you are probably already familiar with your package manager. If you install Python through such a package manager, you might need to install a secondary package for Python *development* as well, which might for example be named `python-devel`.\n", + "\n", + "macOS does not ship with a package manager by default, but you can install [MacPorts](https://www.macports.org) or [HomeBrew](https://brew.sh).\n", + "\n", + "Windows does not follow the same conventions as Linux and macOS, but there are options. [Chocolatey](https://chocolatey.org) is a dedicated package manager for Windows. Since Windows 10, Microsoft provides its own [native package manager](https://docs.microsoft.com/en-us/windows/package-manager/) as well. At the DH Lab, we have tried neither of these options, so we cannot comment on how well they work. Alternatively, you can use the `pacman` package manager that is included with [Git for Windows](https://gitforwindows.org), which we recommend to install anyway. More about Git in a later section." + ] + }, + { + "cell_type": "markdown", + "id": "935deb08", + "metadata": { + "id": "SprAGQ0AzjfR" + }, + "source": [ + "## Basic workflow for running standalone Python\n", + "\n", + "Notebooks contain a mixture of formatted text and runnable snippets of Python code. Standalone Python programs, on the other hand, are plain text files containing only Python code (including comments) with the `.py` extension. Suppose that you copy-paste the following code into a new file using your code editor, and save it with the name `hello.py`:\n", + "\n", + "```python\n", + "print('Hello, world!')\n", + "```\n", + "\n", + "Using a shell, you can now navigate to the folder containing `hello.py` and type the following command in order to run it:\n", + "\n", + "```shell\n", + "python3 hello.py\n", + "```\n", + "\n", + "On Windows, depending on how you installed Python, you might need to run `py` instead of `python3`.\n", + "\n", + "*On Windows, the `python3` or `py` command will only work if your shell knows where to find Python. If you installed Python through a package manager, use the shell that ships with it, such as Anaconda Prompt. If you downloaded Python directly from the website, search the web on how to set the `PATH` environment variable and optionally also `PATHEXT`).*\n", + "\n", + "Typing just `python` instead of `python3` might work, too, but in some cases, this is still Python version 2 because it was installed on your PC by default. You can check the version like this:\n", + "\n", + "```shell\n", + "python --version\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "5322e3ae", + "metadata": { + "id": "Q0sFMv09Vzdp" + }, + "source": [ + "## Modules\n", + "\n", + "The official name for a plain text file that contains Python code is *module*. The `hello.py` from the previous section is an example of a module. Modules are named this way because your program can consist of multiple files, so they enable *modular programming*. Spreading your code over multiple files can make it easier to find back the right piece of code (if you chunk your code in a logical way). Individual modules can also be reused in new programs.\n", + "\n", + "A module or group of modules that is specifically meant for reuse is called a *library*. By convention, a library is usually saved in a separate folder and we call that folder a *package*. We will discuss libraries and packages again in the section on dependencies.\n", + "\n", + "Suppose that we have a module named `cheerlead.py` with the following functions (the short notation used here was discussed in the \"Iterables\" section of the final exercise tips):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c41da46b", + "metadata": { + "id": "N69uA9Ibrakg" + }, + "outputs": [], + "source": [ + "announce = 'We have a: {}'.format\n", + "yell = 'Go go {}!!!'.format\n", + "\n", + "def cheer(name):\n", + " letters = '\\n'.join(map(announce, name))\n", + " return '\\n'.join([letters, yell(name)])" + ] + }, + { + "cell_type": "markdown", + "id": "df3142da", + "metadata": { + "id": "A-5PwPAYra7B" + }, + "source": [ + "and we have a second module in the same folder, named `greet.py`, which uses the `cheer` function from `cheerlead.py`:\n", + "\n", + "```python\n", + "from cheerlead import cheer\n", + "\n", + "name = input('Welcome, visitor. What is your name? ')\n", + "print(cheer(name))\n", + "```\n", + "\n", + "We can now run\n", + "\n", + "```shell\n", + "python3 greet.py\n", + "```\n", + "\n", + "While `greet.py` contains only part of the program, Python knows where to find the rest by following the `import` statements. You can read more about modules [here](https://docs.python.org/3/tutorial/modules.html)." + ] + }, + { + "cell_type": "markdown", + "id": "59cb9ad6", + "metadata": { + "id": "Lp68EBfdDp6B" + }, + "source": [ + "## Dependencies\n", + "\n", + "So far, we have used the word \"package\" liberally, but there are actually two kinds of \"package\": it can either refer to any software that you install using your package manager (such as Python itself), or to a collection of Python modules that was published for general reuse. This section is about the latter kind. When you use such a Python package in your project, this is called a dependency.\n", + "\n", + "All the `import` statements that we have seen in examples so far, can be roughly divided into three categories. Firstly, imports from other modules in the same directory, which are often written by the same author. The following examples appeared in previous sections:\n", + "\n", + "```python\n", + "from cheerlead import cheer\n", + "from greet import main as greet_main\n", + "```\n", + "\n", + "Secondly, imports from *standard modules*. These modules are part of the standard library that ships with Python, so you never need to install them manually. Most of the following examples appeared in the tips:\n", + "\n", + "```python\n", + "import csv\n", + "import os.path as op\n", + "from operator import add\n", + "from itertools import repeat\n", + "from functools import reduce\n", + "from statistics import mean, stdev\n", + "from datetime import datetime as dt\n", + "```\n", + "\n", + "Finally, imports from *third-party packages*. These are neither part of your own code, nor of the Python standard library. The following examples appeared in the last few sections of the tips:\n", + "\n", + "```python\n", + "import pandas\n", + "from sklearn.cluster import AgglomerativeClustering\n", + "```\n", + "\n", + "In Google Colab, many third-party packages are installed by default, so the code examples just worked. In the general case, however, third-party packages must be downloaded first. When your program requires such packages in order to run, it is necessary to document those dependencies. In the next three subsections, we discuss the main ingredients that you need in order to manage your dependencies well." + ] + }, + { + "cell_type": "markdown", + "id": "6e6268a4", + "metadata": { + "id": "nxzGlbHfRIN1" + }, + "source": [ + "### Virtual environments\n", + "\n", + "Once you start programming, you never stop. After your first program, there will be more projects. At some point, you might even be working on two or more projects in parallel.\n", + "\n", + "It is unlikely that two projects have exactly the same dependencies. In many cases, the dependencies of one project will conflict with the dependencies of another. For this reason (and a few other reasons), it is highly recommended to keep separate sets of installed dependencies for each project. This is the job of virtual environments.\n", + "\n", + "A virtual environment is a folder in which you install third-party packages. You can have as many virtual environments as you want. Each virtual environment can also have its own version of Python. When you *activate* a virtual environment, its installed packages become available to you, while the packages inside all other virtual environments are invisible. When you install additional packages, they are added to the active virtual environment only.\n", + "\n", + "In the most basic case, you run `python3 -m venv` in the shell and tell it to create a new environment in a given directory. For example, the following command will create an environment in the folder `.env` within your current working directory:\n", + "\n", + "```shell\n", + "python3 -m venv .env\n", + "```\n", + "\n", + "After the above command, you can activate the environment like this on Linux, macOS and emulating environments like the MSYS2 included in Git for Windows:\n", + "\n", + "```shell\n", + "source .env/bin/activate\n", + "```\n", + "\n", + "and like this in the Windows Command Prompt:\n", + "\n", + "```shell\n", + ".env\\Scripts\\activate.bat\n", + "```\n", + "\n", + "After this you can run `deactivate` to leave the environment again. There are a couple of variations possible, such as selecting a different version of Python. You can read more about creating and using virtual environments [here](https://docs.python.org/3/tutorial/venv.html).\n", + "\n", + "Beyond the basic case, there are various tools that add sophistications, such as storing environments in a central place and switching between them with shorter commands. Anaconda/miniconda takes this quite far by including environment creation and switching into its own `conda` command. You can read more about that [here](https://docs.conda.io/projects/conda/en/latest/user-guide/getting-started.html)." + ] + }, + { + "cell_type": "markdown", + "id": "a586382b", + "metadata": { + "id": "Aydk8okoYqV3" + }, + "source": [ + "### Installing packages\n", + "\n", + "By default, every virtual environment has the `pip` command, which lets you install third-party packages that were published to the [Python Package Index (PyPI)](https://pypi.org). Basically, this is a second, dedicated package manager just for Python packages. Manually installing a single package, for example `pandas`, looks like this:\n", + "\n", + "```shell\n", + "python3 -m pip install pandas\n", + "```\n", + "\n", + "Of course, it is also possible to update packages and to uninstall them again. You can read more about pip [here](https://packaging.python.org/installing/). Again, `conda` has its own way of doing things, which you can read more about [here](https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-pkgs.html)." + ] + }, + { + "cell_type": "markdown", + "id": "f42db6fa", + "metadata": { + "id": "oypf9UVDcbVS" + }, + "source": [ + "### `requirements.txt`\n", + "\n", + "When using an external package, it is often the case that the feature you need was not yet present in an older version. It might also be removed again in a newer version. For this reason, the **versions** of the packages that you use, **are very important**. Each package itself might again depend on other packages (such indirect dependencies are automatically included when you `pip install` a package), and the versions of those indirect dependencies matter as well.\n", + "\n", + "It is **vitally important** to record the exact versions of all your direct and indirect dependencies, so that you can reproduce your environment at a later time or on a different computer. Without this, *it might be impossible to get your program to work again*.\n", + "\n", + "By convention, we store a listing of all direct and indirect dependencies with their exact versions in a file called `requirements.txt`. You can quickly create or update a `requirements.txt` based on the contents of your active virtual environment with `pip freeze`:\n", + "\n", + "```shell\n", + "python3 -m pip freeze > requirements.txt\n", + "```\n", + "\n", + "With `conda`, you can also do this:\n", + "\n", + "```shell\n", + "conda list > requirements.txt\n", + "```\n", + "\n", + "You should run either command again every time you add, update or remove a package. When publishing your code, **always** include the `requirements.txt`.\n", + "\n", + "You can install all the exact versions of packages specified in a `requirements.txt` using `pip install -r`:\n", + "\n", + "```shell\n", + "python3 -m pip install -r requirements.txt\n", + "```\n", + "\n", + "See [here](https://pip.pypa.io/en/latest/user_guide/#requirements-files) for more information about `requirements.txt`. There are some tools, such as [pip-tools](https://pypi.org/project/pip-tools/), that streamline the management of the `requirements.txt` and which can also automatically remove indirect dependencies that are no longer needed after an update.\n", + "\n", + "The `setup.py` or `setup.cfg` that comes with [`setuptools`](https://setuptools.pypa.io/en/latest/userguide/quickstart.html) is a more sophisticated alternative to the `requirements.txt`. It is recommended to convert to using `setuptools` when you consider publishing your package to PyPI. Like `requirements.txt`, it integrates well with `pip`." + ] + }, + { + "cell_type": "markdown", + "id": "39509fd5", + "metadata": { + "id": "rPBZJd32-I_i" + }, + "source": [ + "## The interactive Python prompt\n", + "\n", + "If you just enter `python3` in the shell, without passing the name of a module, Python will run in interactive mode. It shows a prompt that looks like this:\n", + "\n", + " >>>\n", + "\n", + "After the prompt, you can enter any Python code. It can also be an `import` statement in order to retrieve functions from an existing module. Python will run the code that you enter, print any output and then present you with a new prompt. Among other things, this is useful for debugging individual functions, for using Python as an advanced calculator, or for trying out short snippets of code before typing them into a module.\n", + "\n", + "You can exit the interactive prompt by typing `quit()` (note that this doesn't work inside a regular module). You can also type `help()` for very helpful interactive documentation." + ] + }, + { + "cell_type": "markdown", + "id": "9c39731e", + "metadata": { + "id": "yn6rwOg-0Tmz" + }, + "source": [ + "## `main`\n", + "\n", + "In the example from the [Modules section](#scrollTo=Q0sFMv09Vzdp), there was a very strict task division between `cheerlead.py` and `greet.py`. `cheerlead.py` contained only reusable functions. If you were to run `python cheerlead.py`, it would silently exit without reading input or printing output. `greet.py`, on the other hand, contained *no* reusable code and was *only* fit for being run as a program. If we were to do `import greet` inside another module, that module would always ask for the same input and print the same output as `greet.py` would do, even if that other module is actually supposed to do something else. We could say that `cheerlead.py` is a reusable module while `greet.py` is a program entry point, although this isn't an official distinction.\n", + "\n", + "The task division doesn't have to be so strict. We can write modules that are both fit for reuse and that can also be run as a program. In order to do this, we follow the convention of defining a `main` function. All code that is supposed to run only when the module is run as a program, should go inside `main`. We can rewrite `greet.py` to follow this convention:\n", + "\n", + "```python\n", + "from cheerlead import cheer\n", + "\n", + "def main():\n", + " # same code as before, now inside main\n", + " name = input('Welcome, visitor. What is your name? ')\n", + " print(cheer(name))\n", + "\n", + "# call main, but only if this module was\n", + "# run as the entry point of the program\n", + "if __name__ == '__main__':\n", + " main()\n", + "```\n", + "\n", + "With the above adjustment, `python greet.py` will read input and print output as before, but the same will not happen if we do `import greet` inside another module and then run that module. Though we can still achieve that by explicitly importing and calling `main` if we want to:\n", + "\n", + "```python\n", + "from greet import main as greet_main\n", + "\n", + "def main():\n", + " print('This module is mostly doing the same as greet.py.')\n", + " greet_main()\n", + "\n", + "if __name__ == '__main__':\n", + " main()\n", + "```\n", + "\n", + "We could now continue to add a `main` function to `cheerlead.py` as well, and to add reusable functions to `greet.py` for importing in other modules. In this way, every module can be a reusable module and a program entry point at the same time." + ] + }, + { + "cell_type": "markdown", + "id": "48fe5b48", + "metadata": { + "id": "a4INew0rDA5I" + }, + "source": [ + "## Exit status (exit code)\n", + "\n", + "A long-standing convention in shells is that programs \"return\" a number when they exit, similar to how a function might return a value. So far, we have done nothing to indicate what should be this *exit status* of our program. By default, Python assumes that we want to set the exit status to `0` (zero), so that has been the implicit exit code of our programs. We can see the exit code of the last command that we ran using `echo %ERRORLEVEL%` in the Windows Command Prompt, or `echo $?` in most other shells:\n", + "\n", + "```shell\n", + "python3 greet.py\n", + "echo $?\n", + "```\n", + "\n", + "The shell uses the exit status to detect whether a command ran successfully. Zero indicates success while non-zero exit codes indicate failure. Some programs use different numbers to indicate different ways in which they can fail.\n", + "\n", + "You are not required to set the exit code explicitly. If your program terminates with an error, Python will set the exit status to `1` by default, so that the shell understands that the program failed. You *can* however set the exit code explicitly using `sys.exit` (which also immediately terminates the program). A common pattern is to return a number from the `main` function and then pass this to `sys.exit`. We demonstrate this pattern in the small program below:\n", + "\n", + "```python\n", + "import sys\n", + "\n", + "def main():\n", + " # Let's see whether Python is consistent today.\n", + " if 1 < 2:\n", + " return 0 # success\n", + " else:\n", + " return 1 # failure\n", + "\n", + "if __name__ == '__main__':\n", + " sys.exit(main())\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "5b1160c3", + "metadata": { + "id": "9lR2Badd7pon" + }, + "source": [ + "## Program arguments\n", + "\n", + "We have previously shown the program `greet.py`, which will ask us for a name and then cheer it. Arguably, it would be a bit more efficient if we could pass the name already when calling the program, so we could skip the interactive question. Something like this:\n", + "\n", + "```shell\n", + "python3 greet.py Julian\n", + "```\n", + "\n", + "In the above example command, `Julian` is an argument to the `greet.py` program, similar to how we can pass arguments to functions.\n", + "\n", + "Using program arguments is a great way to make your program more flexible. For example, by making the path to a file a program argument, you make it easier to run your program with other files, or on another computer, where files might be located in a different folder.\n", + "\n", + "In the previous section, we saw `sys.exit`, which lets us set the program exit code; the same `sys` standard module also provides `argv`, which is a list with the program arguments, including the name of your program as the first element. Since `main` represents the program as a whole, let's extend the pattern for invoking `main` to include both program arguments and the exit status:\n", + "\n", + "```python\n", + "import sys\n", + "\n", + "def main(argv):\n", + " if len(argv) == 1:\n", + " print(\"You didn't pass any arguments.\")\n", + " return 1\n", + " print('You passed these arguments:')\n", + " print(argv[1:])\n", + " return 0\n", + "\n", + "if __name__ == '__main__':\n", + " sys.exit(main(sys.argv))\n", + "```\n", + "\n", + "In the general case, a program may have many parameters, some of which may be optional. There is a syntax for working with named arguments as well, which would look like `python3 greet.py --name Julian` in our example above. By convention, the most important named arguments can also be abbreviated, which would look like `-n` instead of `--name`. You may also have parameters that accept a variable number of values, and then we generally expect that we can pass named arguments in any order. As you might imagine, extracting all that information correctly from `sys.argv`, which is just a simple list of strings, would be really difficult. Thankfully, you don't have to do this yourself.\n", + "\n", + "The `argparse` standard module gives you the means to just describe which arguments your program expects. It will read `sys.argv` for you and figure out what goes with what. The Python documentation includes an excellent [tutorial](https://docs.python.org/3/howto/argparse.html) on how to use it, so we won't do that here.\n", + "\n", + "Assuming that you have read that tutorial, here is a pattern that we suggest for integrating `argparse` in the invocation of your `main` function:\n", + "\n", + "```python\n", + "import sys\n", + "import argparse\n", + "\n", + "# The argument parser is defined outside of the main function.\n", + "# In a large program with many options, you might even have\n", + "# a dedicated module just for the argument parser.\n", + "argparser = argparse.ArgumentParser(\n", + " description='Description of your program',\n", + " # maybe you also want to define an epilog\n", + ")\n", + "\n", + "def main(argv):\n", + " options = argparser.parse_args(argv[1:])\n", + " # the options object can be passed to other functions that\n", + " # main calls internally\n", + " return 0\n", + "\n", + "if __name__ == '__main__':\n", + " sys.exit(main(sys.argv))\n", + "```\n", + "\n", + "As a final tip, we will mention that you can save a set of arguments to a file and then pass the name of that file to your program in order to have all arguments in the file applied. This is functionality that `argparse` gives us for free. Suppose that I often want to run `python3 greet.py --name Julian`. I can write the following in a file named `greet.conf`:\n", + "\n", + " --name\n", + " Julian\n", + "\n", + "If I then run `python3 greet.py @greet.conf`, it will do exactly the same as when I would run `python3 greet.py --name Julian`. In this case, the difference is not so impressive, but when you are passing more than a few arguments, this can be very convenient." + ] + }, + { + "cell_type": "markdown", + "id": "60cacd70", + "metadata": { + "id": "XXiw0yh4IoWE" + }, + "source": [ + "## Assertions\n", + "\n", + "*This section is mostly redundant with module 5 of the course. It is retained for consistency with the next section.*\n", + "\n", + "Python has an `assert` keyword that lets you check that a condition that *should* be `True` is *actually* `True`. This is different from `if`, where the truth of the condition is optional; if you `assert` a condition and it is `False`, your program will stop with an error message. We can use this to protect ourselves and other programmers from mistakes. For example, we can add an assertion to our `cheer` function as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8867703e", + "metadata": { + "id": "Hv82mos_Ditb" + }, + "outputs": [], + "source": [ + "def cheer(name):\n", + " assert isinstance(name, str)\n", + " letters = '\\n'.join(map(announce, name))\n", + " return '\\n'.join([letters, yell(name)])" + ] + }, + { + "cell_type": "markdown", + "id": "0205cfe9", + "metadata": { + "id": "pdYC4dG8qbi1" + }, + "source": [ + "Now, our program will still work if used as intended, but if somebody passes a number, the program will fail with an `AssertionError`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a9014bc", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 402 + }, + "executionInfo": { + "elapsed": 313, + "status": "error", + "timestamp": 1638205958138, + "user": { + "displayName": "J Gonggrijp", + "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s64", + "userId": "10559993329647399108" + }, + "user_tz": -60 + }, + "id": "c1H0veEyqxlr", + "outputId": "b9d7d157-e102-41cb-d64e-679ae19f2fa5" + }, + "outputs": [], + "source": [ + "print(cheer('Kermit'))\n", + "print(cheer(100))" + ] + }, + { + "cell_type": "markdown", + "id": "a20e7818", + "metadata": { + "id": "5TGC07rTr_mb" + }, + "source": [ + "We can make the error more informative by adding a comma and a string that will appear when the assertion fails:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "645891b4", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 317 + }, + "executionInfo": { + "elapsed": 317, + "status": "error", + "timestamp": 1638206188361, + "user": { + "displayName": "J Gonggrijp", + "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s64", + "userId": "10559993329647399108" + }, + "user_tz": -60 + }, + "id": "9TUQ7gVzsdyS", + "outputId": "87ddce03-0a2b-4fdc-c877-e0c045b3ff33" + }, + "outputs": [], + "source": [ + "def cheer(name):\n", + " assert isinstance(name, str), 'name must be a string'\n", + " letters = '\\n'.join(map(announce, name))\n", + " return '\\n'.join([letters, yell(name)])\n", + "\n", + "print(cheer(100))" + ] + }, + { + "cell_type": "markdown", + "id": "462d30ca", + "metadata": { + "id": "S3n1rFNcswVL" + }, + "source": [ + "You can use as few or as many assertions in your code as you want. This is mostly a matter of taste. You don't need to omit them for performance; if you have a program that needs to run for a long time and you worry that the assertions might be slowing it down, just run `python3 -o program.py` instead of `python3 program.py` and all assertions will be skipped. Just keep in mind that assertions are meant for checking assumptions, rather than for reporting errors; for reporting errors there is the `raise` keyword, which you can read more about in Python's [exceptions tutorial](https://docs.python.org/3/tutorial/errors.html).\n", + "\n", + "There is however a special type of function that is all about writing assertions: the unittest." + ] + }, + { + "cell_type": "markdown", + "id": "28d4ccf1", + "metadata": { + "id": "Ajt86gVUv02n" + }, + "source": [ + "## Testing\n", + "\n", + "So far, when we wrote a function and we wanted to check that it worked, we just wrote a line in which we called the function. We did this in notebooks, and we could also use the interactive Python prompt for this purpose.\n", + "\n", + "```python\n", + "print(yell('Kermit'))\n", + "```\n", + "\n", + "We expect this line to output `Go go Kermit!!!`.\n", + "\n", + "If we edit the `yell` function again in the future, we will want to write such a line of code again to check that it still works. However, this would mean repeating ourselves. Better would be to save our test code in a function so we can run it as often as we want. Such a function is called a unittest. A unittest for the `yell` function might look like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d35fa65", + "metadata": { + "id": "eB4uCJ4xyvE8" + }, + "outputs": [], + "source": [ + "def test_yell():\n", + " assert yell('Kermit') == 'Go go Kermit!!!'" + ] + }, + { + "cell_type": "markdown", + "id": "23a12537", + "metadata": { + "id": "Gr-9hXvnzLhh" + }, + "source": [ + "We might as well write tests for `announce` and `cheer` while we're at it and save all of our tests in a module named `cheerlead_test.py`, next to the `cheerlead.py` module:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78e415f9", + "metadata": { + "id": "SCtmdYJA0FgH" + }, + "outputs": [], + "source": [ + "from cheerlead import announce, yell, cheer\n", + "\n", + "def test_yell():\n", + " assert yell('Kermit') == 'Go go Kermit!!!'\n", + "\n", + "def test_announce():\n", + " assert announce('x') == 'We have a: x'\n", + "\n", + "def test_cheer():\n", + " name = 'Rose'\n", + " output = cheer(name)\n", + " lines = output.splitlines()\n", + " assert len(lines) == len(name) + 1\n", + " for index, letter in enumerate(name):\n", + " assert lines[index] == announce(letter)\n", + " assert lines[-1] == yell(name)\n", + "\n", + "def main():\n", + " # Note that we are putting function names in a list.\n", + " # This is allowed! You can do it, too!\n", + " tests = [test_yell, test_announce, test_cheer]\n", + " for test in tests:\n", + " test()\n", + "\n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "id": "62b4b752", + "metadata": { + "id": "kb_40quq2DN7" + }, + "source": [ + "Now, whenever we change the contents of `cheerlead.py`, we only need to run `python cheerlead_test.py` in order to check that everything still works. If everything works, we get no output, otherwise we will see an `AssertionError`. We can add or update tests as desired. There is still some room for improvement, though:\n", + "\n", + "- It is a bit counterintuitive to remember that *no output* means that all tests *pass*. It would be nice to get some kind of explicit confirmation instead.\n", + "- The `cheerlead_test.py` module as a whole is essentially a list of test functions, but we have to list those tests *again* in the `tests` variable inside the `main` function. We have to manually update that variable every time we add or remove a test, so we are repeating ourselves. Ideally, we should have a program that automatically detects any test functions that are present and then runs them all.\n", + "- The `main` function will stop as soon as any assertion fails. That means that if `test_yell` fails, we get no information on whether `announce` and `cheer` are working as intended. It would be more convenient if all tests always run, even if some of them fail.\n", + "\n", + "The [third-party `pytest` package](https://docs.pytest.org/) solves all of these problems. If we `pip install pytest` and remember to update our `requirements.txt`, we can still write our test module the same way, but we don't need the `main` function and the `tests` variable anymore:\n", + "\n", + "```python\n", + "from cheerlead import announce, yell, cheer\n", + "\n", + "def test_yell():\n", + " assert yell('Kermit') == 'Go go Kermit!!!'\n", + "\n", + "def test_announce():\n", + " assert announce('x') == 'We have a: x'\n", + "\n", + "def test_cheer():\n", + " name = 'Rose'\n", + " output = cheer(name)\n", + " lines = output.splitlines()\n", + " assert len(lines) == len(name) + 1\n", + " for index, letter in enumerate(name):\n", + " assert lines[index] == announce(letter)\n", + " assert lines[-1] == yell(name)\n", + "```\n", + "\n", + "Now, when we run just the command `pytest`, it will do all of the following:\n", + "\n", + "- It recognizes all modules with a name ending in `_test.py` as test modules.\n", + "- In every test module, it recognizes all functions with a name starting with `test_` as unittests.\n", + "- It runs *all* tests, even if some tests fail along the way.\n", + "- It shows which tests are being run, indicating for each whether it passed or not.\n", + "- When an assertion fails, instead of just showing us the raw `AssertionError`, it gives us a detailed breakdown of the failing condition.\n", + "\n", + "On top of all that, `pytest` gives us many other tools to make testing even more flexible and efficient. For example, we can also check that functions will fail in a particular way if you pass them particular arguments. Testing with `pytest` is such a helpful, powerful tool during development that we recommend using it in every Python project. You can read all about it [here](https://docs.pytest.org/)." + ] + }, + { + "cell_type": "markdown", + "id": "8cfe953b", + "metadata": { + "id": "esIxQGvUQZhH" + }, + "source": [ + "## Version control (Git)\n", + "\n", + "A version control system (VCS) keeps track of the changes that you make to your code. This enables you to find back changes that you made in the past and find out why you made them. You can also use this to revert an old change, while keeping changes that came after it. VCSs do this by efficiently storing all versions of your code in a system called a *repository*.\n", + "\n", + "All modern VCSs also allow you to create *branches*, which you can think of as parallel series of versions, or \"alternative histories\" of your code. You might work on a new feature on one branch, recording several changes as you go, then switch to another branch to fix a bug that you noticed while working on the feature, but which isn't related to the feature itself. Fixing the bug on a separate branch lets you focus on the bug, without having to worry about unfinished code related to the new feature. When you finish the bugfix, you switch back to the feature branch, and when you finish that as well, you can *merge* the branches, creating a single version of your program that includes *both* the bugfix *and* the new feature.\n", + "\n", + "Branches are also a great tool for collaborating with other programmers. When working on a large project, you might develop one feature while another programmer is developing another feature at the same time on another branch. You don't have to see each other's changes until you merge your branches together. This is such an efficient workflow that all software development teams are using it. However, once you get used to a VCS, you'll want to use it even for a small project that you work on alone.\n", + "\n", + "Git is a distributed VCS (DVCS) that has become very popular. \"Distributed\" basically means that it can handle branches very well: developers store their branches locally and they can optionally synchronize them with collaborators. Popular platforms like GitHub, BitBucket and GitLab facilitate such collaboration (although it can also be done without such a platform). Git is by no means the only good DVCS (for example, Mercurial is also good), but if you have not chosen a DVCS yet, Git is a fine choice.\n", + "\n", + "The book [Pro Git](https://git-scm.com/book) is a detailed introduction to Git. You can either read it on screen, free of charge, or buy it in print. If you want to get started quickly with a short tutorial and save the book for later, [Git Immersion](https://gitimmersion.com) is a good choice.\n", + "\n", + "Some advice:\n", + "\n", + "- Save many commits (versions) with small changes between them. Don't overhaul entire modules and then record only a single new commit, because this largely defeats the purpose of having a VCS.\n", + "- One logical change per commit. If you fix a typo, then that fix is a commit all by itself, even if you changed only a single character. If you rename a function which you happen to call in twenty different modules, then the name changes in the module where it is defined as well as in all the modules where it is called all belong together in a single commit. Other, unrelated changes that you might have made in those same modules should *not* be part of the same commit.\n", + "- If you worked a little bit \"out of order\", Git has tricks that let you include only some of the changes that you made in the next commit, while keeping the remainder for subsequent commits. Look into \"interactive staging\" on how to do this.\n", + "- Similarly, if you make a change that with hindsight should have been included in an earlier commit, you can fix this afterwards. Look into \"interactive rebase\" on how to do this. Note that you should only rebase commits that you have not shared with other people yet.\n", + "- If, with hindsight, you combined changes in a commit that should have been in separate commits, you can also split that commit afterwards during an interactive rebase. However, keep in mind that reordering and merging small commits is much easier than breaking apart large commits. This is another reason why you should err on the side of tiny commits." + ] + } + ], + "metadata": { + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/Life-after-the-course.py b/lessons/Life-after-the-course.py new file mode 100644 index 0000000..5c53942 --- /dev/null +++ b/lessons/Life-after-the-course.py @@ -0,0 +1,524 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="QMJDG-UsDngf" +# # Life after the course +# +# Entry level programming in Python - CDH +# +# Congratulations, you finished the course. During the course, you learned basic Python techniques as well as important refactoring and debugging skills. Finally, you learned how to import CSV files and obtained leads on how to conduct your own analysis. +# +# Due to the limited time, we could not cover all the basic skills that a programmer needs in order to be economic with her development time. Also, while online notebooks are a great teaching tool and we would not have been able to cover as much ground without them, you will be better able to reuse your own code if you learn how to run standalone Python programs on your own computer. +# +# In order to help you with the next steps on your learning journey, we list the concepts below and provide links to web pages where you can learn more about the topic in question. We recommend that you scroll through the entire notebook; all principles and techniques discussed here are essential best practices. + +# %% [markdown] id="F2I_ZaA9J496" +# ## Troubleshooting +# +# Whether you are trying to install something and it is refusing or your own code is throwing errors at you, more often than not a well-chosen web search will lead you to a solution. Good queries generally include the name of the program or package that you use, the name of the feature or function that you use, and the name of the error that you get (or a concise description of the problem: "no output", "lower value than expected", etcetera). For example [`pandas read_csv EncodingError`](https://duckduckgo.com/?q=pandas+read_csv+EncodingError). +# +# In many cases, a web search will lead you to [Stack Overflow](https://stackoverflow.com) (or a related question-and-answer site). You can also post your own questions about code-related problems here. There are some [tips](https://stackoverflow.com/help/how-to-ask) to help you maximize the change that you'll get a useful answer. If somebody answers your question adequately, be sure to [accept the answer](https://stackoverflow.com/help/someone-answers). + +# %% [markdown] id="cCxnaLPZVblw" +# ## Command line interface (CLI, shell) +# +# Command line interfaces (CLIs) let you control your computer by typing commands instead of by clicking with your mouse. Every PC comes with a CLI by default (including macs). On Windows, the default CLI is called Command Prompt (`cmd.exe`) while on other platforms, it is usually called Terminal. In both cases, the text-based interface running within the window is called the *shell*. Since the distinction is not so important most of the time, we often use the word "shell" or "terminal" to refer to either. +# +# You don't need to be a shell expert, but being able to navigate your file system and to run basic commands can be very useful when programming on your own PC. There are many [introductory tutorials online](https://duckduckgo.com/?q=basic+command+line+interface+tutorial). https://ss64.com is a useful cross-platform reference of standard commands and syntax. + +# %% [markdown] id="E343lYdIZvPB" +# ## Software needed for editing and running Python on your PC +# +# Running standalone Python on your PC is a bit different from using notebooks. At the very least, you will need a code editor and a Python interpreter. As the name suggests, a Python interpreter is a program that reads Python code and executes the program that is described in the code. +# +# Over time, programmers usually end up installing a lot of other software as well. For this reason, it is generally worthwhile to install a package manager. + +# %% [markdown] id="TsJgFTy7hiPv" +# ### Editor +# +# While there are more options, we will mention a few general-purpose code editors that are both widely used and actively maintained: +# +# - [BBEdit](https://www.barebones.com/products/bbedit/index.html) (macOS) +# - [Notepad++](https://notepad-plus-plus.org) (Windows) +# - [Sublime Text](https://www.sublimetext.com) (cross-platform) +# - [TextMate](https://macromates.com) (macOS) +# - [Visual Studio Code](https://code.visualstudio.com) (cross-platform) +# +# There are also "integrated development environments" (IDEs) specifically targeted at Python programming, such as [Spyder](https://www.spyder-ide.org) for data scientists and [PyCharm](https://www.jetbrains.com/pycharm/) for application developers. These have lots of bells and whistles. Some people find them very convenient. We mention them here for completeness, but we recommend learning how to work without one first. + +# %% [markdown] id="agGDd9aQk_or" +# ### Python and a package manager +# +# macOS and Linux come with Python preinstalled by default, though this is usually an outdated version of Python. In principle, you *could* also just [download Python from the website](https://www.python.org/downloads/). However, since it is likely that you'll be installing more software alongside Python, we recommend investing some additional time to install a package manager instead, which will give you a central workflow for installing Python as well as any related software. Especially on Windows, using a package manager will likely provide a smoother experience as well. +# +# One approach to installing a package manager is [Anaconda](https://www.anaconda.com). It is specifically targeted at data scientists working with Python and it ships with many commonly used Python packages by default, as well as the Spyder IDE that we mentioned in the editor section. For a much smaller download, you can install [miniconda](https://docs.conda.io/en/latest/miniconda.html) instead, to get just Python, Anaconda's package manager (`conda`) and a small amount of supporting software. While conventient, `conda` is not a complete solution like the general purpose package managers that we discuss next. +# +# Linux distribution generally ship with a general purpose package manager by default, such as `apt-get` or `yum`. If you work with Linux, you are probably already familiar with your package manager. If you install Python through such a package manager, you might need to install a secondary package for Python *development* as well, which might for example be named `python-devel`. +# +# macOS does not ship with a package manager by default, but you can install [MacPorts](https://www.macports.org) or [HomeBrew](https://brew.sh). +# +# Windows does not follow the same conventions as Linux and macOS, but there are options. [Chocolatey](https://chocolatey.org) is a dedicated package manager for Windows. Since Windows 10, Microsoft provides its own [native package manager](https://docs.microsoft.com/en-us/windows/package-manager/) as well. At the DH Lab, we have tried neither of these options, so we cannot comment on how well they work. Alternatively, you can use the `pacman` package manager that is included with [Git for Windows](https://gitforwindows.org), which we recommend to install anyway. More about Git in a later section. + +# %% [markdown] id="SprAGQ0AzjfR" +# ## Basic workflow for running standalone Python +# +# Notebooks contain a mixture of formatted text and runnable snippets of Python code. Standalone Python programs, on the other hand, are plain text files containing only Python code (including comments) with the `.py` extension. Suppose that you copy-paste the following code into a new file using your code editor, and save it with the name `hello.py`: +# +# ```python +# print('Hello, world!') +# ``` +# +# Using a shell, you can now navigate to the folder containing `hello.py` and type the following command in order to run it: +# +# ```shell +# python3 hello.py +# ``` +# +# On Windows, depending on how you installed Python, you might need to run `py` instead of `python3`. +# +# *On Windows, the `python3` or `py` command will only work if your shell knows where to find Python. If you installed Python through a package manager, use the shell that ships with it, such as Anaconda Prompt. If you downloaded Python directly from the website, search the web on how to set the `PATH` environment variable and optionally also `PATHEXT`).* +# +# Typing just `python` instead of `python3` might work, too, but in some cases, this is still Python version 2 because it was installed on your PC by default. You can check the version like this: +# +# ```shell +# python --version +# ``` + +# %% [markdown] id="Q0sFMv09Vzdp" +# ## Modules +# +# The official name for a plain text file that contains Python code is *module*. The `hello.py` from the previous section is an example of a module. Modules are named this way because your program can consist of multiple files, so they enable *modular programming*. Spreading your code over multiple files can make it easier to find back the right piece of code (if you chunk your code in a logical way). Individual modules can also be reused in new programs. +# +# A module or group of modules that is specifically meant for reuse is called a *library*. By convention, a library is usually saved in a separate folder and we call that folder a *package*. We will discuss libraries and packages again in the section on dependencies. +# +# Suppose that we have a module named `cheerlead.py` with the following functions (the short notation used here was discussed in the "Iterables" section of the final exercise tips): + +# %% id="N69uA9Ibrakg" +announce = 'We have a: {}'.format +yell = 'Go go {}!!!'.format + +def cheer(name): + letters = '\n'.join(map(announce, name)) + return '\n'.join([letters, yell(name)]) + + +# %% [markdown] id="A-5PwPAYra7B" +# and we have a second module in the same folder, named `greet.py`, which uses the `cheer` function from `cheerlead.py`: +# +# ```python +# from cheerlead import cheer +# +# name = input('Welcome, visitor. What is your name? ') +# print(cheer(name)) +# ``` +# +# We can now run +# +# ```shell +# python3 greet.py +# ``` +# +# While `greet.py` contains only part of the program, Python knows where to find the rest by following the `import` statements. You can read more about modules [here](https://docs.python.org/3/tutorial/modules.html). + +# %% [markdown] id="Lp68EBfdDp6B" +# ## Dependencies +# +# So far, we have used the word "package" liberally, but there are actually two kinds of "package": it can either refer to any software that you install using your package manager (such as Python itself), or to a collection of Python modules that was published for general reuse. This section is about the latter kind. When you use such a Python package in your project, this is called a dependency. +# +# All the `import` statements that we have seen in examples so far, can be roughly divided into three categories. Firstly, imports from other modules in the same directory, which are often written by the same author. The following examples appeared in previous sections: +# +# ```python +# from cheerlead import cheer +# from greet import main as greet_main +# ``` +# +# Secondly, imports from *standard modules*. These modules are part of the standard library that ships with Python, so you never need to install them manually. Most of the following examples appeared in the tips: +# +# ```python +# import csv +# import os.path as op +# from operator import add +# from itertools import repeat +# from functools import reduce +# from statistics import mean, stdev +# from datetime import datetime as dt +# ``` +# +# Finally, imports from *third-party packages*. These are neither part of your own code, nor of the Python standard library. The following examples appeared in the last few sections of the tips: +# +# ```python +# import pandas +# from sklearn.cluster import AgglomerativeClustering +# ``` +# +# In Google Colab, many third-party packages are installed by default, so the code examples just worked. In the general case, however, third-party packages must be downloaded first. When your program requires such packages in order to run, it is necessary to document those dependencies. In the next three subsections, we discuss the main ingredients that you need in order to manage your dependencies well. + +# %% [markdown] id="nxzGlbHfRIN1" +# ### Virtual environments +# +# Once you start programming, you never stop. After your first program, there will be more projects. At some point, you might even be working on two or more projects in parallel. +# +# It is unlikely that two projects have exactly the same dependencies. In many cases, the dependencies of one project will conflict with the dependencies of another. For this reason (and a few other reasons), it is highly recommended to keep separate sets of installed dependencies for each project. This is the job of virtual environments. +# +# A virtual environment is a folder in which you install third-party packages. You can have as many virtual environments as you want. Each virtual environment can also have its own version of Python. When you *activate* a virtual environment, its installed packages become available to you, while the packages inside all other virtual environments are invisible. When you install additional packages, they are added to the active virtual environment only. +# +# In the most basic case, you run `python3 -m venv` in the shell and tell it to create a new environment in a given directory. For example, the following command will create an environment in the folder `.env` within your current working directory: +# +# ```shell +# python3 -m venv .env +# ``` +# +# After the above command, you can activate the environment like this on Linux, macOS and emulating environments like the MSYS2 included in Git for Windows: +# +# ```shell +# source .env/bin/activate +# ``` +# +# and like this in the Windows Command Prompt: +# +# ```shell +# .env\Scripts\activate.bat +# ``` +# +# After this you can run `deactivate` to leave the environment again. There are a couple of variations possible, such as selecting a different version of Python. You can read more about creating and using virtual environments [here](https://docs.python.org/3/tutorial/venv.html). +# +# Beyond the basic case, there are various tools that add sophistications, such as storing environments in a central place and switching between them with shorter commands. Anaconda/miniconda takes this quite far by including environment creation and switching into its own `conda` command. You can read more about that [here](https://docs.conda.io/projects/conda/en/latest/user-guide/getting-started.html). + +# %% [markdown] id="Aydk8okoYqV3" +# ### Installing packages +# +# By default, every virtual environment has the `pip` command, which lets you install third-party packages that were published to the [Python Package Index (PyPI)](https://pypi.org). Basically, this is a second, dedicated package manager just for Python packages. Manually installing a single package, for example `pandas`, looks like this: +# +# ```shell +# python3 -m pip install pandas +# ``` +# +# Of course, it is also possible to update packages and to uninstall them again. You can read more about pip [here](https://packaging.python.org/installing/). Again, `conda` has its own way of doing things, which you can read more about [here](https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-pkgs.html). + +# %% [markdown] id="oypf9UVDcbVS" +# ### `requirements.txt` +# +# When using an external package, it is often the case that the feature you need was not yet present in an older version. It might also be removed again in a newer version. For this reason, the **versions** of the packages that you use, **are very important**. Each package itself might again depend on other packages (such indirect dependencies are automatically included when you `pip install` a package), and the versions of those indirect dependencies matter as well. +# +# It is **vitally important** to record the exact versions of all your direct and indirect dependencies, so that you can reproduce your environment at a later time or on a different computer. Without this, *it might be impossible to get your program to work again*. +# +# By convention, we store a listing of all direct and indirect dependencies with their exact versions in a file called `requirements.txt`. You can quickly create or update a `requirements.txt` based on the contents of your active virtual environment with `pip freeze`: +# +# ```shell +# python3 -m pip freeze > requirements.txt +# ``` +# +# With `conda`, you can also do this: +# +# ```shell +# conda list > requirements.txt +# ``` +# +# You should run either command again every time you add, update or remove a package. When publishing your code, **always** include the `requirements.txt`. +# +# You can install all the exact versions of packages specified in a `requirements.txt` using `pip install -r`: +# +# ```shell +# python3 -m pip install -r requirements.txt +# ``` +# +# See [here](https://pip.pypa.io/en/latest/user_guide/#requirements-files) for more information about `requirements.txt`. There are some tools, such as [pip-tools](https://pypi.org/project/pip-tools/), that streamline the management of the `requirements.txt` and which can also automatically remove indirect dependencies that are no longer needed after an update. +# +# The `setup.py` or `setup.cfg` that comes with [`setuptools`](https://setuptools.pypa.io/en/latest/userguide/quickstart.html) is a more sophisticated alternative to the `requirements.txt`. It is recommended to convert to using `setuptools` when you consider publishing your package to PyPI. Like `requirements.txt`, it integrates well with `pip`. + +# %% [markdown] id="rPBZJd32-I_i" +# ## The interactive Python prompt +# +# If you just enter `python3` in the shell, without passing the name of a module, Python will run in interactive mode. It shows a prompt that looks like this: +# +# >>> +# +# After the prompt, you can enter any Python code. It can also be an `import` statement in order to retrieve functions from an existing module. Python will run the code that you enter, print any output and then present you with a new prompt. Among other things, this is useful for debugging individual functions, for using Python as an advanced calculator, or for trying out short snippets of code before typing them into a module. +# +# You can exit the interactive prompt by typing `quit()` (note that this doesn't work inside a regular module). You can also type `help()` for very helpful interactive documentation. + +# %% [markdown] id="yn6rwOg-0Tmz" +# ## `main` +# +# In the example from the [Modules section](#scrollTo=Q0sFMv09Vzdp), there was a very strict task division between `cheerlead.py` and `greet.py`. `cheerlead.py` contained only reusable functions. If you were to run `python cheerlead.py`, it would silently exit without reading input or printing output. `greet.py`, on the other hand, contained *no* reusable code and was *only* fit for being run as a program. If we were to do `import greet` inside another module, that module would always ask for the same input and print the same output as `greet.py` would do, even if that other module is actually supposed to do something else. We could say that `cheerlead.py` is a reusable module while `greet.py` is a program entry point, although this isn't an official distinction. +# +# The task division doesn't have to be so strict. We can write modules that are both fit for reuse and that can also be run as a program. In order to do this, we follow the convention of defining a `main` function. All code that is supposed to run only when the module is run as a program, should go inside `main`. We can rewrite `greet.py` to follow this convention: +# +# ```python +# from cheerlead import cheer +# +# def main(): +# # same code as before, now inside main +# name = input('Welcome, visitor. What is your name? ') +# print(cheer(name)) +# +# # call main, but only if this module was +# # run as the entry point of the program +# if __name__ == '__main__': +# main() +# ``` +# +# With the above adjustment, `python greet.py` will read input and print output as before, but the same will not happen if we do `import greet` inside another module and then run that module. Though we can still achieve that by explicitly importing and calling `main` if we want to: +# +# ```python +# from greet import main as greet_main +# +# def main(): +# print('This module is mostly doing the same as greet.py.') +# greet_main() +# +# if __name__ == '__main__': +# main() +# ``` +# +# We could now continue to add a `main` function to `cheerlead.py` as well, and to add reusable functions to `greet.py` for importing in other modules. In this way, every module can be a reusable module and a program entry point at the same time. + +# %% [markdown] id="a4INew0rDA5I" +# ## Exit status (exit code) +# +# A long-standing convention in shells is that programs "return" a number when they exit, similar to how a function might return a value. So far, we have done nothing to indicate what should be this *exit status* of our program. By default, Python assumes that we want to set the exit status to `0` (zero), so that has been the implicit exit code of our programs. We can see the exit code of the last command that we ran using `echo %ERRORLEVEL%` in the Windows Command Prompt, or `echo $?` in most other shells: +# +# ```shell +# python3 greet.py +# echo $? +# ``` +# +# The shell uses the exit status to detect whether a command ran successfully. Zero indicates success while non-zero exit codes indicate failure. Some programs use different numbers to indicate different ways in which they can fail. +# +# You are not required to set the exit code explicitly. If your program terminates with an error, Python will set the exit status to `1` by default, so that the shell understands that the program failed. You *can* however set the exit code explicitly using `sys.exit` (which also immediately terminates the program). A common pattern is to return a number from the `main` function and then pass this to `sys.exit`. We demonstrate this pattern in the small program below: +# +# ```python +# import sys +# +# def main(): +# # Let's see whether Python is consistent today. +# if 1 < 2: +# return 0 # success +# else: +# return 1 # failure +# +# if __name__ == '__main__': +# sys.exit(main()) +# ``` + +# %% [markdown] id="9lR2Badd7pon" +# ## Program arguments +# +# We have previously shown the program `greet.py`, which will ask us for a name and then cheer it. Arguably, it would be a bit more efficient if we could pass the name already when calling the program, so we could skip the interactive question. Something like this: +# +# ```shell +# python3 greet.py Julian +# ``` +# +# In the above example command, `Julian` is an argument to the `greet.py` program, similar to how we can pass arguments to functions. +# +# Using program arguments is a great way to make your program more flexible. For example, by making the path to a file a program argument, you make it easier to run your program with other files, or on another computer, where files might be located in a different folder. +# +# In the previous section, we saw `sys.exit`, which lets us set the program exit code; the same `sys` standard module also provides `argv`, which is a list with the program arguments, including the name of your program as the first element. Since `main` represents the program as a whole, let's extend the pattern for invoking `main` to include both program arguments and the exit status: +# +# ```python +# import sys +# +# def main(argv): +# if len(argv) == 1: +# print("You didn't pass any arguments.") +# return 1 +# print('You passed these arguments:') +# print(argv[1:]) +# return 0 +# +# if __name__ == '__main__': +# sys.exit(main(sys.argv)) +# ``` +# +# In the general case, a program may have many parameters, some of which may be optional. There is a syntax for working with named arguments as well, which would look like `python3 greet.py --name Julian` in our example above. By convention, the most important named arguments can also be abbreviated, which would look like `-n` instead of `--name`. You may also have parameters that accept a variable number of values, and then we generally expect that we can pass named arguments in any order. As you might imagine, extracting all that information correctly from `sys.argv`, which is just a simple list of strings, would be really difficult. Thankfully, you don't have to do this yourself. +# +# The `argparse` standard module gives you the means to just describe which arguments your program expects. It will read `sys.argv` for you and figure out what goes with what. The Python documentation includes an excellent [tutorial](https://docs.python.org/3/howto/argparse.html) on how to use it, so we won't do that here. +# +# Assuming that you have read that tutorial, here is a pattern that we suggest for integrating `argparse` in the invocation of your `main` function: +# +# ```python +# import sys +# import argparse +# +# # The argument parser is defined outside of the main function. +# # In a large program with many options, you might even have +# # a dedicated module just for the argument parser. +# argparser = argparse.ArgumentParser( +# description='Description of your program', +# # maybe you also want to define an epilog +# ) +# +# def main(argv): +# options = argparser.parse_args(argv[1:]) +# # the options object can be passed to other functions that +# # main calls internally +# return 0 +# +# if __name__ == '__main__': +# sys.exit(main(sys.argv)) +# ``` +# +# As a final tip, we will mention that you can save a set of arguments to a file and then pass the name of that file to your program in order to have all arguments in the file applied. This is functionality that `argparse` gives us for free. Suppose that I often want to run `python3 greet.py --name Julian`. I can write the following in a file named `greet.conf`: +# +# --name +# Julian +# +# If I then run `python3 greet.py @greet.conf`, it will do exactly the same as when I would run `python3 greet.py --name Julian`. In this case, the difference is not so impressive, but when you are passing more than a few arguments, this can be very convenient. + +# %% [markdown] id="XXiw0yh4IoWE" +# ## Assertions +# +# *This section is mostly redundant with module 5 of the course. It is retained for consistency with the next section.* +# +# Python has an `assert` keyword that lets you check that a condition that *should* be `True` is *actually* `True`. This is different from `if`, where the truth of the condition is optional; if you `assert` a condition and it is `False`, your program will stop with an error message. We can use this to protect ourselves and other programmers from mistakes. For example, we can add an assertion to our `cheer` function as follows: + +# %% id="Hv82mos_Ditb" +def cheer(name): + assert isinstance(name, str) + letters = '\n'.join(map(announce, name)) + return '\n'.join([letters, yell(name)]) + + +# %% [markdown] id="pdYC4dG8qbi1" +# Now, our program will still work if used as intended, but if somebody passes a number, the program will fail with an `AssertionError`: + +# %% colab={"base_uri": "https://localhost:8080/", "height": 402} id="c1H0veEyqxlr" executionInfo={"status": "error", "timestamp": 1638205958138, "user_tz": -60, "elapsed": 313, "user": {"displayName": "J Gonggrijp", "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s64", "userId": "10559993329647399108"}} outputId="b9d7d157-e102-41cb-d64e-679ae19f2fa5" +print(cheer('Kermit')) +print(cheer(100)) + + +# %% [markdown] id="5TGC07rTr_mb" +# We can make the error more informative by adding a comma and a string that will appear when the assertion fails: + +# %% colab={"base_uri": "https://localhost:8080/", "height": 317} id="9TUQ7gVzsdyS" executionInfo={"status": "error", "timestamp": 1638206188361, "user_tz": -60, "elapsed": 317, "user": {"displayName": "J Gonggrijp", "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s64", "userId": "10559993329647399108"}} outputId="87ddce03-0a2b-4fdc-c877-e0c045b3ff33" +def cheer(name): + assert isinstance(name, str), 'name must be a string' + letters = '\n'.join(map(announce, name)) + return '\n'.join([letters, yell(name)]) + +print(cheer(100)) + + +# %% [markdown] id="S3n1rFNcswVL" +# You can use as few or as many assertions in your code as you want. This is mostly a matter of taste. You don't need to omit them for performance; if you have a program that needs to run for a long time and you worry that the assertions might be slowing it down, just run `python3 -o program.py` instead of `python3 program.py` and all assertions will be skipped. Just keep in mind that assertions are meant for checking assumptions, rather than for reporting errors; for reporting errors there is the `raise` keyword, which you can read more about in Python's [exceptions tutorial](https://docs.python.org/3/tutorial/errors.html). +# +# There is however a special type of function that is all about writing assertions: the unittest. + +# %% [markdown] id="Ajt86gVUv02n" +# ## Testing +# +# So far, when we wrote a function and we wanted to check that it worked, we just wrote a line in which we called the function. We did this in notebooks, and we could also use the interactive Python prompt for this purpose. +# +# ```python +# print(yell('Kermit')) +# ``` +# +# We expect this line to output `Go go Kermit!!!`. +# +# If we edit the `yell` function again in the future, we will want to write such a line of code again to check that it still works. However, this would mean repeating ourselves. Better would be to save our test code in a function so we can run it as often as we want. Such a function is called a unittest. A unittest for the `yell` function might look like this: + +# %% id="eB4uCJ4xyvE8" +def test_yell(): + assert yell('Kermit') == 'Go go Kermit!!!' + + +# %% [markdown] id="Gr-9hXvnzLhh" +# We might as well write tests for `announce` and `cheer` while we're at it and save all of our tests in a module named `cheerlead_test.py`, next to the `cheerlead.py` module: + +# %% id="SCtmdYJA0FgH" +from cheerlead import announce, yell, cheer + +def test_yell(): + assert yell('Kermit') == 'Go go Kermit!!!' + +def test_announce(): + assert announce('x') == 'We have a: x' + +def test_cheer(): + name = 'Rose' + output = cheer(name) + lines = output.splitlines() + assert len(lines) == len(name) + 1 + for index, letter in enumerate(name): + assert lines[index] == announce(letter) + assert lines[-1] == yell(name) + +def main(): + # Note that we are putting function names in a list. + # This is allowed! You can do it, too! + tests = [test_yell, test_announce, test_cheer] + for test in tests: + test() + +if __name__ == '__main__': + main() + +# %% [markdown] id="kb_40quq2DN7" +# Now, whenever we change the contents of `cheerlead.py`, we only need to run `python cheerlead_test.py` in order to check that everything still works. If everything works, we get no output, otherwise we will see an `AssertionError`. We can add or update tests as desired. There is still some room for improvement, though: +# +# - It is a bit counterintuitive to remember that *no output* means that all tests *pass*. It would be nice to get some kind of explicit confirmation instead. +# - The `cheerlead_test.py` module as a whole is essentially a list of test functions, but we have to list those tests *again* in the `tests` variable inside the `main` function. We have to manually update that variable every time we add or remove a test, so we are repeating ourselves. Ideally, we should have a program that automatically detects any test functions that are present and then runs them all. +# - The `main` function will stop as soon as any assertion fails. That means that if `test_yell` fails, we get no information on whether `announce` and `cheer` are working as intended. It would be more convenient if all tests always run, even if some of them fail. +# +# The [third-party `pytest` package](https://docs.pytest.org/) solves all of these problems. If we `pip install pytest` and remember to update our `requirements.txt`, we can still write our test module the same way, but we don't need the `main` function and the `tests` variable anymore: +# +# ```python +# from cheerlead import announce, yell, cheer +# +# def test_yell(): +# assert yell('Kermit') == 'Go go Kermit!!!' +# +# def test_announce(): +# assert announce('x') == 'We have a: x' +# +# def test_cheer(): +# name = 'Rose' +# output = cheer(name) +# lines = output.splitlines() +# assert len(lines) == len(name) + 1 +# for index, letter in enumerate(name): +# assert lines[index] == announce(letter) +# assert lines[-1] == yell(name) +# ``` +# +# Now, when we run just the command `pytest`, it will do all of the following: +# +# - It recognizes all modules with a name ending in `_test.py` as test modules. +# - In every test module, it recognizes all functions with a name starting with `test_` as unittests. +# - It runs *all* tests, even if some tests fail along the way. +# - It shows which tests are being run, indicating for each whether it passed or not. +# - When an assertion fails, instead of just showing us the raw `AssertionError`, it gives us a detailed breakdown of the failing condition. +# +# On top of all that, `pytest` gives us many other tools to make testing even more flexible and efficient. For example, we can also check that functions will fail in a particular way if you pass them particular arguments. Testing with `pytest` is such a helpful, powerful tool during development that we recommend using it in every Python project. You can read all about it [here](https://docs.pytest.org/). + +# %% [markdown] id="esIxQGvUQZhH" +# ## Version control (Git) +# +# A version control system (VCS) keeps track of the changes that you make to your code. This enables you to find back changes that you made in the past and find out why you made them. You can also use this to revert an old change, while keeping changes that came after it. VCSs do this by efficiently storing all versions of your code in a system called a *repository*. +# +# All modern VCSs also allow you to create *branches*, which you can think of as parallel series of versions, or "alternative histories" of your code. You might work on a new feature on one branch, recording several changes as you go, then switch to another branch to fix a bug that you noticed while working on the feature, but which isn't related to the feature itself. Fixing the bug on a separate branch lets you focus on the bug, without having to worry about unfinished code related to the new feature. When you finish the bugfix, you switch back to the feature branch, and when you finish that as well, you can *merge* the branches, creating a single version of your program that includes *both* the bugfix *and* the new feature. +# +# Branches are also a great tool for collaborating with other programmers. When working on a large project, you might develop one feature while another programmer is developing another feature at the same time on another branch. You don't have to see each other's changes until you merge your branches together. This is such an efficient workflow that all software development teams are using it. However, once you get used to a VCS, you'll want to use it even for a small project that you work on alone. +# +# Git is a distributed VCS (DVCS) that has become very popular. "Distributed" basically means that it can handle branches very well: developers store their branches locally and they can optionally synchronize them with collaborators. Popular platforms like GitHub, BitBucket and GitLab facilitate such collaboration (although it can also be done without such a platform). Git is by no means the only good DVCS (for example, Mercurial is also good), but if you have not chosen a DVCS yet, Git is a fine choice. +# +# The book [Pro Git](https://git-scm.com/book) is a detailed introduction to Git. You can either read it on screen, free of charge, or buy it in print. If you want to get started quickly with a short tutorial and save the book for later, [Git Immersion](https://gitimmersion.com) is a good choice. +# +# Some advice: +# +# - Save many commits (versions) with small changes between them. Don't overhaul entire modules and then record only a single new commit, because this largely defeats the purpose of having a VCS. +# - One logical change per commit. If you fix a typo, then that fix is a commit all by itself, even if you changed only a single character. If you rename a function which you happen to call in twenty different modules, then the name changes in the module where it is defined as well as in all the modules where it is called all belong together in a single commit. Other, unrelated changes that you might have made in those same modules should *not* be part of the same commit. +# - If you worked a little bit "out of order", Git has tricks that let you include only some of the changes that you made in the next commit, while keeping the remainder for subsequent commits. Look into "interactive staging" on how to do this. +# - Similarly, if you make a change that with hindsight should have been included in an earlier commit, you can fix this afterwards. Look into "interactive rebase" on how to do this. Note that you should only rebase commits that you have not shared with other people yet. +# - If, with hindsight, you combined changes in a commit that should have been in separate commits, you can also split that commit afterwards during an interactive rebase. However, keep in mind that reordering and merging small commits is much easier than breaking apart large commits. This is another reason why you should err on the side of tiny commits. diff --git a/lessons/Project - text analysis.ipynb b/lessons/Project - text analysis.ipynb index 35fe994..220179d 100644 --- a/lessons/Project - text analysis.ipynb +++ b/lessons/Project - text analysis.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "90944614", "metadata": { "id": "kIc3b-0fqM6n" }, @@ -16,6 +17,7 @@ }, { "cell_type": "markdown", + "id": "2ae803c1", "metadata": { "id": "OSpUjXmAqJI9" }, @@ -27,6 +29,7 @@ }, { "cell_type": "markdown", + "id": "fb2be566", "metadata": { "id": "v1MTwBNTpTcN" }, @@ -38,6 +41,7 @@ { "cell_type": "code", "execution_count": null, + "id": "db9db033", "metadata": { "id": "fOZBq4OLp2Lz" }, @@ -69,6 +73,7 @@ }, { "cell_type": "markdown", + "id": "ea26b5d9", "metadata": { "id": "U8QVnwgwqTR6" }, @@ -82,6 +87,7 @@ { "cell_type": "code", "execution_count": null, + "id": "c91e83b2", "metadata": { "id": "TdLKVbsvqhGb" }, @@ -116,6 +122,7 @@ }, { "cell_type": "markdown", + "id": "c3edd9ca", "metadata": { "id": "f5q3jw66q3EG" }, @@ -127,6 +134,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a80a620f", "metadata": { "id": "5lTZXUgwq6lW" }, @@ -162,6 +170,7 @@ }, { "cell_type": "markdown", + "id": "3542c287", "metadata": { "id": "Um-nFxcTq1WA" }, @@ -171,6 +180,7 @@ }, { "cell_type": "markdown", + "id": "7757c790", "metadata": { "id": "YNbDTnBrheRq" }, @@ -191,6 +201,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4feffb6c", "metadata": { "id": "q_58MJYQadJO" }, @@ -203,6 +214,7 @@ }, { "cell_type": "markdown", + "id": "bd06cfdd", "metadata": { "id": "EuaRmuhwhTIZ" }, @@ -223,6 +235,7 @@ }, { "cell_type": "markdown", + "id": "d640e37f", "metadata": { "id": "SSnrAH0xjUx8" }, @@ -233,22 +246,14 @@ } ], "metadata": { - "colab": { - "authorship_tag": "ABX9TyPdQ81CO+C9Rw0GWjNMy5CU", - "provenance": [], - "toc_visible": true - }, "jupytext": { "main_language": "python" }, "kernelspec": { "display_name": "Python 3", "name": "python3" - }, - "language_info": { - "name": "python" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 5 } diff --git a/lessons/Tips.ipynb b/lessons/Tips.ipynb new file mode 100644 index 0000000..ecc2d92 --- /dev/null +++ b/lessons/Tips.ipynb @@ -0,0 +1,2508 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "30271bc3", + "metadata": { + "id": "eL_rkx7cIm77" + }, + "source": [ + "# Tips\n", + "\n", + "This notebook contains tips for the final exercise of the CDH entry level Python course at Utrecht University. Participants were asked to think of an analysis that they might want to perform and to submit a description of this analysis in advance of the course. The tips in this notebook were chosen based on those submissions.\n", + "\n", + "There is some overlap with the material that was already discussed during the lectures.\n", + "\n", + "While the notebook is written such that you can read it top to bottom, you are welcome to cherry-pick the topics that interest you. There is a table of contents in the left margin. Some sections contain internal links to other sections.\n", + "\n", + "As a general tip, you can get a complete overview of the Python standard library [over here][python-libs].\n", + "\n", + "[python-libs]: https://docs.python.org/3/library/index.html" + ] + }, + { + "cell_type": "markdown", + "id": "c9f4555b", + "metadata": { + "id": "wBOMsp2twgbB" + }, + "source": [ + "## Converting between types\n", + "\n", + "String to int:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25ec0057", + "metadata": { + "id": "1aU45qPvwntM" + }, + "outputs": [], + "source": [ + "int('123')" + ] + }, + { + "cell_type": "markdown", + "id": "98e66066", + "metadata": { + "id": "cfrMiRd3wy9d" + }, + "source": [ + "Integer to string:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd8fb65c", + "metadata": { + "id": "LNJEIXCtw6rq" + }, + "outputs": [], + "source": [ + "str(123)" + ] + }, + { + "cell_type": "markdown", + "id": "533130d5", + "metadata": { + "id": "RtGlRbICxf__" + }, + "source": [ + "Float to string:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad408414", + "metadata": { + "id": "ejGhZs8SxjUN" + }, + "outputs": [], + "source": [ + "str(0.5)" + ] + }, + { + "cell_type": "markdown", + "id": "6b118aca", + "metadata": { + "id": "TdaVUNpBxmQ8" + }, + "source": [ + "String to float:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "066bb9dc", + "metadata": { + "id": "Nwk3D9VExoU_" + }, + "outputs": [], + "source": [ + "float('0.5')" + ] + }, + { + "cell_type": "markdown", + "id": "0e332cb2", + "metadata": { + "id": "6CYPccQYxwCm" + }, + "source": [ + "Boolean to string:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48c44a73", + "metadata": { + "id": "JJf6fjNGxzvC" + }, + "outputs": [], + "source": [ + "str(True)" + ] + }, + { + "cell_type": "markdown", + "id": "9b21466a", + "metadata": { + "id": "LV7o-rkDx3MY" + }, + "source": [ + "String to boolean:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74495cdc", + "metadata": { + "id": "UUPNXO4mx5eb" + }, + "outputs": [], + "source": [ + "print('Direct boolean from string does not work:', bool('False'))\n", + "\n", + "# So we have to write a function.\n", + "def boolean_from_string(string):\n", + " if string == 'False':\n", + " return False\n", + " else:\n", + " return True\n", + "\n", + "print(boolean_from_string('True'))\n", + "print(boolean_from_string('False'))" + ] + }, + { + "cell_type": "markdown", + "id": "3e934f61", + "metadata": { + "id": "CfnNAUKmyhOj" + }, + "source": [ + "Integer to float:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8aad9336", + "metadata": { + "id": "st9vZgf0yixm" + }, + "outputs": [], + "source": [ + "float(123)" + ] + }, + { + "cell_type": "markdown", + "id": "132c81a2", + "metadata": { + "id": "Gmw_vdGoyl3c" + }, + "source": [ + "Float to integer:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e52fc846", + "metadata": { + "id": "JZ_l3IdhynF-" + }, + "outputs": [], + "source": [ + "int(0.5)" + ] + }, + { + "cell_type": "markdown", + "id": "c5df48cf", + "metadata": { + "id": "97z6FUGz8uAS" + }, + "source": [ + "## Strings\n", + "\n", + "Strings have a couple of tricks that may be useful for the final exercise. We illustrate them quickly below. A complete overview of all string methods can be found [here](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str).\n", + "\n", + "[`str.startswith`](https://docs.python.org/3/library/stdtypes.html#str.startswith) and [`str.endswith`](https://docs.python.org/3/library/stdtypes.html#str.endswith) will tell you whether a string begins or ends with a particular other string, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d01c3a66", + "metadata": { + "id": "07ik0vd2-6tQ" + }, + "outputs": [], + "source": [ + "for word in ['magazine', 'kangaroo', 'rooster', 'broom']:\n", + " if word.startswith('roo'):\n", + " print('\"' + word + '\" starts with \"roo\"')\n", + " if word.endswith('roo'):\n", + " print('\"' + word + '\" ends with \"roo\"')" + ] + }, + { + "cell_type": "markdown", + "id": "e3219447", + "metadata": { + "id": "FVyAad6OAtNX" + }, + "source": [ + "[`str.lower`](https://docs.python.org/3/library/stdtypes.html#str.lower) and [`str.upper`](https://docs.python.org/3/library/stdtypes.html#str.upper) return a copy of a string that is converted to all lowercase or all uppercase, respectively. These functions are useful when you don't want case to influence comparisons." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e03b820", + "metadata": { + "id": "9sjgompVA_Qi" + }, + "outputs": [], + "source": [ + "word1 = 'banana'\n", + "word2 = 'Banana'\n", + "\n", + "print('case-sensitive:', word1 == word2)\n", + "print('case-insensitive:', word1.lower() == word2.lower())" + ] + }, + { + "cell_type": "markdown", + "id": "87cc6eb9", + "metadata": { + "id": "TyjWFAWR_0Dp" + }, + "source": [ + "[`str.join`](https://docs.python.org/3/library/stdtypes.html#str.join) can glue a sequence of strings together, as we have seen in [9. String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm#scrollTo=Join_an_iterable_into_a_string)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5e85912", + "metadata": { + "id": "JlqAc5N8AQPu" + }, + "outputs": [], + "source": [ + "print(' + '.join(['1', '2', '3', '4']))\n", + "print(', '.join(['do', 're', 'mi', 'fa', 'sol', 'la', 'ti', 'do']))" + ] + }, + { + "cell_type": "markdown", + "id": "f7f0010d", + "metadata": { + "id": "g0x1VvE5B71w" + }, + "source": [ + "[`str.split`](https://docs.python.org/3/library/stdtypes.html#str.split) is the opposite of `str.join`: it will split a string by a given separator and return a list with the fragments. If you don't specify a separator, it will split by whitespace." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9238a6d9", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "R11wYmWFCVdb", + "outputId": "ca8804e9-80a5-4771-e3f0-1adee880f66b" + }, + "outputs": [], + "source": [ + "print('1 + 2 + 3 + 4'.split(' + '))\n", + "print('1 + 2 + 3 + 4'.split('+'))\n", + "print('1 + 2 + 3 + 4'.split())\n", + "print('1 2 3 4'.split())" + ] + }, + { + "cell_type": "markdown", + "id": "5a66b64e", + "metadata": { + "id": "0csn-TVPC8qG" + }, + "source": [ + "[`str.splitlines`](https://docs.python.org/3/library/stdtypes.html#str.splitlines) is basically `str.split('\\n')`, but it cleverly recognizes many types of line endings (which might differ between platforms) and it has an option to keep the line endings in the resulting fragments." + ] + }, + { + "cell_type": "markdown", + "id": "61992913", + "metadata": { + "id": "k1xy1XmaED7X" + }, + "source": [ + "[`str.strip`](https://docs.python.org/3/library/stdtypes.html#str.strip) will return a new string with the whitespace removed from the beginning and end. You can also specify a different set of characters that should be removed. The default mode of removing whitespace is useful for cleaning up text that was downloaded from the internet or that was copypasted from some kind of document." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa8e8ba3", + "metadata": { + "id": "djsFEC5DE6md" + }, + "outputs": [], + "source": [ + "\" This string isn't very tidy. \".strip()" + ] + }, + { + "cell_type": "markdown", + "id": "18d75dc6", + "metadata": { + "id": "G4tC_OiFFth3" + }, + "source": [ + "### Escapes\n", + "\n", + "There are some characters that normally aren't allowed to appear in a literal string. We can shoehorn them in anyway by placing a backslash `\\` in front of the character, or another letter that acts as a placeholder. This is called *escaping* the character. The combination of the backslash and the following character is called an *escape sequence*. Python also uses these escape sequences when echoing raw strings back at us. The following escape sequences occur often:\n", + "\n", + "`\\n` - linefeed (\"newline\") character.\n", + "\n", + "`\\r\\n` - carriage return plus linefeed. For archeological reasons I will not go into, these two characters together count as a single line separator in Microsoft Windows. So you are likely to find it in files that were created in Windows.\n", + "\n", + "`\\t` - tab character.\n", + "\n", + "`\\'` - straight single quote (escape not needed in strings delimited by double quotes).\n", + "\n", + "`\\\"` - straight double quote (escape not needed in strings delimited by single quotes).\n", + "\n", + "`\\\\` - the backslash itself.\n", + "\n", + "You can get a complete overview of escape sequences if you scroll down from [here](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals)." + ] + }, + { + "cell_type": "markdown", + "id": "9c247d72", + "metadata": { + "id": "5BlYPydVDvWt" + }, + "source": [ + "### Searching for substrings\n", + "\n", + "Searching for a substring within a larger string is a common task, especially in the humanities. [`str.find`](https://docs.python.org/3/library/stdtypes.html#str.find) will tell us the first position in the large string at which the substring is found, or `-1` if it is not found:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2fd6ebd", + "metadata": { + "id": "kxLt4yApEtpV" + }, + "outputs": [], + "source": [ + "print('kangaroo'.find('roo'))\n", + "print('kangaroo'.find('skip'))" + ] + }, + { + "cell_type": "markdown", + "id": "a071b226", + "metadata": { + "id": "_SmGTTzwFAFj" + }, + "source": [ + "`str.find` accepts an optional second argument, which lets us specify the position from which to start searching. We can use this to continue searching for more occurrences after the first match. For example, the following function returns *all* positions of the substring `needle` within the larger string `haystack`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b4982c4", + "metadata": { + "id": "lAlw_L-sFikq" + }, + "outputs": [], + "source": [ + "def str_find_all(haystack, needle):\n", + " results = []\n", + " position = -1\n", + " # while True lets you repeat the same code \"forever\"\n", + " while True:\n", + " position = haystack.find(needle, position + 1)\n", + " if position is -1:\n", + " # break lets you \"break out\" of the infinite loop\n", + " break\n", + " else:\n", + " results.append(position)\n", + " return results\n", + "\n", + "str_find_all('Colorless green ideas sleep furiously', 'e')" + ] + }, + { + "cell_type": "markdown", + "id": "276ece5a", + "metadata": { + "id": "_U9XtQyTLQyC" + }, + "source": [ + "With the optional third argument, we can also make `str.find` *stop* searching at a given position. In the example below, `'roo'` is not found in the first four characters of `'kangaroo'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da099884", + "metadata": { + "id": "jL0nWgb4LivF" + }, + "outputs": [], + "source": [ + "print('kangaroo'.find('roo', 0, 4))" + ] + }, + { + "cell_type": "markdown", + "id": "b710ccdd", + "metadata": { + "id": "lVzhE5VuMQdZ" + }, + "source": [ + "During the lectures, we mentioned a simpler way to search for a substring that only tells you whether the substring appears at all:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5fb8c47", + "metadata": { + "id": "KfieQsYOMkK7" + }, + "outputs": [], + "source": [ + "'roo' in 'kangaroo'" + ] + }, + { + "cell_type": "markdown", + "id": "b06536d7", + "metadata": { + "id": "LhCDt6xTMq2i" + }, + "source": [ + "If you need pattern-based search, [regular expressions](https://docs.python.org/3/library/re.html) are the tool of choice. A regular expression lets you describe a set of similar strings with a single pattern. For example, the following notation will match all occurrences of the word \"colour\" in a text, even if it is capitalized or spelled \"color\":\n", + "\n", + "```\n", + "[Cc]olou?r\n", + "```\n", + "\n", + "The `search` function from the standard module `re` (described by the above link) lets us use such patterns to search through a string:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97d827e6", + "metadata": { + "id": "jOJl_23IOvyG" + }, + "outputs": [], + "source": [ + "import re\n", + "\n", + "re.search('[Cc]olou?r', 'Colorless green ideas sleep furiously')" + ] + }, + { + "cell_type": "markdown", + "id": "e91e4717", + "metadata": { + "id": "Me7D092xO-u0" + }, + "source": [ + "Regular expressions are a powerful tool and the `re` module has many bells and whistles. Please refer to [the documentation](https://docs.python.org/3/library/re.html) for the details." + ] + }, + { + "cell_type": "markdown", + "id": "97a6daf0", + "metadata": { + "id": "mUmNSdNzMq_M" + }, + "source": [ + "### Format strings\n", + "\n", + "A format string is a perfectly normal string that contains some special notation, i.e., pairs of braces `{}`. While the presence of those braces does not oblige us to anything, we *can* use their presence (and insert them on purpose) in order to benefit from the [`str.format`](https://docs.python.org/3/library/stdtypes.html#str.format) method. For example, if I have the following string,\n", + "\n", + "```python\n", + "'Ta-da!'\n", + "```\n", + "\n", + "I can turn it into a format string simply by inserting a pair of braces, anywhere I like:\n", + "\n", + "```python\n", + "'Ta-da: {}'\n", + "```\n", + "\n", + "If I call `str.format` on a format string, it will interpret pairs of braces as placeholders and replace them by any arguments that I pass." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8bb0be5b", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 37 + }, + "id": "NQmg3z2cPPFW", + "outputId": "c6647728-a0ac-4f50-e5ed-36e9b0c94cbf" + }, + "outputs": [], + "source": [ + "'Ta-da: {}'.format('this is Python!')" + ] + }, + { + "cell_type": "markdown", + "id": "6a5edc9f", + "metadata": { + "id": "ZtwiQhMJPcAd" + }, + "source": [ + "You can insert as many placeholders and pass as many arguments as you like, as long as there are at least as many arguments as placeholders. Of course, you usually want the number of arguments to exactly match the number of placeholders in the format string, but `str.format` will simply skip any arguments that remain." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e66e12eb", + "metadata": { + "id": "x_f2iRsQQNqr" + }, + "outputs": [], + "source": [ + "print('Ta-da: {}'.format(1, 2, 3))\n", + "print('Ta{}da{} {}'.format('-', ':', 'success!'))" + ] + }, + { + "cell_type": "markdown", + "id": "8adea43c", + "metadata": { + "id": "7GOzHkgLRGYi" + }, + "source": [ + "Format strings are a great way to compose strings out of some fixed parts and some variable parts, especially if the fixed parts aren't entirely regular. Consider the following code:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c425d28f", + "metadata": { + "id": "mTVp_EOmR_nm" + }, + "outputs": [], + "source": [ + "YELL_START = 'Go go '\n", + "YELL_END = '!!!'\n", + "\n", + "def yell(name):\n", + " return YELL_START + name + YELL_END\n", + "\n", + "yell('Jelte')" + ] + }, + { + "cell_type": "markdown", + "id": "1e08c44c", + "metadata": { + "id": "fwTdCa4QSOuv" + }, + "source": [ + "Using a format string, this code would be a bit more explicit and a bit easier to read and write as well:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e55260e", + "metadata": { + "id": "KvbXfNykSmiB" + }, + "outputs": [], + "source": [ + "YELL_FORMAT = 'Go go {}!!!'\n", + "\n", + "def yell(name):\n", + " return YELL_FORMAT.format(name)\n", + "\n", + "yell('Jelte')" + ] + }, + { + "cell_type": "markdown", + "id": "311d5c81", + "metadata": { + "id": "TbnEKRdlTGTj" + }, + "source": [ + "The above discussion addresses about 90% of all string formatting needs, but for the remaining 10%, `str.format` is chock-full of bells and whistles. You can use named arguments, reuse the same argument in multiple places, align placeholders to a particular width with filler characters of choice, specify how to format numbers, and so forth and so on. You can read all about it [here](https://docs.python.org/3/library/string.html#formatstrings)." + ] + }, + { + "cell_type": "markdown", + "id": "4a9d637e", + "metadata": { + "id": "0FJ_vXwlwT_5" + }, + "source": [ + "### Cross-platform file paths\n", + "\n", + "Consider the location of the current notebook (`Tips.ipynb`). It is located inside a folder called `Colab Notebooks`, which is inside a folder called `My Drive`, which is inside my Google Drive account. In Windows, we write such paths as follows:\n", + "\n", + " My Drive\\Colab Notebooks\\Tips.ipynb\n", + "\n", + "In macOS and Linux, on the other hand, we separate the path components with forward slashes:\n", + "\n", + " My Drive/Colab Notebooks/Tips.ipynb\n", + "\n", + "In Python, we often need to create a string with a file path, which would be different depending on whether the code is running on Windows or elsewhere (backslashes appear twice because they need to be [escaped](#scrollTo=Escapes)):\n", + "\n", + "```py\n", + "windows_path = 'My Drive\\\\Colab Notebooks\\\\Tips.ipynb'\n", + "maclinux_path = 'My Drive/Colab Notebooks/Tips.ipynb'\n", + "```\n", + "\n", + "We generally want our code to work on all platforms, without having to change the path notation or having to introduce `if`/`else` statements everywhere to choose between alternative file paths. This is where the standard module [`os.path`][os.path] comes to our rescue. It provides a function called `join`, which glues the path components together with the separator that is appropriate for whatever platform the code is running on:\n", + "\n", + "```py\n", + "import os.path as op\n", + "\n", + "crossplatform_path = op.join('My Drive', 'Colab Notebooks', 'Tips.ipynb')\n", + "```\n", + "\n", + "The above snippet is repeated in the code cell below. If you run it, you will see that Google Colab runs on a system that uses the same path convention as macOS and Linux.\n", + "\n", + "Besides glueing path components together, [`os.path`][os.path] also provides tools for taking paths apart again and for converting back and forth between absolute and relative paths.\n", + "\n", + "[os.path]: https://docs.python.org/3/library/os.path.html" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72d36661", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AfuPbq5iwRsR", + "outputId": "48cb5576-1a6d-4b82-fcb2-46c76c0bbb3d" + }, + "outputs": [], + "source": [ + "import os.path as op\n", + "\n", + "crossplatform_path = op.join('My Drive', 'Colab Notebooks', 'Tips.ipynb')\n", + "\n", + "print(crossplatform_path)" + ] + }, + { + "cell_type": "markdown", + "id": "2b17f0be", + "metadata": { + "id": "zHyB8HjpU9hv" + }, + "source": [ + "## Tuples\n", + "\n", + "*This section was originally written when tuples were not yet included in the lectures. It is retained for consistency with the remainder of the notebook.*\n", + "\n", + "When you have two things, you can call them a pair. When you have three things, you can call them a triple. Four things, a quadruple. We continue with quintuple, sextuple, septuple, octuple. See where this is going? ;-)\n", + "\n", + "The general mathematical term for small groups like those is *N*-tuple, commonly abbreviated as \"tuple\". Python has a data type for tuples, which you almost never need to be aware of. It's a feature that [silently](https://docs.python.org/3/library/functions.html#func-tuple) makes things work, just like cleaners silently and inconspicuously prevent our society from collapsing.\n", + "\n", + "You don't need to create your own tuples for the final exercise (at least not consciously), but since the terminology is bound to come up, I will give a short explanation of what they are. A tuple is similar to a list, with two main differences:\n", + "\n", + "1. Tuples are intended for small(ish) groups of values, while lists will happily store millions of values for you.\n", + "2. Tuples are *immutable*: you cannot change their contents after creation.\n", + "\n", + "When you see values separated by commas, and they are not in a list, a parameter list or an argument list, you are probably looking at a tuple. I will mention it when they appear in other tips. Here, I will just mention two situations where tuples may appear all by themselves.\n", + "\n", + "Tuples are sometimes used to return two or more values from a function at the same time. For example, the built-in function [`divmod`](https://docs.python.org/3/library/functions.html#divmod) can tell you the quotient and remainder of two numbers in one call:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49f3e533", + "metadata": { + "id": "LrwtKmfpayq0" + }, + "outputs": [], + "source": [ + "quotient, remainder = divmod(29, 11)\n", + "\n", + "print('quotient:', quotient, 'check:', quotient == 29 // 11)\n", + "print('remainder:', remainder, 'check:', remainder == 29 % 11)" + ] + }, + { + "cell_type": "markdown", + "id": "effd545c", + "metadata": { + "id": "Qm-KzHTdbtCq" + }, + "source": [ + "`divmod(29, 11)` was returning a tuple and we *unpacked* that tuple into the variables `quotient` and `remainder`. That's because their names where to the left of the assignment operator. On the other hand, when we put comma-separated values to the *right* of an assignment operator, it means that we *pack* those values into a new tuple. We can do a nice trick with this and swap two variables by packing them into a tuple and then immediately unpacking them again in reverse order:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74ec9af7", + "metadata": { + "id": "ttECYgPLc6ra" + }, + "outputs": [], + "source": [ + "winner = 'Bert'\n", + "loser = 'Ernie'\n", + "\n", + "winner, loser = loser, winner\n", + "\n", + "print('winner:', winner)\n", + "print('loser:', loser)" + ] + }, + { + "cell_type": "markdown", + "id": "1aed53ea", + "metadata": { + "id": "hpnieEnwpmhK" + }, + "source": [ + "## Dictionaries\n", + "\n", + "*This section was originally written when dictionaries were not yet included in the lectures. It is retained for consistency with the remainder of the notebook.*\n", + "\n", + "On the first day of the course, we have seen lists, which can hold an arbitrary number of values. The values are numbered sequentially, with the first having index `0`. You can create an empty list by calling `list()` or by writing an empty pair of square brackets, `[]`.\n", + "\n", + "A [dictionary][dict] is also a data structure that can hold an arbitrary number of values. The difference from a list is that the values are not numbered sequentially. Instead, every value has an arbitrary unique **key** which you need to set explicitly. An empty dictionary is created by calling `dict()` or by writing an empty pair of braces, `{}`.\n", + "\n", + "Dictionaries are useful when you want to associate values with each other. For example, your dataset might have a nominal column called `fruit`, with the possible levels being `apple`, `banana` and `cherry`, and you might want to count for each level in how many rows it occurs. In this case your will be associating fruits with frequencies, and a dictionary is the appropriate data structure for storing those associations. In the code below, we illustrate how dictionaries work and how you might use one to tally frequencies.\n", + "\n", + "[dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97604c4f", + "metadata": { + "id": "7JVH7h_Bu6mS" + }, + "outputs": [], + "source": [ + "# In the general case, we can create a dictionary and\n", + "# immediately set key-value pairs with the brace notation:\n", + "example_dict = {\n", + " 'apple': 'juicy',\n", + " 'banana': 'fragrant',\n", + " 'cherry': 'sweet',\n", + "}\n", + "\n", + "# Retrieving a value associated with a key can be done by\n", + "# placing the key between square brackets, similar to\n", + "# indexing a list:\n", + "example_dict['cherry']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "529f9e11", + "metadata": { + "id": "zhIMlt2TxoGC" + }, + "outputs": [], + "source": [ + "# If you try to read a key that isn't present in the\n", + "# dictionary, you will get a KeyError:\n", + "example_dict['date']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d79de0be", + "metadata": { + "id": "vOk3VAnIyWgz" + }, + "outputs": [], + "source": [ + "# If we want to know whether a key is present in a dict,\n", + "# we can use the `in` operator:\n", + "print('cherry' in example_dict)\n", + "print('date' in example_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "362769c0", + "metadata": { + "id": "yE8urPkiy-Iw" + }, + "outputs": [], + "source": [ + "# If we want to retrieve the value for a key if it exists,\n", + "# and fall back to a default value otherwise, we can use `get`:\n", + "print(example_dict.get('cherry', 'oops'))\n", + "print(example_dict.get('date', 'oops'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72526db7", + "metadata": { + "id": "BSXQQZfs0OfU" + }, + "outputs": [], + "source": [ + "# You can update and add key-value pairs by assignment.\n", + "example_dict['banana'] = 'yellow'\n", + "example_dict['date'] = 'wrinkly'\n", + "\n", + "# You can remove keys with the `del` operator.\n", + "del example_dict['apple']\n", + "\n", + "# Let's see what we have now.\n", + "example_dict" + ] + }, + { + "cell_type": "markdown", + "id": "c01cd968", + "metadata": { + "id": "VisTnjOjxodE" + }, + "source": [ + "In the next two examples, we use [tuple unpacking](#scrollTo=Tuples)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44d978da", + "metadata": { + "id": "XnZcOLDM1zM-" + }, + "outputs": [], + "source": [ + "# We can iterate over the keys and values of dictionary.\n", + "for key, value in example_dict.items():\n", + " print('A', key, 'is', value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a7cfea18", + "metadata": { + "id": "PdcV9FVm2X48" + }, + "outputs": [], + "source": [ + "# Now let's see how we can use a dictionary to tally.\n", + "# Suppose we have the following table of fruit orders:\n", + "orders = [\n", + " ['2021-11-15', 'banana', 100],\n", + " ['2021-11-16', 'apple', 33],\n", + " ['2021-11-17', 'banana', 150],\n", + "]\n", + "\n", + "# We will pretend we haven't already seen those data and\n", + "# start with an empty dict.\n", + "fruit_tally = {}\n", + "\n", + "# Now we iterate over the orders and fill our dict.\n", + "for date, fruit, quantity in orders:\n", + " # First we retrieve how many times we have seen `fruit`\n", + " # before. If we haven't seen it before, the key isn't in the\n", + " # dict, so we provide 0 as a fallback value.\n", + " tally = fruit_tally.get(fruit, 0)\n", + " # Now we can add or update the tally for this `fruit`.\n", + " fruit_tally[fruit] = tally + 1\n", + "\n", + "# Did we count correctly?\n", + "fruit_tally" + ] + }, + { + "cell_type": "markdown", + "id": "f61f1d13", + "metadata": { + "id": "g_90Pk6j4Plm" + }, + "source": [ + "For exercise, you can try adapting the above code example to compute total ordered quantities per fruit, or a list of order dates per fruit." + ] + }, + { + "cell_type": "markdown", + "id": "752b729d", + "metadata": { + "id": "hhu9T00mFSHd" + }, + "source": [ + "## Iterables\n", + "\n", + "*This section was originally written when iterables were not yet included in the lectures. It is retained for consistency with the remainder of the notebook.*\n", + "\n", + "In the FizzBuzz exercises, we saw the following notation for creating a list with the integers from `1` (inclusive) to `101` (exclusive):\n", + "\n", + "```python\n", + "list(range(1, 101))\n", + "```\n", + "\n", + "We also saw that you can pass only one argument, in which case it sets the end value and the start value is assumed to be `0`. On the other hand, if you pass *three* arguments, the third arguments sets a step value; this lets you skip every second element and/or make a decreasing series.\n", + "\n", + "It's clear what such a `list(range(begin, end))` expression does, but the curious might wonder what you get if you leave off the outer `list()`. What is a `range` just by itself? In any case, you can still loop over it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a7fb35d", + "metadata": { + "id": "XTmiVuEAGJlL" + }, + "outputs": [], + "source": [ + "for number in range(0, 3):\n", + " print(number)" + ] + }, + { + "cell_type": "markdown", + "id": "55db3059", + "metadata": { + "id": "axzQrGdBLWDF" + }, + "source": [ + "However, unlike with a list, we cannot add our own elements to it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6809a99", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 164 + }, + "id": "oHDOpj2GLevE", + "outputId": "2d0ae6bd-61b2-497a-e8da-b1a45d1c3b97" + }, + "outputs": [], + "source": [ + "range(0, 3).append(3)" + ] + }, + { + "cell_type": "markdown", + "id": "21842280", + "metadata": { + "id": "6q2Tt_4eKc_K" + }, + "source": [ + "If we try to print it, it remains mysterious:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08d7978c", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-MtW-ANdKn_d", + "outputId": "39abd51e-e1bf-4ef9-997a-52d88f452448" + }, + "outputs": [], + "source": [ + "print(range(0, 3))" + ] + }, + { + "cell_type": "markdown", + "id": "c0534792", + "metadata": { + "id": "X6KSp6FbMPRP" + }, + "source": [ + "I will no longer keep you in suspense. The value returned by [`range`][range] is a *generator*. You can think of a generator as a function that returns multiple times, each time producing the next value in some kind of sequence. This means that a generator, unlike a list, does not store all elements of its sequence at the same time; rather, it produces the next one when you ask for it.\n", + "\n", + "> For the final exercise, you don't need to write generators yourself, so we will not spend any text on how to do that. It is however useful to know how to use them, so we discuss that below.\n", + "\n", + "Python's `for` loop knows how to take one value at a time out of a generator, just like it knows how to take one value at a time out of a list. We call the more general class of things that `for` can loop over *iterables*. There are many more types of iterables besides lists and generators (including [tuples](#scrollTo=Tuples) and [dictionaries](#scrollTo=Dictionaries)) and `for` is able to deal with all of them, because all iterables follow the same **iterator convention**.\n", + "\n", + "In fact, this convention is not restricted to `for`. Most functions in the Python standard library that work with sequences, accept not just lists but any iterable. We have already seen this in action when we did `list(range(0, 101))`: `list` accepted an iterable, `range(0, 101)`, took out its values one by one, stored all of them in a list, and finally returned that list to you.\n", + "\n", + "Since generators don't store all of their values at the same time and we might not always need all values in a sequence, they are potentially more efficient than lists. For this reason, many standard library functions also *return* generators rather than lists.\n", + "\n", + "By combining functions that consume and return iterables, we can often replace loops by shorter expressions. In the next few subsections, we illustrate the most important functions on iterables.\n", + "\n", + "[range]: https://docs.python.org/3/library/functions.html#func-range" + ] + }, + { + "cell_type": "markdown", + "id": "3c35c541", + "metadata": { + "id": "RelVVKVzX9T7" + }, + "source": [ + "### `enumerate`\n", + "\n", + "The built-in [`enumerate`][enumerate] accepts any iterable and returns a generator. As the name suggests, it numbers the values in the input sequence, while also echoing back the values themselves (in [pairs](#scrollTo=Tuples)):\n", + "\n", + "[enumerate]: https://docs.python.org/3/library/functions.html#enumerate" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb6c2b91", + "metadata": { + "id": "Zq8dHhH-Y-Cx" + }, + "outputs": [], + "source": [ + "example_list = ['Colorless', 'green', 'ideas', 'sleep', 'furiously']\n", + "\n", + "list(enumerate(example_list))" + ] + }, + { + "cell_type": "markdown", + "id": "e9fe5527", + "metadata": { + "id": "LMPR_En8Zhn7" + }, + "source": [ + "This can be useful when you are looping over a sequence, and you need not only the values but also their index in the sequence:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6e8ac2c", + "metadata": { + "id": "ADqR2KG2Zdkc" + }, + "outputs": [], + "source": [ + "for index, value in enumerate(example_list):\n", + " print('Word number', index, 'is', value)" + ] + }, + { + "cell_type": "markdown", + "id": "dc2fb34c", + "metadata": { + "id": "cSdPo0RGbvre" + }, + "source": [ + "For comparison, this is what the above loop would look like without `enumerate`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc64cf1d", + "metadata": { + "id": "tEOM7Iwwbz9r" + }, + "outputs": [], + "source": [ + "index = 0\n", + "for value in example_list:\n", + " print('Word number', index, 'is', value)\n", + " index = index + 1" + ] + }, + { + "cell_type": "markdown", + "id": "8befe200", + "metadata": { + "id": "vX59S1uDaBLI" + }, + "source": [ + "### `filter`\n", + "\n", + "The built-in [`filter`][filter] accepts a function and an iterable. It passes each value in the iterable to the function in a separate call. It returns a generator with only the values in the input sequence for which the function returned `True`.\n", + "\n", + "[filter]: https://docs.python.org/3/library/functions.html#filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "180b677b", + "metadata": { + "id": "YgQ2RCRbbJDY" + }, + "outputs": [], + "source": [ + "def odd(number):\n", + " return number % 2 == 1\n", + "\n", + "fibonacci_10 = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]\n", + "\n", + "list(filter(odd, fibonacci_10))" + ] + }, + { + "cell_type": "markdown", + "id": "bdabb9ea", + "metadata": { + "id": "30NBqwSZbqee" + }, + "source": [ + "For comparison, this is what the last line would look like without `filter`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b8c15ff", + "metadata": { + "id": "UuzJGcjOcOL8" + }, + "outputs": [], + "source": [ + "result_list = []\n", + "for number in fibonacci_10:\n", + " if odd(number):\n", + " result_list.append(number)\n", + "result_list" + ] + }, + { + "cell_type": "markdown", + "id": "1fdd65d3", + "metadata": { + "id": "81vNGdqgc1PI" + }, + "source": [ + "### `map`\n", + "\n", + "The built-in [`map`][map] accepts a function and an iterable. It passes each value in the iterable as the first argument to the function in a separate call. It returns a generator with the return values of each of those calls.\n", + "\n", + "[map]: https://docs.python.org/3/library/functions.html#map" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e5817c7", + "metadata": { + "id": "Gdw4kFoCeQ4x" + }, + "outputs": [], + "source": [ + "def square(number):\n", + " return number ** 2\n", + "\n", + "list(map(square, range(10)))" + ] + }, + { + "cell_type": "markdown", + "id": "d2669e09", + "metadata": { + "id": "CMWKdfZued1f" + }, + "source": [ + "For comparison, code without `map` that produces the same output as the last line:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16ae95a4", + "metadata": { + "id": "I13FNH_ZeptA" + }, + "outputs": [], + "source": [ + "result_list = []\n", + "for number in range(10):\n", + " result_list.append(square(number))\n", + "result_list" + ] + }, + { + "cell_type": "markdown", + "id": "b0a859e9", + "metadata": { + "id": "S5NtKmJ21L_u" + }, + "source": [ + "You can also pass multiple iterables to `map`. In that case, `map` will take one value from each iterable per iteration and pass the value from the first iterable as the first argument to the function, the corresponding value from the second iterable as the second argument, and so on. In the following example, we use a [format string](#scrollTo=Format_strings)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23171b53", + "metadata": { + "id": "5W6NwA8D2Kz3" + }, + "outputs": [], + "source": [ + "sentence = '{} {} {} {}.'.format\n", + "\n", + "# The following lists have been shuffled.\n", + "# For fun, you can try reordering them so the correct words\n", + "# from each list match up again. :-)\n", + "# (But run the code first so you know what it does.)\n", + "properties = ['Gentle', 'Playful', 'Stubborn', 'Thirsty']\n", + "people = ['camels', 'plumbers', 'giants', 'children']\n", + "verbs = ['tighten', 'devour', 'ruin', 'capture']\n", + "objects = ['nightmares', 'massage chairs', 'cactuses', 'rusty fittings']\n", + "\n", + "phrases = map(sentence, properties, people, verbs, objects)\n", + "for phrase in phrases:\n", + " print(phrase)" + ] + }, + { + "cell_type": "markdown", + "id": "5629dff8", + "metadata": { + "id": "G7fup_SyBode" + }, + "source": [ + "Without `map` (and without `enumerate` or `range`), the last loop would have looked like this instead:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdf40a23", + "metadata": { + "id": "9a1XKPuFBtpF" + }, + "outputs": [], + "source": [ + "index = 0\n", + "for prop in properties:\n", + " group = people[index]\n", + " verb = verbs[index]\n", + " obj = objects[index]\n", + " print(sentence(prop, group, verb, obj))\n", + " index = index + 1" + ] + }, + { + "cell_type": "markdown", + "id": "5292f759", + "metadata": { + "id": "9DCBUR61DNRr" + }, + "source": [ + "If you pass iterables of unequal lengths, `map` will stop at the end of the shortest iterable. In the next subsection, we will take a quick look at how this can sometimes be useful." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b307e36", + "metadata": { + "id": "pR-4fFPZDMOi" + }, + "outputs": [], + "source": [ + "# operator.mul is a function that multiplies two numbers. It\n", + "# does exactly the same thing as the `*` operator, but as a\n", + "# function so you can pass it as an argument to other functions.\n", + "# More about the operator module in the next subsection.\n", + "from operator import mul\n", + "\n", + "small = [1, 2, 3]\n", + "large = [5, 7, 11, 13, 17, 19, 23, 29]\n", + "\n", + "list(map(mul, small, large))" + ] + }, + { + "cell_type": "markdown", + "id": "9b38edee", + "metadata": { + "id": "yBYdJR9VHLQU" + }, + "source": [ + "### More built-in functions\n", + "\n", + "`range`, `enumerate`, `filter` and especially `map` are the functions on iterables that you'll be using the most. There are however more built-in functions on iterables worth knowing about. Below, we briefly mention a few.\n", + "\n", + "- [`all`](https://docs.python.org/3/library/functions.html#all) consumes an iterable. If the sequence contains at least one `False` value, it returns `False`. Otherwise it returns `True` (including when it is empty). It's like the `and` operator, but operating on an arbitrary number of operands instead of exactly two.\n", + "- [`any`](https://docs.python.org/3/library/functions.html#any) is the exact opposite of `all`: it returns `True` if the sequence contains at least one `True` value, otherwise `False`. Can be thought of as the \"long form\" of the `or` operator.\n", + "- [`len`](https://docs.python.org/3/library/functions.html#len) can tell you the length of lists, strings, and some other iterables that can \"know\" their size in advance, including `range`.\n", + "- [`list`](https://docs.python.org/3/library/functions.html#func-list), as we have seen in previous examples, can store the values from any iterable into a list.\n", + "- [`max`](https://docs.python.org/3/library/functions.html#max) can be passed a single iterable argument, in which case it will return the maximum value of the sequence. You can however also pass multiple arguments, in which case it will simply compare them directly (e.g. `max(range(10))` will return `9` and `max(3, 4)` will return `4`). Likewise for [`min`](https://docs.python.org/3/library/functions.html#min).\n", + "- [`str.join`](https://docs.python.org/3/library/stdtypes.html#str.join), which I covered in more detail in [Strings](#scrollTo=Strings), can take the strings that it will glue together from any iterable sequence.\n", + "- [`sum`](https://docs.python.org/3/library/functions.html#sum), as the name suggests, will return the sum of all values in an iterable sequence." + ] + }, + { + "cell_type": "markdown", + "id": "2e87100a", + "metadata": { + "id": "LugfHklV0b4C" + }, + "source": [ + "### Operators\n", + "\n", + "Given two lists of numbers, we might want to create a third list where each element is the sum of the correponding elements of the first two lists. We cannot pass the `+` operator to `map`, because the `+` operator is not a function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "628daa3f", + "metadata": { + "id": "ddcs1QaK1APC" + }, + "outputs": [], + "source": [ + "first_list = [1, 2, 3]\n", + "second_list = [7, 7, 5]\n", + "\n", + "list(map(+, first_list, second_list))" + ] + }, + { + "cell_type": "markdown", + "id": "339672d4", + "metadata": { + "id": "Ix-TPRvY1Pc-" + }, + "source": [ + "Fortunately, the [`operator`](https://docs.python.org/3/library/operator.html) standard module exports function versions of most operators, so we can do this instead:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e21ec3c", + "metadata": { + "id": "UG_UUx8S1jQw" + }, + "outputs": [], + "source": [ + "from operator import add\n", + "\n", + "first_list = [1, 2, 3]\n", + "second_list = [7, 7, 5]\n", + "\n", + "list(map(add, first_list, second_list))" + ] + }, + { + "cell_type": "markdown", + "id": "9667d701", + "metadata": { + "id": "ezmNWLLM2G4w" + }, + "source": [ + "The operators that you would most likely use this way, and their corresponding functions exported from `operator`, are the following (full list [here](https://docs.python.org/3/library/operator.html#mapping-operators-to-functions)):\n", + "\n", + "`+` - `add` (for adding numbers)\n", + "\n", + "`+` - `concat` (for concatenating strings or lists)\n", + "\n", + "`-` - `neg` (unary minus to flip the sign of a number)\n", + "\n", + "`-` - `sub` (binary minus to subtract two numbers)\n", + "\n", + "`in` - `contains` (for checking whether a value appears in an iterable)\n", + "\n", + "`*` - `mul`\n", + "\n", + "`/` - `truediv` (`//` is `floordiv`)\n", + "\n", + "`%` - `mod`\n", + "\n", + "`**` - `pow`\n", + "\n", + "`<` - `lt`\n", + "\n", + "`>` - `gt`\n", + "\n", + "`==` - `eq`\n", + "\n", + "`!=` - `ne`" + ] + }, + { + "cell_type": "markdown", + "id": "b7c27ac3", + "metadata": { + "id": "0SMYES0-gyBX" + }, + "source": [ + "### Bound methods\n", + "\n", + "In the `map` subsection, I used an [example](#scrollTo=5W6NwA8D2Kz3&line=1&uniqifier=1) with the notation `'{} {} {} {}.'.format`. I stored that in a variable and then passed that as a function to `map`. It turns out this is a general thing we can do in more situations, so let's briefly touch on how this works.\n", + "\n", + "The essence is that" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94c37446", + "metadata": { + "id": "51cj58Pdogj_" + }, + "outputs": [], + "source": [ + "'{} {} {} {}.'.format(1, 2, 3, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "14444afa", + "metadata": { + "id": "O-0kegzaonFi" + }, + "source": [ + "is equivalent to" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64a3582c", + "metadata": { + "id": "GqxgL5Rgorx3" + }, + "outputs": [], + "source": [ + "str.format('{} {} {} {}.', 1, 2, 3, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "c68bc527", + "metadata": { + "id": "3DWOZQHKpClX" + }, + "source": [ + "We generally use the first form because it is more convenient, but Python is actually translating it to the second form behind our backs. The [format string](#scrollTo=Format_strings) `'{} {} {} {}.'` is being passed as the first argument to the function `str.format` in both cases.\n", + "\n", + "If we do `'{} {} {} {}.'.format` without actually calling the function and passing an argument list, Python understands that we want to use `'{} {} {} {}.'` as the first argument when we later make the call. It returns a special, *bound* version of `str.format` that already has our format string filled in, so we only need to supply the remaining arguments. This is a special form of *partial application* (we will see the more general form of partial application in the [next subsection](#scrollTo=partial)). Python's support for this special form has something to do with classes and objects, which you can optionally read more about in [a later section](#scrollTo=Classes_and_objects).\n", + "\n", + "With this theory out of the way, let's look at a couple more examples of how we can use both bound and unbound functions in `map` and similar functions. We use [string](#scrollTo=Strings) and [dictionary](#scrollTo=Dictionaries) functions in this example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f63c8a9", + "metadata": { + "id": "xSptat6auBDW" + }, + "outputs": [], + "source": [ + "# We can map the unbound str.lower to lowercase a sequence of strings.\n", + "strings = ['Hawaii', 'Iceland', 'Hokkaido', 'Vanuatu']\n", + "print(list(map(str.lower, strings)))\n", + "\n", + "# We can filter by the bound dict.get to check for associated values.\n", + "topography = {\n", + " 'Iceland': 'volcanic',\n", + " 'Vanuatu': 'Melanesia',\n", + "}\n", + "# Give me only the islands I know something about.\n", + "print(list(filter(topography.get, strings)))" + ] + }, + { + "cell_type": "markdown", + "id": "fd2ddf90", + "metadata": { + "id": "EqorhEmy6pxq" + }, + "source": [ + "With bound methods, we can achieve ultimate minimalism in our [example](#scrollTo=fwTdCa4QSOuv&line=1&uniqifier=1) from the format strings section, repeated here:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b5c81c1", + "metadata": { + "id": "Q49H5CvR7DbK" + }, + "outputs": [], + "source": [ + "YELL_FORMAT = 'Go go {}!!!'\n", + "\n", + "def yell(name):\n", + " return YELL_FORMAT.format(name)\n", + "\n", + "yell('Jelte')" + ] + }, + { + "cell_type": "markdown", + "id": "85c18ad0", + "metadata": { + "id": "kJnvBskM7GvW" + }, + "source": [ + "because we can suffice with this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "595367df", + "metadata": { + "id": "BHiKKKM77JKL" + }, + "outputs": [], + "source": [ + "yell = 'Go go {}!!!'.format\n", + "\n", + "yell('Jelte')" + ] + }, + { + "cell_type": "markdown", + "id": "dcb1187f", + "metadata": { + "id": "XO0Q3vhf74Nd" + }, + "source": [ + "### `itertools` and `functools`\n", + "\n", + "The [`itertools`](https://docs.python.org/3/library/itertools.html) and [`functools`](https://docs.python.org/3/library/functools.html) standard modules let you turn your iterable-fu up to 11. Most of the contents of these modules are a bit advanced, but there are a couple of tricks in there that might be useful during the final exercise. We quickly illustrate them below." + ] + }, + { + "cell_type": "markdown", + "id": "9257af02", + "metadata": { + "id": "ucNV6xs0Tr8x" + }, + "source": [ + "#### `repeat`\n", + "\n", + "[`itertools.repeat`](https://docs.python.org/3/library/itertools.html#itertools.repeat), as the name suggests, will keep repeating a value that you specify. *Forever*. That means you should definitely not try to loop over it! However, you can use it when you need to `map` a function that takes multiple arguments, where some arguments come from a (finite) iterable sequence while at least one argument is the same every time. Consider the following example code, which prints a triangle of stars:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6635059e", + "metadata": { + "id": "HbbMbbNvckz7" + }, + "outputs": [], + "source": [ + "def centered_stars(center, width):\n", + " padding = center - width // 2\n", + " return ' ' * padding + '*' * width\n", + "\n", + "lines = []\n", + "for width in range(1, 6, 2):\n", + " lines.append(centered_stars(2, width))\n", + "\n", + "print('\\n'.join(lines))" + ] + }, + { + "cell_type": "markdown", + "id": "bb61a9e4", + "metadata": { + "id": "YHOPuLOwh3Qx" + }, + "source": [ + "We can replace the loop by an expression using `map` and `repeat`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c8dc20e7", + "metadata": { + "id": "I8NMn7KUh-3S" + }, + "outputs": [], + "source": [ + "from itertools import repeat\n", + "\n", + "lines = map(centered_stars, repeat(2), range(1, 6, 2))\n", + "\n", + "print('\\n'.join(lines))" + ] + }, + { + "cell_type": "markdown", + "id": "de025185", + "metadata": { + "id": "PW2498IlmijJ" + }, + "source": [ + "#### `partial`\n", + "\n", + "[`functools.partial`](https://docs.python.org/3/library/functools.html#functools.partial) takes a function plus any number of other arguments, and then returns a new version of the function to which those arguments have already been applied." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18cacbe8", + "metadata": { + "id": "i_Pjs-rGnfH2" + }, + "outputs": [], + "source": [ + "from functools import partial\n", + "\n", + "# center_2_stars is a version of centered_stars when the first\n", + "# parameter (`center`) is fixed to the value 2. This version\n", + "# accepts only one argument, `width`.\n", + "center_2_stars = partial(centered_stars, 2)\n", + "\n", + "center_2_stars(3)" + ] + }, + { + "cell_type": "markdown", + "id": "5bbbec61", + "metadata": { + "id": "yCkaJitHnrXk" + }, + "source": [ + "While `functools.partial` does not operate on iterables by itself, it can be really useful when you want to adjust functions before you pass them to `filter`, `map` and the like. We could have used it instead of `itertools.repeat` to eliminate the loop from our triangle example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a0ec877e", + "metadata": { + "id": "jjr_D7jpoYul" + }, + "outputs": [], + "source": [ + "lines = map(center_2_stars, range(1, 6, 2))\n", + "\n", + "print('\\n'.join(lines))" + ] + }, + { + "cell_type": "markdown", + "id": "d779b495", + "metadata": { + "id": "IRRPl6piyQn8" + }, + "source": [ + "`partial` also works with keyword arguments. In some cases, this makes it possible to partially apply arguments out of order. This doesn't work for the operators, but there are some workarounds possible. Consider writing a function that subtracts `3` from whatever number you pass to it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf2ab4e5", + "metadata": { + "id": "WT3OJFSr18MW" + }, + "outputs": [], + "source": [ + "def minus_3(number):\n", + " return number - 3\n", + "\n", + "minus_3(4)" + ] + }, + { + "cell_type": "markdown", + "id": "fcfe1359", + "metadata": { + "id": "MVmN8nSt2Ow1" + }, + "source": [ + "It would be nice if we could skip writing a function ourselves and instead just combine `operator.sub` with `functools.partial`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1cdf782e", + "metadata": { + "id": "DK8Y4dWHzY_J" + }, + "outputs": [], + "source": [ + "from operator import sub\n", + "from functools import partial\n", + "\n", + "minus_3 = partial(sub, b=3)\n", + "\n", + "minus_3(4)" + ] + }, + { + "cell_type": "markdown", + "id": "c66fbfc6", + "metadata": { + "id": "pQpjPTbb2oNd" + }, + "source": [ + "The above code doesn't work, but we can avoid the problem by adding `-3` instead of subtracting `+3`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d46aedb", + "metadata": { + "id": "H3MwPyuF27vg" + }, + "outputs": [], + "source": [ + "from operator import add\n", + "from functools import partial\n", + "\n", + "minus_3 = partial(add, -3)\n", + "\n", + "minus_3(4)" + ] + }, + { + "cell_type": "markdown", + "id": "e4dadf9a", + "metadata": { + "id": "-LH3TiwCpNbp" + }, + "source": [ + "#### `reduce`\n", + "\n", + "[`functools.reduce`](https://docs.python.org/3/library/functools.html#functools.reduce) is for when you want to combine (or *reduce*) all values from a sequence to a single value. It accepts a function, an iterable and an optional starting value. If you don't supply a starting value, it will use the first value in the sequence instead.\n", + "\n", + "`reduce` keeps a work-in-progress value of sorts, which is often called the *accumulator*. The accumulator is initially set to the starting value. For every remaining value in the iterable sequence, `reduce` calls the function with two arguments: the accumulator and the value itself. The return value from the function becomes the new accumulator. After the last value in the sequence, the latest accumulator is returned as the final result.\n", + "\n", + "For illustration, here is how you might use `reduce` to reverse a string:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb1c3b29", + "metadata": { + "id": "kN5Uw_iB6QE6" + }, + "outputs": [], + "source": [ + "from functools import reduce\n", + "\n", + "def prepend_letter(accumulator, next_letter):\n", + " return next_letter + accumulator\n", + "\n", + "def reverse_string(string):\n", + " # In this case, we reduce a sequence of characters to a new string.\n", + " return reduce(prepend_letter, string)\n", + "\n", + "reverse_string('abcdef')" + ] + }, + { + "cell_type": "markdown", + "id": "f53360bc", + "metadata": { + "id": "AxfCOlrU7H75" + }, + "source": [ + "And here is how we could write our own implementations of the built-in functions `max` and `sum` using `reduce`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a88142d1", + "metadata": { + "id": "P1CltqPc7UvG" + }, + "outputs": [], + "source": [ + "from functools import reduce\n", + "from operator import add\n", + "\n", + "def greater(a, b):\n", + " if a < b:\n", + " return b\n", + " else:\n", + " return a\n", + "\n", + "def max(iterable):\n", + " return reduce(greater, iterable)\n", + "\n", + "def sum(iterable):\n", + " return reduce(add, iterable)\n", + "\n", + "numbers = [3, 5, 4]\n", + "\n", + "print('max:', max(numbers))\n", + "print('sum:', sum(numbers))" + ] + }, + { + "cell_type": "markdown", + "id": "01ac90e1", + "metadata": { + "id": "_zR8E_94YHCv" + }, + "source": [ + "## Calculations\n", + "\n", + "As we have written in the course manual, Python is \"batteries included\"—it comes with a lot of functionality out of the box. This is certainly also the case for numerical computations and even some basic statistics. We highlight some common tools below.\n", + "\n", + "- The built-in functions [`abs`][abs], [`max`][max], [`min`][min], [`pow`][pow], [`round`][round] and [`sum`][sum] do exactly what the names suggest. You can also use the `**` operator for powers.\n", + "- The built-in [`range`][range] function, which we have seen before, lets you generate linear series of numbers.\n", + "- The [`math`][math] standard module contains a wealth of general mathematical functions and constants, such as [`log`][math.log], [`sqrt`][math.sqrt], [`cos`][math.cos], [`pi`][math.pi] and [`tau`][math.tau].\n", + "- The [`random`][random] standard module covers most random number generating needs, as well as random shuffling and sampling.\n", + "- The [`statistics`][statistics] standard module includes the common staples of statistical analysis, such as [`mean`][statistics.mean], [`median`][statistics.median], [`mode`][statistics.mode], [`stdev`][statistics.stdev], [`variance`][statistics.variance], [`covariance`][statistics.covariance], [`correlation`][statistics.correlation] and even a simple [`linear_regression`][statistics.linear_regression]. ~~Unfortunately, however, the latter three are not available in Google Colab, because they were only recently added to the Python standard library and Colab is not running the latest (\"bleeding edge\") version of Python. The next two subsections offer some alternatives.~~\n", + "\n", + "A complete overview of numerical functionality in the Python standard library can be found [here][python-numeric].\n", + "\n", + "[abs]: https://docs.python.org/3/library/functions.html#abs\n", + "[max]: https://docs.python.org/3/library/functions.html#max\n", + "[min]: https://docs.python.org/3/library/functions.html#min\n", + "[pow]: https://docs.python.org/3/library/functions.html#pow\n", + "[range]: https://docs.python.org/3/library/functions.html#func-range\n", + "[round]: https://docs.python.org/3/library/functions.html#round\n", + "[sum]: https://docs.python.org/3/library/functions.html#sum\n", + "[math]: https://docs.python.org/3/library/math.html\n", + "[math.log]: https://docs.python.org/3/library/math.html#math.log\n", + "[math.sqrt]: https://docs.python.org/3/library/math.html#math.sqrt\n", + "[math.cos]: https://docs.python.org/3/library/math.html#math.cos\n", + "[math.pi]: https://docs.python.org/3/library/math.html#math.pi\n", + "[math.tau]: https://docs.python.org/3/library/math.html#math.tau\n", + "[random]: https://docs.python.org/3/library/random.html\n", + "[statistics]: https://docs.python.org/3/library/statistics.html\n", + "[statistics.mean]: https://docs.python.org/3/library/statistics.html#statistics.mean\n", + "[statistics.median]: https://docs.python.org/3/library/statistics.html#statistics.median\n", + "[statistics.mode]: https://docs.python.org/3/library/statistics.html#statistics.mode\n", + "[statistics.stdev]: https://docs.python.org/3/library/statistics.html#statistics.stdev\n", + "[statistics.variance]: https://docs.python.org/3/library/statistics.html#statistics.variance\n", + "[statistics.covariance]: https://docs.python.org/3/library/statistics.html#statistics.covariance\n", + "[statistics.correlation]: https://docs.python.org/3/library/statistics.html#statistics.correlation\n", + "[statistics.linear_regression]: https://docs.python.org/3/library/statistics.html#statistics.linear_regression\n", + "[python-numeric]: https://docs.python.org/3/library/numeric.html" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "931fff5c", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 322, + "status": "ok", + "timestamp": 1701791082060, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "xoBLhOpvmu2P", + "outputId": "5e3ccc4f-d8e3-4102-a3bf-517a8ccddfbc" + }, + "outputs": [], + "source": [ + "!python --version" + ] + }, + { + "cell_type": "markdown", + "id": "d324ae1a", + "metadata": { + "id": "rKxMNbMMuMCw" + }, + "source": [ + "### Computing covariance and correlation yourself\n", + "\n", + "Given two equally long sequences of numbers and their averages (which you might have already computed because you needed them elsewhere), you can compute the sample covariance and correlation as follows using [iterables](#scrollTo=Iterables):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5be766fb", + "metadata": { + "id": "moprSI-g90tZ" + }, + "outputs": [], + "source": [ + "from itertools import repeat\n", + "from operator import sub, mul\n", + "from statistics import mean, stdev\n", + "\n", + "def differences(series, average):\n", + " return map(sub, series, repeat(average))\n", + "\n", + "def covariance(series1, series2, average1=None, average2=None):\n", + " differences1 = differences(series1, average1 or mean(series1))\n", + " differences2 = differences(series2, average2 or mean(series2))\n", + " products = map(mul, differences1, differences2)\n", + " return sum(products) / (len(series1) - 1)\n", + "\n", + "def correlation(series1, series2, average1=None, average2=None):\n", + " '''Pearson's correlation coefficient.'''\n", + " cov = covariance(series1, series2, average1, average2)\n", + " stdev1 = stdev(series1, average1)\n", + " stdev2 = stdev(series2, average2)\n", + " return cov / (stdev1 * stdev2)\n", + "\n", + "column1 = [1, 2, 3, 4, 5, 6, 7]\n", + "column2 = [4, 5, 6, 5, 5, 8, 9]\n", + "column3 = [8, 7, 6, 5, 4, 3, 2]\n", + "\n", + "print('covariance 1-2:', covariance(column1, column2))\n", + "print('correlation 1-2:', correlation(column1, column2))\n", + "print('correlation 2-1:', correlation(column2, column1))\n", + "print('correlation 1-3:', correlation(column1, column3))\n", + "print('correlation 2-3:', correlation(column2, column3))" + ] + }, + { + "cell_type": "markdown", + "id": "6f2fb914", + "metadata": { + "id": "JYTbShRDBoS1" + }, + "source": [ + "### Using covariance and correlation from an external package\n", + "\n", + "[pandas](https://pandas.pydata.org/pandas-docs/stable/index.html) provides implementations of [covariance](https://pandas.pydata.org/pandas-docs/stable/user_guide/computation.html#covariance), [correlation](https://pandas.pydata.org/pandas-docs/stable/user_guide/computation.html#correlation) and many other statistical functions. If you want to do serious statistics, you should probably use pandas or some other third-party package that can do the heavy lifting for you. If you choose this path, head over to [dataframes](#scrollTo=pandas_dataframes_and_read_csv) first." + ] + }, + { + "cell_type": "markdown", + "id": "93d43808", + "metadata": { + "id": "F6mIIM3zLw1p" + }, + "source": [ + "## Classes and objects\n", + "\n", + "For the final exercise, you do not need to create your own classes. However, since you may encounter the terminology and the notation when using third-party packages, here is a *very* quick explanation.\n", + "\n", + "An **object** is a value with internal structure. For example, I might have a variable with the name `jack` that holds a description of my friend Jack. The parts of that description are called its **attributes**. In this example, `jack.name` might hold the string `'Jack'` and `jack.phone` might hold the string `'+31612345678'` (not his real phone number ;-). `jack` is the object and `name` and `phone` are its attributes.\n", + "\n", + "Attributes can hold all types of values, including functions. In the latter case, the attribute is also called a **method**. For example, `jack.call()` might dial Jack's number. Attributes might also themselves be objects with their own nested attributes, so you can have chains of names connected with dots, for example `house.kitchen.sink`. In the latter dotted name, `kitchen` and `sink` are both attributes, and `house` and `kitchen` must both be objects, though `sink` might be an object as well.\n", + "\n", + "Whenever you see two names connected with a dot, the left one must be an object. You have already encountered a few types of objects. Every list has an `.append()` method, so lists are objects. When you call `csv.reader()`, the `csv` module is an object (though we usually don't use the words \"attribute\" and \"method\" to refer to the parts of a module; we rather say that `csv.reader` is a *qualified name*). In fact, *nearly everything* in Python is an object; this is why it is called an *object-oriented* programming language.\n", + "\n", + "Objects are generally created using a **class**. You can think of a class as a template for creating objects of a particular shape. For example, our `jack` object might have been created using the `Person` class. We say that `jack` is an **instance** of `Person` and also that we **instantiated** `Person` when we created `jack`. Typically, you can expect all instances of a class to have the same attributes and methods. Packages often organize their documentation by class, listing the methods of its instances and their usage.\n", + "\n", + "Instantiating a class looks exactly like calling a function and storing the return value in a variable. The only visible clue that you're instantiating a class rather than calling a function, is that classes usually have a name starting with an uppercase letter:\n", + "\n", + "```py\n", + "jack = Person(name='Jack')\n", + "```\n", + "\n", + "There is no danger in thinking of a class instantiation as a function call, or in instantiating a class without knowing it because its name starts with a lowercase letter. In reality, however, a class is not a function but an object!" + ] + }, + { + "cell_type": "markdown", + "id": "6c75735f", + "metadata": { + "id": "MoyqHwBhvBTH" + }, + "source": [ + "## Working with times and calendar dates\n", + "\n", + "The [`datetime`][datetime] standard module provides tools for working with dates and times. It exports the `date`, `time` and `datetime` [classes](#scrollTo=Classes_and_objects), which let you represent a date, a time or both, exactly as the names suggest.\n", + "\n", + "[datetime]: https://docs.python.org/3/library/datetime.html" + ] + }, + { + "cell_type": "markdown", + "id": "351e1228", + "metadata": { + "id": "W5ZlB-uXkC6S" + }, + "source": [ + "### Parsing dates from text\n", + "\n", + "If you are working with dates for the final course exercise, it is most likely that you are extracting date strings from a CSV. Such a string might, for example, look like `'2021/11/15'`. In some cases, you may need to extract specific information from those dates, such as the year, month, hour or even weekday. For such use cases, it is advisable to convert the date string to a `date`, `time` or `datetime` object first by *parsing* it.\n", + "\n", + "The [`datetime.strptime`][datetime.strptime] function can do this job for you. It takes two parameters: the date string that needs to be parsed, and a second string that describes the *format* of the date. Our example above consisted of four digits that identify the year, a slash, two digits that identify the month, another slash, and finally two digits that identify the day of the month. We write that as `'%Y/%m/%d'`. In such a format string, a `%` with a letter after it is a placeholder for a particular piece of the date or time, while all other characters simply represent themselves. You can find a list of all available placeholders [here][datetime-formats].\n", + "\n", + "[datetime.strptime]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior\n", + "[datetime-formats]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e95a4fd", + "metadata": { + "id": "cOo_KnhWsR2m" + }, + "outputs": [], + "source": [ + "from datetime import datetime as dt\n", + "\n", + "yesterday_str = '2021/11/15'\n", + "date_format = '%Y/%m/%d'\n", + "\n", + "yesterday_obj = dt.strptime(yesterday_str, date_format)\n", + "print('datetime:', yesterday_obj)\n", + "\n", + "# dt.strptime always returns a full datetime, even if the input\n", + "# string and the format string contain only a date or only a time.\n", + "# You can reduce the datetime object to just a date or just a time\n", + "# by calling a method of the same name:\n", + "print('date: ', yesterday_obj.date())\n", + "print('time: ', yesterday_obj.time())" + ] + }, + { + "cell_type": "markdown", + "id": "673a822e", + "metadata": { + "id": "iDp5QxvpxZIo" + }, + "source": [ + "### Extracting information from date and time objects\n", + "\n", + "Once you have a `date`, `time`, or `datetime` object, extracting information from it is very easy, as we demonstrate below. [`datetime`][datetime.datetime] objects have all date-related attributes and methods of `date` as well as all time-related attributes and methods of `time`. You can find the most important attributes [here][datetime-attributes] and the most important methods [here][datetime-methods] (you can also scroll up from the latter link for some additional methods related to time zones).\n", + "\n", + "[datetime.datetime]: https://docs.python.org/3/library/datetime.html#datetime-objects\n", + "[datetime-attributes]: https://docs.python.org/3/library/datetime.html#datetime.datetime.year\n", + "[datetime-methods]: https://docs.python.org/3/library/datetime.html#datetime.datetime.utcoffset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "601a0c46", + "metadata": { + "id": "M9pQ2otg0EQU" + }, + "outputs": [], + "source": [ + "# Year, month etcetera attributes are all represented as numbers.\n", + "print('year: ', yesterday_obj.year)\n", + "print('month: ', yesterday_obj.month)\n", + "print('hour: ', yesterday_obj.hour)\n", + "\n", + "# Python starts the week on Monday and starts numbering at zero.\n", + "print('weekday: ', yesterday_obj.weekday())\n", + "# The ISO 8601 standard also starts on Monday, but starts numbering at one.\n", + "print('isoweekday:', yesterday_obj.isoweekday())" + ] + }, + { + "cell_type": "markdown", + "id": "6e089550", + "metadata": { + "id": "GLBue3palj8M" + }, + "source": [ + "## Sorting\n", + "\n", + "Python has a built-in function [`sorted`][sorted], which can sort anything that you can loop over (including the key-value pairs of a [dictionary](#scrollTo=Dictionaries)). It always returns a list with the result.\n", + "\n", + "By default, it will sort ascending, i.e., by increasing order of magnitude. Numbers are compared by value, strings are compared lexicographically (with all uppercase letters sorting before all lowercase letters), and lists are compared by the first item, with subsequent items being used as tie breakers if previous items compared equal.\n", + "\n", + "[sorted]: https://docs.python.org/3/library/functions.html#sorted\n", + "[sorting-howto]: https://docs.python.org/3/howto/sorting.html#sortinghowto" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b512ba06", + "metadata": { + "id": "FIw_4XyNn9UK" + }, + "outputs": [], + "source": [ + "list_of_numbers = [5, 3, 4]\n", + "list_of_strings = ['Good', 'day', 'to', 'you']\n", + "list_of_lists = [\n", + " [6, 'zucchini'],\n", + " [5, 'eggs'],\n", + " [6, 'broccoli'],\n", + "]\n", + "\n", + "print(sorted(list_of_numbers))\n", + "print(sorted(list_of_strings))\n", + "print(sorted(list_of_lists))" + ] + }, + { + "cell_type": "markdown", + "id": "96a797cf", + "metadata": { + "id": "_uzxS1S1setr" + }, + "source": [ + "### Sorting in descending order\n", + "\n", + "If you want to sort descending instead, pass the named argument `reverse=True`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "83d2a3f6", + "metadata": { + "id": "e2RSQaXFszpT" + }, + "outputs": [], + "source": [ + "sorted(list_of_numbers, reverse=True)" + ] + }, + { + "cell_type": "markdown", + "id": "86c18578", + "metadata": { + "id": "YKx3ObxltgXz" + }, + "source": [ + "### Custom comparison\n", + "\n", + "If you want `sorted` to perform a different kind of comparison in order to decide on the order of the items, you can pass a function as the named `key` argument. This function should take one item as its parameter and return a new value that `sorted` will use for the comparison instead.\n", + "\n", + "Below, we use the function `str.lower` to do a case-insensitive sort:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "610ac3e1", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5_w-T2ryuknR", + "outputId": "d9f68252-3e93-48c0-fdf9-f001a107fdf2" + }, + "outputs": [], + "source": [ + "sorted(list_of_strings, key=str.lower)" + ] + }, + { + "cell_type": "markdown", + "id": "79086ccc", + "metadata": { + "id": "92yLh2OWvO-q" + }, + "source": [ + "The [`operator`][operator] standard module exports several useful functions that let you create instant simple functions for the purpose of sorting. Most importantly, [`itemgetter`][operator.itemgetter] lets you sort sequences by a different item than the first and [`attrgetter`][operator.attrgetter] lets you sort [objects](#scrollTo=Classes_and_objects) by a particular attribute. There is also [`methodcaller`][operator.methodcaller] which lets you sort by the result of a method call.\n", + "\n", + "Below, we use `itemgetter` to sort the key-value pairs of a [dictionary](#scrollTo=Dictionaries) by value instead of by key:\n", + "\n", + "[operator]: https://docs.python.org/3/library/operator.html#module-operator\n", + "[operator.itemgetter]: https://docs.python.org/3/library/operator.html#operator.itemgetter\n", + "[operator.attrgetter]: https://docs.python.org/3/library/operator.html#operator.attrgetter\n", + "[operator.methodcaller]: https://docs.python.org/3/library/operator.html#operator.methodcaller" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0dba03df", + "metadata": { + "id": "HigJMisJydem" + }, + "outputs": [], + "source": [ + "from operator import itemgetter\n", + "\n", + "example_dict = {'banana': 'yellow', 'cherry': 'sweet', 'date': 'wrinkly'}\n", + "\n", + "sorted(example_dict.items(), key=itemgetter(1))" + ] + }, + { + "cell_type": "markdown", + "id": "951e1c4c", + "metadata": { + "id": "P16V-uttzQXw" + }, + "source": [ + "And below, we use `attrgetter` to sort [dates](#scrollTo=Working_with_times_and_calendar_dates) by month:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "024b1368", + "metadata": { + "id": "jG1RMiBzzpky" + }, + "outputs": [], + "source": [ + "from operator import attrgetter\n", + "from datetime import date\n", + "\n", + "list_of_dates = [\n", + " date(year=2021, month=11, day=16),\n", + " date(year=2022, month=3, day=17),\n", + " date(year=2020, month=5, day=18),\n", + "]\n", + "\n", + "sorted(list_of_dates, key=attrgetter('month'))" + ] + }, + { + "cell_type": "markdown", + "id": "ed34bb42", + "metadata": { + "id": "7rsvpuMn1kSl" + }, + "source": [ + "## `pandas` dataframes and `read_csv`\n", + "\n", + "[pandas][pandas] is a package that provides general data structures and data analysis tools for Python. If you venture into statistics or datamining with Python, it is likely that you will sooner or later encounter [`pandas.DataFrame`][pandas.DataFrame] (which is a [class](#scrollTo=Classes_and_objects)). It holds tabular data in which each column might have a different type. Other packages often use this data structure, too.\n", + "\n", + "If you encounter `pandas` during the final course exercise, it is probably because you are using a function from a third-party package that expects you to pass in the data as a `DataFrame`. In this case, it is useful to know that `pandas` provides the [`read_csv`][pandas.read_csv] function. It accepts a file or file name as its first parameter and returns the contents of the CSV file as a `DataFrame`. You can then pass this object to the function that expects a `DataFrame`. The `read_csv` function also accepts a couple of optional parameters that let you specify details about the way the CSV file is formatted, its columns, etcetera.\n", + "\n", + "In the example code below, we illustrate how you can create a `DataFrame` using `read_csv`. Note that we define a [cross-platform path](#scrollTo=Cross_platform_file_paths) using [`os.path`](https://docs.python.org/3/library/os.path.html). In the [next section](#scrollTo=Clustering_with_scikit_learn), we illustrate how the `data` object may be passed to a third-party analysis function.\n", + "\n", + "[pandas]: https://pandas.pydata.org/pandas-docs/stable/index.html\n", + "[pandas.DataFrame]: https://pandas.pydata.org/pandas-docs/stable/user_guide/dsintro.html#dataframe\n", + "[pandas.read_csv]: https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#io-read-csv-table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6cc5346f", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 142 + }, + "id": "1m-b-UVLF_rM", + "outputId": "0b542154-2e25-4f71-c445-856836f0a749" + }, + "outputs": [], + "source": [ + "#requires pandas\n", + "\n", + "import os.path as op\n", + "import pandas\n", + "\n", + "file_path = op.join('sample_data', 'california_housing_test.csv')\n", + "\n", + "data = pandas.read_csv(file_path)\n", + "# `data` is an instance of pandas.DataFrame with several columns\n", + "# containing geographic center points of neighborhoods in\n", + "# California as well as demographics about the inhabitants and\n", + "# their houses.\n", + "\n", + "# You may sometimes want to pass only a subset of the dataset\n", + "# to a function. For this purpose, dataframes can be sliced in\n", + "# a way that is similar to lists. The following example will\n", + "# contain only the 'total_rooms' column:\n", + "data.loc[:, 'total_rooms']\n", + "\n", + "# The following example will include two columns, in a different\n", + "# order than they appeared in the CSV:\n", + "data.loc[:, ['households', 'population']]\n", + "# You can also use this notation if you want to use a subset of\n", + "# multiple columns.\n", + "\n", + "# For slicing rows by position, you use the `iloc` attribute\n", + "# instead of `loc`:\n", + "data.iloc[0:3]\n", + "\n", + "# Both ways of slicing can be combined:\n", + "data.loc[:, ['households', 'population']].iloc[0:3]" + ] + }, + { + "cell_type": "markdown", + "id": "064bc70f", + "metadata": { + "id": "l0NeIki5L-LI" + }, + "source": [ + "## Visualizing data with `matplotlib`\n", + "\n", + "[`matplotlib`](https://matplotlib.org) is a comprehensive and easy to use package for creating data graphics. It is preinstalled on Google Colab, so you can use it right away. Here is a quick example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ac75a69", + "metadata": { + "id": "ZBxC0c32NN7v" + }, + "outputs": [], + "source": [ + "import matplotlib as mpl\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "fig, ax = plt.subplots() # Create a figure containing a single axes.\n", + "ax.plot([1, 2, 3, 4], [1, 4, 2, 3]); # Plot some data on the axes." + ] + }, + { + "cell_type": "markdown", + "id": "58e1cd6b", + "metadata": { + "id": "RIGMSlw7NhnT" + }, + "source": [ + "The above example was shamelessly copied from `matplotlib`'s [Quick start guide](https://matplotlib.org/stable/tutorials/introductory/quick_start.html), which is the best place to start learning how to use the package yourself." + ] + }, + { + "cell_type": "markdown", + "id": "64254db9", + "metadata": { + "id": "EYVhwfCP2oEK" + }, + "source": [ + "## Clustering with scikit-learn\n", + "\n", + "[scikit-learn][sklearn] is a package that provides many data mining and machine learning tools, including cluster analysis. You can find the documentation [here][sklearn.cluster]. We give a very minimal example of hierarchical clustering with Ward linkage below. You can find a more extensive example [here][sklearn-example]. Note that we are using the `data` object, which is a `pandas.DataFrame`, from the [previous section](#scrollTo=pandas_dataframes_and_read_csv).\n", + "\n", + "[sklearn]: https://scikit-learn.org/stable/index.html\n", + "[sklearn.cluster]: https://scikit-learn.org/stable/modules/clustering.html\n", + "[sklearn-example]: https://scikit-learn.org/stable/auto_examples/cluster/plot_digits_linkage.html#sphx-glr-auto-examples-cluster-plot-digits-linkage-py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3dd08463", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "upo6gPd46sVt", + "outputId": "6938e3d2-8104-4f8c-fca6-f10b70d36dbb" + }, + "outputs": [], + "source": [ + "#requires sklearn\n", + "\n", + "from sklearn.cluster import AgglomerativeClustering\n", + "\n", + "# We start by creating the object that will *eventually* contain\n", + "# the clustering.\n", + "clustering = AgglomerativeClustering()\n", + "# Next, we feed in the data through the `fit` method. We will\n", + "# cluster the neighborhoods by geographical location.\n", + "clustering.fit(data.loc[:, ['latitude', 'longitude']])\n", + "# The clustering is now established. We illustrate below by\n", + "# printing the cluster assignment of the first 20 rows in the\n", + "# dataset.\n", + "print(clustering.labels_[:20])\n", + "# In a more serious application, we would probably inspect the\n", + "# cluster dendrogram or plot the data with a coloring to indicate\n", + "# the cluster assignment of each data point. The scikit-learn\n", + "# documentation explains how to do such things." + ] + }, + { + "cell_type": "markdown", + "id": "48bda077", + "metadata": { + "id": "hofJ0jOJ-fKo" + }, + "source": [ + "## Parsing XML and HTML\n", + "\n", + "In the humanities, data are often stored in XML format, for example [TEI](https://en.wikipedia.org/wiki/Text_Encoding_Initiative). XML is a more complicated format than CSV, so extracting information from it is also a bit more involved.\n", + "\n", + "Another, similar format is HTML, the language that web pages are written in. You may encounter this when scraping the web; more about this in [the next section](#scrollTo=lEy6U7SF-uFe).\n", + "\n", + "XML and HTML can both be parsed (and written) using [Beautiful Soup](https://beautiful-soup-4.readthedocs.io/en/latest/). It is preinstalled in Google Colab and can be imported by the name `bs4`." + ] + }, + { + "cell_type": "markdown", + "id": "1774ba5b", + "metadata": { + "id": "lEy6U7SF-uFe" + }, + "source": [ + "## Fetching information from the internet\n", + "\n", + "[Requests](https://docs.python-requests.org/en/latest/) lets you fetch information from the internet, similar to how your browser might view or download information from a particular web address. It is a Python alternative to the command line program `curl` or the graphical program Postman, if you happen to know either.\n", + "\n", + "The package is preinstalled on Google Colab and can be imported as `requests`. How to do this is explained in the [Quickstart](https://docs.python-requests.org/en/latest/user/quickstart/). You can use Beautiful Soup for parsing HTML and XML responses, as explained in [the previous section](#scrollTo=hofJ0jOJ-fKo&line=7&uniqifier=1)." + ] + }, + { + "cell_type": "markdown", + "id": "47716ab5", + "metadata": { + "id": "1ft-t0CGvOl2" + }, + "source": [ + "## Network visualizations\n", + "\n", + "[This article](https://towardsdatascience.com/visualizing-networks-in-python-d70f4cbeb259) lists four packages that you could use to visualize networks. Most of them can interface with [pandas](#scrollTo=pandas_dataframes_and_read_csv)." + ] + }, + { + "cell_type": "markdown", + "id": "8027c316", + "metadata": { + "id": "Fd6n2BgZwlUi" + }, + "source": [ + "## Natural language processing\n", + "\n", + "While there are more options, you should probably start by taking a look at [NLTK](https://www.nltk.org/) if you want to do any form of natural language processing, for example:\n", + "\n", + "- tokenization\n", + "- stopword removal (`corpus` module)\n", + "- stemming\n", + "- parsing\n", + "- sentiment analysis" + ] + } + ], + "metadata": { + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/Tips.py b/lessons/Tips.py new file mode 100644 index 0000000..83174e7 --- /dev/null +++ b/lessons/Tips.py @@ -0,0 +1,1202 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="eL_rkx7cIm77" +# # Tips +# +# This notebook contains tips for the final exercise of the CDH entry level Python course at Utrecht University. Participants were asked to think of an analysis that they might want to perform and to submit a description of this analysis in advance of the course. The tips in this notebook were chosen based on those submissions. +# +# There is some overlap with the material that was already discussed during the lectures. +# +# While the notebook is written such that you can read it top to bottom, you are welcome to cherry-pick the topics that interest you. There is a table of contents in the left margin. Some sections contain internal links to other sections. +# +# As a general tip, you can get a complete overview of the Python standard library [over here][python-libs]. +# +# [python-libs]: https://docs.python.org/3/library/index.html + +# %% [markdown] id="wBOMsp2twgbB" +# ## Converting between types +# +# String to int: + +# %% id="1aU45qPvwntM" +int('123') + +# %% [markdown] id="cfrMiRd3wy9d" +# Integer to string: + +# %% id="LNJEIXCtw6rq" +str(123) + +# %% [markdown] id="RtGlRbICxf__" +# Float to string: + +# %% id="ejGhZs8SxjUN" +str(0.5) + +# %% [markdown] id="TdaVUNpBxmQ8" +# String to float: + +# %% id="Nwk3D9VExoU_" +float('0.5') + +# %% [markdown] id="6CYPccQYxwCm" +# Boolean to string: + +# %% id="JJf6fjNGxzvC" +str(True) + +# %% [markdown] id="LV7o-rkDx3MY" +# String to boolean: + +# %% id="UUPNXO4mx5eb" +print('Direct boolean from string does not work:', bool('False')) + +# So we have to write a function. +def boolean_from_string(string): + if string == 'False': + return False + else: + return True + +print(boolean_from_string('True')) +print(boolean_from_string('False')) + +# %% [markdown] id="CfnNAUKmyhOj" +# Integer to float: + +# %% id="st9vZgf0yixm" +float(123) + +# %% [markdown] id="Gmw_vdGoyl3c" +# Float to integer: + +# %% id="JZ_l3IdhynF-" +int(0.5) + +# %% [markdown] id="97z6FUGz8uAS" +# ## Strings +# +# Strings have a couple of tricks that may be useful for the final exercise. We illustrate them quickly below. A complete overview of all string methods can be found [here](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str). +# +# [`str.startswith`](https://docs.python.org/3/library/stdtypes.html#str.startswith) and [`str.endswith`](https://docs.python.org/3/library/stdtypes.html#str.endswith) will tell you whether a string begins or ends with a particular other string, respectively. + +# %% id="07ik0vd2-6tQ" +for word in ['magazine', 'kangaroo', 'rooster', 'broom']: + if word.startswith('roo'): + print('"' + word + '" starts with "roo"') + if word.endswith('roo'): + print('"' + word + '" ends with "roo"') + +# %% [markdown] id="FVyAad6OAtNX" +# [`str.lower`](https://docs.python.org/3/library/stdtypes.html#str.lower) and [`str.upper`](https://docs.python.org/3/library/stdtypes.html#str.upper) return a copy of a string that is converted to all lowercase or all uppercase, respectively. These functions are useful when you don't want case to influence comparisons. + +# %% id="9sjgompVA_Qi" +word1 = 'banana' +word2 = 'Banana' + +print('case-sensitive:', word1 == word2) +print('case-insensitive:', word1.lower() == word2.lower()) + +# %% [markdown] id="TyjWFAWR_0Dp" +# [`str.join`](https://docs.python.org/3/library/stdtypes.html#str.join) can glue a sequence of strings together, as we have seen in [9. String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm#scrollTo=Join_an_iterable_into_a_string). + +# %% id="JlqAc5N8AQPu" +print(' + '.join(['1', '2', '3', '4'])) +print(', '.join(['do', 're', 'mi', 'fa', 'sol', 'la', 'ti', 'do'])) + +# %% [markdown] id="g0x1VvE5B71w" +# [`str.split`](https://docs.python.org/3/library/stdtypes.html#str.split) is the opposite of `str.join`: it will split a string by a given separator and return a list with the fragments. If you don't specify a separator, it will split by whitespace. + +# %% colab={"base_uri": "https://localhost:8080/"} id="R11wYmWFCVdb" outputId="ca8804e9-80a5-4771-e3f0-1adee880f66b" +print('1 + 2 + 3 + 4'.split(' + ')) +print('1 + 2 + 3 + 4'.split('+')) +print('1 + 2 + 3 + 4'.split()) +print('1 2 3 4'.split()) + +# %% [markdown] id="0csn-TVPC8qG" +# [`str.splitlines`](https://docs.python.org/3/library/stdtypes.html#str.splitlines) is basically `str.split('\n')`, but it cleverly recognizes many types of line endings (which might differ between platforms) and it has an option to keep the line endings in the resulting fragments. + +# %% [markdown] id="k1xy1XmaED7X" +# [`str.strip`](https://docs.python.org/3/library/stdtypes.html#str.strip) will return a new string with the whitespace removed from the beginning and end. You can also specify a different set of characters that should be removed. The default mode of removing whitespace is useful for cleaning up text that was downloaded from the internet or that was copypasted from some kind of document. + +# %% id="djsFEC5DE6md" +" This string isn't very tidy. ".strip() + +# %% [markdown] id="G4tC_OiFFth3" +# ### Escapes +# +# There are some characters that normally aren't allowed to appear in a literal string. We can shoehorn them in anyway by placing a backslash `\` in front of the character, or another letter that acts as a placeholder. This is called *escaping* the character. The combination of the backslash and the following character is called an *escape sequence*. Python also uses these escape sequences when echoing raw strings back at us. The following escape sequences occur often: +# +# `\n` - linefeed ("newline") character. +# +# `\r\n` - carriage return plus linefeed. For archeological reasons I will not go into, these two characters together count as a single line separator in Microsoft Windows. So you are likely to find it in files that were created in Windows. +# +# `\t` - tab character. +# +# `\'` - straight single quote (escape not needed in strings delimited by double quotes). +# +# `\"` - straight double quote (escape not needed in strings delimited by single quotes). +# +# `\\` - the backslash itself. +# +# You can get a complete overview of escape sequences if you scroll down from [here](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals). + +# %% [markdown] id="5BlYPydVDvWt" +# ### Searching for substrings +# +# Searching for a substring within a larger string is a common task, especially in the humanities. [`str.find`](https://docs.python.org/3/library/stdtypes.html#str.find) will tell us the first position in the large string at which the substring is found, or `-1` if it is not found: + +# %% id="kxLt4yApEtpV" +print('kangaroo'.find('roo')) +print('kangaroo'.find('skip')) + + +# %% [markdown] id="_SmGTTzwFAFj" +# `str.find` accepts an optional second argument, which lets us specify the position from which to start searching. We can use this to continue searching for more occurrences after the first match. For example, the following function returns *all* positions of the substring `needle` within the larger string `haystack`: + +# %% id="lAlw_L-sFikq" +def str_find_all(haystack, needle): + results = [] + position = -1 + # while True lets you repeat the same code "forever" + while True: + position = haystack.find(needle, position + 1) + if position is -1: + # break lets you "break out" of the infinite loop + break + else: + results.append(position) + return results + +str_find_all('Colorless green ideas sleep furiously', 'e') + +# %% [markdown] id="_U9XtQyTLQyC" +# With the optional third argument, we can also make `str.find` *stop* searching at a given position. In the example below, `'roo'` is not found in the first four characters of `'kangaroo'`. + +# %% id="jL0nWgb4LivF" +print('kangaroo'.find('roo', 0, 4)) + +# %% [markdown] id="lVzhE5VuMQdZ" +# During the lectures, we mentioned a simpler way to search for a substring that only tells you whether the substring appears at all: + +# %% id="KfieQsYOMkK7" +'roo' in 'kangaroo' + +# %% [markdown] id="LhCDt6xTMq2i" +# If you need pattern-based search, [regular expressions](https://docs.python.org/3/library/re.html) are the tool of choice. A regular expression lets you describe a set of similar strings with a single pattern. For example, the following notation will match all occurrences of the word "colour" in a text, even if it is capitalized or spelled "color": +# +# ``` +# [Cc]olou?r +# ``` +# +# The `search` function from the standard module `re` (described by the above link) lets us use such patterns to search through a string: + +# %% id="jOJl_23IOvyG" +import re + +re.search('[Cc]olou?r', 'Colorless green ideas sleep furiously') + +# %% [markdown] id="Me7D092xO-u0" +# Regular expressions are a powerful tool and the `re` module has many bells and whistles. Please refer to [the documentation](https://docs.python.org/3/library/re.html) for the details. + +# %% [markdown] id="mUmNSdNzMq_M" +# ### Format strings +# +# A format string is a perfectly normal string that contains some special notation, i.e., pairs of braces `{}`. While the presence of those braces does not oblige us to anything, we *can* use their presence (and insert them on purpose) in order to benefit from the [`str.format`](https://docs.python.org/3/library/stdtypes.html#str.format) method. For example, if I have the following string, +# +# ```python +# 'Ta-da!' +# ``` +# +# I can turn it into a format string simply by inserting a pair of braces, anywhere I like: +# +# ```python +# 'Ta-da: {}' +# ``` +# +# If I call `str.format` on a format string, it will interpret pairs of braces as placeholders and replace them by any arguments that I pass. + +# %% colab={"base_uri": "https://localhost:8080/", "height": 37} id="NQmg3z2cPPFW" outputId="c6647728-a0ac-4f50-e5ed-36e9b0c94cbf" +'Ta-da: {}'.format('this is Python!') + +# %% [markdown] id="ZtwiQhMJPcAd" +# You can insert as many placeholders and pass as many arguments as you like, as long as there are at least as many arguments as placeholders. Of course, you usually want the number of arguments to exactly match the number of placeholders in the format string, but `str.format` will simply skip any arguments that remain. + +# %% id="x_f2iRsQQNqr" +print('Ta-da: {}'.format(1, 2, 3)) +print('Ta{}da{} {}'.format('-', ':', 'success!')) + +# %% [markdown] id="7GOzHkgLRGYi" +# Format strings are a great way to compose strings out of some fixed parts and some variable parts, especially if the fixed parts aren't entirely regular. Consider the following code: + +# %% id="mTVp_EOmR_nm" +YELL_START = 'Go go ' +YELL_END = '!!!' + +def yell(name): + return YELL_START + name + YELL_END + +yell('Jelte') + +# %% [markdown] id="fwTdCa4QSOuv" +# Using a format string, this code would be a bit more explicit and a bit easier to read and write as well: + +# %% id="KvbXfNykSmiB" +YELL_FORMAT = 'Go go {}!!!' + +def yell(name): + return YELL_FORMAT.format(name) + +yell('Jelte') + +# %% [markdown] id="TbnEKRdlTGTj" +# The above discussion addresses about 90% of all string formatting needs, but for the remaining 10%, `str.format` is chock-full of bells and whistles. You can use named arguments, reuse the same argument in multiple places, align placeholders to a particular width with filler characters of choice, specify how to format numbers, and so forth and so on. You can read all about it [here](https://docs.python.org/3/library/string.html#formatstrings). + +# %% [markdown] id="0FJ_vXwlwT_5" +# ### Cross-platform file paths +# +# Consider the location of the current notebook (`Tips.ipynb`). It is located inside a folder called `Colab Notebooks`, which is inside a folder called `My Drive`, which is inside my Google Drive account. In Windows, we write such paths as follows: +# +# My Drive\Colab Notebooks\Tips.ipynb +# +# In macOS and Linux, on the other hand, we separate the path components with forward slashes: +# +# My Drive/Colab Notebooks/Tips.ipynb +# +# In Python, we often need to create a string with a file path, which would be different depending on whether the code is running on Windows or elsewhere (backslashes appear twice because they need to be [escaped](#scrollTo=Escapes)): +# +# ```py +# windows_path = 'My Drive\\Colab Notebooks\\Tips.ipynb' +# maclinux_path = 'My Drive/Colab Notebooks/Tips.ipynb' +# ``` +# +# We generally want our code to work on all platforms, without having to change the path notation or having to introduce `if`/`else` statements everywhere to choose between alternative file paths. This is where the standard module [`os.path`][os.path] comes to our rescue. It provides a function called `join`, which glues the path components together with the separator that is appropriate for whatever platform the code is running on: +# +# ```py +# import os.path as op +# +# crossplatform_path = op.join('My Drive', 'Colab Notebooks', 'Tips.ipynb') +# ``` +# +# The above snippet is repeated in the code cell below. If you run it, you will see that Google Colab runs on a system that uses the same path convention as macOS and Linux. +# +# Besides glueing path components together, [`os.path`][os.path] also provides tools for taking paths apart again and for converting back and forth between absolute and relative paths. +# +# [os.path]: https://docs.python.org/3/library/os.path.html + +# %% colab={"base_uri": "https://localhost:8080/"} id="AfuPbq5iwRsR" outputId="48cb5576-1a6d-4b82-fcb2-46c76c0bbb3d" +import os.path as op + +crossplatform_path = op.join('My Drive', 'Colab Notebooks', 'Tips.ipynb') + +print(crossplatform_path) + +# %% [markdown] id="zHyB8HjpU9hv" +# ## Tuples +# +# *This section was originally written when tuples were not yet included in the lectures. It is retained for consistency with the remainder of the notebook.* +# +# When you have two things, you can call them a pair. When you have three things, you can call them a triple. Four things, a quadruple. We continue with quintuple, sextuple, septuple, octuple. See where this is going? ;-) +# +# The general mathematical term for small groups like those is *N*-tuple, commonly abbreviated as "tuple". Python has a data type for tuples, which you almost never need to be aware of. It's a feature that [silently](https://docs.python.org/3/library/functions.html#func-tuple) makes things work, just like cleaners silently and inconspicuously prevent our society from collapsing. +# +# You don't need to create your own tuples for the final exercise (at least not consciously), but since the terminology is bound to come up, I will give a short explanation of what they are. A tuple is similar to a list, with two main differences: +# +# 1. Tuples are intended for small(ish) groups of values, while lists will happily store millions of values for you. +# 2. Tuples are *immutable*: you cannot change their contents after creation. +# +# When you see values separated by commas, and they are not in a list, a parameter list or an argument list, you are probably looking at a tuple. I will mention it when they appear in other tips. Here, I will just mention two situations where tuples may appear all by themselves. +# +# Tuples are sometimes used to return two or more values from a function at the same time. For example, the built-in function [`divmod`](https://docs.python.org/3/library/functions.html#divmod) can tell you the quotient and remainder of two numbers in one call: + +# %% id="LrwtKmfpayq0" +quotient, remainder = divmod(29, 11) + +print('quotient:', quotient, 'check:', quotient == 29 // 11) +print('remainder:', remainder, 'check:', remainder == 29 % 11) + +# %% [markdown] id="Qm-KzHTdbtCq" +# `divmod(29, 11)` was returning a tuple and we *unpacked* that tuple into the variables `quotient` and `remainder`. That's because their names where to the left of the assignment operator. On the other hand, when we put comma-separated values to the *right* of an assignment operator, it means that we *pack* those values into a new tuple. We can do a nice trick with this and swap two variables by packing them into a tuple and then immediately unpacking them again in reverse order: + +# %% id="ttECYgPLc6ra" +winner = 'Bert' +loser = 'Ernie' + +winner, loser = loser, winner + +print('winner:', winner) +print('loser:', loser) + +# %% [markdown] id="hpnieEnwpmhK" +# ## Dictionaries +# +# *This section was originally written when dictionaries were not yet included in the lectures. It is retained for consistency with the remainder of the notebook.* +# +# On the first day of the course, we have seen lists, which can hold an arbitrary number of values. The values are numbered sequentially, with the first having index `0`. You can create an empty list by calling `list()` or by writing an empty pair of square brackets, `[]`. +# +# A [dictionary][dict] is also a data structure that can hold an arbitrary number of values. The difference from a list is that the values are not numbered sequentially. Instead, every value has an arbitrary unique **key** which you need to set explicitly. An empty dictionary is created by calling `dict()` or by writing an empty pair of braces, `{}`. +# +# Dictionaries are useful when you want to associate values with each other. For example, your dataset might have a nominal column called `fruit`, with the possible levels being `apple`, `banana` and `cherry`, and you might want to count for each level in how many rows it occurs. In this case your will be associating fruits with frequencies, and a dictionary is the appropriate data structure for storing those associations. In the code below, we illustrate how dictionaries work and how you might use one to tally frequencies. +# +# [dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict + +# %% id="7JVH7h_Bu6mS" +# In the general case, we can create a dictionary and +# immediately set key-value pairs with the brace notation: +example_dict = { + 'apple': 'juicy', + 'banana': 'fragrant', + 'cherry': 'sweet', +} + +# Retrieving a value associated with a key can be done by +# placing the key between square brackets, similar to +# indexing a list: +example_dict['cherry'] + +# %% id="zhIMlt2TxoGC" +# If you try to read a key that isn't present in the +# dictionary, you will get a KeyError: +example_dict['date'] + +# %% id="vOk3VAnIyWgz" +# If we want to know whether a key is present in a dict, +# we can use the `in` operator: +print('cherry' in example_dict) +print('date' in example_dict) + +# %% id="yE8urPkiy-Iw" +# If we want to retrieve the value for a key if it exists, +# and fall back to a default value otherwise, we can use `get`: +print(example_dict.get('cherry', 'oops')) +print(example_dict.get('date', 'oops')) + +# %% id="BSXQQZfs0OfU" +# You can update and add key-value pairs by assignment. +example_dict['banana'] = 'yellow' +example_dict['date'] = 'wrinkly' + +# You can remove keys with the `del` operator. +del example_dict['apple'] + +# Let's see what we have now. +example_dict + +# %% [markdown] id="VisTnjOjxodE" +# In the next two examples, we use [tuple unpacking](#scrollTo=Tuples). + +# %% id="XnZcOLDM1zM-" +# We can iterate over the keys and values of dictionary. +for key, value in example_dict.items(): + print('A', key, 'is', value) + +# %% id="PdcV9FVm2X48" +# Now let's see how we can use a dictionary to tally. +# Suppose we have the following table of fruit orders: +orders = [ + ['2021-11-15', 'banana', 100], + ['2021-11-16', 'apple', 33], + ['2021-11-17', 'banana', 150], +] + +# We will pretend we haven't already seen those data and +# start with an empty dict. +fruit_tally = {} + +# Now we iterate over the orders and fill our dict. +for date, fruit, quantity in orders: + # First we retrieve how many times we have seen `fruit` + # before. If we haven't seen it before, the key isn't in the + # dict, so we provide 0 as a fallback value. + tally = fruit_tally.get(fruit, 0) + # Now we can add or update the tally for this `fruit`. + fruit_tally[fruit] = tally + 1 + +# Did we count correctly? +fruit_tally + +# %% [markdown] id="g_90Pk6j4Plm" +# For exercise, you can try adapting the above code example to compute total ordered quantities per fruit, or a list of order dates per fruit. + +# %% [markdown] id="hhu9T00mFSHd" +# ## Iterables +# +# *This section was originally written when iterables were not yet included in the lectures. It is retained for consistency with the remainder of the notebook.* +# +# In the FizzBuzz exercises, we saw the following notation for creating a list with the integers from `1` (inclusive) to `101` (exclusive): +# +# ```python +# list(range(1, 101)) +# ``` +# +# We also saw that you can pass only one argument, in which case it sets the end value and the start value is assumed to be `0`. On the other hand, if you pass *three* arguments, the third arguments sets a step value; this lets you skip every second element and/or make a decreasing series. +# +# It's clear what such a `list(range(begin, end))` expression does, but the curious might wonder what you get if you leave off the outer `list()`. What is a `range` just by itself? In any case, you can still loop over it: + +# %% id="XTmiVuEAGJlL" +for number in range(0, 3): + print(number) + +# %% [markdown] id="axzQrGdBLWDF" +# However, unlike with a list, we cannot add our own elements to it: + +# %% colab={"base_uri": "https://localhost:8080/", "height": 164} id="oHDOpj2GLevE" outputId="2d0ae6bd-61b2-497a-e8da-b1a45d1c3b97" +range(0, 3).append(3) + +# %% [markdown] id="6q2Tt_4eKc_K" +# If we try to print it, it remains mysterious: + +# %% colab={"base_uri": "https://localhost:8080/"} id="-MtW-ANdKn_d" outputId="39abd51e-e1bf-4ef9-997a-52d88f452448" +print(range(0, 3)) + +# %% [markdown] id="X6KSp6FbMPRP" +# I will no longer keep you in suspense. The value returned by [`range`][range] is a *generator*. You can think of a generator as a function that returns multiple times, each time producing the next value in some kind of sequence. This means that a generator, unlike a list, does not store all elements of its sequence at the same time; rather, it produces the next one when you ask for it. +# +# > For the final exercise, you don't need to write generators yourself, so we will not spend any text on how to do that. It is however useful to know how to use them, so we discuss that below. +# +# Python's `for` loop knows how to take one value at a time out of a generator, just like it knows how to take one value at a time out of a list. We call the more general class of things that `for` can loop over *iterables*. There are many more types of iterables besides lists and generators (including [tuples](#scrollTo=Tuples) and [dictionaries](#scrollTo=Dictionaries)) and `for` is able to deal with all of them, because all iterables follow the same **iterator convention**. +# +# In fact, this convention is not restricted to `for`. Most functions in the Python standard library that work with sequences, accept not just lists but any iterable. We have already seen this in action when we did `list(range(0, 101))`: `list` accepted an iterable, `range(0, 101)`, took out its values one by one, stored all of them in a list, and finally returned that list to you. +# +# Since generators don't store all of their values at the same time and we might not always need all values in a sequence, they are potentially more efficient than lists. For this reason, many standard library functions also *return* generators rather than lists. +# +# By combining functions that consume and return iterables, we can often replace loops by shorter expressions. In the next few subsections, we illustrate the most important functions on iterables. +# +# [range]: https://docs.python.org/3/library/functions.html#func-range + +# %% [markdown] id="RelVVKVzX9T7" +# ### `enumerate` +# +# The built-in [`enumerate`][enumerate] accepts any iterable and returns a generator. As the name suggests, it numbers the values in the input sequence, while also echoing back the values themselves (in [pairs](#scrollTo=Tuples)): +# +# [enumerate]: https://docs.python.org/3/library/functions.html#enumerate + +# %% id="Zq8dHhH-Y-Cx" +example_list = ['Colorless', 'green', 'ideas', 'sleep', 'furiously'] + +list(enumerate(example_list)) + +# %% [markdown] id="LMPR_En8Zhn7" +# This can be useful when you are looping over a sequence, and you need not only the values but also their index in the sequence: + +# %% id="ADqR2KG2Zdkc" +for index, value in enumerate(example_list): + print('Word number', index, 'is', value) + +# %% [markdown] id="cSdPo0RGbvre" +# For comparison, this is what the above loop would look like without `enumerate`: + +# %% id="tEOM7Iwwbz9r" +index = 0 +for value in example_list: + print('Word number', index, 'is', value) + index = index + 1 + + +# %% [markdown] id="vX59S1uDaBLI" +# ### `filter` +# +# The built-in [`filter`][filter] accepts a function and an iterable. It passes each value in the iterable to the function in a separate call. It returns a generator with only the values in the input sequence for which the function returned `True`. +# +# [filter]: https://docs.python.org/3/library/functions.html#filter + +# %% id="YgQ2RCRbbJDY" +def odd(number): + return number % 2 == 1 + +fibonacci_10 = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] + +list(filter(odd, fibonacci_10)) + +# %% [markdown] id="30NBqwSZbqee" +# For comparison, this is what the last line would look like without `filter`: + +# %% id="UuzJGcjOcOL8" +result_list = [] +for number in fibonacci_10: + if odd(number): + result_list.append(number) +result_list + + +# %% [markdown] id="81vNGdqgc1PI" +# ### `map` +# +# The built-in [`map`][map] accepts a function and an iterable. It passes each value in the iterable as the first argument to the function in a separate call. It returns a generator with the return values of each of those calls. +# +# [map]: https://docs.python.org/3/library/functions.html#map + +# %% id="Gdw4kFoCeQ4x" +def square(number): + return number ** 2 + +list(map(square, range(10))) + +# %% [markdown] id="CMWKdfZued1f" +# For comparison, code without `map` that produces the same output as the last line: + +# %% id="I13FNH_ZeptA" +result_list = [] +for number in range(10): + result_list.append(square(number)) +result_list + +# %% [markdown] id="S5NtKmJ21L_u" +# You can also pass multiple iterables to `map`. In that case, `map` will take one value from each iterable per iteration and pass the value from the first iterable as the first argument to the function, the corresponding value from the second iterable as the second argument, and so on. In the following example, we use a [format string](#scrollTo=Format_strings). + +# %% id="5W6NwA8D2Kz3" +sentence = '{} {} {} {}.'.format + +# The following lists have been shuffled. +# For fun, you can try reordering them so the correct words +# from each list match up again. :-) +# (But run the code first so you know what it does.) +properties = ['Gentle', 'Playful', 'Stubborn', 'Thirsty'] +people = ['camels', 'plumbers', 'giants', 'children'] +verbs = ['tighten', 'devour', 'ruin', 'capture'] +objects = ['nightmares', 'massage chairs', 'cactuses', 'rusty fittings'] + +phrases = map(sentence, properties, people, verbs, objects) +for phrase in phrases: + print(phrase) + +# %% [markdown] id="G7fup_SyBode" +# Without `map` (and without `enumerate` or `range`), the last loop would have looked like this instead: + +# %% id="9a1XKPuFBtpF" +index = 0 +for prop in properties: + group = people[index] + verb = verbs[index] + obj = objects[index] + print(sentence(prop, group, verb, obj)) + index = index + 1 + +# %% [markdown] id="9DCBUR61DNRr" +# If you pass iterables of unequal lengths, `map` will stop at the end of the shortest iterable. In the next subsection, we will take a quick look at how this can sometimes be useful. + +# %% id="pR-4fFPZDMOi" +# operator.mul is a function that multiplies two numbers. It +# does exactly the same thing as the `*` operator, but as a +# function so you can pass it as an argument to other functions. +# More about the operator module in the next subsection. +from operator import mul + +small = [1, 2, 3] +large = [5, 7, 11, 13, 17, 19, 23, 29] + +list(map(mul, small, large)) + +# %% [markdown] id="yBYdJR9VHLQU" +# ### More built-in functions +# +# `range`, `enumerate`, `filter` and especially `map` are the functions on iterables that you'll be using the most. There are however more built-in functions on iterables worth knowing about. Below, we briefly mention a few. +# +# - [`all`](https://docs.python.org/3/library/functions.html#all) consumes an iterable. If the sequence contains at least one `False` value, it returns `False`. Otherwise it returns `True` (including when it is empty). It's like the `and` operator, but operating on an arbitrary number of operands instead of exactly two. +# - [`any`](https://docs.python.org/3/library/functions.html#any) is the exact opposite of `all`: it returns `True` if the sequence contains at least one `True` value, otherwise `False`. Can be thought of as the "long form" of the `or` operator. +# - [`len`](https://docs.python.org/3/library/functions.html#len) can tell you the length of lists, strings, and some other iterables that can "know" their size in advance, including `range`. +# - [`list`](https://docs.python.org/3/library/functions.html#func-list), as we have seen in previous examples, can store the values from any iterable into a list. +# - [`max`](https://docs.python.org/3/library/functions.html#max) can be passed a single iterable argument, in which case it will return the maximum value of the sequence. You can however also pass multiple arguments, in which case it will simply compare them directly (e.g. `max(range(10))` will return `9` and `max(3, 4)` will return `4`). Likewise for [`min`](https://docs.python.org/3/library/functions.html#min). +# - [`str.join`](https://docs.python.org/3/library/stdtypes.html#str.join), which I covered in more detail in [Strings](#scrollTo=Strings), can take the strings that it will glue together from any iterable sequence. +# - [`sum`](https://docs.python.org/3/library/functions.html#sum), as the name suggests, will return the sum of all values in an iterable sequence. + +# %% [markdown] id="LugfHklV0b4C" +# ### Operators +# +# Given two lists of numbers, we might want to create a third list where each element is the sum of the correponding elements of the first two lists. We cannot pass the `+` operator to `map`, because the `+` operator is not a function: + +# %% id="ddcs1QaK1APC" +first_list = [1, 2, 3] +second_list = [7, 7, 5] + +list(map(+, first_list, second_list)) + +# %% [markdown] id="Ix-TPRvY1Pc-" +# Fortunately, the [`operator`](https://docs.python.org/3/library/operator.html) standard module exports function versions of most operators, so we can do this instead: + +# %% id="UG_UUx8S1jQw" +from operator import add + +first_list = [1, 2, 3] +second_list = [7, 7, 5] + +list(map(add, first_list, second_list)) + +# %% [markdown] id="ezmNWLLM2G4w" +# The operators that you would most likely use this way, and their corresponding functions exported from `operator`, are the following (full list [here](https://docs.python.org/3/library/operator.html#mapping-operators-to-functions)): +# +# `+` - `add` (for adding numbers) +# +# `+` - `concat` (for concatenating strings or lists) +# +# `-` - `neg` (unary minus to flip the sign of a number) +# +# `-` - `sub` (binary minus to subtract two numbers) +# +# `in` - `contains` (for checking whether a value appears in an iterable) +# +# `*` - `mul` +# +# `/` - `truediv` (`//` is `floordiv`) +# +# `%` - `mod` +# +# `**` - `pow` +# +# `<` - `lt` +# +# `>` - `gt` +# +# `==` - `eq` +# +# `!=` - `ne` + +# %% [markdown] id="0SMYES0-gyBX" +# ### Bound methods +# +# In the `map` subsection, I used an [example](#scrollTo=5W6NwA8D2Kz3&line=1&uniqifier=1) with the notation `'{} {} {} {}.'.format`. I stored that in a variable and then passed that as a function to `map`. It turns out this is a general thing we can do in more situations, so let's briefly touch on how this works. +# +# The essence is that + +# %% id="51cj58Pdogj_" +'{} {} {} {}.'.format(1, 2, 3, 4) + +# %% [markdown] id="O-0kegzaonFi" +# is equivalent to + +# %% id="GqxgL5Rgorx3" +str.format('{} {} {} {}.', 1, 2, 3, 4) + +# %% [markdown] id="3DWOZQHKpClX" +# We generally use the first form because it is more convenient, but Python is actually translating it to the second form behind our backs. The [format string](#scrollTo=Format_strings) `'{} {} {} {}.'` is being passed as the first argument to the function `str.format` in both cases. +# +# If we do `'{} {} {} {}.'.format` without actually calling the function and passing an argument list, Python understands that we want to use `'{} {} {} {}.'` as the first argument when we later make the call. It returns a special, *bound* version of `str.format` that already has our format string filled in, so we only need to supply the remaining arguments. This is a special form of *partial application* (we will see the more general form of partial application in the [next subsection](#scrollTo=partial)). Python's support for this special form has something to do with classes and objects, which you can optionally read more about in [a later section](#scrollTo=Classes_and_objects). +# +# With this theory out of the way, let's look at a couple more examples of how we can use both bound and unbound functions in `map` and similar functions. We use [string](#scrollTo=Strings) and [dictionary](#scrollTo=Dictionaries) functions in this example. + +# %% id="xSptat6auBDW" +# We can map the unbound str.lower to lowercase a sequence of strings. +strings = ['Hawaii', 'Iceland', 'Hokkaido', 'Vanuatu'] +print(list(map(str.lower, strings))) + +# We can filter by the bound dict.get to check for associated values. +topography = { + 'Iceland': 'volcanic', + 'Vanuatu': 'Melanesia', +} +# Give me only the islands I know something about. +print(list(filter(topography.get, strings))) + +# %% [markdown] id="EqorhEmy6pxq" +# With bound methods, we can achieve ultimate minimalism in our [example](#scrollTo=fwTdCa4QSOuv&line=1&uniqifier=1) from the format strings section, repeated here: + +# %% id="Q49H5CvR7DbK" +YELL_FORMAT = 'Go go {}!!!' + +def yell(name): + return YELL_FORMAT.format(name) + +yell('Jelte') + +# %% [markdown] id="kJnvBskM7GvW" +# because we can suffice with this: + +# %% id="BHiKKKM77JKL" +yell = 'Go go {}!!!'.format + +yell('Jelte') + + +# %% [markdown] id="XO0Q3vhf74Nd" +# ### `itertools` and `functools` +# +# The [`itertools`](https://docs.python.org/3/library/itertools.html) and [`functools`](https://docs.python.org/3/library/functools.html) standard modules let you turn your iterable-fu up to 11. Most of the contents of these modules are a bit advanced, but there are a couple of tricks in there that might be useful during the final exercise. We quickly illustrate them below. + +# %% [markdown] id="ucNV6xs0Tr8x" +# #### `repeat` +# +# [`itertools.repeat`](https://docs.python.org/3/library/itertools.html#itertools.repeat), as the name suggests, will keep repeating a value that you specify. *Forever*. That means you should definitely not try to loop over it! However, you can use it when you need to `map` a function that takes multiple arguments, where some arguments come from a (finite) iterable sequence while at least one argument is the same every time. Consider the following example code, which prints a triangle of stars: + +# %% id="HbbMbbNvckz7" +def centered_stars(center, width): + padding = center - width // 2 + return ' ' * padding + '*' * width + +lines = [] +for width in range(1, 6, 2): + lines.append(centered_stars(2, width)) + +print('\n'.join(lines)) + +# %% [markdown] id="YHOPuLOwh3Qx" +# We can replace the loop by an expression using `map` and `repeat`: + +# %% id="I8NMn7KUh-3S" +from itertools import repeat + +lines = map(centered_stars, repeat(2), range(1, 6, 2)) + +print('\n'.join(lines)) + +# %% [markdown] id="PW2498IlmijJ" +# #### `partial` +# +# [`functools.partial`](https://docs.python.org/3/library/functools.html#functools.partial) takes a function plus any number of other arguments, and then returns a new version of the function to which those arguments have already been applied. + +# %% id="i_Pjs-rGnfH2" +from functools import partial + +# center_2_stars is a version of centered_stars when the first +# parameter (`center`) is fixed to the value 2. This version +# accepts only one argument, `width`. +center_2_stars = partial(centered_stars, 2) + +center_2_stars(3) + +# %% [markdown] id="yCkaJitHnrXk" +# While `functools.partial` does not operate on iterables by itself, it can be really useful when you want to adjust functions before you pass them to `filter`, `map` and the like. We could have used it instead of `itertools.repeat` to eliminate the loop from our triangle example: + +# %% id="jjr_D7jpoYul" +lines = map(center_2_stars, range(1, 6, 2)) + +print('\n'.join(lines)) + + +# %% [markdown] id="IRRPl6piyQn8" +# `partial` also works with keyword arguments. In some cases, this makes it possible to partially apply arguments out of order. This doesn't work for the operators, but there are some workarounds possible. Consider writing a function that subtracts `3` from whatever number you pass to it: + +# %% id="WT3OJFSr18MW" +def minus_3(number): + return number - 3 + +minus_3(4) + +# %% [markdown] id="MVmN8nSt2Ow1" +# It would be nice if we could skip writing a function ourselves and instead just combine `operator.sub` with `functools.partial`. + +# %% id="DK8Y4dWHzY_J" +from operator import sub +from functools import partial + +minus_3 = partial(sub, b=3) + +minus_3(4) + +# %% [markdown] id="pQpjPTbb2oNd" +# The above code doesn't work, but we can avoid the problem by adding `-3` instead of subtracting `+3`: + +# %% id="H3MwPyuF27vg" +from operator import add +from functools import partial + +minus_3 = partial(add, -3) + +minus_3(4) + +# %% [markdown] id="-LH3TiwCpNbp" +# #### `reduce` +# +# [`functools.reduce`](https://docs.python.org/3/library/functools.html#functools.reduce) is for when you want to combine (or *reduce*) all values from a sequence to a single value. It accepts a function, an iterable and an optional starting value. If you don't supply a starting value, it will use the first value in the sequence instead. +# +# `reduce` keeps a work-in-progress value of sorts, which is often called the *accumulator*. The accumulator is initially set to the starting value. For every remaining value in the iterable sequence, `reduce` calls the function with two arguments: the accumulator and the value itself. The return value from the function becomes the new accumulator. After the last value in the sequence, the latest accumulator is returned as the final result. +# +# For illustration, here is how you might use `reduce` to reverse a string: + +# %% id="kN5Uw_iB6QE6" +from functools import reduce + +def prepend_letter(accumulator, next_letter): + return next_letter + accumulator + +def reverse_string(string): + # In this case, we reduce a sequence of characters to a new string. + return reduce(prepend_letter, string) + +reverse_string('abcdef') + +# %% [markdown] id="AxfCOlrU7H75" +# And here is how we could write our own implementations of the built-in functions `max` and `sum` using `reduce`: + +# %% id="P1CltqPc7UvG" +from functools import reduce +from operator import add + +def greater(a, b): + if a < b: + return b + else: + return a + +def max(iterable): + return reduce(greater, iterable) + +def sum(iterable): + return reduce(add, iterable) + +numbers = [3, 5, 4] + +print('max:', max(numbers)) +print('sum:', sum(numbers)) + +# %% [markdown] id="_zR8E_94YHCv" +# ## Calculations +# +# As we have written in the course manual, Python is "batteries included"—it comes with a lot of functionality out of the box. This is certainly also the case for numerical computations and even some basic statistics. We highlight some common tools below. +# +# - The built-in functions [`abs`][abs], [`max`][max], [`min`][min], [`pow`][pow], [`round`][round] and [`sum`][sum] do exactly what the names suggest. You can also use the `**` operator for powers. +# - The built-in [`range`][range] function, which we have seen before, lets you generate linear series of numbers. +# - The [`math`][math] standard module contains a wealth of general mathematical functions and constants, such as [`log`][math.log], [`sqrt`][math.sqrt], [`cos`][math.cos], [`pi`][math.pi] and [`tau`][math.tau]. +# - The [`random`][random] standard module covers most random number generating needs, as well as random shuffling and sampling. +# - The [`statistics`][statistics] standard module includes the common staples of statistical analysis, such as [`mean`][statistics.mean], [`median`][statistics.median], [`mode`][statistics.mode], [`stdev`][statistics.stdev], [`variance`][statistics.variance], [`covariance`][statistics.covariance], [`correlation`][statistics.correlation] and even a simple [`linear_regression`][statistics.linear_regression]. ~~Unfortunately, however, the latter three are not available in Google Colab, because they were only recently added to the Python standard library and Colab is not running the latest ("bleeding edge") version of Python. The next two subsections offer some alternatives.~~ +# +# A complete overview of numerical functionality in the Python standard library can be found [here][python-numeric]. +# +# [abs]: https://docs.python.org/3/library/functions.html#abs +# [max]: https://docs.python.org/3/library/functions.html#max +# [min]: https://docs.python.org/3/library/functions.html#min +# [pow]: https://docs.python.org/3/library/functions.html#pow +# [range]: https://docs.python.org/3/library/functions.html#func-range +# [round]: https://docs.python.org/3/library/functions.html#round +# [sum]: https://docs.python.org/3/library/functions.html#sum +# [math]: https://docs.python.org/3/library/math.html +# [math.log]: https://docs.python.org/3/library/math.html#math.log +# [math.sqrt]: https://docs.python.org/3/library/math.html#math.sqrt +# [math.cos]: https://docs.python.org/3/library/math.html#math.cos +# [math.pi]: https://docs.python.org/3/library/math.html#math.pi +# [math.tau]: https://docs.python.org/3/library/math.html#math.tau +# [random]: https://docs.python.org/3/library/random.html +# [statistics]: https://docs.python.org/3/library/statistics.html +# [statistics.mean]: https://docs.python.org/3/library/statistics.html#statistics.mean +# [statistics.median]: https://docs.python.org/3/library/statistics.html#statistics.median +# [statistics.mode]: https://docs.python.org/3/library/statistics.html#statistics.mode +# [statistics.stdev]: https://docs.python.org/3/library/statistics.html#statistics.stdev +# [statistics.variance]: https://docs.python.org/3/library/statistics.html#statistics.variance +# [statistics.covariance]: https://docs.python.org/3/library/statistics.html#statistics.covariance +# [statistics.correlation]: https://docs.python.org/3/library/statistics.html#statistics.correlation +# [statistics.linear_regression]: https://docs.python.org/3/library/statistics.html#statistics.linear_regression +# [python-numeric]: https://docs.python.org/3/library/numeric.html + +# %% colab={"base_uri": "https://localhost:8080/"} id="xoBLhOpvmu2P" outputId="5e3ccc4f-d8e3-4102-a3bf-517a8ccddfbc" executionInfo={"status": "ok", "timestamp": 1701791082060, "user_tz": -60, "elapsed": 322, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# !python --version + +# %% [markdown] id="rKxMNbMMuMCw" +# ### Computing covariance and correlation yourself +# +# Given two equally long sequences of numbers and their averages (which you might have already computed because you needed them elsewhere), you can compute the sample covariance and correlation as follows using [iterables](#scrollTo=Iterables): + +# %% id="moprSI-g90tZ" +from itertools import repeat +from operator import sub, mul +from statistics import mean, stdev + +def differences(series, average): + return map(sub, series, repeat(average)) + +def covariance(series1, series2, average1=None, average2=None): + differences1 = differences(series1, average1 or mean(series1)) + differences2 = differences(series2, average2 or mean(series2)) + products = map(mul, differences1, differences2) + return sum(products) / (len(series1) - 1) + +def correlation(series1, series2, average1=None, average2=None): + '''Pearson's correlation coefficient.''' + cov = covariance(series1, series2, average1, average2) + stdev1 = stdev(series1, average1) + stdev2 = stdev(series2, average2) + return cov / (stdev1 * stdev2) + +column1 = [1, 2, 3, 4, 5, 6, 7] +column2 = [4, 5, 6, 5, 5, 8, 9] +column3 = [8, 7, 6, 5, 4, 3, 2] + +print('covariance 1-2:', covariance(column1, column2)) +print('correlation 1-2:', correlation(column1, column2)) +print('correlation 2-1:', correlation(column2, column1)) +print('correlation 1-3:', correlation(column1, column3)) +print('correlation 2-3:', correlation(column2, column3)) + +# %% [markdown] id="JYTbShRDBoS1" +# ### Using covariance and correlation from an external package +# +# [pandas](https://pandas.pydata.org/pandas-docs/stable/index.html) provides implementations of [covariance](https://pandas.pydata.org/pandas-docs/stable/user_guide/computation.html#covariance), [correlation](https://pandas.pydata.org/pandas-docs/stable/user_guide/computation.html#correlation) and many other statistical functions. If you want to do serious statistics, you should probably use pandas or some other third-party package that can do the heavy lifting for you. If you choose this path, head over to [dataframes](#scrollTo=pandas_dataframes_and_read_csv) first. + +# %% [markdown] id="F6mIIM3zLw1p" +# ## Classes and objects +# +# For the final exercise, you do not need to create your own classes. However, since you may encounter the terminology and the notation when using third-party packages, here is a *very* quick explanation. +# +# An **object** is a value with internal structure. For example, I might have a variable with the name `jack` that holds a description of my friend Jack. The parts of that description are called its **attributes**. In this example, `jack.name` might hold the string `'Jack'` and `jack.phone` might hold the string `'+31612345678'` (not his real phone number ;-). `jack` is the object and `name` and `phone` are its attributes. +# +# Attributes can hold all types of values, including functions. In the latter case, the attribute is also called a **method**. For example, `jack.call()` might dial Jack's number. Attributes might also themselves be objects with their own nested attributes, so you can have chains of names connected with dots, for example `house.kitchen.sink`. In the latter dotted name, `kitchen` and `sink` are both attributes, and `house` and `kitchen` must both be objects, though `sink` might be an object as well. +# +# Whenever you see two names connected with a dot, the left one must be an object. You have already encountered a few types of objects. Every list has an `.append()` method, so lists are objects. When you call `csv.reader()`, the `csv` module is an object (though we usually don't use the words "attribute" and "method" to refer to the parts of a module; we rather say that `csv.reader` is a *qualified name*). In fact, *nearly everything* in Python is an object; this is why it is called an *object-oriented* programming language. +# +# Objects are generally created using a **class**. You can think of a class as a template for creating objects of a particular shape. For example, our `jack` object might have been created using the `Person` class. We say that `jack` is an **instance** of `Person` and also that we **instantiated** `Person` when we created `jack`. Typically, you can expect all instances of a class to have the same attributes and methods. Packages often organize their documentation by class, listing the methods of its instances and their usage. +# +# Instantiating a class looks exactly like calling a function and storing the return value in a variable. The only visible clue that you're instantiating a class rather than calling a function, is that classes usually have a name starting with an uppercase letter: +# +# ```py +# jack = Person(name='Jack') +# ``` +# +# There is no danger in thinking of a class instantiation as a function call, or in instantiating a class without knowing it because its name starts with a lowercase letter. In reality, however, a class is not a function but an object! + +# %% [markdown] id="MoyqHwBhvBTH" +# ## Working with times and calendar dates +# +# The [`datetime`][datetime] standard module provides tools for working with dates and times. It exports the `date`, `time` and `datetime` [classes](#scrollTo=Classes_and_objects), which let you represent a date, a time or both, exactly as the names suggest. +# +# [datetime]: https://docs.python.org/3/library/datetime.html + +# %% [markdown] id="W5ZlB-uXkC6S" +# ### Parsing dates from text +# +# If you are working with dates for the final course exercise, it is most likely that you are extracting date strings from a CSV. Such a string might, for example, look like `'2021/11/15'`. In some cases, you may need to extract specific information from those dates, such as the year, month, hour or even weekday. For such use cases, it is advisable to convert the date string to a `date`, `time` or `datetime` object first by *parsing* it. +# +# The [`datetime.strptime`][datetime.strptime] function can do this job for you. It takes two parameters: the date string that needs to be parsed, and a second string that describes the *format* of the date. Our example above consisted of four digits that identify the year, a slash, two digits that identify the month, another slash, and finally two digits that identify the day of the month. We write that as `'%Y/%m/%d'`. In such a format string, a `%` with a letter after it is a placeholder for a particular piece of the date or time, while all other characters simply represent themselves. You can find a list of all available placeholders [here][datetime-formats]. +# +# [datetime.strptime]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior +# [datetime-formats]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes + +# %% id="cOo_KnhWsR2m" +from datetime import datetime as dt + +yesterday_str = '2021/11/15' +date_format = '%Y/%m/%d' + +yesterday_obj = dt.strptime(yesterday_str, date_format) +print('datetime:', yesterday_obj) + +# dt.strptime always returns a full datetime, even if the input +# string and the format string contain only a date or only a time. +# You can reduce the datetime object to just a date or just a time +# by calling a method of the same name: +print('date: ', yesterday_obj.date()) +print('time: ', yesterday_obj.time()) + +# %% [markdown] id="iDp5QxvpxZIo" +# ### Extracting information from date and time objects +# +# Once you have a `date`, `time`, or `datetime` object, extracting information from it is very easy, as we demonstrate below. [`datetime`][datetime.datetime] objects have all date-related attributes and methods of `date` as well as all time-related attributes and methods of `time`. You can find the most important attributes [here][datetime-attributes] and the most important methods [here][datetime-methods] (you can also scroll up from the latter link for some additional methods related to time zones). +# +# [datetime.datetime]: https://docs.python.org/3/library/datetime.html#datetime-objects +# [datetime-attributes]: https://docs.python.org/3/library/datetime.html#datetime.datetime.year +# [datetime-methods]: https://docs.python.org/3/library/datetime.html#datetime.datetime.utcoffset + +# %% id="M9pQ2otg0EQU" +# Year, month etcetera attributes are all represented as numbers. +print('year: ', yesterday_obj.year) +print('month: ', yesterday_obj.month) +print('hour: ', yesterday_obj.hour) + +# Python starts the week on Monday and starts numbering at zero. +print('weekday: ', yesterday_obj.weekday()) +# The ISO 8601 standard also starts on Monday, but starts numbering at one. +print('isoweekday:', yesterday_obj.isoweekday()) + +# %% [markdown] id="GLBue3palj8M" +# ## Sorting +# +# Python has a built-in function [`sorted`][sorted], which can sort anything that you can loop over (including the key-value pairs of a [dictionary](#scrollTo=Dictionaries)). It always returns a list with the result. +# +# By default, it will sort ascending, i.e., by increasing order of magnitude. Numbers are compared by value, strings are compared lexicographically (with all uppercase letters sorting before all lowercase letters), and lists are compared by the first item, with subsequent items being used as tie breakers if previous items compared equal. +# +# [sorted]: https://docs.python.org/3/library/functions.html#sorted +# [sorting-howto]: https://docs.python.org/3/howto/sorting.html#sortinghowto + +# %% id="FIw_4XyNn9UK" +list_of_numbers = [5, 3, 4] +list_of_strings = ['Good', 'day', 'to', 'you'] +list_of_lists = [ + [6, 'zucchini'], + [5, 'eggs'], + [6, 'broccoli'], +] + +print(sorted(list_of_numbers)) +print(sorted(list_of_strings)) +print(sorted(list_of_lists)) + +# %% [markdown] id="_uzxS1S1setr" +# ### Sorting in descending order +# +# If you want to sort descending instead, pass the named argument `reverse=True`. + +# %% id="e2RSQaXFszpT" +sorted(list_of_numbers, reverse=True) + +# %% [markdown] id="YKx3ObxltgXz" +# ### Custom comparison +# +# If you want `sorted` to perform a different kind of comparison in order to decide on the order of the items, you can pass a function as the named `key` argument. This function should take one item as its parameter and return a new value that `sorted` will use for the comparison instead. +# +# Below, we use the function `str.lower` to do a case-insensitive sort: + +# %% colab={"base_uri": "https://localhost:8080/"} id="5_w-T2ryuknR" outputId="d9f68252-3e93-48c0-fdf9-f001a107fdf2" +sorted(list_of_strings, key=str.lower) + +# %% [markdown] id="92yLh2OWvO-q" +# The [`operator`][operator] standard module exports several useful functions that let you create instant simple functions for the purpose of sorting. Most importantly, [`itemgetter`][operator.itemgetter] lets you sort sequences by a different item than the first and [`attrgetter`][operator.attrgetter] lets you sort [objects](#scrollTo=Classes_and_objects) by a particular attribute. There is also [`methodcaller`][operator.methodcaller] which lets you sort by the result of a method call. +# +# Below, we use `itemgetter` to sort the key-value pairs of a [dictionary](#scrollTo=Dictionaries) by value instead of by key: +# +# [operator]: https://docs.python.org/3/library/operator.html#module-operator +# [operator.itemgetter]: https://docs.python.org/3/library/operator.html#operator.itemgetter +# [operator.attrgetter]: https://docs.python.org/3/library/operator.html#operator.attrgetter +# [operator.methodcaller]: https://docs.python.org/3/library/operator.html#operator.methodcaller + +# %% id="HigJMisJydem" +from operator import itemgetter + +example_dict = {'banana': 'yellow', 'cherry': 'sweet', 'date': 'wrinkly'} + +sorted(example_dict.items(), key=itemgetter(1)) + +# %% [markdown] id="P16V-uttzQXw" +# And below, we use `attrgetter` to sort [dates](#scrollTo=Working_with_times_and_calendar_dates) by month: + +# %% id="jG1RMiBzzpky" +from operator import attrgetter +from datetime import date + +list_of_dates = [ + date(year=2021, month=11, day=16), + date(year=2022, month=3, day=17), + date(year=2020, month=5, day=18), +] + +sorted(list_of_dates, key=attrgetter('month')) + +# %% [markdown] id="7rsvpuMn1kSl" +# ## `pandas` dataframes and `read_csv` +# +# [pandas][pandas] is a package that provides general data structures and data analysis tools for Python. If you venture into statistics or datamining with Python, it is likely that you will sooner or later encounter [`pandas.DataFrame`][pandas.DataFrame] (which is a [class](#scrollTo=Classes_and_objects)). It holds tabular data in which each column might have a different type. Other packages often use this data structure, too. +# +# If you encounter `pandas` during the final course exercise, it is probably because you are using a function from a third-party package that expects you to pass in the data as a `DataFrame`. In this case, it is useful to know that `pandas` provides the [`read_csv`][pandas.read_csv] function. It accepts a file or file name as its first parameter and returns the contents of the CSV file as a `DataFrame`. You can then pass this object to the function that expects a `DataFrame`. The `read_csv` function also accepts a couple of optional parameters that let you specify details about the way the CSV file is formatted, its columns, etcetera. +# +# In the example code below, we illustrate how you can create a `DataFrame` using `read_csv`. Note that we define a [cross-platform path](#scrollTo=Cross_platform_file_paths) using [`os.path`](https://docs.python.org/3/library/os.path.html). In the [next section](#scrollTo=Clustering_with_scikit_learn), we illustrate how the `data` object may be passed to a third-party analysis function. +# +# [pandas]: https://pandas.pydata.org/pandas-docs/stable/index.html +# [pandas.DataFrame]: https://pandas.pydata.org/pandas-docs/stable/user_guide/dsintro.html#dataframe +# [pandas.read_csv]: https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#io-read-csv-table + +# %% colab={"base_uri": "https://localhost:8080/", "height": 142} id="1m-b-UVLF_rM" outputId="0b542154-2e25-4f71-c445-856836f0a749" +#requires pandas + +import os.path as op +import pandas + +file_path = op.join('sample_data', 'california_housing_test.csv') + +data = pandas.read_csv(file_path) +# `data` is an instance of pandas.DataFrame with several columns +# containing geographic center points of neighborhoods in +# California as well as demographics about the inhabitants and +# their houses. + +# You may sometimes want to pass only a subset of the dataset +# to a function. For this purpose, dataframes can be sliced in +# a way that is similar to lists. The following example will +# contain only the 'total_rooms' column: +data.loc[:, 'total_rooms'] + +# The following example will include two columns, in a different +# order than they appeared in the CSV: +data.loc[:, ['households', 'population']] +# You can also use this notation if you want to use a subset of +# multiple columns. + +# For slicing rows by position, you use the `iloc` attribute +# instead of `loc`: +data.iloc[0:3] + +# Both ways of slicing can be combined: +data.loc[:, ['households', 'population']].iloc[0:3] + +# %% [markdown] id="l0NeIki5L-LI" +# ## Visualizing data with `matplotlib` +# +# [`matplotlib`](https://matplotlib.org) is a comprehensive and easy to use package for creating data graphics. It is preinstalled on Google Colab, so you can use it right away. Here is a quick example: + +# %% id="ZBxC0c32NN7v" +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np + +fig, ax = plt.subplots() # Create a figure containing a single axes. +ax.plot([1, 2, 3, 4], [1, 4, 2, 3]); # Plot some data on the axes. + +# %% [markdown] id="RIGMSlw7NhnT" +# The above example was shamelessly copied from `matplotlib`'s [Quick start guide](https://matplotlib.org/stable/tutorials/introductory/quick_start.html), which is the best place to start learning how to use the package yourself. + +# %% [markdown] id="EYVhwfCP2oEK" +# ## Clustering with scikit-learn +# +# [scikit-learn][sklearn] is a package that provides many data mining and machine learning tools, including cluster analysis. You can find the documentation [here][sklearn.cluster]. We give a very minimal example of hierarchical clustering with Ward linkage below. You can find a more extensive example [here][sklearn-example]. Note that we are using the `data` object, which is a `pandas.DataFrame`, from the [previous section](#scrollTo=pandas_dataframes_and_read_csv). +# +# [sklearn]: https://scikit-learn.org/stable/index.html +# [sklearn.cluster]: https://scikit-learn.org/stable/modules/clustering.html +# [sklearn-example]: https://scikit-learn.org/stable/auto_examples/cluster/plot_digits_linkage.html#sphx-glr-auto-examples-cluster-plot-digits-linkage-py + +# %% colab={"base_uri": "https://localhost:8080/"} id="upo6gPd46sVt" outputId="6938e3d2-8104-4f8c-fca6-f10b70d36dbb" +#requires sklearn + +from sklearn.cluster import AgglomerativeClustering + +# We start by creating the object that will *eventually* contain +# the clustering. +clustering = AgglomerativeClustering() +# Next, we feed in the data through the `fit` method. We will +# cluster the neighborhoods by geographical location. +clustering.fit(data.loc[:, ['latitude', 'longitude']]) +# The clustering is now established. We illustrate below by +# printing the cluster assignment of the first 20 rows in the +# dataset. +print(clustering.labels_[:20]) +# In a more serious application, we would probably inspect the +# cluster dendrogram or plot the data with a coloring to indicate +# the cluster assignment of each data point. The scikit-learn +# documentation explains how to do such things. + +# %% [markdown] id="hofJ0jOJ-fKo" +# ## Parsing XML and HTML +# +# In the humanities, data are often stored in XML format, for example [TEI](https://en.wikipedia.org/wiki/Text_Encoding_Initiative). XML is a more complicated format than CSV, so extracting information from it is also a bit more involved. +# +# Another, similar format is HTML, the language that web pages are written in. You may encounter this when scraping the web; more about this in [the next section](#scrollTo=lEy6U7SF-uFe). +# +# XML and HTML can both be parsed (and written) using [Beautiful Soup](https://beautiful-soup-4.readthedocs.io/en/latest/). It is preinstalled in Google Colab and can be imported by the name `bs4`. + +# %% [markdown] id="lEy6U7SF-uFe" +# ## Fetching information from the internet +# +# [Requests](https://docs.python-requests.org/en/latest/) lets you fetch information from the internet, similar to how your browser might view or download information from a particular web address. It is a Python alternative to the command line program `curl` or the graphical program Postman, if you happen to know either. +# +# The package is preinstalled on Google Colab and can be imported as `requests`. How to do this is explained in the [Quickstart](https://docs.python-requests.org/en/latest/user/quickstart/). You can use Beautiful Soup for parsing HTML and XML responses, as explained in [the previous section](#scrollTo=hofJ0jOJ-fKo&line=7&uniqifier=1). + +# %% [markdown] id="1ft-t0CGvOl2" +# ## Network visualizations +# +# [This article](https://towardsdatascience.com/visualizing-networks-in-python-d70f4cbeb259) lists four packages that you could use to visualize networks. Most of them can interface with [pandas](#scrollTo=pandas_dataframes_and_read_csv). + +# %% [markdown] id="Fd6n2BgZwlUi" +# ## Natural language processing +# +# While there are more options, you should probably start by taking a look at [NLTK](https://www.nltk.org/) if you want to do any form of natural language processing, for example: +# +# - tokenization +# - stopword removal (`corpus` module) +# - stemming +# - parsing +# - sentiment analysis diff --git a/requirements.in b/requirements.in new file mode 100644 index 0000000..9926b8c --- /dev/null +++ b/requirements.in @@ -0,0 +1,2 @@ +jupytext +notebook \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c148201 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,335 @@ +# +# This file is autogenerated by pip-compile with Python 3.8 +# by the following command: +# +# pip-compile +# +anyio==4.5.0 + # via + # httpx + # jupyter-server +appnope==0.1.4 + # via + # ipykernel + # ipython +argon2-cffi==23.1.0 + # via jupyter-server +argon2-cffi-bindings==21.2.0 + # via argon2-cffi +arrow==1.3.0 + # via isoduration +asttokens==2.4.1 + # via stack-data +async-lru==2.0.4 + # via jupyterlab +attrs==24.2.0 + # via + # jsonschema + # referencing +babel==2.16.0 + # via jupyterlab-server +backcall==0.2.0 + # via ipython +beautifulsoup4==4.12.3 + # via nbconvert +bleach==6.1.0 + # via nbconvert +certifi==2024.8.30 + # via + # httpcore + # httpx + # requests +cffi==1.17.1 + # via argon2-cffi-bindings +charset-normalizer==3.3.2 + # via requests +comm==0.2.2 + # via ipykernel +debugpy==1.8.6 + # via ipykernel +decorator==5.1.1 + # via ipython +defusedxml==0.7.1 + # via nbconvert +exceptiongroup==1.2.2 + # via anyio +executing==2.1.0 + # via stack-data +fastjsonschema==2.20.0 + # via nbformat +fqdn==1.5.1 + # via jsonschema +h11==0.14.0 + # via httpcore +httpcore==1.0.5 + # via httpx +httpx==0.27.2 + # via jupyterlab +idna==3.10 + # via + # anyio + # httpx + # jsonschema + # requests +importlib-metadata==8.5.0 + # via + # jupyter-client + # jupyter-lsp + # jupyterlab + # jupyterlab-server + # nbconvert +importlib-resources==6.4.5 + # via + # jsonschema + # jsonschema-specifications + # jupyterlab +ipykernel==6.29.5 + # via jupyterlab +ipython==8.12.3 + # via ipykernel +isoduration==20.11.0 + # via jsonschema +jedi==0.19.1 + # via ipython +jinja2==3.1.4 + # via + # jupyter-server + # jupyterlab + # jupyterlab-server + # nbconvert +json5==0.9.25 + # via jupyterlab-server +jsonpointer==3.0.0 + # via jsonschema +jsonschema[format-nongpl]==4.23.0 + # via + # jupyter-events + # jupyterlab-server + # nbformat +jsonschema-specifications==2023.12.1 + # via jsonschema +jupyter-client==8.6.3 + # via + # ipykernel + # jupyter-server + # nbclient +jupyter-core==5.7.2 + # via + # ipykernel + # jupyter-client + # jupyter-server + # jupyterlab + # nbclient + # nbconvert + # nbformat +jupyter-events==0.10.0 + # via jupyter-server +jupyter-lsp==2.2.5 + # via jupyterlab +jupyter-server==2.14.2 + # via + # jupyter-lsp + # jupyterlab + # jupyterlab-server + # notebook + # notebook-shim +jupyter-server-terminals==0.5.3 + # via jupyter-server +jupyterlab==4.2.5 + # via notebook +jupyterlab-pygments==0.3.0 + # via nbconvert +jupyterlab-server==2.27.3 + # via + # jupyterlab + # notebook +jupytext==1.16.4 + # via -r requirements.in +markdown-it-py==3.0.0 + # via + # jupytext + # mdit-py-plugins +markupsafe==2.1.5 + # via + # jinja2 + # nbconvert +matplotlib-inline==0.1.7 + # via + # ipykernel + # ipython +mdit-py-plugins==0.4.2 + # via jupytext +mdurl==0.1.2 + # via markdown-it-py +mistune==3.0.2 + # via nbconvert +nbclient==0.10.0 + # via nbconvert +nbconvert==7.16.4 + # via jupyter-server +nbformat==5.10.4 + # via + # jupyter-server + # jupytext + # nbclient + # nbconvert +nest-asyncio==1.6.0 + # via ipykernel +notebook==7.2.2 + # via -r requirements.in +notebook-shim==0.2.4 + # via + # jupyterlab + # notebook +overrides==7.7.0 + # via jupyter-server +packaging==24.1 + # via + # ipykernel + # jupyter-server + # jupyterlab + # jupyterlab-server + # jupytext + # nbconvert +pandocfilters==1.5.1 + # via nbconvert +parso==0.8.4 + # via jedi +pexpect==4.9.0 + # via ipython +pickleshare==0.7.5 + # via ipython +pkgutil-resolve-name==1.3.10 + # via jsonschema +platformdirs==4.3.6 + # via jupyter-core +prometheus-client==0.21.0 + # via jupyter-server +prompt-toolkit==3.0.48 + # via ipython +psutil==6.0.0 + # via ipykernel +ptyprocess==0.7.0 + # via + # pexpect + # terminado +pure-eval==0.2.3 + # via stack-data +pycparser==2.22 + # via cffi +pygments==2.18.0 + # via + # ipython + # nbconvert +python-dateutil==2.9.0.post0 + # via + # arrow + # jupyter-client +python-json-logger==2.0.7 + # via jupyter-events +pytz==2024.2 + # via babel +pyyaml==6.0.2 + # via + # jupyter-events + # jupytext +pyzmq==26.2.0 + # via + # ipykernel + # jupyter-client + # jupyter-server +referencing==0.35.1 + # via + # jsonschema + # jsonschema-specifications + # jupyter-events +requests==2.32.3 + # via jupyterlab-server +rfc3339-validator==0.1.4 + # via + # jsonschema + # jupyter-events +rfc3986-validator==0.1.1 + # via + # jsonschema + # jupyter-events +rpds-py==0.20.0 + # via + # jsonschema + # referencing +send2trash==1.8.3 + # via jupyter-server +six==1.16.0 + # via + # asttokens + # bleach + # python-dateutil + # rfc3339-validator +sniffio==1.3.1 + # via + # anyio + # httpx +soupsieve==2.6 + # via beautifulsoup4 +stack-data==0.6.3 + # via ipython +terminado==0.18.1 + # via + # jupyter-server + # jupyter-server-terminals +tinycss2==1.3.0 + # via nbconvert +tomli==2.0.1 + # via + # jupyterlab + # jupytext +tornado==6.4.1 + # via + # ipykernel + # jupyter-client + # jupyter-server + # jupyterlab + # notebook + # terminado +traitlets==5.14.3 + # via + # comm + # ipykernel + # ipython + # jupyter-client + # jupyter-core + # jupyter-events + # jupyter-server + # jupyterlab + # matplotlib-inline + # nbclient + # nbconvert + # nbformat +types-python-dateutil==2.9.0.20240906 + # via arrow +typing-extensions==4.12.2 + # via + # anyio + # async-lru + # ipython +uri-template==1.3.0 + # via jsonschema +urllib3==2.2.3 + # via requests +wcwidth==0.2.13 + # via prompt-toolkit +webcolors==24.8.0 + # via jsonschema +webencodings==0.5.1 + # via + # bleach + # tinycss2 +websocket-client==1.8.0 + # via jupyter-server +zipp==3.20.2 + # via + # importlib-metadata + # importlib-resources + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/solutions/01 introduction solutions.ipynb b/solutions/01 introduction solutions.ipynb index 08bee73..2ce2c3c 100644 --- a/solutions/01 introduction solutions.ipynb +++ b/solutions/01 introduction solutions.ipynb @@ -10,11 +10,11 @@ "\n", "## Exercise solutions\n", "\n", - "[Module 1](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)\n", + "[Module 1](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B)\n", "\n", "## CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)" + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)" ] }, { @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -72,7 +72,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -129,7 +129,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -171,7 +171,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -259,18 +259,8 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 223, - "status": "ok", - "timestamp": 1680706039219, - "user": { - "displayName": "Julian Gonggrijp", - "userId": "06467962548183964912" - }, - "user_tz": -120 - }, "id": "-ZuiJ92yNqpi" }, "outputs": [], @@ -291,7 +281,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -613,13 +603,13 @@ "source": [ "## Next module\n", "\n", - "[2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy)" + "[2. Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA) - [solutions](https://colab.research.google.com/drive/1R0_DYzufN6PSKpot8iBmn1Gh6mNSOCy5)" ] } ], "metadata": { "colab": { - "authorship_tag": "ABX9TyOdt4EUgMll2VGl6yXOOeib", + "authorship_tag": "ABX9TyOduKnaBzfyKDbIqW1Og80q", "provenance": [], "toc_visible": true }, diff --git a/solutions/01 introduction solutions.py b/solutions/01 introduction solutions.py index d806144..ee66423 100644 --- a/solutions/01 introduction solutions.py +++ b/solutions/01 introduction solutions.py @@ -16,11 +16,11 @@ # # ## Exercise solutions # -# [Module 1](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) +# [Module 1](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B) # # ## CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # %% [markdown] id="pKIEfocbMaIR" # ## Exercise 1.1: Try it out @@ -55,7 +55,7 @@ # %% [markdown] id="snD4bhFA4VTx" # Normally, a bare value by itself is never shown. It is a special "service" of notebooks that the last value in a code block is shown. This is why we see only `'cherry'` and not `'apricot'` or `'banana'`. -# %% id="-ZuiJ92yNqpi" executionInfo={"status": "ok", "timestamp": 1680706039219, "user_tz": -120, "elapsed": 223, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# %% id="-ZuiJ92yNqpi" # apricot # banana # cherry @@ -132,4 +132,4 @@ # %% [markdown] id="zI0ohEpPUwpC" # ## Next module # -# [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy) +# [2. Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA) - [solutions](https://colab.research.google.com/drive/1R0_DYzufN6PSKpot8iBmn1Gh6mNSOCy5) diff --git a/solutions/02 values and expressions solutions.ipynb b/solutions/02 values and expressions solutions.ipynb index 624a872..c250916 100644 --- a/solutions/02 values and expressions solutions.ipynb +++ b/solutions/02 values and expressions solutions.ipynb @@ -10,13 +10,13 @@ "\n", "### Exercise solutions\n", "\n", - "[Module 2](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)\n", + "[Module 2](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA)\n", "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) - [solutions](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd)" + "Previous module: [1. Introduction](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B) - [solutions](https://colab.research.google.com/drive/13q9wUC6QRT3hDuvLOuctuph9yZYeEvVu)" ] }, { @@ -840,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -968,7 +968,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1311,7 +1311,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1366,13 +1366,13 @@ "source": [ "## Next module\n", "\n", - "[3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI)" + "[3. Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR) - [solutions](https://colab.research.google.com/drive/1-vBxja7MWudomSKEg1NU5D3JO0SEQc3J)" ] } ], "metadata": { "colab": { - "authorship_tag": "ABX9TyPhSPWbSkxQNLvGfLg7Ov8q", + "authorship_tag": "ABX9TyP767ZQu2v3x3Sr9F/yRCM+", "provenance": [], "toc_visible": true }, diff --git a/solutions/02 values and expressions solutions.py b/solutions/02 values and expressions solutions.py index 5874e48..39d6531 100644 --- a/solutions/02 values and expressions solutions.py +++ b/solutions/02 values and expressions solutions.py @@ -16,13 +16,13 @@ # # ### Exercise solutions # -# [Module 2](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) +# [Module 2](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA) # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) - [solutions](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd) +# Previous module: [1. Introduction](https://colab.research.google.com/drive/1KVMLkUIYyUHK3svD9pLfVdhzpdhqIA1B) - [solutions](https://colab.research.google.com/drive/13q9wUC6QRT3hDuvLOuctuph9yZYeEvVu) # %% [markdown] id="pDU1uK2Igmki" # ## Exercise 2.1: Variables and state @@ -383,4 +383,4 @@ # %% [markdown] id="jXSxbjf4q6q5" # ## Next module # -# [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI) +# [3. Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR) - [solutions](https://colab.research.google.com/drive/1-vBxja7MWudomSKEg1NU5D3JO0SEQc3J) diff --git a/solutions/03 conditionals solutions.ipynb b/solutions/03 conditionals solutions.ipynb index c71a850..74608fb 100644 --- a/solutions/03 conditionals solutions.ipynb +++ b/solutions/03 conditionals solutions.ipynb @@ -10,13 +10,13 @@ "\n", "### Exercise solutions\n", "\n", - "[Module 3](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)\n", + "[Module 3](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR)\n", "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy)" + "Previous module: [2. Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA) - [solutions](https://colab.research.google.com/drive/1R0_DYzufN6PSKpot8iBmn1Gh6mNSOCy5)" ] }, { @@ -345,12 +345,12 @@ "- if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`;\n", "- in all remaining cases, print `value`.\n", "\n", - "Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)!" + "Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA#scrollTo=Exercise_2_4_Bonus)!" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -424,13 +424,13 @@ "source": [ "## Next module\n", "\n", - "[4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn)" + "[4. Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt) - [solutions](https://colab.research.google.com/drive/1mf_sQpAVxz8oRbsZnA548cC3TtVaQxmU)" ] } ], "metadata": { "colab": { - "authorship_tag": "ABX9TyN+nbIqsypUJCun9Hzkloun", + "authorship_tag": "ABX9TyNReXh+4a/yYH4EVWiYsm8b", "provenance": [], "toc_visible": true }, diff --git a/solutions/03 conditionals solutions.py b/solutions/03 conditionals solutions.py index f991280..9d873d8 100644 --- a/solutions/03 conditionals solutions.py +++ b/solutions/03 conditionals solutions.py @@ -16,13 +16,13 @@ # # ### Exercise solutions # -# [Module 3](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) +# [Module 3](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR) # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy) +# Previous module: [2. Values and expressions](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA) - [solutions](https://colab.research.google.com/drive/1R0_DYzufN6PSKpot8iBmn1Gh6mNSOCy5) # %% [markdown] id="tvXa9KWXAwge" # ## Exercise 3.1: if/elif/else @@ -151,7 +151,7 @@ # - if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`; # - in all remaining cases, print `value`. # -# Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)! +# Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/1FUicKa-_d5CINVGrHQdwnEpVFZlMbtkA#scrollTo=Exercise_2_4_Bonus)! # %% id="SZGeQtqEhiAK" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680782728257, "user_tz": -120, "elapsed": 233, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="f067c099-61f7-4d68-851d-8e7db89ed04f" value = 9 @@ -194,4 +194,4 @@ # %% [markdown] id="YBC4OfihzFho" # ## Next module # -# [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn) +# [4. Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt) - [solutions](https://colab.research.google.com/drive/1mf_sQpAVxz8oRbsZnA548cC3TtVaQxmU) diff --git a/solutions/04 datastructures solutions.ipynb b/solutions/04 datastructures solutions.ipynb index 927cdb4..e722595 100644 --- a/solutions/04 datastructures solutions.ipynb +++ b/solutions/04 datastructures solutions.ipynb @@ -10,13 +10,13 @@ "\n", "### Exercise solutions\n", "\n", - "[Module 4](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)\n", + "[Module 4](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt)\n", "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI)" + "Previous module: [3. Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR) - [solutions](https://colab.research.google.com/drive/1-vBxja7MWudomSKEg1NU5D3JO0SEQc3J)" ] }, { @@ -99,7 +99,7 @@ "id": "TyebsOIpU6hv" }, "source": [ - "2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. \n", + "2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code.\n", "\n" ] }, @@ -217,7 +217,7 @@ "# defaults to 1. If you give Python a slice [BEGIN:END:STEP], it\n", "# visits the indices as follows:\n", "# 1. Set the index to BEGIN.\n", - "# 2. If (STEP < 0 and index not after END) \n", + "# 2. If (STEP < 0 and index not after END)\n", "# or (STEP >= 0 and index not before END): stop.\n", "# 3. Visit the index in the given list or other iterable.\n", "# 4. Set the index to index + STEP.\n", @@ -289,13 +289,13 @@ "source": [ "## Next module\n", "\n", - "[5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK)" + "[5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH) - [solutions](https://colab.research.google.com/drive/1yrrB0EYglFKJ0_zhb91xY7FGgJosSUFw)" ] } ], "metadata": { "colab": { - "authorship_tag": "ABX9TyOIQlgg0FQF3naGWr9eJVVL", + "authorship_tag": "ABX9TyMcSuXjx2Fo4LqFB9MXHZXA", "provenance": [], "toc_visible": true }, diff --git a/solutions/04 datastructures solutions.py b/solutions/04 datastructures solutions.py index cd9d588..61704cb 100644 --- a/solutions/04 datastructures solutions.py +++ b/solutions/04 datastructures solutions.py @@ -16,13 +16,13 @@ # # ### Exercise solutions # -# [Module 4](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) +# [Module 4](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt) # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI) +# Previous module: [3. Conditionals](https://colab.research.google.com/drive/1qQzRBD1e-1yCKtUNAt8L2lRb8tGjYpkR) - [solutions](https://colab.research.google.com/drive/1-vBxja7MWudomSKEg1NU5D3JO0SEQc3J) # %% [markdown] id="70aMsClGPRy9" # ## Exercise 4.1: Lists @@ -59,7 +59,7 @@ # a nested single element of the latter. # %% [markdown] id="TyebsOIpU6hv" -# 2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. +# 2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. # # @@ -142,7 +142,7 @@ # defaults to 1. If you give Python a slice [BEGIN:END:STEP], it # visits the indices as follows: # 1. Set the index to BEGIN. -# 2. If (STEP < 0 and index not after END) +# 2. If (STEP < 0 and index not after END) # or (STEP >= 0 and index not before END): stop. # 3. Visit the index in the given list or other iterable. # 4. Set the index to index + STEP. @@ -188,4 +188,4 @@ # %% [markdown] id="HiEWGB1V1W4U" # ## Next module # -# [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK) +# [5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH) - [solutions](https://colab.research.google.com/drive/1yrrB0EYglFKJ0_zhb91xY7FGgJosSUFw) diff --git a/solutions/05 assertions solutions.ipynb b/solutions/05 assertions solutions.ipynb index dc73f1f..8434134 100644 --- a/solutions/05 assertions solutions.ipynb +++ b/solutions/05 assertions solutions.ipynb @@ -10,13 +10,13 @@ "\n", "### Exercise solutions\n", "\n", - "[5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM)\n", + "[5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH)\n", "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn)" + "Previous module: [4. Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt) - [solutions](https://colab.research.google.com/drive/1mf_sQpAVxz8oRbsZnA548cC3TtVaQxmU)" ] }, { @@ -31,18 +31,8 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 4, - "status": "ok", - "timestamp": 1681309651463, - "user": { - "displayName": "Julian Gonggrijp", - "userId": "06467962548183964912" - }, - "user_tz": -120 - }, "id": "ztDylwg9biL5" }, "outputs": [], @@ -53,7 +43,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -91,18 +81,8 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 874, - "status": "ok", - "timestamp": 1681309654788, - "user": { - "displayName": "Julian Gonggrijp", - "userId": "06467962548183964912" - }, - "user_tz": -120 - }, "id": "orOWCpWVbzKf" }, "outputs": [], @@ -120,18 +100,8 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 186, - "status": "ok", - "timestamp": 1681310066688, - "user": { - "displayName": "Julian Gonggrijp", - "userId": "06467962548183964912" - }, - "user_tz": -120 - }, "id": "F6NjZ7gOb05u" }, "outputs": [], @@ -149,18 +119,8 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 309, - "status": "ok", - "timestamp": 1681310068748, - "user": { - "displayName": "Julian Gonggrijp", - "userId": "06467962548183964912" - }, - "user_tz": -120 - }, "id": "KB_YkNSIb2KT" }, "outputs": [], @@ -171,18 +131,8 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 191, - "status": "ok", - "timestamp": 1681310100358, - "user": { - "displayName": "Julian Gonggrijp", - "userId": "06467962548183964912" - }, - "user_tz": -120 - }, "id": "1iUK81Nvb3Ri" }, "outputs": [], @@ -193,7 +143,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -252,35 +202,27 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 195, - "status": "ok", - "timestamp": 1681310291983, - "user": { - "displayName": "Julian Gonggrijp", - "userId": "06467962548183964912" - }, - "user_tz": -120 - }, "id": "Q_yIUKSRdVjF" }, "outputs": [], "source": [ "# One possible solution\n", "a = 12\n", + "\n", "b = 30\n", "\n", "c = b - a\n", "\n", "# Another possible solution\n", "a = 12\n", + "\n", "b = 24\n", "\n", "c = (a + b) / 2\n", "\n", - "assert a < b, 'a should be smaller than b'\n", + "assert a < b, 'a should be less than b'\n", "assert a != b, 'a and b should not be equal'\n", "assert c == 18, 'c should be 18'" ] @@ -296,18 +238,8 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 428, - "status": "ok", - "timestamp": 1681310446308, - "user": { - "displayName": "Julian Gonggrijp", - "userId": "06467962548183964912" - }, - "user_tz": -120 - }, "id": "UOp8NFVOfR6Z" }, "outputs": [], @@ -327,7 +259,7 @@ "source": [ "## Next module\n", "\n", - "[6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) - [solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-)" + "[6. Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ) - [solutions](https://colab.research.google.com/drive/19mQnQILY7oRREvwUsAD2SmqQHiOffgx_)" ] } ], diff --git a/solutions/05 assertions solutions.py b/solutions/05 assertions solutions.py index bd801e0..40d889a 100644 --- a/solutions/05 assertions solutions.py +++ b/solutions/05 assertions solutions.py @@ -16,26 +16,26 @@ # # ### Exercise solutions # -# [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) +# [5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH) # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn) +# Previous module: [4. Datastructures](https://colab.research.google.com/drive/1CS9CxET2V1j0FQzy82AWBZZCbc8M_qWt) - [solutions](https://colab.research.google.com/drive/1mf_sQpAVxz8oRbsZnA548cC3TtVaQxmU) # %% [markdown] id="m_FYfvXbbZXe" # ## Exercise 5.1: Assertions # In each of the code blocks below, try to predict what will be the output, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! -# %% id="ztDylwg9biL5" executionInfo={"status": "ok", "timestamp": 1681309651463, "user_tz": -120, "elapsed": 4, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# %% id="ztDylwg9biL5" assert True # The assertion passes, so no output! # %% id="0Uk4w2DBbxfD" colab={"base_uri": "https://localhost:8080/", "height": 164} executionInfo={"status": "error", "timestamp": 1681309645640, "user_tz": -120, "elapsed": 45, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="fd5ed9ba-c7ea-488a-b537-a6641517d2c0" assert False, "The assertion fails because the value is False" -# %% id="orOWCpWVbzKf" executionInfo={"status": "ok", "timestamp": 1681309654788, "user_tz": -120, "elapsed": 874, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# %% id="orOWCpWVbzKf" assert "True" # You can think of assert as implicitly wrapping its argument in bool(): @@ -46,7 +46,7 @@ # Hence, the string "True" is converted to the boolean True, the assertion # passes and there is no output. -# %% id="F6NjZ7gOb05u" executionInfo={"status": "ok", "timestamp": 1681310066688, "user_tz": -120, "elapsed": 186, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# %% id="F6NjZ7gOb05u" assert "False", "The assertion fails because the value is False" # The string "False" is nonempty, so bool() converts it to True # (which, yes, is very confusing!). Hence, the assertion passes @@ -57,11 +57,11 @@ # while a string with just a space passes: assert bool(" "), "The assertion fails because the value is False" -# %% id="KB_YkNSIb2KT" executionInfo={"status": "ok", "timestamp": 1681310068748, "user_tz": -120, "elapsed": 309, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# %% id="KB_YkNSIb2KT" assert 1 # bool(1) is True, so pass, so no output -# %% id="1iUK81Nvb3Ri" executionInfo={"status": "ok", "timestamp": 1681310100358, "user_tz": -120, "elapsed": 191, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# %% id="1iUK81Nvb3Ri" assert 1 == True, "The number 1 is not True" # As we saw in module 2, 1 is indeed the same value as True @@ -78,27 +78,29 @@ # In the code block below, change the value for `b`, and create an expression for `c` using `a` and `b` so that the assert statements succeed. # -# %% id="Q_yIUKSRdVjF" executionInfo={"status": "ok", "timestamp": 1681310291983, "user_tz": -120, "elapsed": 195, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# %% id="Q_yIUKSRdVjF" # One possible solution a = 12 + b = 30 c = b - a # Another possible solution a = 12 + b = 24 c = (a + b) / 2 -assert a < b, 'a should be smaller than b' +assert a < b, 'a should be less than b' assert a != b, 'a and b should not be equal' assert c == 18, 'c should be 18' # %% [markdown] id="1u_bBUpSfQr5" # In the code block below, change `students` so that it satisfies the `assert` statements. Also, add messages to the assert statements that explain what they test (in human language). -# %% id="UOp8NFVOfR6Z" executionInfo={"status": "ok", "timestamp": 1681310446308, "user_tz": -120, "elapsed": 428, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# %% id="UOp8NFVOfR6Z" students = ['bert', 'ernie', 'pino'] @@ -108,4 +110,4 @@ # %% [markdown] id="JaaguG-D3k_i" # ## Next module # -# [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) - [solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-) +# [6. Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ) - [solutions](https://colab.research.google.com/drive/19mQnQILY7oRREvwUsAD2SmqQHiOffgx_) diff --git a/solutions/06 Loops - Solutions.ipynb b/solutions/06 Loops - Solutions.ipynb index a20476b..e912967 100644 --- a/solutions/06 Loops - Solutions.ipynb +++ b/solutions/06 Loops - Solutions.ipynb @@ -9,13 +9,14 @@ "# CDH course \"Programming in Python\"\n", "\n", "### Exercise Solutions\n", - "[Solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-#scrollTo=fqMJHzNk5yXQ)\n", + "\n", + "[Module 6](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ)\n", "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK)" + "Previous module: [5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH) - [solutions](https://colab.research.google.com/drive/1yrrB0EYglFKJ0_zhb91xY7FGgJosSUFw)" ] }, { @@ -68,7 +69,7 @@ "\n", "for word in words:\n", " full_text = full_text + word + ' '\n", - " # print(word)\n", + " print(word)\n", "print(full_text)" ] }, @@ -83,7 +84,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -126,7 +127,7 @@ " reverse_fruits.append(fruit)\n", "print(reverse_fruits)\n", "\n", - "#solution 2:\n", + "#solution 2 (best):\n", "reverse_fruits = []\n", "for fruit in fruits:\n", " reverse_fruits = [fruit] + reverse_fruits\n", @@ -180,16 +181,16 @@ }, "source": [ "> Things that you might notice:\n", - "> \n", + ">\n", "> - It is possible to call `range` with one, two or three arguments.\n", "> - `enumerate` is always called with only one argument.\n", "> - Some noticed that you can write the string `'pirate'` and pass it directly to `enumerate`, without storing the string in a variable first. This is a good observation, and it is possible with all values and functions in general!\n", "> - The program is counting iterations. The variable `iteration_count` is initially set to `0`, incremented at every loop iteration and finally printed at the end.\n", "> - When the `iteration_count` is greater than `2`, the loop does not print the current `value`. In other words, the program only prints the first two values in each iterable.\n", "> - The program prints the type of the first value in the iterable only.\n", - "> \n", + ">\n", "> Reasons:\n", - "> \n", + ">\n", "> - All the various versions of `miracle` are there to illustrate the behavior of `range` and `enumerate`.\n", "> - We print the type of the iterable as a whole at the start, so you can see what kind of value comes out of the call to `range` or `enumerate`.\n", "> - We keep track of the number of iterations so you can see how many values are produced by the iterable. We could have used `enumerate` for this, but this would be confusing, since the behavior of `enumerate` itself is the topic of this exercise. Therefore, we use custom logic with the `iteration_count` variable instead.\n", @@ -214,16 +215,16 @@ }, "source": [ "> Possible elements of a good answer for `range`:\n", - "> \n", + ">\n", "> - If you pass only one value, this sets the exclusive end. It generates whole numbers from `0` (zero) up to but not including the end.\n", "> - If you pass two values, the first sets the inclusive start and the second sets the exclusive end.\n", "> - If you pass three values, the first sets the inclusive start, the second the exclusive end and the third a step size.\n", "> - If you omit the step size (so you pass only one or two arguments), it defaults to `1`.\n", "> - A negative step size causes the range to count \"backwards\" (from high numbers to low numbers).\n", "> - You are not allowed to pass floats; the function only supports integers.\n", - "> \n", + ">\n", "> Possible elements of a good answer for `enumerate`:\n", - "> \n", + ">\n", "> - It requires another iterable as argument (we see a list, a string and a range in the examples).\n", "> - The same values from the input iterable also appear in the output iterable, but wrapped in a tuple.\n", "> - In the output iterable, every tuple has a numerical index in addition to the corresponding value from the input iterable.\n", @@ -233,7 +234,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -301,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -362,7 +363,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -439,7 +440,7 @@ "source": [ "## Next module\n", "\n", - "[7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr)" + "[7. Functions](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg) - [solution](https://colab.research.google.com/drive/1BdBQeTKBvEPn1CTIJC5sDDY6VBMs32Sq)" ] } ], diff --git a/solutions/06 Loops - Solutions.py b/solutions/06 Loops - Solutions.py index 4a576a0..6b09303 100644 --- a/solutions/06 Loops - Solutions.py +++ b/solutions/06 Loops - Solutions.py @@ -15,13 +15,14 @@ # # CDH course "Programming in Python" # # ### Exercise Solutions -# [Solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-#scrollTo=fqMJHzNk5yXQ) +# +# [Module 6](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ) # # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK) +# Previous module: [5. Assertions](https://colab.research.google.com/drive/1ixrL5RCpNhtQN_MtCbpy4E5PYEG1N-qH) - [solutions](https://colab.research.google.com/drive/1yrrB0EYglFKJ0_zhb91xY7FGgJosSUFw) # %% [markdown] id="0Gun_3cX1ey8" # ## Exercise 1: basic `for` loops @@ -34,7 +35,7 @@ for word in words: full_text = full_text + word + ' ' - # print(word) + print(word) print(full_text) # %% [markdown] id="8zB10pLC2ZaT" @@ -52,7 +53,7 @@ reverse_fruits.append(fruit) print(reverse_fruits) -#solution 2: +#solution 2 (best): reverse_fruits = [] for fruit in fruits: reverse_fruits = [fruit] + reverse_fruits @@ -89,16 +90,16 @@ # %% [markdown] id="otBq-xEi-SiC" # > Things that you might notice: -# > +# > # > - It is possible to call `range` with one, two or three arguments. # > - `enumerate` is always called with only one argument. # > - Some noticed that you can write the string `'pirate'` and pass it directly to `enumerate`, without storing the string in a variable first. This is a good observation, and it is possible with all values and functions in general! # > - The program is counting iterations. The variable `iteration_count` is initially set to `0`, incremented at every loop iteration and finally printed at the end. # > - When the `iteration_count` is greater than `2`, the loop does not print the current `value`. In other words, the program only prints the first two values in each iterable. # > - The program prints the type of the first value in the iterable only. -# > +# > # > Reasons: -# > +# > # > - All the various versions of `miracle` are there to illustrate the behavior of `range` and `enumerate`. # > - We print the type of the iterable as a whole at the start, so you can see what kind of value comes out of the call to `range` or `enumerate`. # > - We keep track of the number of iterations so you can see how many values are produced by the iterable. We could have used `enumerate` for this, but this would be confusing, since the behavior of `enumerate` itself is the topic of this exercise. Therefore, we use custom logic with the `iteration_count` variable instead. @@ -112,16 +113,16 @@ # %% [markdown] id="ZyllJHKCBtoy" # > Possible elements of a good answer for `range`: -# > +# > # > - If you pass only one value, this sets the exclusive end. It generates whole numbers from `0` (zero) up to but not including the end. # > - If you pass two values, the first sets the inclusive start and the second sets the exclusive end. # > - If you pass three values, the first sets the inclusive start, the second the exclusive end and the third a step size. # > - If you omit the step size (so you pass only one or two arguments), it defaults to `1`. # > - A negative step size causes the range to count "backwards" (from high numbers to low numbers). # > - You are not allowed to pass floats; the function only supports integers. -# > +# > # > Possible elements of a good answer for `enumerate`: -# > +# > # > - It requires another iterable as argument (we see a list, a string and a range in the examples). # > - The same values from the input iterable also appear in the output iterable, but wrapped in a tuple. # > - In the output iterable, every tuple has a numerical index in addition to the corresponding value from the input iterable. @@ -212,4 +213,4 @@ # %% [markdown] id="0eGibfk04LI0" # ## Next module # -# [7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) +# [7. Functions](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg) - [solution](https://colab.research.google.com/drive/1BdBQeTKBvEPn1CTIJC5sDDY6VBMs32Sq) diff --git a/solutions/07 Functions solutions.ipynb b/solutions/07 Functions solutions.ipynb index f1061dd..e2b7885 100644 --- a/solutions/07 Functions solutions.ipynb +++ b/solutions/07 Functions solutions.ipynb @@ -8,11 +8,15 @@ "source": [ "# Module 7: Functions\n", "\n", + "### Exercise solutions\n", + "\n", + "[Module 7](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg)\n", + "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)\n", + "Previous module: [6. Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ) - [solutions](https://colab.research.google.com/drive/19mQnQILY7oRREvwUsAD2SmqQHiOffgx_)\n", "\n", "### This module\n", "\n", @@ -61,7 +65,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -97,18 +101,8 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 5, - "status": "ok", - "timestamp": 1681905000500, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "vdTIwoGxM_JV" }, "outputs": [], @@ -123,7 +117,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -162,7 +156,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -204,7 +198,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -247,7 +241,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -291,7 +285,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -337,10 +331,10 @@ "# make a function. the code in the body will not be executed until we call the function below.\n", "def example(name):\n", " print(name) # 5th print: we are printing the *parameter* `name` that we\n", - " # passed on to the function when we called it, which is 'Jelte'. There is a \n", + " # passed on to the function when we called it, which is 'Jelte'. There is a\n", " # global parameter which is also called `name` (with value 'Julian'), but\n", " # the parameter takes precedence.\n", - " \n", + "\n", " name = 'Berit' # update the value of the *parameter*\n", "\n", " print(name) # 6th print: print the new value of the parameter\n", @@ -378,18 +372,8 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 366, - "status": "ok", - "timestamp": 1681906464925, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "ajcRnvzQQ9c5" }, "outputs": [], @@ -410,18 +394,8 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 393, - "status": "ok", - "timestamp": 1681906766119, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "giU_bIKrRME4" }, "outputs": [], @@ -432,7 +406,7 @@ "\n", " Input can be of any type: anything other than a string will return False.\n", " For strings, checks that the string is not empty and the first character\n", - " is an uppercase latin character. \n", + " is an uppercase latin character.\n", " '''\n", " if not word or type(word) != str:\n", " return False\n", @@ -449,18 +423,8 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 8, - "status": "ok", - "timestamp": 1681907208099, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "pAChbRWn-SKS" }, "outputs": [], @@ -493,18 +457,8 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 399, - "status": "ok", - "timestamp": 1681907269669, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "Ap33-rF-UbsB" }, "outputs": [], @@ -549,18 +503,8 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 10, - "status": "ok", - "timestamp": 1681907469796, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "I5s4_a53ENJC" }, "outputs": [], @@ -579,7 +523,7 @@ " for (index, character) in enumerate(text):\n", " if character == 'a':\n", " a_index = index\n", - " \n", + "\n", " return a_index\n", "\n", "assert last_a_index('banana') == 5\n", @@ -598,18 +542,8 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 2, - "status": "ok", - "timestamp": 1681907653952, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "kyQwfz-mYvfW" }, "outputs": [], @@ -620,7 +554,7 @@ "def replace(text, query, replacement):\n", " '''\n", " Replace each occurence of a character in a string\n", - " \n", + "\n", " Input:\n", " - text: a string in which you want to replace something\n", " - query: the character you wish to replace\n", @@ -634,7 +568,7 @@ " replaced = replaced + replacement\n", " else:\n", " replaced = replaced + character\n", - " \n", + "\n", " return replaced\n", "\n", "assert replace('small', 'a', 'e') == 'smell'\n", @@ -644,18 +578,8 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 350, - "status": "ok", - "timestamp": 1681908669621, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "2ajcWTx1gi2r" }, "outputs": [], @@ -672,7 +596,7 @@ "def replace(text, query, replacement):\n", " '''\n", " Replace each occurence of a substring in a string\n", - " \n", + "\n", " Input:\n", " - text: a string in which you want to replace something\n", " - query: the string you wish to replace\n", @@ -683,7 +607,7 @@ "\n", " if not query:\n", " return None\n", - " \n", + "\n", " size = len(query)\n", " new_text = '' # where we build our new text\n", " position = 0 # our position as we move through the text\n", @@ -697,16 +621,16 @@ " # if it doesn't match, move ahead by one character\n", " new_text += text[position]\n", " position += 1\n", - " \n", + "\n", " return new_text\n", - " \n", + "\n", "\n", "assert replace('small', 'a', 'e') == 'smell'\n", "assert replace('banana', 'a', 'o') == 'bonono'\n", "assert replace('cherry', 'a', 'x') == 'cherry'\n", "assert replace('small', '', '?') == None\n", "assert replace('banana', 'an', 'ab') == 'bababa'\n", - "assert replace('banana', 'banana', 'apple') == 'apple' \n", + "assert replace('banana', 'banana', 'apple') == 'apple'\n", "assert replace('banana', 'e', 'o') == 'banana'" ] }, @@ -730,7 +654,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -770,7 +694,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -808,7 +732,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -845,7 +769,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -893,7 +817,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -951,18 +875,8 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 6, - "status": "ok", - "timestamp": 1681909191045, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "SaygnjMygwNF" }, "outputs": [], @@ -978,18 +892,8 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 6, - "status": "ok", - "timestamp": 1681909211878, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "GqIgTcG6hMuG" }, "outputs": [], @@ -1006,7 +910,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1052,24 +956,14 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 662, - "status": "ok", - "timestamp": 1681911393494, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "TnuU_I0Tq9wQ" }, "outputs": [], "source": [ - "# You may pretend that it is forever April\n", - "current_month = 4\n", + "# You may pretend that it is forever November\n", + "current_month = 11\n", "\n", "months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']\n", "\n", @@ -1078,7 +972,7 @@ " Return the name of a month based on an number.\n", "\n", " Input should be an integer between 1 and 12. Returns None if this is not the case.\n", - " If no month number is given, defaults to April.\n", + " If no month number is given, defaults to November.\n", "\n", " Returns a string with the name of the Month.\n", " '''\n", @@ -1094,7 +988,7 @@ "assert month(3) == 'March'\n", "assert month(4) == 'April'\n", "assert month(11) == 'November'\n", - "assert month() == 'April'" + "assert month() == 'November'" ] }, { @@ -1108,24 +1002,14 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 8, - "status": "ok", - "timestamp": 1681911394923, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "WUGQqmJysrqS" }, "outputs": [], "source": [ - "# You may pretend it is forever Tuesday\n", - "current_weekday = 2\n", + "# You may pretend it is forever Wednesday\n", + "current_weekday = 3\n", "\n", "weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']\n", "\n", @@ -1134,20 +1018,20 @@ " Return the name of a weekday based on an number.\n", "\n", " Input should be an integer between 0 and 7. Returns None if this is not the case.\n", - " If no weekday number is given, defaults to Tuesday.\n", + " If no weekday number is given, defaults to Wednesday.\n", "\n", " Returns a string with the name of the weekday. Day 1 is 'Monday' while day 0\n", " and day 7 are both 'Sunday'.\n", " '''\n", " if type(number) is not int or number < 0 or number > 7:\n", " return None\n", - " \n", + "\n", " index = (number - 1) % 7\n", " return weekdays[index]\n", "\n", "# Your definition of weekday here\n", "\n", - "assert weekday() == 'Tuesday'\n", + "assert weekday() == 'Wednesday'\n", "assert weekday(0) == 'Sunday'\n", "assert weekday(7) == 'Sunday'\n", "assert weekday(4) == 'Thursday'" @@ -1173,18 +1057,8 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 444, - "status": "ok", - "timestamp": 1681911460720, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "JFhOX_Z5uVfC" }, "outputs": [], @@ -1208,18 +1082,18 @@ " '''\n", " if type(number) is not int or number < 0 or number > size:\n", " return None\n", - " \n", + "\n", " index = (number - 1) % size\n", " return names[index]\n", - " \n", + "\n", " return name\n", "\n", "# now we can make our month and weekday functions based on make_namer\n", "\n", - "current_month = 4\n", + "current_month = 11\n", "months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']\n", "\n", - "current_weekday = 2\n", + "current_weekday = 3\n", "weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']\n", "\n", "month = make_namer(months, current_month)\n", @@ -1230,9 +1104,9 @@ "assert month(3) == 'March'\n", "assert month(4) == 'April'\n", "assert month(11) == 'November'\n", - "assert month() == 'April'\n", + "assert month() == 'November'\n", "\n", - "assert weekday() == 'Tuesday'\n", + "assert weekday() == 'Wednesday'\n", "assert weekday(0) == 'Sunday'\n", "assert weekday(7) == 'Sunday'\n", "assert weekday(4) == 'Thursday'" @@ -1249,18 +1123,8 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 9, - "status": "ok", - "timestamp": 1681912144802, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "0KyXFHEWwZ45" }, "outputs": [], @@ -1292,7 +1156,7 @@ "\n", " nice_string = day_string + ', ' + month_string + ' ' + str(day_of_month) + ', ' + str(year)\n", " return nice_string\n", - " \n", + "\n", " return format_date\n", "\n", "# Your definition of create_date_format here\n", @@ -1317,18 +1181,8 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 11, - "status": "ok", - "timestamp": 1681912282529, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "407dPPK966R9" }, "outputs": [], @@ -1380,18 +1234,8 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 3, - "status": "ok", - "timestamp": 1681912437888, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "NFADyIW3-7qt" }, "outputs": [], @@ -1436,18 +1280,8 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 8, - "status": "ok", - "timestamp": 1681912825413, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "4c0A4kMfDdvt" }, "outputs": [], @@ -1465,7 +1299,7 @@ " for position in range(0, len(sequence), 10):\n", " chunk = items[position: position + 10]\n", " chunks.append(chunk)\n", - " \n", + "\n", " return chunks\n", "\n", "assert chunk10('Jelte!!!!!') == [list('Jelte!!!!!')]\n", @@ -1488,18 +1322,8 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 9, - "status": "ok", - "timestamp": 1681913046725, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "5g4BRdpLJLIc" }, "outputs": [], @@ -1538,12 +1362,12 @@ }, "source": [ "5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

\n", - "Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can." + "Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ#scrollTo=uyqbuhKsUlhG), in as little code as you can." ] }, { "cell_type": "code", - "execution_count": 92, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1589,7 +1413,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1641,7 +1465,7 @@ "source": [ "## Next module\n", "\n", - "[8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)" + "[8. Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS)" ] } ], diff --git a/solutions/07 Functions solutions.py b/solutions/07 Functions solutions.py index 57d2374..6db43f2 100644 --- a/solutions/07 Functions solutions.py +++ b/solutions/07 Functions solutions.py @@ -14,11 +14,15 @@ # %% [markdown] id="fqMJHzNk5yXQ" # # Module 7: Functions # +# ### Exercise solutions +# +# [Module 7](https://colab.research.google.com/drive/146De3ZjgWYldNBmKkDyu8-m_jkzUTrGg) +# # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) +# Previous module: [6. Loops](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ) - [solutions](https://colab.research.google.com/drive/19mQnQILY7oRREvwUsAD2SmqQHiOffgx_) # # ### This module # @@ -46,7 +50,7 @@ def greet(name): print(greet('Berit')) -# %% executionInfo={"elapsed": 5, "status": "ok", "timestamp": 1681905000500, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="vdTIwoGxM_JV" +# %% id="vdTIwoGxM_JV" name = 'Luka' def exclaim(name): @@ -108,10 +112,10 @@ def add(left, right): # make a function. the code in the body will not be executed until we call the function below. def example(name): print(name) # 5th print: we are printing the *parameter* `name` that we - # passed on to the function when we called it, which is 'Jelte'. There is a + # passed on to the function when we called it, which is 'Jelte'. There is a # global parameter which is also called `name` (with value 'Julian'), but # the parameter takes precedence. - + name = 'Berit' # update the value of the *parameter* print(name) # 6th print: print the new value of the parameter @@ -141,7 +145,7 @@ def example(name): # %% [markdown] id="Rwvwlpp0-Hrt" # 3. In each of the following code blocks, write a docstring for the function, then add assertions and run the block to check that your documentation is correct. -# %% executionInfo={"elapsed": 366, "status": "ok", "timestamp": 1681906464925, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="ajcRnvzQQ9c5" +# %% id="ajcRnvzQQ9c5" def odd(number): ''' Checks whether a number is odd @@ -156,14 +160,14 @@ def odd(number): assert odd(0) == False -# %% executionInfo={"elapsed": 393, "status": "ok", "timestamp": 1681906766119, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="giU_bIKrRME4" +# %% id="giU_bIKrRME4" def magic(word): ''' Checks that the input is a nonempty string and starts with a capital letter. Input can be of any type: anything other than a string will return False. For strings, checks that the string is not empty and the first character - is an uppercase latin character. + is an uppercase latin character. ''' if not word or type(word) != str: return False @@ -178,7 +182,7 @@ def magic(word): assert magic('') == False -# %% executionInfo={"elapsed": 8, "status": "ok", "timestamp": 1681907208099, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="pAChbRWn-SKS" +# %% id="pAChbRWn-SKS" def join_commas(words): ''' Joins a iterable of strings into a single string, with ', ' between each item. @@ -199,7 +203,7 @@ def join_commas(words): # %% [markdown] id="mc9RtAeATiHw" # 4. Write a function `is_number` that takes one argument. If the argument is a number, it should return `True`; otherwise, it should return `False`. Make sure to include a docstring! -# %% executionInfo={"elapsed": 399, "status": "ok", "timestamp": 1681907269669, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="Ap33-rF-UbsB" +# %% id="Ap33-rF-UbsB" # Your definition of is_number here def is_number(value): @@ -226,7 +230,7 @@ def is_number(value): # %% [markdown] id="nM43w3VlB3-O" # 1. Write a function `last_a_index` that, given a string, returns the **last** index of the letter `'a'` in the string. If there is no `'a'` in the string, the function should return `None`. -# %% executionInfo={"elapsed": 10, "status": "ok", "timestamp": 1681907469796, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="I5s4_a53ENJC" +# %% id="I5s4_a53ENJC" # Define last_a_index here def last_a_index(text): @@ -241,7 +245,7 @@ def last_a_index(text): for (index, character) in enumerate(text): if character == 'a': a_index = index - + return a_index assert last_a_index('banana') == 5 @@ -252,14 +256,14 @@ def last_a_index(text): # %% [markdown] id="z4z3-dOaVROx" # 2. Write a function `replace` with three string arguments: `text`, `query` and `replacement`. It should return a copy of `text` in which all occurrences of `query` are replaced by `replacement`. For example, `replace('small', 'a', 'e')` should return `'smell'`. You may assume that `query` is always a single character. For an additional challenge, you can try to also handle multi-character query strings. For ultimate challenge, try to think of a way to handle the empty string! -# %% executionInfo={"elapsed": 2, "status": "ok", "timestamp": 1681907653952, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="kyQwfz-mYvfW" +# %% id="kyQwfz-mYvfW" # Define replace here # solution one: does not work with multi-character strings def replace(text, query, replacement): ''' Replace each occurence of a character in a string - + Input: - text: a string in which you want to replace something - query: the character you wish to replace @@ -273,7 +277,7 @@ def replace(text, query, replacement): replaced = replaced + replacement else: replaced = replaced + character - + return replaced assert replace('small', 'a', 'e') == 'smell' @@ -281,7 +285,7 @@ def replace(text, query, replacement): assert replace('cherry', 'a', 'x') == 'cherry' -# %% executionInfo={"elapsed": 350, "status": "ok", "timestamp": 1681908669621, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="2ajcWTx1gi2r" +# %% id="2ajcWTx1gi2r" # solution 2: handle multi-character strings and empty strings # There no clear answer for what should happen in the case of empty strings! @@ -294,7 +298,7 @@ def replace(text, query, replacement): def replace(text, query, replacement): ''' Replace each occurence of a substring in a string - + Input: - text: a string in which you want to replace something - query: the string you wish to replace @@ -305,7 +309,7 @@ def replace(text, query, replacement): if not query: return None - + size = len(query) new_text = '' # where we build our new text position = 0 # our position as we move through the text @@ -319,16 +323,16 @@ def replace(text, query, replacement): # if it doesn't match, move ahead by one character new_text += text[position] position += 1 - + return new_text - + assert replace('small', 'a', 'e') == 'smell' assert replace('banana', 'a', 'o') == 'bonono' assert replace('cherry', 'a', 'x') == 'cherry' assert replace('small', '', '?') == None assert replace('banana', 'an', 'ab') == 'bababa' -assert replace('banana', 'banana', 'apple') == 'apple' +assert replace('banana', 'banana', 'apple') == 'apple' assert replace('banana', 'e', 'o') == 'banana' @@ -405,7 +409,7 @@ def enumerate(items): # %% [markdown] id="CKma4p6Egkwb" # 2. In each of the following code blocks, something is missing in the function definition. Add the missing element. -# %% executionInfo={"elapsed": 6, "status": "ok", "timestamp": 1681909191045, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="SaygnjMygwNF" +# %% id="SaygnjMygwNF" def combine(left, right): """ Compute the sum and the product of the arguments. """ sum = left + right @@ -415,7 +419,7 @@ def combine(left, right): assert combine(2, 3) == (5, 6) -# %% executionInfo={"elapsed": 6, "status": "ok", "timestamp": 1681909211878, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="GqIgTcG6hMuG" +# %% id="GqIgTcG6hMuG" def announce_time(hour, minute=0): # minute should be optional, so we add a default value. """ Announce the time in a speaking clock manner. """ if minute < 10: @@ -436,9 +440,9 @@ def echo(value): # %% [markdown] id="YG5XrO1PoIJx" # 3. In the following code block, write a function `month`, which returns a string name for given numeric month. Month `1` is `'January'` and month `12` is `December`. When no argument is passed, it should default to the current month. **Tip:** you can avoid writing a big `if`/`elif`/`else` tree by using a list of month names and using the month number as a list index. -# %% executionInfo={"elapsed": 662, "status": "ok", "timestamp": 1681911393494, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="TnuU_I0Tq9wQ" -# You may pretend that it is forever April -current_month = 4 +# %% id="TnuU_I0Tq9wQ" +# You may pretend that it is forever November +current_month = 11 months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] @@ -447,7 +451,7 @@ def month(number=current_month): Return the name of a month based on an number. Input should be an integer between 1 and 12. Returns None if this is not the case. - If no month number is given, defaults to April. + If no month number is given, defaults to November. Returns a string with the name of the Month. ''' @@ -463,14 +467,14 @@ def month(number=current_month): assert month(3) == 'March' assert month(4) == 'April' assert month(11) == 'November' -assert month() == 'April' +assert month() == 'November' # %% [markdown] id="WuRrElhUsD40" # 4. In the following code block, write a function `weekday`, which is analogous to `month` in the previous exercise. Day `1` is `'Monday'` while day `0` and day `7` are both `'Sunday'`. Can you avoid writing the string `'Sunday'` twice in your code? -# %% executionInfo={"elapsed": 8, "status": "ok", "timestamp": 1681911394923, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="WUGQqmJysrqS" -# You may pretend it is forever Tuesday -current_weekday = 2 +# %% id="WUGQqmJysrqS" +# You may pretend it is forever Wednesday +current_weekday = 3 weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] @@ -479,20 +483,20 @@ def weekday(number=current_weekday): Return the name of a weekday based on an number. Input should be an integer between 0 and 7. Returns None if this is not the case. - If no weekday number is given, defaults to Tuesday. + If no weekday number is given, defaults to Wednesday. Returns a string with the name of the weekday. Day 1 is 'Monday' while day 0 and day 7 are both 'Sunday'. ''' if type(number) is not int or number < 0 or number > 7: return None - + index = (number - 1) % 7 return weekdays[index] # Your definition of weekday here -assert weekday() == 'Tuesday' +assert weekday() == 'Wednesday' assert weekday(0) == 'Sunday' assert weekday(7) == 'Sunday' assert weekday(4) == 'Thursday' @@ -504,7 +508,7 @@ def weekday(number=current_weekday): # %% [markdown] id="8au2fNRutw8i" # 1. In exercises 7.3.4 and 7.3.5, where you just implemented `month` and `weekday`, your code was likely very similar. How could you implement both functions without repeating yourself? Check your solution with assertions. -# %% executionInfo={"elapsed": 444, "status": "ok", "timestamp": 1681911460720, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="JFhOX_Z5uVfC" +# %% id="JFhOX_Z5uVfC" def make_namer(names, default): ''' Make a function to give names (months, weekdays, etc) based on a number. @@ -524,18 +528,18 @@ def name(number=default): ''' if type(number) is not int or number < 0 or number > size: return None - + index = (number - 1) % size return names[index] - + return name # now we can make our month and weekday functions based on make_namer -current_month = 4 +current_month = 11 months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] -current_weekday = 2 +current_weekday = 3 weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] month = make_namer(months, current_month) @@ -546,9 +550,9 @@ def name(number=default): assert month(3) == 'March' assert month(4) == 'April' assert month(11) == 'November' -assert month() == 'April' +assert month() == 'November' -assert weekday() == 'Tuesday' +assert weekday() == 'Wednesday' assert weekday(0) == 'Sunday' assert weekday(7) == 'Sunday' assert weekday(4) == 'Thursday' @@ -557,7 +561,7 @@ def name(number=default): # %% [markdown] id="Gx54DrgJuWKg" # 2. Write a function `create_date_format`, which takes two arguments that determine how a date should be displayed. The first argument is function that transforms a weekday number to a string suitable for display, the second argument is a function that transforms a month number. Both arguments default to just displaying the number as-is. `create_date_format` returns a new function. The latter function takes any date defined by a year, month, day and weekday, and returns a string suitable for displaying that date as a whole. -# %% executionInfo={"elapsed": 9, "status": "ok", "timestamp": 1681912144802, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="0KyXFHEWwZ45" +# %% id="0KyXFHEWwZ45" # For this code block, you need to have `month` and `weekday` # in your runtime from previous exercises. @@ -585,7 +589,7 @@ def format_date(year, month, day_of_month, day_of_week): nice_string = day_string + ', ' + month_string + ' ' + str(day_of_month) + ', ' + str(year) return nice_string - + return format_date # Your definition of create_date_format here @@ -602,7 +606,7 @@ def format_date(year, month, day_of_month, day_of_week): # # 1. Write two functions `fizz(number)` and `buzz(number)`. If `number` is divisible by 3, `fizz` should return the string `'Fizz'`; otherwise, it should return the empty string `''`. `buzz` should work like `fizz`, but for divisibility by 5 and the string `'Buzz'`. -# %% executionInfo={"elapsed": 11, "status": "ok", "timestamp": 1681912282529, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="407dPPK966R9" +# %% id="407dPPK966R9" # Your solution here def fizz(number): @@ -642,7 +646,7 @@ def buzz(number): # - If the number is divisible by 5 but not by 3, return `'Buzz'`. # - In all other cases, return the number itself **as a string**. -# %% executionInfo={"elapsed": 3, "status": "ok", "timestamp": 1681912437888, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="NFADyIW3-7qt" +# %% id="NFADyIW3-7qt" # Your solution here def fizzbuzz(number): @@ -675,7 +679,7 @@ def fizzbuzz(number): # %% [markdown] id="o_3wq4agCCZH" # 3. Write a function `chunk10(sequence)` that takes an iterable `sequence` and returns a list of lists: the first sublist contains the first ten elements of `sequence`, the second sublist contains the next ten elements of `sequence`, and so on. You may assume that the number of values in `sequence` is an exact multiple of 10. -# %% executionInfo={"elapsed": 8, "status": "ok", "timestamp": 1681912825413, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="4c0A4kMfDdvt" +# %% id="4c0A4kMfDdvt" # Your solution here def chunk10(sequence): @@ -689,7 +693,7 @@ def chunk10(sequence): for position in range(0, len(sequence), 10): chunk = items[position: position + 10] chunks.append(chunk) - + return chunks assert chunk10('Jelte!!!!!') == [list('Jelte!!!!!')] @@ -704,7 +708,7 @@ def chunk10(sequence): # %% [markdown] id="HBA4z4yVIhsn" # 4. In step 1, you may have written definitions for `fizz` and `buzz` that look very similar to each other. Can you think of a way to avoid repeating the common elements? -# %% executionInfo={"elapsed": 9, "status": "ok", "timestamp": 1681913046725, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="5g4BRdpLJLIc" +# %% id="5g4BRdpLJLIc" # Your solution here def fizz_or_buzz(number, message='Fizz', denominator=3): @@ -733,7 +737,7 @@ def buzz(number): # %% [markdown] id="BXzFYNGmPeO6" # 5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

-# Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can. +# Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/14qxBVO9t3w-pFFnMuS_yhggUmM5S0BnZ#scrollTo=uyqbuhKsUlhG), in as little code as you can. # %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 597, "status": "ok", "timestamp": 1681913126765, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="nDGUS26IMApD" outputId="a78e10b9-f6d3-433f-f06c-d78ee65f65e4" # The following code is just for illustration. @@ -756,4 +760,4 @@ def buzz(number): # %% [markdown] id="Dntbbioh29xm" # ## Next module # -# [8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi) +# [8. Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS) diff --git a/solutions/09 string manipulation solutions.ipynb b/solutions/09 string manipulation solutions.ipynb index 308af07..55629c7 100644 --- a/solutions/09 string manipulation solutions.ipynb +++ b/solutions/09 string manipulation solutions.ipynb @@ -1,5 +1,29 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 9: String manipulation\n", + "\n", + "### Exercise solutions\n", + "\n", + "[Module 9](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm)\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", + "\n", + "Previous module: [8. Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS)\n", + "\n", + "### This module\n", + "\n", + "- Storing code in a variable so you can reuse it.\n", + "- Being explicit about the purpose of your code." + ] + }, { "cell_type": "markdown", "metadata": { @@ -72,18 +96,8 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 356, - "status": "ok", - "timestamp": 1681825113419, - "user": { - "displayName": "Jelte van Boheemen", - "userId": "01262930844892157638" - }, - "user_tz": -120 - }, "id": "Ui3gmvCNmHfB" }, "outputs": [], @@ -102,18 +116,8 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 244, - "status": "ok", - "timestamp": 1681825107244, - "user": { - "displayName": "Jelte van Boheemen", - "userId": "01262930844892157638" - }, - "user_tz": -120 - }, "id": "1tDEnzrumNdO" }, "outputs": [], @@ -149,7 +153,7 @@ "id": "Rq3LXgkMlywN" }, "source": [ - "This does not work. `Matilda.upper()` equals `'MATILDA'`. \n", + "This does not work. `Matilda.upper()` equals `'MATILDA'`.\n", "\n", "`'MATILDA'.lower()` equals `'matilda'`. The first captilal is lost in translation." ] @@ -196,18 +200,8 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 5, - "status": "ok", - "timestamp": 1681824924342, - "user": { - "displayName": "Jelte van Boheemen", - "userId": "01262930844892157638" - }, - "user_tz": -120 - }, "id": "BVKc0bQAnGYq" }, "outputs": [], @@ -247,7 +241,7 @@ "id": "ZpepgX5Lk0Mk" }, "source": [ - "We join the words on the empty string, and not on a space. Thus, the resulting sentence contains all the words concatenated. " + "We join the words on the empty string, and not on a space. Thus, the resulting sentence contains all the words concatenated." ] }, { @@ -270,18 +264,8 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 231, - "status": "ok", - "timestamp": 1681825855742, - "user": { - "displayName": "Jelte van Boheemen", - "userId": "01262930844892157638" - }, - "user_tz": -120 - }, "id": "ZYHALHE7mU4-" }, "outputs": [], @@ -326,7 +310,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -368,7 +352,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -410,7 +394,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -491,12 +475,23 @@ "source": [ "Same as above" ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dntbbioh29xm" + }, + "source": [ + "## Next module\n", + "\n", + "[10. Dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj) - [solutions](https://colab.research.google.com/drive/1j1hikQCS3Px0_q4hguuIqWSXSmyKcAiN)" + ] } ], "metadata": { "colab": { - "authorship_tag": "ABX9TyMi0JBuu4ELq/AQbr5Ws5LZ", - "provenance": [] + "provenance": [], + "toc_visible": true }, "kernelspec": { "display_name": "Python 3", diff --git a/solutions/09 string manipulation solutions.py b/solutions/09 string manipulation solutions.py index fa81140..f4e6b78 100644 --- a/solutions/09 string manipulation solutions.py +++ b/solutions/09 string manipulation solutions.py @@ -11,6 +11,24 @@ # name: python3 # --- +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 9: String manipulation +# +# ### Exercise solutions +# +# [Module 9](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm) +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) +# +# Previous module: [8. Debugging](https://colab.research.google.com/drive/1r6wuOuEHabI0vmBg15HVFBLiNKRb-jnS) +# +# ### This module +# +# - Storing code in a variable so you can reuse it. +# - Being explicit about the purpose of your code. + # %% [markdown] id="5hIj-tbVleEq" # ## Exercise 9.1: String Utilities # In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! @@ -33,13 +51,13 @@ # %% [markdown] id="K_daSu19liik" # Python is case sensitive. The capitalized `A` is not in the string. -# %% executionInfo={"elapsed": 356, "status": "ok", "timestamp": 1681825113419, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="Ui3gmvCNmHfB" +# %% id="Ui3gmvCNmHfB" assert 'A' in 'Matilda'.upper() # %% [markdown] id="i2bI-L6glqzO" # After capitalization of the whole string, `A` is in there. -# %% executionInfo={"elapsed": 244, "status": "ok", "timestamp": 1681825107244, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="1tDEnzrumNdO" +# %% id="1tDEnzrumNdO" name = 'Matilda' assert name.upper() == 'MATILDA' @@ -51,7 +69,7 @@ assert name.upper().lower() == 'Matilda' # %% [markdown] id="Rq3LXgkMlywN" -# This does not work. `Matilda.upper()` equals `'MATILDA'`. +# This does not work. `Matilda.upper()` equals `'MATILDA'`. # # `'MATILDA'.lower()` equals `'matilda'`. The first captilal is lost in translation. @@ -67,7 +85,7 @@ # %% [markdown] id="qsPQT5VGlNZ2" # We can only replace strings with other strings. `'Matilda'.replace('a', '4')` would work -# %% executionInfo={"elapsed": 5, "status": "ok", "timestamp": 1681824924342, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="BVKc0bQAnGYq" +# %% id="BVKc0bQAnGYq" list_of_words = ['I', 'ate', 'a', 'banana'] sentence = 'I ate a banana' @@ -83,7 +101,7 @@ assert ''.join(list_of_words) == sentence # %% [markdown] id="ZpepgX5Lk0Mk" -# We join the words on the empty string, and not on a space. Thus, the resulting sentence contains all the words concatenated. +# We join the words on the empty string, and not on a space. Thus, the resulting sentence contains all the words concatenated. # %% [markdown] id="Xbi4ATS0mQzp" # ## Exercise 9.2: Additional utilities @@ -97,7 +115,7 @@ # - `string.find()` # - `string.split(',', x)` (describe what 'x' does. It should be a number) -# %% executionInfo={"elapsed": 231, "status": "ok", "timestamp": 1681825855742, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="ZYHALHE7mU4-" +# %% id="ZYHALHE7mU4-" # string.startswith(substring) checks if a string starts with a substring assert 'matilda'.startswith('mat') @@ -160,3 +178,8 @@ # %% [markdown] id="LI43JmY2pcIg" # Same as above + +# %% [markdown] id="Dntbbioh29xm" +# ## Next module +# +# [10. Dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj) - [solutions](https://colab.research.google.com/drive/1j1hikQCS3Px0_q4hguuIqWSXSmyKcAiN) diff --git a/solutions/10 - Dictionaries solutions.ipynb b/solutions/10 - Dictionaries solutions.ipynb index 4b15cfb..6907e78 100644 --- a/solutions/10 - Dictionaries solutions.ipynb +++ b/solutions/10 - Dictionaries solutions.ipynb @@ -8,11 +8,15 @@ "source": [ "# Dictionaries\n", "\n", + "### Exercise solutions\n", + "\n", + "[Module 10](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj)\n", + "\n", "### CDH course \"Programming in Python\"\n", "\n", - "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", "\n", - "Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n", + "Previous module: [9. String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm) - [solutions](https://colab.research.google.com/drive/1wOUWUUP4KdYUFzp5-fALi5t_k_4ovow1)\n", "\n", "### This module\n", "\n", @@ -39,7 +43,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -125,18 +129,8 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 227, - "status": "ok", - "timestamp": 1681894473607, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "S7gCNyLCxdrO" }, "outputs": [], @@ -150,18 +144,8 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 259, - "status": "ok", - "timestamp": 1681894484122, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "nScDQipK35qN" }, "outputs": [], @@ -190,18 +174,8 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 217, - "status": "ok", - "timestamp": 1681894489366, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "awf-lLQO3N1U" }, "outputs": [], @@ -214,18 +188,8 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 281, - "status": "ok", - "timestamp": 1681894585893, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "MDpIZpbm3-BG" }, "outputs": [], @@ -246,7 +210,7 @@ " current_count = counts.get(item, 0)\n", " new_count = current_count + 1\n", " counts[item] = new_count\n", - " \n", + "\n", " return counts\n", "\n", "fruit_counts = count_items(fruit_basket)\n", @@ -268,48 +232,28 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 241, - "status": "ok", - "timestamp": 1681894602379, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "mdNug4ct5645" }, "outputs": [], "source": [ "# the variable sent0 contains the first sentence of The Catcher in the Rye\n", "# split into single words\n", - "sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', \n", - " 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', \n", - " 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', \n", - " 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', \n", - " 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', \n", - " 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', \n", - " 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', \n", + "sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the',\n", + " 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is',\n", + " 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood',\n", + " 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied',\n", + " 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that',\n", + " 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t',\n", + " 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want',\n", " 'to', 'know', 'the', 'truth.']" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 3, - "status": "ok", - "timestamp": 1681894603343, - "user": { - "displayName": "Luka van der Plas", - "userId": "16305747382115943293" - }, - "user_tz": -120 - }, "id": "XGY3qSEk6B9j" }, "outputs": [], @@ -319,7 +263,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -377,6 +321,17 @@ "assert most_frequent(word_counts) == (['and'], 4)\n", "assert most_frequent({}) == ([], 0)" ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "y5FcFvgypMfE" + }, + "source": [ + "## Next module\n", + "\n", + "[11 - Working with files](https://colab.research.google.com/drive/1_mDpeRCHzrGJstcxEWZgq5YMhHujPdfe) - [solutions](https://colab.research.google.com/drive/1a5fHoa8eY_ABZereDQLcSnV5w3AUYSav)" + ] } ], "metadata": { @@ -386,7 +341,8 @@ "file_id": "16mecEPkVGUBIFoHIALO7dE8qv7PynHL9", "timestamp": 1681824660733 } - ] + ], + "toc_visible": true }, "kernelspec": { "display_name": "Python 3", diff --git a/solutions/10 - Dictionaries solutions.py b/solutions/10 - Dictionaries solutions.py index 70896de..103a49a 100644 --- a/solutions/10 - Dictionaries solutions.py +++ b/solutions/10 - Dictionaries solutions.py @@ -14,11 +14,15 @@ # %% [markdown] id="kjdcNtg3k8Aa" # # Dictionaries # +# ### Exercise solutions +# +# [Module 10](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj) +# # ### CDH course "Programming in Python" # -# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) # -# Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) +# Previous module: [9. String manipulation](https://colab.research.google.com/drive/15djL6RWOHmSo7rpQMOLE1ga9bICxBLmm) - [solutions](https://colab.research.google.com/drive/1wOUWUUP4KdYUFzp5-fALi5t_k_4ovow1) # # ### This module # @@ -74,7 +78,7 @@ def starts_with_vowel(word): # %% [markdown] id="Gtp5V9dE0LxK" # 2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`). -# %% id="S7gCNyLCxdrO" executionInfo={"status": "ok", "timestamp": 1681894473607, "user_tz": -120, "elapsed": 227, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +# %% id="S7gCNyLCxdrO" lots_of_fruit = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange', 'cucumber': 'green', 'kiwi': 'green', 'strawberry': 'red', 'pineapple': 'yellow','blackberry': 'black', 'cherry': 'red', @@ -82,7 +86,7 @@ def starts_with_vowel(word): 'lemon': 'yellow', 'lime': 'green'} -# %% id="nScDQipK35qN" executionInfo={"status": "ok", "timestamp": 1681894484122, "user_tz": -120, "elapsed": 259, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +# %% id="nScDQipK35qN" def count_fruits(color): '''Count the number of fruits in `lots_of_fruit` that match this colour.''' count = 0 @@ -98,14 +102,14 @@ def count_fruits(color): # %% [markdown] id="-Qp6R3Kp3GId" # 3 . The list `fruit_basket` contains a bunch of fruits. Can you make a dictionary `fruit_counts` which gives the amount for each fruit in `fruit_basket`? (Do not count the fruits by hand!) -# %% id="awf-lLQO3N1U" executionInfo={"status": "ok", "timestamp": 1681894489366, "user_tz": -120, "elapsed": 217, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +# %% id="awf-lLQO3N1U" fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange', 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry', 'strawberry', 'strawberry', 'orange'] -# %% id="MDpIZpbm3-BG" executionInfo={"status": "ok", "timestamp": 1681894585893, "user_tz": -120, "elapsed": 281, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +# %% id="MDpIZpbm3-BG" def count_items(items): ''' Count the items in a list. @@ -122,7 +126,7 @@ def count_items(items): current_count = counts.get(item, 0) new_count = current_count + 1 counts[item] = new_count - + return counts fruit_counts = count_items(fruit_basket) @@ -136,19 +140,19 @@ def count_items(items): # # Write a function that takes a dictionary like `word_counts` tells us the most commonly occuring item and the count. Note that there can be multiple items that occurred the most. -# %% id="mdNug4ct5645" executionInfo={"status": "ok", "timestamp": 1681894602379, "user_tz": -120, "elapsed": 241, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +# %% id="mdNug4ct5645" # the variable sent0 contains the first sentence of The Catcher in the Rye # split into single words -sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', - 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', - 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', - 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', - 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', - 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', - 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', +sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', + 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', + 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', + 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', + 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', + 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', + 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', 'to', 'know', 'the', 'truth.'] -# %% id="XGY3qSEk6B9j" executionInfo={"status": "ok", "timestamp": 1681894603343, "user_tz": -120, "elapsed": 3, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +# %% id="XGY3qSEk6B9j" word_counts = count_items(sent0) # we recycle our function from the last exercise @@ -182,3 +186,8 @@ def most_frequent(counts): assert most_frequent(fruit_counts) == (['grape'], 9) assert most_frequent(word_counts) == (['and'], 4) assert most_frequent({}) == ([], 0) + +# %% [markdown] id="y5FcFvgypMfE" +# ## Next module +# +# [11 - Working with files](https://colab.research.google.com/drive/1_mDpeRCHzrGJstcxEWZgq5YMhHujPdfe) - [solutions](https://colab.research.google.com/drive/1a5fHoa8eY_ABZereDQLcSnV5w3AUYSav) diff --git a/solutions/11 working with files solutions.ipynb b/solutions/11 working with files solutions.ipynb index 4a8d7ea..0d277d5 100644 --- a/solutions/11 working with files solutions.ipynb +++ b/solutions/11 working with files solutions.ipynb @@ -1,5 +1,30 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "aBR46fQqgyGt" + }, + "source": [ + "# Module 11: Working with files\n", + "\n", + "### Exercise solutions\n", + "\n", + "[Module 11](https://colab.research.google.com/drive/1_mDpeRCHzrGJstcxEWZgq5YMhHujPdfe)\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl)\n", + "\n", + "Previous module: [10. dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj) - [solutions](https://colab.research.google.com/drive/1j1hikQCS3Px0_q4hguuIqWSXSmyKcAiN)\n", + "\n", + "### This module\n", + "\n", + "- Reading files\n", + "- Writing files\n", + "- Use existing code" + ] + }, { "cell_type": "markdown", "metadata": { @@ -49,7 +74,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -122,7 +147,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -167,18 +192,8 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 226, - "status": "ok", - "timestamp": 1681916502255, - "user": { - "displayName": "Jelte van Boheemen", - "userId": "01262930844892157638" - }, - "user_tz": -120 - }, "id": "BnpSq28I766w" }, "outputs": [], @@ -244,18 +259,8 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": { - "executionInfo": { - "elapsed": 194, - "status": "ok", - "timestamp": 1681915598793, - "user": { - "displayName": "Jelte van Boheemen", - "userId": "01262930844892157638" - }, - "user_tz": -120 - }, "id": "3UI9XbgN-Ffb" }, "outputs": [], @@ -267,7 +272,7 @@ " with open(PATH) as csv_file:\n", " reader = csv.reader(csv_file, delimiter=',')\n", " return list(reader)\n", - " \n", + "\n", "assert len(read_data()) == 3001" ] }, @@ -392,19 +397,12 @@ "# finally, write to a new file\n", "write_table_to_csv(new_data, 'sample_data/california_housing_test_expanded.csv')\n" ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "UuvUPP01_uUL" - }, - "source": [] } ], "metadata": { "colab": { - "authorship_tag": "ABX9TyNBT3v2osSyHD05LL0HU2aj", - "provenance": [] + "provenance": [], + "toc_visible": true }, "kernelspec": { "display_name": "Python 3", diff --git a/solutions/11 working with files solutions.py b/solutions/11 working with files solutions.py index d1974cc..cf4e5ba 100644 --- a/solutions/11 working with files solutions.py +++ b/solutions/11 working with files solutions.py @@ -11,6 +11,25 @@ # name: python3 # --- +# %% [markdown] id="aBR46fQqgyGt" +# # Module 11: Working with files +# +# ### Exercise solutions +# +# [Module 11](https://colab.research.google.com/drive/1_mDpeRCHzrGJstcxEWZgq5YMhHujPdfe) +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1YgmnpA7tRylvGBpp2PTFGvSV2P8tw5jl) +# +# Previous module: [10. dictionaries](https://colab.research.google.com/drive/1Dssqf65thuWCNZ9I3ezaawelaWpeaWoj) - [solutions](https://colab.research.google.com/drive/1j1hikQCS3Px0_q4hguuIqWSXSmyKcAiN) +# +# ### This module +# +# - Reading files +# - Writing files +# - Use existing code + # %% [markdown] id="YC0q3Z4HiM5Z" # ## Exercise 11.1 - Files # In the code block below, try to predict what will be printed, then run the code. @@ -63,7 +82,7 @@ # %% [markdown] id="EuCKWl9z7zUf" # The row is split into columns. They still contain strings, not numbers. And the last value includes the newline character `\n` -# %% id="BnpSq28I766w" executionInfo={"status": "ok", "timestamp": 1681916502255, "user_tz": -120, "elapsed": 226, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}} +# %% id="BnpSq28I766w" def clean_value(value): ''' Clean a single value. Assumes value is a string representation @@ -110,7 +129,7 @@ def clean_values(values): # %% [markdown] id="nqZZLHi4-HyR" # #### 11.2.1 -# %% id="3UI9XbgN-Ffb" executionInfo={"status": "ok", "timestamp": 1681915598793, "user_tz": -120, "elapsed": 194, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}} +# %% id="3UI9XbgN-Ffb" import csv PATH = 'sample_data/california_housing_test.csv' @@ -118,7 +137,7 @@ def read_data(): with open(PATH) as csv_file: reader = csv.reader(csv_file, delimiter=',') return list(reader) - + assert len(read_data()) == 3001 @@ -230,6 +249,3 @@ def write_table_to_csv(data, filepath): # finally, write to a new file write_table_to_csv(new_data, 'sample_data/california_housing_test_expanded.csv') - -# %% [markdown] id="UuvUPP01_uUL" -#