{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 3. Exercise solutions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Exercise 1" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "314.1592653589793\n" ] } ], "source": [ "def circle_area(r):\n", " '''Return circle area'''\n", " return pi * r**2 / 4 \n", "\n", "\n", "# Import pi from the math library\n", "from math import pi\n", "\n", "# Test function with input with r=20, save returned value\n", "A20 = circle_area(r=20)\n", "\n", "# Print the result\n", "print(A20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* **Note**: Calling the funtion as `circle_area(20)` and `circle_area(r=20)` is the same." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Exercise 2" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[78.53981633974483, 113.09733552923255, 201.06192982974676, 314.1592653589793, 490.8738521234052, 804.247719318987]\n" ] } ], "source": [ "def circle_areas(radii):\n", " # Use list comprehension to return a list of radii\n", " return [circle_area(r) for r in radii]\n", "\n", "\n", "# Define list of radii\n", "list_of_radii = [10, 12, 16, 20, 25, 32]\n", "\n", "# Call function with input list\n", "print(circle_areas(list_of_radii))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* **Note 1:** Call to function `circle_area` defined in Exercise instead of defining the expression `pi * r**2 / 4` again. The concept of functions calling other functions can be used to make modular programs that are easy to follow and maintain. Each function only needs to do a small thing in itself.\n", "\n", "* **Note 2:** The input parameter when the function was *defined* was a list called `radii`, but when the function was *called*, the input list was called `list_of_radii`. Thus, the input parameters passed into a function do not need to have the same name as when the function is defined." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Exercise 3" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[False, True, False, True, True, False, True]\n" ] } ], "source": [ "def is_pile_long(pile_lengths):\n", " \n", " # Create True or False value by list comprehension with if/else\n", " return [True if length >= 5 else False for length in pile_lengths]\n", "\n", "\n", "# Define a list of some pile lengths to test\n", "piles = [4.51, 6.12, 4.15, 7.31, 5.01, 4.99, 5.00]\n", "\n", "# Call function \n", "print(is_pile_long(piles))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* **Note:** The built-in boolean values `True` and `False` *must* be capitalized to be recognized by Python. If for example `true` is used, Python will assume that it is a variable that you have named `true` and give an error if it is not defined. All editors will highlight the special words recognized by Python, so if the editor does not highlight, it's a sign that something is wrong." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Exercise 4" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4.608176875690327\n", "6.3728037317960675\n" ] } ], "source": [ "# Import sqrt from the math library\n", "from math import sqrt\n", "\n", "\n", "def dist_point_to_line(x, y, x1, y1, x2, y2):\n", " '''Return distance between a point and a line defined by two points.\n", "\n", " Args:\n", " x : x-coordinate of point \n", " y : y-coordinate of point\n", " x1 : x-coordinate of point 1 defining the line\n", " y1 : y-coordinate of point 1 defining the line\n", " x2 : x-coordinate of point 2 defining the line\n", " y2 : y-coordinate of point 2 defining the line\n", "\n", " Returns:\n", " The distance between the point and the line \n", " '''\n", " return abs( (y2 - y1) * x - (x2 - x1) * y + x2 * y1 - x1 * y2) / sqrt((x2 - x1)**2 + (y2 - y1)**2)\n", "\n", "\n", "# Call the function with the two test cases\n", "print(dist_point_to_line(2, 1, 5, 5, 1, 6))\n", "print(dist_point_to_line(1.4, 5.2, 10.1, 2.24, 34.142, 13.51))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* **Note:** `abs()` used to get the numerical value." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Exercise 5\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[3.4114062067851583, 28.899880223334442, 2.745765971314884, 25.405268987115498, 3.466876226407682, 6.157172178100044]\n" ] } ], "source": [ "# Two points defining the line\n", "x1, y1, x2, y2 = 2, 3, 8, 7\n", "\n", "# Define points for distance to line calculation\n", "x_coords = [4.1, 22.2, 7.7, 62.2, 7.8, 1.1]\n", "y_coords = [0.3, 51.2, 3.5, 12.6, 2.7, 9.8]\n", "\n", "# Call function dist_point_to_line for all (x, y) points\n", "distances = [dist_point_to_line(x_coords[i], y_coords[i], x1, y1, x2, y2) for i in range(len(x_coords))]\n", "\n", "# Print new list\n", "print(distances)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A way that is more Pythonic than the above is using `zip`, which takes the two coordinate lists and puts \n", "them side by side, almost like a zipper. It is often more clean and expressive than using a loop counter `i` over the length of one of the lists." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[3.4114062067851583, 28.899880223334442, 2.745765971314884, 25.405268987115498, 3.466876226407682, 6.157172178100044]\n" ] } ], "source": [ "# Solution using zip\n", "distances_zip = [dist_point_to_line(x, y, x1, y1, x2, y2) for x, y in zip(x_coords, y_coords)]\n", "print(distances_zip)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[3.41, 28.9, 2.75, 25.41, 3.47, 6.16]\n" ] } ], "source": [ "# Results rounded to two decimals using the round() function\n", "print([round(dist, 2) for dist in distances])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Exercise 6" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "12.0\n" ] } ], "source": [ "def polygon_area(xv, yv, signed=False):\n", " ''' Return the area of a non-self-intersecting polygon given the coordinates of its vertices'''\n", "\n", " # Perform shoelace multiplication\n", " a1 = [xv[i] * yv[i+1] for i in range(len(xv)-1)]\n", " a2 = [yv[i] * xv[i+1] for i in range(len(yv)-1)]\n", "\n", " # Check if area should be signed and return area\n", " if signed: # <--- Same as \"if signed == True:\"\n", " return 1/2 * ( sum(a1) - sum(a2) )\n", " else:\n", " return 1/2 * abs( sum(a1) - sum(a2) )\n", "\n", "\n", "# Define the polygon vertices to test\n", "x = [3, 4, 7, 8, 8.5, 3]\n", "y = [5, 3, 0, 1, 3, 5]\n", "\n", "# Calculate area by calling the function\n", "A = polygon_area(x, y)\n", "\n", "# Print the area\n", "print(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Exercise 7" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6.083333333333333 2.5833333333333335\n" ] } ], "source": [ "def polygon_centroid(x, y):\n", "\n", " # Initialize empty lists for holding summation terms\n", " cx, cy = [], []\n", " \n", " # Loop over vertices and put the summation terms in the lists\n", " for i in range(len(x)-1):\n", " \n", " # Compute and append summation terms to each list\n", " cx.append((x[i] + x[i+1]) * (x[i] * y[i+1] - x[i+1] * y[i]))\n", " cy.append((y[i] + y[i+1]) * (x[i] * y[i+1] - x[i+1] * y[i]))\n", "\n", " # Calculate the signed polygon area by calling already defined function\n", " A = polygon_area(x, y, signed=True) \n", " \n", " # Sum summation terms and divide by 6A to get coordinates\n", " Cx = sum(cx) / (6*A)\n", " Cy = sum(cy) / (6*A)\n", " \n", " return Cx, Cy \n", "\n", "\n", "# Define lists of vertex coordinates for testing\n", "x = [3, 4, 7, 8, 8.5, 3]\n", "y = [5, 3, 0, 1, 3, 5]\n", "\n", "# Compute centroid by calling function, store in two variables\n", "cx, cy = polygon_centroid(x, y)\n", "\n", "# Print result\n", "print(cx, cy)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Polygon centroid is at (Cx, Cy) = (6.1, 2.6)\n" ] } ], "source": [ "# Print result as text with formatted decimals\n", "print(f'Polygon centroid is at (Cx, Cy) = ({cx:.1f}, {cy:.1f})')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Appetizer for next time - Plotting\n", "\n", "### Plotting the solution for the polygon centroid exercise:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "# Plot polygon from exercises with centroid and area\n", "plt.plot(x, y, '.-', label='Polygon')\n", "plt.plot(cx, cy, 'x', label='Centroid')\n", "\n", "# Plot coordinates of centroid as text\n", "plt.annotate(f'({cx:.1f}, {cy:.1f})', xy=(cx, cy),\n", " xytext=(cx, cy), textcoords='offset points')\n", "\n", "# Set labels, titles and legend\n", "plt.xlabel('x')\n", "plt.ylabel('y')\n", "plt.title(f'Polygon with A={A}')\n", "plt.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Function for plotting an arbitrary polygon\n", "The plotting code above could be turned into a function to plot an arbitrary polygon together with its center of gravity and put its area in the title: " ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "\n", "def plot_polygon(xv, yv, plot_centroid=True): \n", " '''Plot the polygon with the specified vertex coordinates. \n", " \n", " The plot is created with legend and the computed area of the \n", " polygon shown in the title. The plot shows the centroid of \n", " the polygon by default, but this can be turned off by setting \n", " plot_centroid=False.\n", " \n", " Args:\n", " xv (list) : x-coordinates of polygon vertices\n", " yv (list) : y-coordinates of polygon vertices\n", " plot_centroid (bool) : Plot centroid of polygon (Cx, Cy).\n", " Defaults to plotting the centroid. \n", " '''\n", " \n", " # Compute area of polygon\n", " A = polygon_area(xv, yv)\n", " \n", " # Compute polygon centroid\n", " cx, cy = polygon_centroid(xv, yv)\n", " \n", " # Plot the polygon\n", " plt.plot(xv, yv, '.-', label='Polygon')\n", " \n", " # Plot the centroid with coordinates if that was chosen\n", " if plot_centroid: # <- Eqiuvalent to: if plot_centroid == True:\n", " plt.plot(cx, cy, 'x', label='Centroid')\n", " plt.annotate(f'({cx:.1f}, {cy:.1f})', xy=(cx, cy),\n", " xytext=(cx, cy), textcoords='offset points')\n", " \n", " # Set labels, titles and legend\n", " plt.xlabel('x')\n", " plt.ylabel('y')\n", " plt.title(f'Polygon with A={A}')\n", " plt.legend()\n", " plt.show()\n", " \n", " \n", "# Define vertices of some random polygon\n", "x_polygon = [-5, 2, 5, 7, 8, 5, 1, -5]\n", "y_polygon = [-2, -15, -13, -10, -6, 2, 5, -2]\n", "\n", "# Call function to plot polygon with area and centroid shown\n", "plot_polygon(x_polygon, y_polygon)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* **Note 1:** Optional input parameter `plot_centroid` has `True` as default argument. `True` is immutable.\n", "* **Note 2:** The area of the polygon is actually calculated both in the `polygon_area()` function and in the `polygon_centroid()` function, which is maybe not so clean. A way to overcome this could be to have `polygon_centroid()` return the area. Thereby running the `polygon_area()`function alone would not be necessary." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# End of exercises\n", "\n", "*The cell below is for setting the style of this document. It's not part of the exercises.*" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import HTML\n", "HTML(''.format(open('../css/cowi.css').read()))" ] } ], "metadata": { "hide_input": false, "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.5" }, "latex_envs": { "LaTeX_envs_menu_present": true, "autoclose": false, "autocomplete": true, "bibliofile": "biblio.bib", "cite_by": "apalike", "current_citInitial": 1, "eqLabelWithNumbers": true, "eqNumInitial": 1, "hotkeys": { "equation": "Ctrl-E", "itemize": "Ctrl-I" }, "labels_anchors": false, "latex_user_defs": false, "report_style_numbering": false, "user_envs_cfg": false }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": false, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 2 }