Merge in "02-functions"
Merge branch "02-functions" into "develop" Summary of the merged in commits: *4643927
: Adjust content overview in README.md *0eb4b70
: Add review and exercises for notebook 02 *72db877
: Add initial version of notebook 02 *ec3097e
: Add the sample_package folder to the project *c3fc760
: Add sample_module.py to the project *22a85bc
: Streamline previous content *f606838
: Add numpy to the dependencies
This commit is contained in:
commit
8e841274e6
15 changed files with 4727 additions and 307 deletions
|
@ -19,7 +19,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
"This course is set up to be a *thorough* introduction to programming in [Python](https://www.python.org/).\n",
|
"This book is set up to be a *thorough* introduction to programming in [Python](https://www.python.org/).\n",
|
||||||
"\n",
|
"\n",
|
||||||
"It teaches the concepts behind and the syntax of the core Python language as defined by the [Python Software Foundation](https://www.python.org/psf/) in the official [language reference](https://docs.python.org/3/reference/index.html) and introduces additions to the language as distributed with the [standard library](https://docs.python.org/3/library/index.html) that come with every installation.\n",
|
"It teaches the concepts behind and the syntax of the core Python language as defined by the [Python Software Foundation](https://www.python.org/psf/) in the official [language reference](https://docs.python.org/3/reference/index.html) and introduces additions to the language as distributed with the [standard library](https://docs.python.org/3/library/index.html) that come with every installation.\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -58,10 +58,10 @@
|
||||||
"source": [
|
"source": [
|
||||||
"To be suitable for *total beginners*, there are *no* formal prerequisites. It is only expected that the student has:\n",
|
"To be suitable for *total beginners*, there are *no* formal prerequisites. It is only expected that the student has:\n",
|
||||||
"\n",
|
"\n",
|
||||||
"- a *solid* understanding of the **English language** (i.e., usage of *technical* terms with *narrow* and *distinct* meanings),\n",
|
"- a *solid* understanding of the **English language**,\n",
|
||||||
"- knowledge of **basic mathematics** from high school (i.e., addition, subtraction, multiplication, division, and a little bit of calculus and statistics),\n",
|
"- knowledge of **basic mathematics** from high school,\n",
|
||||||
"- the ability to **think conceptually** and **reason logically** (i.e., *not* just memorizing), and\n",
|
"- the ability to **think conceptually** and **reason logically**, and\n",
|
||||||
"- the willingness to **invest 2-4 hours a day for a month** (cf., \"ABC\"-rule at the end)"
|
"- the willingness to **invest 2-4 hours a day for a month**."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
"The course's **main goal** is to **prepare** the student **for further studies** in the \"field\" of **data science**."
|
"The **main goal** of this introduction is to **prepare** the student **for further studies** in the \"field\" of **data science**."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
"This includes but is not limited to more advanced courses on topics such as:\n",
|
"This includes but is not limited to topics such as:\n",
|
||||||
"- linear algebra\n",
|
"- linear algebra\n",
|
||||||
"- statistics & econometrics\n",
|
"- statistics & econometrics\n",
|
||||||
"- data cleaning & wrangling\n",
|
"- data cleaning & wrangling\n",
|
||||||
|
@ -154,7 +154,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
"To follow this course, a working installation of **Python 3.6** or higher is needed.\n",
|
"To follow this book, a working installation of **Python 3.6** or higher is needed.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"A popular and beginner friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack\". Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-07* with Python 3.7 at the time of this writing) for your operating system.\n",
|
"A popular and beginner friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack\". Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-07* with Python 3.7 at the time of this writing) for your operating system.\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -180,7 +180,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
"To download the course's materials as a ZIP file, open the accompanying [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your personal user folder so that the files show up right away)."
|
"To download the materials accompanying this book as a ZIP file, open this [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your personal user folder so that the files show up right away)."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -252,7 +252,7 @@
|
||||||
"\n",
|
"\n",
|
||||||
"Furthermore, Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community (both in academia and business) and often provide a more intuitive alternative to terminal based ways of running Python (e.g., the default [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) as shown above or a more advanced interactive version like [IPython](https://ipython.org/)) or even a full-fledged [Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment) (e.g., the commercial [PyCharm](https://www.jetbrains.com/pycharm/) or the free [Spyder](https://github.com/spyder-ide/spyder)).\n",
|
"Furthermore, Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community (both in academia and business) and often provide a more intuitive alternative to terminal based ways of running Python (e.g., the default [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) as shown above or a more advanced interactive version like [IPython](https://ipython.org/)) or even a full-fledged [Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment) (e.g., the commercial [PyCharm](https://www.jetbrains.com/pycharm/) or the free [Spyder](https://github.com/spyder-ide/spyder)).\n",
|
||||||
"\n",
|
"\n",
|
||||||
"In particular, they allow to mix plain English text with Python code cells. The plain text can be formatted using the [Markdown](https://guides.github.com/features/mastering-markdown/) language and mathematical expressions can be typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Lastly, we can include pictures, plots, and even videos. Because of these features, the notebooks developed for this course come in a self-contained \"tutorial\" style that enables students to learn and review the material on their own."
|
"In particular, they allow to mix plain English text with Python code cells. The plain text can be formatted using the [Markdown](https://guides.github.com/features/mastering-markdown/) language and mathematical expressions can be typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Lastly, we can include pictures, plots, and even videos. Because of these features, the notebooks developed for this book come in a self-contained \"tutorial\" style that enables students to learn and review the material on their own."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -295,7 +295,15 @@
|
||||||
"slide_type": "slide"
|
"slide_type": "slide"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Hello world\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"print(\"Hello world\")"
|
"print(\"Hello world\")"
|
||||||
]
|
]
|
||||||
|
@ -351,7 +359,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
"For this course *programming* is \"defined\" as\n",
|
"In this book *programming* is \"defined\" as:\n",
|
||||||
|
"\n",
|
||||||
"- a **structured** way of **problem solving**\n",
|
"- a **structured** way of **problem solving**\n",
|
||||||
"- by **expressing** the steps of a **computation / process**\n",
|
"- by **expressing** the steps of a **computation / process**\n",
|
||||||
"- and thereby **documenting** the process in a formal way\n",
|
"- and thereby **documenting** the process in a formal way\n",
|
||||||
|
@ -369,7 +378,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
"That is different from *computer science*, which is\n",
|
"That is different from *computer science*, which is:\n",
|
||||||
|
"\n",
|
||||||
"- a field of study comparable to (applied) **mathematics** that\n",
|
"- a field of study comparable to (applied) **mathematics** that\n",
|
||||||
"- asks **abstract** questions (\"Is something computable at all?\"),\n",
|
"- asks **abstract** questions (\"Is something computable at all?\"),\n",
|
||||||
"- develops and analyses **algorithms** and **data structures**,\n",
|
"- develops and analyses **algorithms** and **data structures**,\n",
|
||||||
|
@ -419,6 +429,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
|
"Here is a brief history of and some background on Python:\n",
|
||||||
|
"\n",
|
||||||
"- [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Python’s **[Benevolent Dictator for Life](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life)**) was bored during a week around Christmas 1989 and started Python as a hobby project \"that would keep \\[him\\] occupied\" for some days\n",
|
"- [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Python’s **[Benevolent Dictator for Life](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life)**) was bored during a week around Christmas 1989 and started Python as a hobby project \"that would keep \\[him\\] occupied\" for some days\n",
|
||||||
"- the idea was to create a **general-purpose scripting** language that would allow fast **prototyping** and would **run on every operating system**\n",
|
"- the idea was to create a **general-purpose scripting** language that would allow fast **prototyping** and would **run on every operating system**\n",
|
||||||
"- Python grew through the 90s as van Rossum promoted it via his \"Computer Programming for Everybody\" initiative that had the **goal to encourage a basic level of coding literacy** as an equal knowledge alongside English literacy and math skills\n",
|
"- Python grew through the 90s as van Rossum promoted it via his \"Computer Programming for Everybody\" initiative that had the **goal to encourage a basic level of coding literacy** as an equal knowledge alongside English literacy and math skills\n",
|
||||||
|
@ -427,17 +439,6 @@
|
||||||
"- the language is named after the sketch comedy group [Monty Python](https://en.wikipedia.org/wiki/Monty_Python)"
|
"- the language is named after the sketch comedy group [Monty Python](https://en.wikipedia.org/wiki/Monty_Python)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"metadata": {
|
|
||||||
"slideshow": {
|
|
||||||
"slide_type": "skip"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"source": [
|
|
||||||
"#### Summary"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"Read chapter 0 of the book. Then work through the ten review questions."
|
"Read Chapter 0 of the book. Then work through the ten review questions."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,7 +19,7 @@
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"Read chapter 1 of the book. Then work through the ten review questions."
|
"Read Chapter 1 of the book. Then work through the ten review questions."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -180,7 +180,7 @@
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"**Q10**: [PEP 8](https://www.python.org/dev/peps/pep-0008/) suggests that developers use **$8$ spaces** per level of indentation."
|
"**Q10**: [PEP 8](https://www.python.org/dev/peps/pep-0008/) suggests that developers use **8 spaces** per level of indentation."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -263,7 +263,7 @@
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"**Q11.3**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Use it in the `for`-loop to print the numbers $1$ through $10$ in just one line."
|
"**Q11.3**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Use it in the `for`-loop to print the numbers 1 through 10 in just one line."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -298,7 +298,7 @@
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"**Q11.1**: First, create a list `numbers` with the numbers from $1$ through $100$. You could type all numbers manually but there is of course a smarter way. The built-in [range()](https://docs.python.org/3/library/functions.html#func-range) may be useful here. Read how it works in the documentation. To make the output of [range()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to \"wrap\" it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)."
|
"**Q11.1**: First, create a list `numbers` with the numbers from 1 through 100. You could type all numbers manually but there is of course a smarter way. The built-in [range()](https://docs.python.org/3/library/functions.html#func-range) may be useful here. Read how it works in the documentation. To make the output of [range()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to \"wrap\" it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -335,7 +335,7 @@
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"**Q11.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with $100$ lines of output here."
|
"**Q11.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
3478
02_functions.ipynb
Normal file
3478
02_functions.ipynb
Normal file
File diff suppressed because it is too large
Load diff
397
02_functions_review_and_exercises.ipynb
Normal file
397
02_functions_review_and_exercises.ipynb
Normal file
|
@ -0,0 +1,397 @@
|
||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# Chapter 2: Functions & Modularization"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Content Review"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Read Chapter 2 of the book. Then work through the ten review questions."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Essay Questions "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Answer the following questions briefly with *at most* 300 characters per question!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q1**: What property of the `def` statement makes it a **statement**? Is there a way to use an **expression** to create a function?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q2**: One of the first confusions of experienced programmers coming from other languages to Python regards the observation that **\"everything in Python is an object\"** (cf., this [discussion](https://www.reddit.com/r/learnpython/comments/8rypx9/everything_in_python_is_an_object/)). How does this relate to **functions**?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q3**: What does it mean for a variable to **go out of scope**?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q4**: How can a **global** variable be **shadowed**? Is this good or bad?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q5**: Explain the concept of **forwarding** a function **call**."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q6**: What are **keyword-only arguments** and when is it appropriate to use them?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### True / False Questions"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Motivate your answer with *one short* sentence!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q7**: A mere function **call** is just an **expression**."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q8**: When using the `import` statement, we need to ensure that the imported attributes do **not** overwrite any already defined variables and functions."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q9:** Functions always have a name by which we can call them."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q10**: The [standard library](https://docs.python.org/3/library/index.html) is a collection of numerical tools often used in scientific computing, for example, advanced mathematical functions or utilities for simulation."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Coding Exercises"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Volume of a Sphere"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q11.1**: The [volume of a sphere](https://en.wikipedia.org/wiki/Sphere) is defined as $\\frac{4}{3} * \\pi * r^3$. Calculate this value for $r=10.0$ and round it to 10 digits after the comma. Use the [standard library](https://docs.python.org/3/library/index.html) to obtain a good approximation of $\\pi$."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q11.2**: Encapsulate the logic into a function `sphere_volume()` that takes one *positional* argument `radius` and one *keyword-only* argument `digits` defaulting to `5`. The volume should be returned as a `float` object under *all* circumstances. Document your work appropriately in a docstring according to [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q11.3**: Evaluate the function with `radius = 100.0` and 1, 5, 10, 15, and 20 digits respectively."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q11.4**: What observation do you make?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q11.5**: Using the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in, write a `for`-loop and calculate the volume of a sphere with `radius = 42.0` for all `digits` from `1` through `20`. Print out each volume on a seperate line.\n",
|
||||||
|
"\n",
|
||||||
|
"Note: This is the first task where you actually need to use the [print()](https://docs.python.org/3/library/functions.html#print) built-in function."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q11.6**: What important lesson did you learn about the `float` type?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"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.7.3"
|
||||||
|
},
|
||||||
|
"toc": {
|
||||||
|
"base_numbering": 1,
|
||||||
|
"nav_menu": {},
|
||||||
|
"number_sections": false,
|
||||||
|
"sideBar": true,
|
||||||
|
"skip_h1_title": true,
|
||||||
|
"title_cell": "Table of Contents",
|
||||||
|
"title_sidebar": "Contents",
|
||||||
|
"toc_cell": false,
|
||||||
|
"toc_position": {},
|
||||||
|
"toc_section_display": false,
|
||||||
|
"toc_window_display": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 2
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ As such they can be viewed in a plain web browser:
|
||||||
|
|
||||||
- [00 - Start up](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb)
|
- [00 - Start up](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb)
|
||||||
- [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_of_a_program.ipynb)
|
- [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_of_a_program.ipynb)
|
||||||
|
- [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)
|
||||||
|
|
||||||
However, it is recommended that students **install Python and Jupyter
|
However, it is recommended that students **install Python and Jupyter
|
||||||
locally** and run the code in the notebooks on their own.
|
locally** and run the code in the notebooks on their own.
|
||||||
|
|
11
poetry.lock
generated
11
poetry.lock
generated
|
@ -423,6 +423,14 @@ terminado = ">=0.8.1"
|
||||||
tornado = ">=5.0"
|
tornado = ">=5.0"
|
||||||
traitlets = ">=4.2.1"
|
traitlets = ">=4.2.1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "NumPy is the fundamental package for array computing with Python."
|
||||||
|
name = "numpy"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
version = "1.17.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Utilities for writing pandoc filters in python"
|
description = "Utilities for writing pandoc filters in python"
|
||||||
|
@ -673,7 +681,7 @@ version = "3.5.1"
|
||||||
notebook = ">=4.4.1"
|
notebook = ">=4.4.1"
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "431dace6d8d5c3e390b59e964116bb6219fb45c7a8c09cbfe99d70c704a54320"
|
content-hash = "7c3d541c65a27324b49fc7ed2b84067e223eb4e20c20310ff9e485b016f65f91"
|
||||||
python-versions = "^3.6"
|
python-versions = "^3.6"
|
||||||
|
|
||||||
[metadata.hashes]
|
[metadata.hashes]
|
||||||
|
@ -711,6 +719,7 @@ mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", "
|
||||||
nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"]
|
nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"]
|
||||||
nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"]
|
nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"]
|
||||||
notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"]
|
notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"]
|
||||||
|
numpy = ["05dbfe72684cc14b92568de1bc1f41e5f62b00f714afc9adee42f6311738091f", "0d82cb7271a577529d07bbb05cb58675f2deb09772175fab96dc8de025d8ac05", "10132aa1fef99adc85a905d82e8497a580f83739837d7cbd234649f2e9b9dc58", "12322df2e21f033a60c80319c25011194cd2a21294cc66fee0908aeae2c27832", "16f19b3aa775dddc9814e02a46b8e6ae6a54ed8cf143962b4e53f0471dbd7b16", "3d0b0989dd2d066db006158de7220802899a1e5c8cf622abe2d0bd158fd01c2c", "438a3f0e7b681642898fd7993d38e2bf140a2d1eafaf3e89bb626db7f50db355", "5fd214f482ab53f2cea57414c5fb3e58895b17df6e6f5bca5be6a0bb6aea23bb", "73615d3edc84dd7c4aeb212fa3748fb83217e00d201875a47327f55363cef2df", "7bd355ad7496f4ce1d235e9814ec81ee3d28308d591c067ce92e49f745ba2c2f", "7d077f2976b8f3de08a0dcf5d72083f4af5411e8fddacd662aae27baa2601196", "a4092682778dc48093e8bda8d26ee8360153e2047826f95a3f5eae09f0ae3abf", "b458de8624c9f6034af492372eb2fee41a8e605f03f4732f43fc099e227858b2", "e70fc8ff03a961f13363c2c95ef8285e0cf6a720f8271836f852cc0fa64e97c8", "ee8e9d7cad5fe6dde50ede0d2e978d81eafeaa6233fb0b8719f60214cf226578", "f4a4f6aba148858a5a5d546a99280f71f5ee6ec8182a7d195af1a914195b21a2"]
|
||||||
pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"]
|
pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"]
|
||||||
parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"]
|
parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"]
|
||||||
pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"]
|
pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"]
|
||||||
|
|
|
@ -8,6 +8,7 @@ license = "MIT"
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.6"
|
python = "^3.6"
|
||||||
jupyter = "^1.0"
|
jupyter = "^1.0"
|
||||||
|
numpy = "^1.17"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
black = {version = "^18.3-alpha.0", allows-prereleases = true}
|
black = {version = "^18.3-alpha.0", allows-prereleases = true}
|
||||||
|
|
|
@ -30,6 +30,7 @@ mistune==0.8.4
|
||||||
nbconvert==5.6.0
|
nbconvert==5.6.0
|
||||||
nbformat==4.4.0
|
nbformat==4.4.0
|
||||||
notebook==6.0.1
|
notebook==6.0.1
|
||||||
|
numpy==1.17.2
|
||||||
pandocfilters==1.4.2
|
pandocfilters==1.4.2
|
||||||
parso==0.5.1
|
parso==0.5.1
|
||||||
pexpect==4.7.0
|
pexpect==4.7.0
|
||||||
|
|
75
sample_module.py
Normal file
75
sample_module.py
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
"""This is a sample module.
|
||||||
|
|
||||||
|
It defines three functions average(), average_evens(), and average_odds().
|
||||||
|
The point is to show how we can put Python code in a .py file to be re-used
|
||||||
|
in some other place.
|
||||||
|
|
||||||
|
We should never forget to document the code as well, both on the module
|
||||||
|
level (i.e., this docstring) but also in every function it defines.
|
||||||
|
|
||||||
|
When imported, Python modules are executed top to bottom before the flow of
|
||||||
|
execution returns to wherever they were imported into.
|
||||||
|
|
||||||
|
An important convention is to prefix variables and functions that are not to
|
||||||
|
be used outside the module with a single underscore "_". This way, we can
|
||||||
|
design the code within a module in a modular fashion and only "export" what we
|
||||||
|
want.
|
||||||
|
|
||||||
|
Here, all three functions internally forward the computation to an internal
|
||||||
|
utility function _scaled_average() that contains all the logic common to the
|
||||||
|
three functions. Also, we define one _default_scalar variable that is used as
|
||||||
|
the default for the scalar parameter in each of the functions.
|
||||||
|
|
||||||
|
While this example is stylized, it shows how Python modules are often
|
||||||
|
designed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_default_scalar = 1
|
||||||
|
|
||||||
|
|
||||||
|
def _scaled_average(numbers, scalar):
|
||||||
|
"""Internal utility function to calculate scaled averages."""
|
||||||
|
average = sum(numbers) / len(numbers)
|
||||||
|
return scalar * average
|
||||||
|
|
||||||
|
|
||||||
|
def average(numbers, *, scalar=_default_scalar):
|
||||||
|
"""Calculate the average of all numbers in a list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
numbers (list): list of numbers; may be integers or floats
|
||||||
|
scalar (float, optional): the scalar that multiplies the
|
||||||
|
average of the even numbers
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: (scaled) average
|
||||||
|
"""
|
||||||
|
return _scaled_average(numbers, scalar)
|
||||||
|
|
||||||
|
|
||||||
|
def average_evens(numbers, *, scalar=_default_scalar):
|
||||||
|
"""Calculate the average of all even numbers in a list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
numbers (list): list of numbers; may be integers or floats
|
||||||
|
scalar (float, optional): the scalar that multiplies the
|
||||||
|
average of the even numbers
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: (scaled) average
|
||||||
|
"""
|
||||||
|
return _scaled_average([n for n in numbers if n % 2 == 0], scalar)
|
||||||
|
|
||||||
|
|
||||||
|
def average_odds(numbers, *, scalar=_default_scalar):
|
||||||
|
"""Calculate the average of all odd numbers in a list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
numbers (list): list of numbers; may be integers or floats
|
||||||
|
scalar (float, optional): the scalar that multiplies the
|
||||||
|
average of the even numbers
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: (scaled) average
|
||||||
|
"""
|
||||||
|
return _scaled_average([n for n in numbers if n % 2 != 0], scalar)
|
5
sample_package/__init__.py
Normal file
5
sample_package/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
"""This package provides Vectors and Matrices."""
|
||||||
|
|
||||||
|
from .matrix import Matrix
|
||||||
|
from .utils import norm
|
||||||
|
from .vector import Vector
|
254
sample_package/matrix.py
Normal file
254
sample_package/matrix.py
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
"""This module defines a Matrix class."""
|
||||||
|
|
||||||
|
class Matrix:
|
||||||
|
"""A standard m-by-n-dimensional matrix from linear algebra.
|
||||||
|
|
||||||
|
The class is designed for sub-classing in such a way that
|
||||||
|
the user can adapt the typing class attribute to change,
|
||||||
|
for example, how the entries are stored (e.g., as integers).
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
storage (callable): must return an iterable that is used
|
||||||
|
to store the entries of the matrix; defaults to tuple
|
||||||
|
typing (callable): type casting applied to all vector
|
||||||
|
entries upon creation; defaults to float
|
||||||
|
zero_threshold (float): maximum difference allowed when
|
||||||
|
comparing an entry to zero; defaults to 1e-12
|
||||||
|
"""
|
||||||
|
|
||||||
|
storage = tuple
|
||||||
|
typing = float
|
||||||
|
zero_threshold = 1e-12
|
||||||
|
|
||||||
|
def __init__(self, data):
|
||||||
|
"""Initiate a new matrix.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (iterable of iterables): the matrix's entries;
|
||||||
|
must be provided with rows first, then column;
|
||||||
|
the number of column entries must be consistent across rows
|
||||||
|
where the first row sets the standard;
|
||||||
|
must have at least one element in total
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError:
|
||||||
|
- if the number of columns is inconsistent across the rows
|
||||||
|
- if the provided data do not have enough entries
|
||||||
|
"""
|
||||||
|
self._entries = self.storage(
|
||||||
|
self.storage(self.typing(x) for x in r) for r in data
|
||||||
|
)
|
||||||
|
for row in self._entries[1:]:
|
||||||
|
if len(row) != self.n_cols:
|
||||||
|
raise ValueError("each row must have the same number of entries")
|
||||||
|
if len(self) == 0:
|
||||||
|
raise ValueError("the matrix must have at least one entry")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_columns(cls, data):
|
||||||
|
"""Initiate a new matrix.
|
||||||
|
|
||||||
|
This is an alternative constructor for data provided in column-major order.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (iterable of iterables): the matrix's entries in column-major order;
|
||||||
|
the number of column entries must be consistent per row
|
||||||
|
while the first row sets the correct number;
|
||||||
|
must have at least one element in total
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError:
|
||||||
|
- if the number of columns is inconsistent across the rows
|
||||||
|
- if the provided data do not have enough entries
|
||||||
|
"""
|
||||||
|
return cls(data).transpose()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
name = self.__class__.__name__
|
||||||
|
args = ", ".join(
|
||||||
|
"(" + ", ".join(f"{c:.3f}" for c in r) + ",)" for r in self._entries
|
||||||
|
)
|
||||||
|
return f"{name}(({args}))"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
name = self.__class__.__name__
|
||||||
|
first, last, m, n = self[0], self[-1], self.n_rows, self.n_cols
|
||||||
|
return f"{name}(({first:.1f}, ...), ..., (..., {last:.1f}))[{m:d}x{n:d}]"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def n_rows(self):
|
||||||
|
"""Number of rows in the matrix."""
|
||||||
|
return len(self._entries)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def n_cols(self):
|
||||||
|
"""Number of columns in the matrix."""
|
||||||
|
return len(self._entries[0])
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.n_rows * self.n_cols
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
if isinstance(index, int):
|
||||||
|
if index < 0:
|
||||||
|
index += len(self)
|
||||||
|
if not (0 <= index < len(self)):
|
||||||
|
raise IndexError("integer index out of range")
|
||||||
|
row, col = divmod(index, self.n_cols)
|
||||||
|
return self._entries[row][col]
|
||||||
|
elif (
|
||||||
|
isinstance(index, tuple)
|
||||||
|
and len(index) == 2
|
||||||
|
and isinstance(index[0], int)
|
||||||
|
and isinstance(index[1], int)
|
||||||
|
):
|
||||||
|
return self._entries[index[0]][index[1]]
|
||||||
|
raise TypeError("index must be either an integer or a tuple of two integers")
|
||||||
|
|
||||||
|
def rows(self):
|
||||||
|
"""Iterate over the rows of the matrix.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
rows (Generator): produces Vector instances
|
||||||
|
representing individual rows of the matrix
|
||||||
|
"""
|
||||||
|
return (Vector(r) for r in self._entries)
|
||||||
|
|
||||||
|
def cols(self):
|
||||||
|
"""Iterate over the columns of the matrix.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
columns (Generator): produces Vector instances
|
||||||
|
representing individual columns of the matrix
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
Vector(self._entries[r][c] for r in range(self.n_rows))
|
||||||
|
for c in range(self.n_cols)
|
||||||
|
)
|
||||||
|
|
||||||
|
def entries(self, *, reverse=False, row_major=True):
|
||||||
|
"""Iterate over the entries of the matrix in flat fashion.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
reverse (bool): flag to iterate backwards; defaults to False
|
||||||
|
row_major (bool): flag to iterate in row major order; defaults to False
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
entries (Generator): produces the entries rows of the matrix
|
||||||
|
in the type set in the typing class variable
|
||||||
|
"""
|
||||||
|
if reverse:
|
||||||
|
rows, cols = range(self.n_rows - 1, -1, -1), range(self.n_cols - 1, -1, -1)
|
||||||
|
else:
|
||||||
|
rows, cols = range(self.n_rows), range(self.n_cols)
|
||||||
|
if row_major:
|
||||||
|
return (self._entries[r][c] for r in rows for c in cols)
|
||||||
|
return (self._entries[r][c] for c in cols for r in rows)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self.entries()
|
||||||
|
|
||||||
|
def __reversed__(self):
|
||||||
|
return self.entries(reverse=True)
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
|
||||||
|
raise ValueError("matrices need to be of the same dimensions")
|
||||||
|
return self.__class__(
|
||||||
|
(s_col + o_col for (s_col, o_col) in zip(s_row, o_row))
|
||||||
|
for (s_row, o_row) in zip(self._entries, other._entries)
|
||||||
|
)
|
||||||
|
elif isinstance(other, numbers.Number):
|
||||||
|
return self.__class__((c + other for c in r) for r in self._entries)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
if isinstance(other, Vector):
|
||||||
|
raise TypeError("vectors and matrices cannot be added")
|
||||||
|
return self + other
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
return self + (-other)
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
if isinstance(other, Vector):
|
||||||
|
raise TypeError("vectors and matrices cannot be subtracted")
|
||||||
|
return (-self) + other
|
||||||
|
|
||||||
|
def _matrix_multiply(self, other):
|
||||||
|
if self.n_cols != other.n_rows:
|
||||||
|
raise ValueError("matrices need to have compatible dimensions")
|
||||||
|
return self.__class__((rv * cv for cv in other.cols()) for rv in self.rows())
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
if isinstance(other, numbers.Number):
|
||||||
|
return self.__class__((x * other for x in r) for r in self._entries)
|
||||||
|
elif isinstance(other, Vector):
|
||||||
|
return self._matrix_multiply(other.as_matrix()).as_vector()
|
||||||
|
elif isinstance(other, self.__class__):
|
||||||
|
return self._matrix_multiply(other)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __rmul__(self, other):
|
||||||
|
if isinstance(other, numbers.Number):
|
||||||
|
return self * other
|
||||||
|
elif isinstance(other, Vector):
|
||||||
|
return other.as_matrix(column=False)._matrix_multiply(self).as_vector()
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __truediv__(self, other):
|
||||||
|
if isinstance(other, numbers.Number):
|
||||||
|
return self * (1 / other)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
|
||||||
|
raise ValueError("matrices need to be of the same dimensions")
|
||||||
|
for x, y in zip(self, other):
|
||||||
|
if abs(x - y) > self.zero_threshold:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __pos__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
return self.__class__((-x for x in r) for r in self._entries)
|
||||||
|
|
||||||
|
def __abs__(self):
|
||||||
|
return norm(self)
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(abs(self))
|
||||||
|
|
||||||
|
def __float__(self):
|
||||||
|
if not (self.n_rows == 1 and self.n_cols == 1):
|
||||||
|
raise RuntimeError("matrix must have exactly one entry to become a scalar")
|
||||||
|
return self[0]
|
||||||
|
|
||||||
|
def as_vector(self):
|
||||||
|
"""Cast the matrix as a one-dimensional vector.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
vector (Vector)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError: if not one of the two dimensions is 1
|
||||||
|
"""
|
||||||
|
if not (self.n_rows == 1 or self.n_cols == 1):
|
||||||
|
raise RuntimeError("one dimension (m or n) must be 1")
|
||||||
|
return Vector(x for x in self)
|
||||||
|
|
||||||
|
def transpose(self):
|
||||||
|
"""Transpose the rows and columns of the matrix.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
matrix (Matrix)
|
||||||
|
"""
|
||||||
|
return self.__class__(zip(*self._entries))
|
||||||
|
|
||||||
|
|
||||||
|
from .vector import Vector
|
14
sample_package/utils.py
Normal file
14
sample_package/utils.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
"""This module provides utility functions."""
|
||||||
|
|
||||||
|
|
||||||
|
def norm(vector_or_matrix):
|
||||||
|
"""Calculate the Frobenius or Euclidean norm of a matrix or vector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
vector_or_matrix (Vector/Matrix): the entries whose squares
|
||||||
|
are to be summed up
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
norm (float)
|
||||||
|
"""
|
||||||
|
return math.sqrt(sum(x ** 2 for x in vector_or_matrix))
|
141
sample_package/vector.py
Normal file
141
sample_package/vector.py
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
"""This module defines a Vector class."""
|
||||||
|
|
||||||
|
from .matrix import Matrix
|
||||||
|
|
||||||
|
|
||||||
|
class Vector:
|
||||||
|
"""A standard one-dimensional vector from linear algebra.
|
||||||
|
|
||||||
|
The class is designed for sub-classing in such a way that
|
||||||
|
the user can adapt the typing class attribute to change,
|
||||||
|
for example, how the entries are stored (e.g., as integers).
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
storage (callable): must return an iterable that is used
|
||||||
|
to store the entries of the vector; defaults to tuple
|
||||||
|
typing (callable): type casting applied to all vector
|
||||||
|
entries upon creation; defaults to float
|
||||||
|
zero_threshold (float): maximum difference allowed when
|
||||||
|
comparing an entry to zero; defaults to 1e-12
|
||||||
|
"""
|
||||||
|
|
||||||
|
storage = tuple
|
||||||
|
typing = float
|
||||||
|
zero_threshold = 1e-12
|
||||||
|
|
||||||
|
def __init__(self, data):
|
||||||
|
"""Initiate a new vector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (iterable): the vector's entries;
|
||||||
|
must have at least one element
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: if the provided data do not have enough entries
|
||||||
|
"""
|
||||||
|
self._entries = self.storage(self.typing(x) for x in data)
|
||||||
|
if len(self) == 0:
|
||||||
|
raise ValueError("the vector must have at least one entry")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
name, args = self.__class__.__name__, ", ".join(f"{x:.3f}" for x in self)
|
||||||
|
return f"{name}(({args}))"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
name, first, last, entries = (
|
||||||
|
self.__class__.__name__,
|
||||||
|
self[0],
|
||||||
|
self[-1],
|
||||||
|
len(self),
|
||||||
|
)
|
||||||
|
return f"{name}({first:.1f}, ..., {last:.1f})[{entries:d}]"
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._entries)
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
if not isinstance(index, int):
|
||||||
|
raise TypeError("index must be an integer")
|
||||||
|
return self._entries[index]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._entries)
|
||||||
|
|
||||||
|
def __reversed__(self):
|
||||||
|
return reversed(self._entries)
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if len(self) != len(other):
|
||||||
|
raise ValueError("vectors need to be of the same length")
|
||||||
|
return self.__class__(x + y for (x, y) in zip(self, other))
|
||||||
|
elif isinstance(other, numbers.Number):
|
||||||
|
return self.__class__(x + other for x in self)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
return self + other
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
return self + (-other)
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
return (-self) + other
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if len(self) != len(other):
|
||||||
|
raise ValueError("vectors need to be of the same length")
|
||||||
|
return sum(x * y for (x, y) in zip(self, other))
|
||||||
|
elif isinstance(other, numbers.Number):
|
||||||
|
return self.__class__(x * other for x in self)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __rmul__(self, other):
|
||||||
|
return self * other
|
||||||
|
|
||||||
|
def __truediv__(self, other):
|
||||||
|
if isinstance(other, numbers.Number):
|
||||||
|
return self * (1 / other)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if len(self) != len(other):
|
||||||
|
raise ValueError("vectors need to be of the same length")
|
||||||
|
for x, y in zip(self, other):
|
||||||
|
if abs(x - y) > self.zero_threshold:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __pos__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
return self.__class__(-x for x in self)
|
||||||
|
|
||||||
|
def __abs__(self):
|
||||||
|
return norm(self)
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(abs(self))
|
||||||
|
|
||||||
|
def __float__(self):
|
||||||
|
if len(self) != 1:
|
||||||
|
raise RuntimeError("vector must have exactly one entry to become a scalar")
|
||||||
|
return self[0]
|
||||||
|
|
||||||
|
def as_matrix(self, *, column=True):
|
||||||
|
"""Convert the vector into a matrix.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
column (bool): if the vector should be interpreted as
|
||||||
|
as a column vector or not; defaults to True
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
matrix (Matrix)
|
||||||
|
"""
|
||||||
|
if column:
|
||||||
|
return Matrix([x] for x in self)
|
||||||
|
return Matrix([(x for x in self)])
|
Loading…
Reference in a new issue