Compare commits
3 commits
2fd4ccc54c
...
adc4b104cd
Author | SHA1 | Date | |
---|---|---|---|
![]() |
adc4b104cd | ||
![]() |
d64e302ccf | ||
![]() |
e4ec849cbc |
1361
exercises/release/exercise-08/1__numpy-basics.ipynb
Normal file
1361
exercises/release/exercise-08/1__numpy-basics.ipynb
Normal file
File diff suppressed because it is too large
Load diff
542
exercises/release/exercise-08/2__Optimization_first_steps.ipynb
Normal file
542
exercises/release/exercise-08/2__Optimization_first_steps.ipynb
Normal file
File diff suppressed because one or more lines are too long
517
exercises/source/exercise-07/1__classes.ipynb
Executable file
517
exercises/source/exercise-07/1__classes.ipynb
Executable file
|
@ -0,0 +1,517 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-df7302b349aba739",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"# Classes in Python\n",
|
||||
"\n",
|
||||
"First, let's consider some data in a plain Python dictionary:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-5eb4e7d87487b636",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"car1 = {\n",
|
||||
" 'name': 'Fer',\n",
|
||||
" 'worth': 60000,\n",
|
||||
" 'type_': 'convertible',\n",
|
||||
" 'color': 'red'\n",
|
||||
"}\n",
|
||||
"car2 = {\n",
|
||||
" 'name': 'Jump',\n",
|
||||
" 'worth': 10000,\n",
|
||||
" 'type_': 'van',\n",
|
||||
" 'color': 'blue'\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-b6fe8f994da7634c",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Fer'"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"car1['name']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-2b0e27117c238bca",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"Now, let's make a Python class which will hold this same kind of data:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-17b8022fd73e630c",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Car:\n",
|
||||
" def __init__(self,name,worth,type_,color):\n",
|
||||
" self.name = name\n",
|
||||
" self.worth = worth\n",
|
||||
" self.type_ = type_\n",
|
||||
" self.color = color\n",
|
||||
" def print_car(self):\n",
|
||||
" print(\"%s is worth %d and is a %s %s.\"%(self.name, self.worth, self.color, self.type_))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-98bd27b61013b730",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"car1 = Car(\"Fer\",60000,\"convertible\",\"red\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-1055b5581d221586",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Takeaway message\n",
|
||||
"\n",
|
||||
"- The data in an instances of a class are conceptually very similar to python dicts with a few special features. One of these special features is methods. Another is that you access instance variables with with the `x_instance.name` syntax instead of `x_dict['name']` syntax."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-ce0b6c546e3ec2d5",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Q1 creating an instance of a class\n",
|
||||
"\n",
|
||||
"We have a class defined for vehicles. Create two new vehicles (\"instances of the class\") with the variable names `car1` and `car2`. Set car1 to be a red convertible worth EUR 60,000.00 with a name of Fer, and car2 to be a blue van named Jump worth EUR 10,000.00."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-ae7291a16e80887f",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Vehicle:\n",
|
||||
" def __init__(self, color, worth, name): \n",
|
||||
" self.color = color\n",
|
||||
" self.worth = worth\n",
|
||||
" self.name = name\n",
|
||||
" def get_description(self):\n",
|
||||
" return 'name: {}, worth: {}, color: {}'.format(self.name, self.worth, self.color)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-3f2711d5634d8201",
|
||||
"locked": false,
|
||||
"schema_version": 3,
|
||||
"solution": true,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"car1 = Vehicle('red', 60000, 'Fer')\n",
|
||||
"car2 = Vehicle('blue', 10000, 'Jump')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": true,
|
||||
"grade_id": "cell-081ffcbe096c0760",
|
||||
"locked": true,
|
||||
"points": 1,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This checks if your code works. Do not change it. It should run without error.\n",
|
||||
"assert( car1.get_description()==\"name: Fer, worth: 60000, color: red\" )\n",
|
||||
"assert( car2.get_description()==\"name: Jump, worth: 10000, color: blue\" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-634909b4b67c316a",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Q2 creating a class\n",
|
||||
"\n",
|
||||
"Create a class called `Pet`. The `__init__` function should take 3 arguments: `self`, `name`, and `sound`. The `name`, and `sound` arguments should be assigned to the instance variables `self.name` and `self.sound`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-6976ffa1c6d95602",
|
||||
"locked": false,
|
||||
"schema_version": 3,
|
||||
"solution": true,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Pet:\n",
|
||||
" def __init__(self, name, sound):\n",
|
||||
" self.name = name\n",
|
||||
" self.sound = sound"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": true,
|
||||
"grade_id": "cell-c49bb38441eca03a",
|
||||
"locked": true,
|
||||
"points": 1,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This checks if your code works. Do not change it. It should run without error.\n",
|
||||
"my_pet = Pet('Bob', 'talk')\n",
|
||||
"assert(my_pet.name=='Bob')\n",
|
||||
"assert(my_pet.sound=='talk')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-f34bb63b09eccd59",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Q3 writing a method\n",
|
||||
"\n",
|
||||
"Now, create a new class called `NoisyPet`. The `__init__` function should be like in `Pet`. (It takes 3 arguments: `self`, `name`, and `sound`. The `name`, and `sound` arguments should be assigned to the instance variables `self.name` and `self.sound`.)\n",
|
||||
"\n",
|
||||
"`NoisyPet` should have an additional method called `make_noise` which takes zero additional arguments (of course `self` is required). The `make_noise` for a name of `\"cricket\"` and sound of `\"chirp\"`, this method should return a string like `\"My pet cricket makes a noise like chirp\"`. Thus, you will need to return a string you have created using string formatting."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-30ebdf29d3de2e41",
|
||||
"locked": false,
|
||||
"schema_version": 3,
|
||||
"solution": true,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class NoisyPet:\n",
|
||||
" def __init__(self, name, sound):\n",
|
||||
" self.name = name\n",
|
||||
" self.sound = sound\n",
|
||||
" def make_noise(self):\n",
|
||||
" return \"My pet {} makes a noise like {}\".format(self.name, self.sound)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": true,
|
||||
"grade_id": "cell-232b938072cf100c",
|
||||
"locked": true,
|
||||
"points": 1,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This checks if your code works. Do not change it. It should run without error.\n",
|
||||
"assert(NoisyPet(\"cricket\", \"chirp\").make_noise()==\"My pet cricket makes a noise like chirp\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-ded0dd503cbcd3b7",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Q4 using instances 1\n",
|
||||
"\n",
|
||||
"Now create a list named `my_pets` with 5 instances of the NoisyPet class given the following names and noises.\n",
|
||||
"\n",
|
||||
"| name | noise |\n",
|
||||
"| --- | --- |\n",
|
||||
"| Fido | slobber |\n",
|
||||
"| Mr. Skinny Legs | (silent) |\n",
|
||||
"| cricket | chirp |\n",
|
||||
"| Adelheid | cackle |\n",
|
||||
"| Bello | bark |"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-9e7de69ef49036d8",
|
||||
"locked": false,
|
||||
"schema_version": 3,
|
||||
"solution": true,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"my_pets = [\n",
|
||||
" NoisyPet(\"Fido\", \"slobber\"),\n",
|
||||
" NoisyPet(\"Mr. Skinny Legs\", \"(silent)\"),\n",
|
||||
" NoisyPet(\"cricket\", \"chirp\"),\n",
|
||||
" NoisyPet(\"Adelheid\", \"cackle\"),\n",
|
||||
" NoisyPet(\"Bello\", \"bark\"),\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": true,
|
||||
"grade_id": "cell-ef3307b4ff8e4134",
|
||||
"locked": true,
|
||||
"points": 1,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This checks if your code works. Do not change it. It should run without error.\n",
|
||||
"assert(type(my_pets)==list)\n",
|
||||
"assert(len(my_pets)==5)\n",
|
||||
"for my_pet in my_pets:\n",
|
||||
" assert(isinstance(my_pet,NoisyPet))\n",
|
||||
" my_pet.make_noise()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-7f3ab5609b4c6d4b",
|
||||
"locked": true,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Q5 using instances 2\n",
|
||||
"\n",
|
||||
"Now create a function list named `get_pet_name_length` which takes a single argument, which is an instance of the NoisyPet class. It should return the number of letters in the pet's name."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": false,
|
||||
"grade_id": "cell-4fb1927bd55587b9",
|
||||
"locked": false,
|
||||
"schema_version": 3,
|
||||
"solution": true,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_pet_name_length(pet):\n",
|
||||
" return len(pet.name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {
|
||||
"nbgrader": {
|
||||
"grade": true,
|
||||
"grade_id": "cell-adabc63962b7ba48",
|
||||
"locked": true,
|
||||
"points": 1,
|
||||
"schema_version": 3,
|
||||
"solution": false,
|
||||
"task": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This checks if your code works. Do not change it. It should run without error.\n",
|
||||
"assert(get_pet_name_length(NoisyPet(\"Bello\", \"bark\"))==5)\n",
|
||||
"assert(get_pet_name_length(NoisyPet(\"cricket\", \"chirp\"))==7)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"celltoolbar": "Create Assignment",
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
1269
exercises/source/exercise-07/2__pandas_intro.ipynb
Executable file
1269
exercises/source/exercise-07/2__pandas_intro.ipynb
Executable file
File diff suppressed because one or more lines are too long
69
exercises/source/exercise-07/3__plot_csv.ipynb
Normal file
69
exercises/source/exercise-07/3__plot_csv.ipynb
Normal file
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0a7b652b-7846-464c-acf3-6629ba66a192",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Create a standalone Python program\n",
|
||||
"\n",
|
||||
"The instructions for this exercise are in this Jupyter notebook, but to successfully complete the exercise, you need to write a plain Python `.py` file called `plot_pcr_data.py` that runs from the command line. Write your program so that when you run it like this:\n",
|
||||
"\n",
|
||||
" python plot_pcr_data.py pcr_sample_1.csv\n",
|
||||
"\n",
|
||||
"It will read load the CSV file named `pcr_sample_1.csv` and save a plot called `pcr_sample_1.csv.png`. This data file is the result of a [real-time PCR](https://en.wikipedia.org/wiki/Real-time_polymerase_chain_reaction) experiment in a 6 well plate. The plot should plot number of cycles on the X axis and fluorescence intensity on the Y axis. There should be a line for the data from each well in the experiment.\n",
|
||||
"\n",
|
||||
"Hints:\n",
|
||||
"\n",
|
||||
"- Remember that you can get the command-line arguments to a python program by importing the `sys` module and accessing the `sys.argv` variable, which is a list of strings. So the filename with the data is provided as a command-line argument to your python program.\n",
|
||||
"- Read the CSV data from the provided filename using Pandas `read_csv()` function.\n",
|
||||
"- Plot the results with [seaborn's `lineplot()`](https://seaborn.pydata.org/generated/seaborn.lineplot.html#seaborn.lineplot) function. The X value of the plot will by PCR cycle number and the Y value of the plot will be fluorescence intensity. To use one line per well, use the `hue` keyword argument.\n",
|
||||
"- Save this figure (with matplotlib.pylot's `savefig()`) to a file with the name equal to the original file name with `.png` appended to it (e.g. for the above example with `pcr_sample_1.csv` as input, save the figure to `pcr_sample_1.csv.png`).\n",
|
||||
"\n",
|
||||
"Use the following imports block (and do not import anything else):\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"import sys\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import seaborn as sns\n",
|
||||
"import pandas as pd\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"When you are done with your program `plot_pcr_data.py`, upload it to the directory for this exercise. I will run it with a new CSV data file from a different PCR experiments to check that it works.\n",
|
||||
"\n",
|
||||
"With `pcr_sample_1.csv`, your plot should look like this:\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "79875b52-ccd1-4a13-bf1b-547a15466ebc",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
17
exercises/source/exercise-07/README.md
Normal file
17
exercises/source/exercise-07/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Notes:
|
||||
|
||||
Release these files:
|
||||
|
||||
1__classes.ipynb
|
||||
2__pandas_intro.ipynb
|
||||
3__plot_csv.ipynb
|
||||
pcr_results.png
|
||||
pcr_sample_1.csv
|
||||
|
||||
Do not release these files:
|
||||
|
||||
make_fake_data.ipynb - generate pcr_sample_1.csv
|
||||
pcr_sample_1.csv.png - saved by example solution
|
||||
plot_pcr_data.py - example solution
|
||||
README.md - this file
|
||||
|
118
exercises/source/exercise-07/make_fake_data.ipynb
Normal file
118
exercises/source/exercise-07/make_fake_data.ipynb
Normal file
File diff suppressed because one or more lines are too long
BIN
exercises/source/exercise-07/pcr_results.png
Normal file
BIN
exercises/source/exercise-07/pcr_results.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
271
exercises/source/exercise-07/pcr_sample_1.csv
Normal file
271
exercises/source/exercise-07/pcr_sample_1.csv
Normal file
|
@ -0,0 +1,271 @@
|
|||
well,cycle,fluorescence
|
||||
well 1,0,-3.0
|
||||
well 1,1,-3.0
|
||||
well 1,2,-3.0
|
||||
well 1,3,-3.0
|
||||
well 1,4,-3.0
|
||||
well 1,5,-3.0
|
||||
well 1,6,-3.0
|
||||
well 1,7,-3.0
|
||||
well 1,8,-3.0
|
||||
well 1,9,-3.0
|
||||
well 1,10,-3.0
|
||||
well 1,11,-3.0
|
||||
well 1,12,-3.0
|
||||
well 1,13,-3.0
|
||||
well 1,14,-3.0
|
||||
well 1,15,-1.7999999999999998
|
||||
well 1,16,-1.0799999999999998
|
||||
well 1,17,-0.6479999999999999
|
||||
well 1,18,-0.3887999999999999
|
||||
well 1,19,-0.23327999999999993
|
||||
well 1,20,-0.13996799999999995
|
||||
well 1,21,-0.08398079999999997
|
||||
well 1,22,-0.05038847999999998
|
||||
well 1,23,-0.030233087999999984
|
||||
well 1,24,-0.018139852799999988
|
||||
well 1,25,-0.010883911679999993
|
||||
well 1,26,-0.006530347007999996
|
||||
well 1,27,-0.0039182082047999976
|
||||
well 1,28,-0.0023509249228799984
|
||||
well 1,29,-0.001410554953727999
|
||||
well 1,30,-0.0008463329722367994
|
||||
well 1,31,-0.0005077997833420796
|
||||
well 1,32,-0.0003046798700052478
|
||||
well 1,33,-0.00018280792200314866
|
||||
well 1,34,-0.0001096847532018892
|
||||
well 1,35,-6.581085192113351e-05
|
||||
well 1,36,-3.948651115268011e-05
|
||||
well 1,37,-2.3691906691608062e-05
|
||||
well 1,38,-1.4215144014964837e-05
|
||||
well 1,39,-8.529086408978902e-06
|
||||
well 1,40,-5.117451845387341e-06
|
||||
well 1,41,-3.0704711072324042e-06
|
||||
well 1,42,-1.8422826643394425e-06
|
||||
well 1,43,-1.1053695986036654e-06
|
||||
well 1,44,-6.632217591621993e-07
|
||||
well 2,0,-3.0
|
||||
well 2,1,-3.0
|
||||
well 2,2,-3.0
|
||||
well 2,3,-3.0
|
||||
well 2,4,-3.0
|
||||
well 2,5,-3.0
|
||||
well 2,6,-3.0
|
||||
well 2,7,-3.0
|
||||
well 2,8,-3.0
|
||||
well 2,9,-3.0
|
||||
well 2,10,-3.0
|
||||
well 2,11,-3.0
|
||||
well 2,12,-1.7999999999999998
|
||||
well 2,13,-1.0799999999999998
|
||||
well 2,14,-0.6479999999999999
|
||||
well 2,15,-0.3887999999999999
|
||||
well 2,16,-0.23327999999999993
|
||||
well 2,17,-0.13996799999999995
|
||||
well 2,18,-0.08398079999999997
|
||||
well 2,19,-0.05038847999999998
|
||||
well 2,20,-0.030233087999999984
|
||||
well 2,21,-0.018139852799999988
|
||||
well 2,22,-0.010883911679999993
|
||||
well 2,23,-0.006530347007999996
|
||||
well 2,24,-0.0039182082047999976
|
||||
well 2,25,-0.0023509249228799984
|
||||
well 2,26,-0.001410554953727999
|
||||
well 2,27,-0.0008463329722367994
|
||||
well 2,28,-0.0005077997833420796
|
||||
well 2,29,-0.0003046798700052478
|
||||
well 2,30,-0.00018280792200314866
|
||||
well 2,31,-0.0001096847532018892
|
||||
well 2,32,-6.581085192113351e-05
|
||||
well 2,33,-3.948651115268011e-05
|
||||
well 2,34,-2.3691906691608062e-05
|
||||
well 2,35,-1.4215144014964837e-05
|
||||
well 2,36,-8.529086408978902e-06
|
||||
well 2,37,-5.117451845387341e-06
|
||||
well 2,38,-3.0704711072324042e-06
|
||||
well 2,39,-1.8422826643394425e-06
|
||||
well 2,40,-1.1053695986036654e-06
|
||||
well 2,41,-6.632217591621993e-07
|
||||
well 2,42,-3.9793305549731955e-07
|
||||
well 2,43,-2.3875983329839174e-07
|
||||
well 2,44,-1.4325589997903504e-07
|
||||
well 3,0,-3.0
|
||||
well 3,1,-3.0
|
||||
well 3,2,-3.0
|
||||
well 3,3,-3.0
|
||||
well 3,4,-3.0
|
||||
well 3,5,-3.0
|
||||
well 3,6,-3.0
|
||||
well 3,7,-3.0
|
||||
well 3,8,-3.0
|
||||
well 3,9,-3.0
|
||||
well 3,10,-3.0
|
||||
well 3,11,-3.0
|
||||
well 3,12,-3.0
|
||||
well 3,13,-3.0
|
||||
well 3,14,-3.0
|
||||
well 3,15,-3.0
|
||||
well 3,16,-1.7999999999999998
|
||||
well 3,17,-1.0799999999999998
|
||||
well 3,18,-0.6479999999999999
|
||||
well 3,19,-0.3887999999999999
|
||||
well 3,20,-0.23327999999999993
|
||||
well 3,21,-0.13996799999999995
|
||||
well 3,22,-0.08398079999999997
|
||||
well 3,23,-0.05038847999999998
|
||||
well 3,24,-0.030233087999999984
|
||||
well 3,25,-0.018139852799999988
|
||||
well 3,26,-0.010883911679999993
|
||||
well 3,27,-0.006530347007999996
|
||||
well 3,28,-0.0039182082047999976
|
||||
well 3,29,-0.0023509249228799984
|
||||
well 3,30,-0.001410554953727999
|
||||
well 3,31,-0.0008463329722367994
|
||||
well 3,32,-0.0005077997833420796
|
||||
well 3,33,-0.0003046798700052478
|
||||
well 3,34,-0.00018280792200314866
|
||||
well 3,35,-0.0001096847532018892
|
||||
well 3,36,-6.581085192113351e-05
|
||||
well 3,37,-3.948651115268011e-05
|
||||
well 3,38,-2.3691906691608062e-05
|
||||
well 3,39,-1.4215144014964837e-05
|
||||
well 3,40,-8.529086408978902e-06
|
||||
well 3,41,-5.117451845387341e-06
|
||||
well 3,42,-3.0704711072324042e-06
|
||||
well 3,43,-1.8422826643394425e-06
|
||||
well 3,44,-1.1053695986036654e-06
|
||||
well 4,0,-3.0
|
||||
well 4,1,-3.0
|
||||
well 4,2,-1.7999999999999998
|
||||
well 4,3,-1.0799999999999998
|
||||
well 4,4,-0.6479999999999999
|
||||
well 4,5,-0.3887999999999999
|
||||
well 4,6,-0.23327999999999993
|
||||
well 4,7,-0.13996799999999995
|
||||
well 4,8,-0.08398079999999997
|
||||
well 4,9,-0.05038847999999998
|
||||
well 4,10,-0.030233087999999984
|
||||
well 4,11,-0.018139852799999988
|
||||
well 4,12,-0.010883911679999993
|
||||
well 4,13,-0.006530347007999996
|
||||
well 4,14,-0.0039182082047999976
|
||||
well 4,15,-0.0023509249228799984
|
||||
well 4,16,-0.001410554953727999
|
||||
well 4,17,-0.0008463329722367994
|
||||
well 4,18,-0.0005077997833420796
|
||||
well 4,19,-0.0003046798700052478
|
||||
well 4,20,-0.00018280792200314866
|
||||
well 4,21,-0.0001096847532018892
|
||||
well 4,22,-6.581085192113351e-05
|
||||
well 4,23,-3.948651115268011e-05
|
||||
well 4,24,-2.3691906691608062e-05
|
||||
well 4,25,-1.4215144014964837e-05
|
||||
well 4,26,-8.529086408978902e-06
|
||||
well 4,27,-5.117451845387341e-06
|
||||
well 4,28,-3.0704711072324042e-06
|
||||
well 4,29,-1.8422826643394425e-06
|
||||
well 4,30,-1.1053695986036654e-06
|
||||
well 4,31,-6.632217591621993e-07
|
||||
well 4,32,-3.9793305549731955e-07
|
||||
well 4,33,-2.3875983329839174e-07
|
||||
well 4,34,-1.4325589997903504e-07
|
||||
well 4,35,-8.595353998742102e-08
|
||||
well 4,36,-5.157212399245261e-08
|
||||
well 4,37,-3.0943274395471566e-08
|
||||
well 4,38,-1.8565964637282937e-08
|
||||
well 4,39,-1.1139578782369761e-08
|
||||
well 4,40,-6.683747269421856e-09
|
||||
well 4,41,-4.0102483616531134e-09
|
||||
well 4,42,-2.4061490169918677e-09
|
||||
well 4,43,-1.4436894101951206e-09
|
||||
well 4,44,-8.662136461170723e-10
|
||||
well 5,0,-3.0
|
||||
well 5,1,-3.0
|
||||
well 5,2,-3.0
|
||||
well 5,3,-3.0
|
||||
well 5,4,-3.0
|
||||
well 5,5,-3.0
|
||||
well 5,6,-3.0
|
||||
well 5,7,-3.0
|
||||
well 5,8,-3.0
|
||||
well 5,9,-3.0
|
||||
well 5,10,-3.0
|
||||
well 5,11,-3.0
|
||||
well 5,12,-3.0
|
||||
well 5,13,-3.0
|
||||
well 5,14,-1.7999999999999998
|
||||
well 5,15,-1.0799999999999998
|
||||
well 5,16,-0.6479999999999999
|
||||
well 5,17,-0.3887999999999999
|
||||
well 5,18,-0.23327999999999993
|
||||
well 5,19,-0.13996799999999995
|
||||
well 5,20,-0.08398079999999997
|
||||
well 5,21,-0.05038847999999998
|
||||
well 5,22,-0.030233087999999984
|
||||
well 5,23,-0.018139852799999988
|
||||
well 5,24,-0.010883911679999993
|
||||
well 5,25,-0.006530347007999996
|
||||
well 5,26,-0.0039182082047999976
|
||||
well 5,27,-0.0023509249228799984
|
||||
well 5,28,-0.001410554953727999
|
||||
well 5,29,-0.0008463329722367994
|
||||
well 5,30,-0.0005077997833420796
|
||||
well 5,31,-0.0003046798700052478
|
||||
well 5,32,-0.00018280792200314866
|
||||
well 5,33,-0.0001096847532018892
|
||||
well 5,34,-6.581085192113351e-05
|
||||
well 5,35,-3.948651115268011e-05
|
||||
well 5,36,-2.3691906691608062e-05
|
||||
well 5,37,-1.4215144014964837e-05
|
||||
well 5,38,-8.529086408978902e-06
|
||||
well 5,39,-5.117451845387341e-06
|
||||
well 5,40,-3.0704711072324042e-06
|
||||
well 5,41,-1.8422826643394425e-06
|
||||
well 5,42,-1.1053695986036654e-06
|
||||
well 5,43,-6.632217591621993e-07
|
||||
well 5,44,-3.9793305549731955e-07
|
||||
well 6,0,-3.0
|
||||
well 6,1,-3.0
|
||||
well 6,2,-3.0
|
||||
well 6,3,-3.0
|
||||
well 6,4,-3.0
|
||||
well 6,5,-3.0
|
||||
well 6,6,-3.0
|
||||
well 6,7,-3.0
|
||||
well 6,8,-3.0
|
||||
well 6,9,-3.0
|
||||
well 6,10,-3.0
|
||||
well 6,11,-3.0
|
||||
well 6,12,-3.0
|
||||
well 6,13,-3.0
|
||||
well 6,14,-3.0
|
||||
well 6,15,-3.0
|
||||
well 6,16,-3.0
|
||||
well 6,17,-3.0
|
||||
well 6,18,-3.0
|
||||
well 6,19,-3.0
|
||||
well 6,20,-3.0
|
||||
well 6,21,-3.0
|
||||
well 6,22,-3.0
|
||||
well 6,23,-3.0
|
||||
well 6,24,-3.0
|
||||
well 6,25,-3.0
|
||||
well 6,26,-3.0
|
||||
well 6,27,-3.0
|
||||
well 6,28,-3.0
|
||||
well 6,29,-3.0
|
||||
well 6,30,-3.0
|
||||
well 6,31,-3.0
|
||||
well 6,32,-3.0
|
||||
well 6,33,-3.0
|
||||
well 6,34,-3.0
|
||||
well 6,35,-3.0
|
||||
well 6,36,-3.0
|
||||
well 6,37,-3.0
|
||||
well 6,38,-3.0
|
||||
well 6,39,-3.0
|
||||
well 6,40,-3.0
|
||||
well 6,41,-3.0
|
||||
well 6,42,-3.0
|
||||
well 6,43,-3.0
|
||||
well 6,44,-3.0
|
|
BIN
exercises/source/exercise-07/pcr_sample_1.csv.png
Normal file
BIN
exercises/source/exercise-07/pcr_sample_1.csv.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
10
exercises/source/exercise-07/plot_pcr_data.py
Normal file
10
exercises/source/exercise-07/plot_pcr_data.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
import sys
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
import pandas as pd
|
||||
|
||||
csv_filename = sys.argv[1]
|
||||
df = pd.read_csv(csv_filename)
|
||||
sns.lineplot(data=df, x="cycle", y="fluorescence", hue="well")
|
||||
png_filename = csv_filename + ".png"
|
||||
plt.savefig(png_filename)
|
1568
lectures/lecture-07/1 - numpy.ipynb
Normal file
1568
lectures/lecture-07/1 - numpy.ipynb
Normal file
File diff suppressed because it is too large
Load diff
271
lectures/lecture-07/2 - optimization.ipynb
Normal file
271
lectures/lecture-07/2 - optimization.ipynb
Normal file
|
@ -0,0 +1,271 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Optimization\n",
|
||||
"\n",
|
||||
"Let's consider the following code:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"# position of flower in 2D\n",
|
||||
"flower = np.array([29.1, 19.1])\n",
|
||||
"\n",
|
||||
"def make_bee_track(t):\n",
|
||||
" \"\"\"Find bee position at time t\"\"\"\n",
|
||||
" pos0 = (13.21, 4.56)\n",
|
||||
" velocity = (3.1, 0.25)\n",
|
||||
" pos_x = pos0[0] + t*velocity[0]\n",
|
||||
" pos_y = pos0[1] + t*velocity[1]\n",
|
||||
" return np.array([pos_x,pos_y])\n",
|
||||
"\n",
|
||||
"t = np.linspace(0,15,10)\n",
|
||||
"bee_track = make_bee_track(t)\n",
|
||||
"\n",
|
||||
"fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(4,3))\n",
|
||||
"ax.plot( [flower[0]], [flower[1]], 'or', label='flower' )\n",
|
||||
"ax.plot( bee_track[0], bee_track[1], '.-k', label='bee')\n",
|
||||
"ax.axis('equal')\n",
|
||||
"ax.legend()\n",
|
||||
"ax.set_xlabel('x')\n",
|
||||
"ax.set_ylabel('y')\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"In the above code, we *parameterized* the bee trajectory by the variable `t` in the function `make_bee_track()`. This means we could get a new point on the track by choosing a new value of `t`. For example:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(make_bee_track(0.0))\n",
|
||||
"print(make_bee_track(0.1))\n",
|
||||
"print(make_bee_track(0.2))\n",
|
||||
"print(make_bee_track(1.0))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now let's measure the distance between the bee and the flower."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def compute_distance(a,b):\n",
|
||||
" a = np.array(a)\n",
|
||||
" b = np.array(b)\n",
|
||||
" return np.sqrt(np.sum((a-b)**2))\n",
|
||||
"\n",
|
||||
"n_time_steps = bee_track.shape[1]\n",
|
||||
"distance = np.zeros(n_time_steps)\n",
|
||||
"for i in range(n_time_steps):\n",
|
||||
" bee_pos = bee_track[:,i]\n",
|
||||
" distance[i] = compute_distance(bee_pos, flower)\n",
|
||||
"display(distance)\n",
|
||||
"\n",
|
||||
"fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(4,3))\n",
|
||||
"ax.plot( t, distance )\n",
|
||||
"ax.set_xlabel('t')\n",
|
||||
"ax.set_ylabel('distance')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Given the plot of distance versus t above, we can see the distance is minimized when t is near 5. What is the bee's position when t is 5?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(make_bee_track(5))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can check back to the xy plot to see, indeed, this point is pretty close to the flower.\n",
|
||||
"\n",
|
||||
"What if we want to know, however, exactly the closest point? There are several ways to find this. Here we are going to use a \"brute force\" approach which will work on many different problems. Specifically, we will use [scipy.optimize.minimize_scalar](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html). The overall idea of this kind of *numerical optimization* is that we find the best fitting parameter to minimize our \"error\".\n",
|
||||
"\n",
|
||||
"In this example, we are not so much concerned with the exact algorithm being used, but with the way we call this algorithm.\n",
|
||||
"\n",
|
||||
"Let's create a function which uses the `flower` location to compute distance:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def calc_distance_func(t):\n",
|
||||
" # assume variable 'flower' in our global scope\n",
|
||||
" x1, y1 = flower\n",
|
||||
" # calculate bee position (also assuming 'make_bee_track' in scope)\n",
|
||||
" x2, y2 = make_bee_track(t)\n",
|
||||
" dist = compute_distance((x1,y1), (x2,y2))\n",
|
||||
" print(f't: {t} -> dist: {dist}')\n",
|
||||
" return dist"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"calc_distance_func(0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"calc_distance_func(5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import scipy.optimize\n",
|
||||
"result = scipy.optimize.minimize_scalar(calc_distance_func)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"scipy.optimize.minimize_scalar?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"result"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"type(result)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"result.x"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(calc_distance_func(5))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"calc_distance_func(5.468493134264144)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Where is the bee for this value of `t`?\n",
|
||||
"make_bee_track(5.468493134264144)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(4,3))\n",
|
||||
"ax.plot( [flower[0]], [flower[1]], 'or', label='flower' )\n",
|
||||
"ax.plot( bee_track[0], bee_track[1], '.-k', label='bee')\n",
|
||||
"\n",
|
||||
"x,y = make_bee_track(5.468493134264144)\n",
|
||||
"ax.plot( [x], [y] ,'x', label='closest')\n",
|
||||
"ax.axis('equal')\n",
|
||||
"ax.legend()\n",
|
||||
"ax.set_xlabel('x')\n",
|
||||
"ax.set_ylabel('y')\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
BIN
lectures/lecture-07/FEA8OpTWYAAxNjx.png
Normal file
BIN
lectures/lecture-07/FEA8OpTWYAAxNjx.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 381 KiB |
Loading…
Reference in a new issue