diff --git a/notebooks/Block_0/Examples script/Preliminaries_Numpy_Pandas.ipynb b/notebooks/Block_0/Examples script/Preliminaries_Numpy_Pandas.ipynb index a3a73072747cddb6822667cc2f4f2523d8f21ea0..adce28ab6f81fd27cd55ddd541838f76e77d2416 100644 --- a/notebooks/Block_0/Examples script/Preliminaries_Numpy_Pandas.ipynb +++ b/notebooks/Block_0/Examples script/Preliminaries_Numpy_Pandas.ipynb @@ -963,21 +963,19 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 96, "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "[6, 7, 8, 9, 10]" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "853 ns ± 38.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n" + ] } ], "source": [ + "%%timeit\n", "values = [1,2,3,4,5]\n", "for i in range(len(values)):\n", " values[i] += 5\n", @@ -1009,12 +1007,22 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 97, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.24 µs ± 170 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n" + ] + } + ], "source": [ + "%%timeit\n", "values = [1,2,3,4,5]\n", - "values = np.array(values) + 5" + "values = np.array(values) + 5\n", + "values" ] }, { @@ -1027,255 +1035,918 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 98, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4.07 µs ± 262 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "values = [1,2,3,4,5]\n", + "values = np.array(values)\n", + "values += 5\n", + "values" + ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "We should point out, NumPy actually has functions for things like adding, multiplying, etc. \n", + "But it also supports using the standard math operators. So the following two lines are equivalent:" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 59, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 5, 10, 15, 20])" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = np.multiply(v, 5)\n", + "x" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 60, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 5, 10, 15, 20])" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = v * 5\n", + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will usually use the operators instead of the functions because they are more convenient to \n", + "type and easier to read, but it's really just personal preference.\n", + "\n", + "One more example of operating with scalars and `ndarrays`. Let's say you have a matrix `m` and you want \n", + "to reuse it, but first you need to set all its values to zero. Easy, just multiply by zero and assign \n", + "the result back to the matrix, like this:" + ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 61, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "[6, 7, 8, 9, 10]\n", - "[ 6 7 8 9 10]\n", - "[30 35 40 45 50] \n", - " [30 35 40 45 50] \n", - "\n", - "a =\n", - " [[1 3]\n", - " [5 7]] \n", - "b =\n", - " [[2 4]\n", - " [6 8]]\n", - "a + b =\n", - " [[ 3 7]\n", - " [11 15]]\n", - "a * b =\n", - " [[ 2 12]\n", - " [30 56]]\n" - ] - }, - { - "ename": "ValueError", - "evalue": "operands could not be broadcast together with shapes (2,2) (5,) ", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m<ipython-input-4-7ddd6b5f4e75>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a * b =\\n\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;31m# Shape mismatch:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a * values =\\n\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mvalues\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with shapes (2,2) (5,) " - ] + "data": { + "text/plain": [ + "array([[0, 0, 0],\n", + " [0, 0, 0],\n", + " [0, 0, 0]])" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "# The Python way:\n", - "values = [1, 2, 3, 4, 5]\n", - "for i in range(len(values)):\n", - " values[i] += 5\n", - " \n", - "print(values)\n", + "m *= 0\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Element-wise Matrix Operations\n", "\n", - "# The Numpy way:\n", - "values = np.array([1, 2, 3, 4, 5])\n", - "values += 5\n", + "The same functions and operators that work with scalars and matrices also work with \n", + "other dimensions. You just need to make sure that the items you perform the operation \n", + "on have compatible shapes.\n", "\n", - "print(values)\n", + "Let's say you want to get the squared values of a matrix. That's simply `x = m * m` (or if you \n", + "want to assign the value back to m, it's just `m *= m`\n", "\n", - "# Multiplication\n", - "x = np.multiply(values, 5)\n", - "y = values * 5\n", - "print(x, \"\\n\", y, \"\\n\")\n", + "This works because it's an element-wise multiplication between two identically-shaped matrices. \n", + "(In this case, they are shaped the same because they are actually the same object.)\n", "\n", - "# Element wise matrix operations\n", + "Here's another example:" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 3],\n", + " [5, 7]])" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ "a = np.array([[1,3],[5,7]])\n", + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[2, 4],\n", + " [6, 8]])" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ "b = np.array([[2,4],[6,8]])\n", - "print(\"a =\\n\", a, \"\\nb =\\n\", b)\n", - "print(\"a + b =\\n\", a + b)\n", - "print(\"a * b =\\n\", a * b)\n", - "# Shape mismatch:\n", - "print(\"a * values =\\n\", a * values)" + "b" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 3, 7],\n", + " [11, 15]])" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a + b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Numpy Matrix Multiplication\n", - "Recap element-wise multiplication:" + "And if you try working with incompatible shapes, you would get an error:" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 71, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "m =\n", - " [[1 2 3]\n", - " [4 5 6]] \n", - "n =\n", - " [[0.25 0.5 0.75]\n", - " [1. 1.25 1.5 ]]\n", - "x =\n", - " [[0.25 1. 2.25]\n", - " [4. 6.25 9. ]] \n", - "y =\n", - " [[0.25 1. 2.25]\n", - " [4. 6.25 9. ]]\n" - ] + "data": { + "text/plain": [ + "array([[1, 3],\n", + " [5, 7]])" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "# Elementwise recap:\n", - "m = np.array([[1,2,3],[4,5,6]])\n", - "# Scalar multiplication\n", - "n = m * 0.25\n", - "# Python Elementwise matrix multiplication\n", - "x = m * n\n", - "# Numpy Elementwise matrix multiplication\n", - "y = np.multiply(m, n)\n", - "\n", - "print(\"m =\\n\", m, \"\\nn =\\n\", n)\n", - "print(\"x =\\n\", x, \"\\ny =\\n\", y)" + "a = np.array([[1,3],[5,7]])\n", + "a" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 72, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[2, 3, 6],\n", + " [4, 5, 9],\n", + " [1, 8, 7]])" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "Matrix Product:" + "c = np.array([[2,3,6],[4,5,9],[1,8,7]])\n", + "c" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 73, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "a =\n", - " [[1 2 3 4]\n", - " [5 6 7 8]] \n", - "a.shape =\n", - " (2, 4) \n", - "b =\n", - " [[ 1 2 3]\n", - " [ 4 5 6]\n", - " [ 7 8 9]\n", - " [10 11 12]] \n", - "b.shape =\n", - " (4, 3)\n", - "c = \n", - " [[ 70 80 90]\n", - " [158 184 210]] \n", - "c.shape =\n", - " (2, 3)\n", - "d = \n", - " [[ 70 80 90]\n", - " [158 184 210]] \n", - "d.shape =\n", - " (2, 3)\n" - ] + "data": { + "text/plain": [ + "(2, 2)" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "\"\"\" Using np.matmul \"\"\"\n", - "a = np.array([[1,2,3,4],[5,6,7,8]])\n", - "b = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])\n", - "\n", - "print(\"a =\\n\", a, \"\\na.shape =\\n\", a.shape, \"\\nb =\\n\", b, \"\\nb.shape =\\n\", b.shape)\n", - "\n", - "# Matrix product\n", - "c = np.matmul(a, b)\n", - "print(\"c = \\n\", c, \"\\nc.shape =\\n\", c.shape)\n", - "\n", - "# Dimension mismatch:\n", - "# print(np.matmul(b, a))\n", - "\"\"\" Using np.dot \"\"\"\n", - "d = np.dot(a, b)\n", - "print(\"d = \\n\", d, \"\\nd.shape =\\n\", d.shape)\n" + "a.shape" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 74, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(3, 3)" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "## Transpose" + "c.shape" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 75, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "m = \n", - " [[ 1 2 3 4]\n", - " [ 5 6 7 8]\n", - " [ 9 10 11 12]] \n", - "m.T = \n", - " [[ 1 5 9]\n", - " [ 2 6 10]\n", - " [ 3 7 11]\n", - " [ 4 8 12]]\n", - "m = \n", - " [[ 1 2 3 4]\n", - " [ 5 6 7 200]\n", - " [ 9 10 11 12]] \n", - "m_t = \n", - " [[ 1 5 9]\n", - " [ 2 6 10]\n", - " [ 3 7 11]\n", - " [ 4 200 12]]\n", - "entries [3][1], [1][3], respectively are edited in both matrices\n" + "ename": "ValueError", + "evalue": "operands could not be broadcast together with shapes (2,2) (3,3) ", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-75-e81e582b6fa9>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mc\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with shapes (2,2) (3,3) " ] } ], "source": [ - "m = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])\n", - "print(\"m = \\n\", m,\"\\nm.T = \\n\", m.T)\n", - "\n", - "# note how the transposed matrix is not a copy of the original:\n", - "m_t = m.T\n", - "m_t[3][1] = 200\n", - "print(\"m = \\n\", m, \"\\nm_t = \\n\", m_t)\n", - "print(\"entries [3][1], [1][3], respectively are edited in both matrices\")" + "a + c" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2, 3],\n", + " [4, 5, 6]])" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = np.array([[1,2,3],[4,5,6]])\n", + "m" ] }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0.25, 0.5 , 0.75],\n", + " [1. , 1.25, 1.5 ]])" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n = m * 0.25\n", + "n" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0.25, 1. , 2.25],\n", + " [4. , 6.25, 9. ]])" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m * n" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0.25, 1. , 2.25],\n", + " [4. , 6.25, 9. ]])" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.multiply(m, n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To find the matrix product, you use NumPy's `matmul` function.\n", + "\n", + "If you have compatible shapes, then it's as simple as this:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2, 3, 4],\n", + " [5, 6, 7, 8]])" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = np.array([[1,2,3,4],[5,6,7,8]])\n", + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 4)" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1, 2, 3],\n", + " [ 4, 5, 6],\n", + " [ 7, 8, 9],\n", + " [10, 11, 12]])" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])\n", + "b" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(4, 3)" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 70, 80, 90],\n", + " [158, 184, 210]])" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c = np.matmul(a, b)\n", + "c" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 3)" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If your matrices have incompatible shapes, you'll get an error, like the following:" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-87-af3b88aa2232>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatmul\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)" + ] + } + ], + "source": [ + "np.matmul(b, a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NumPy's `dot` function\n", + "\n", + "You may sometimes see NumPy's `dot` function in places where you would expect a `matmul`. \n", + "It turns out that the results of dot and matmul are the same if the matrices are two dimensional.\n", + "\n", + "So these two results are equivalent:" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2],\n", + " [3, 4]])" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = np.array([[1,2],[3,4]])\n", + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 7, 10],\n", + " [15, 22]])" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.dot(a,a)" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 7, 10],\n", + " [15, 22]])" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a.dot(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 7, 10],\n", + " [15, 22]])" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.matmul(a,a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While these functions return the same results for two dimensional data, you should be careful \n", + "about which you choose when working with other data shapes. You can read more about the \n", + "differences, and find links to other NumPy functions, in the `matmul` and `dot` documentation. \n", + "\n", + "\n", + "\n", + "### Transpose\n", + "\n", + "Getting the transpose of a matrix is really easy in NumPy. Simply access \n", + "its `T` attribute. There is also a `transpose()` function which \n", + "returns the same thing, but you will rarely see that used anywhere because \n", + "typing `T` is so much easier. \n", + "\n", + "For example:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1, 2, 3, 4],\n", + " [ 5, 6, 7, 8],\n", + " [ 9, 10, 11, 12]])" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1, 5, 9],\n", + " [ 2, 6, 10],\n", + " [ 3, 7, 11],\n", + " [ 4, 8, 12]])" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m.T" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "NumPy does this without actually moving any data in memory -\n", + "it simply changes the way it indexes the original matrix - \n", + "so it's quite efficient.\n", + "\n", + "However, that also means you need to be careful with how you modify objects, \n", + "because they are sharing the same data. For example, with the same matrix `m` \n", + "from above, let us make a new variable `m_t` that stores `m`'s transpose. \n", + "Then look what happens if we modify a value in `m_t`:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1, 5, 9],\n", + " [ 2, 6, 10],\n", + " [ 3, 7, 11],\n", + " [ 4, 200, 12]])" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m_t = m.T\n", + "m_t[3][1] = 200\n", + "m_t" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1, 2, 3, 4],\n", + " [ 5, 6, 7, 200],\n", + " [ 9, 10, 11, 12]])" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice how it modified both the transpose and the original matrix, too. \n", + "That's because they are sharing the same copy of data. So remember to \n", + "consider the transpose just as a different view of your matrix, rather \n", + "than a different matrix entirely." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geometric Interpretation of Matrix Operation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following example will show what matrices and vectors are good for. \n", + "\n", + "We are going to start with a picture of a bug." + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "<matplotlib.image.AxesImage at 0x7f56299e9250>" + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib inline \n", + "\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.image as mpimg\n", + "img = mpimg.imread('stinkbug4.JPG')\n", + "plt.imshow(img)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you open the picture with an image viewer, you can enlarge or reduce the picture or rotate it. Now the important question: What's going on behind the scene if you do rotate the picture? Or in other words: What does the computer do to achieve the rotation? \n", + "\n", + "One simple solution is using a matrices and vectors." + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'skimage'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-105-16d8c7ff678e>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mskimage\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mskimage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mrescale\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mimg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrescale\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m.2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mimg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdelete\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimg\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'skimage'" + ] + } + ], + "source": [ + "import skimage\n", + "from skimage.transform import rescale\n", + "img = rescale(img, .2)\n", + "\n", + "img = np.delete(img,np.arange(20), axis=0)\n", + "img = np.delete(img,np.arange(80,100), axis=0)\n", + "img = np.delete(img,np.arange(20), axis=1)\n", + "img = np.delete(img,np.arange(80,100), axis=1)\n", + "\n", + "col =np.zeros(img.shape[0]*img.shape[1])\n", + "\n", + "k = 0\n", + "for i in np.arange(img.shape[0]):\n", + " for j in np.arange(img.shape[1]):\n", + " col[k] = np.round(.99*img[i,j,:],0)\n", + " k = k+1\n", + " \n", + "image = np.array([np.repeat(np.arange(img.shape[0]),img.shape[1]), np.tile(np.arange(img.shape[1]),img.shape[0])]).T\n", + "image[:,0] = image[:,0] - 40\n", + "image[:,1] = image[:,1] - 40\n", + "\n", + "image = image[col==0,:]\n", + "\n", + "\n", + "plt.plot(image[:,0],image[:,1],\"o\",color=\"black\")\n", + "plt.axis('square')\n", + "plt.xlim(-40,40)\n", + "plt.ylim(-40,40)\n", + "plt.savefig(\"bug01.eps\",bbox_inches=\"tight\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/notebooks/Block_0/Examples script/stinkbug4.JPG b/notebooks/Block_0/Examples script/stinkbug4.JPG new file mode 100644 index 0000000000000000000000000000000000000000..eb88408c8e21bc0f89bb4706c7b25be747d589d0 Binary files /dev/null and b/notebooks/Block_0/Examples script/stinkbug4.JPG differ