{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h1>7. Testing Inside Arrays</h1>\n",
    "\n",
    "<h2>10/13/2023</h2>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2>7.0 Last Time...</h2>\n",
    "<ul>\n",
    "    <li>An array is NumPy's version of a list, which allows you to work with matrices.</li>\n",
    "    <li>All elements of an array are of the same type.</li>\n",
    "    <li>You can either create a new array or convert an existing list into one.</li>\n",
    "    <li>The zeros() function is useful for initializing an array when you know the shape and size but not the contents.</li>\n",
    "    <li>The arange() function serves a similar purpose to the range() function with lists.</li>\n",
    "    <li>Similar to lists, arrays can be sliced, but arrays can be sliced across each of their dimensions.</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0.]]\n"
     ]
    }
   ],
   "source": [
    "#1. What is the code to create a 4-row, 5-column array of zeros and assign it to the variable a?\n",
    "import numpy as np\n",
    "a = np.zeros((4,5))\n",
    "print(a)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#2. Using array a from above, answer the following questions:\n",
    "\n",
    "a = np.array([[2,3.2,5.5,-6.4,-2.2,2.4],\n",
    "              [1, 22,  4, 0.1, 5.3, -9],\n",
    "              [3,  1,2.1,  21, 1.1, -2]])\n",
    "\n",
    "#a. What is a[:,3]?\n",
    "\n",
    "#b. What is a[1:4,0:2]?\n",
    "\n",
    "#c. What will b = a[1:,2] do?\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2>7.1 Array Inquiry</h2>\n",
    "\n",
    "There are a series of functions within NumPy that we can use to gain information about a given array.\n",
    "\n",
    "To return the <b>shape</b> of an array (i.e., its dimensions), we use numpy.shape().\n",
    "\n",
    "To return the <b>number of dimensions</b> of an array, we use numpy.ndim().\n",
    "\n",
    "To return the <b>number of elements</b> in an array, we use numpy.size()."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3, 6)\n"
     ]
    }
   ],
   "source": [
    "# Don't forget to import numpy!\n",
    "import numpy as np\n",
    "\n",
    "a = np.array([[2,3.2,5.5,-6.4,-2.2,2.4],\n",
    "              [1, 22,  4, 0.1, 5.3, -9],\n",
    "              [3,  1,2.1,  21, 1.1, -2]])\n",
    "print(np.shape(a))\n",
    "\n",
    "# This is the example array from yesterday's lecture.\n",
    "\n",
    "\n",
    "# Array shape.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n"
     ]
    }
   ],
   "source": [
    "# Array number of dimensions.\n",
    "print(np.ndim(a))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "18\n"
     ]
    }
   ],
   "source": [
    "# Array number of elements.\n",
    "print(np.size(a))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you want to search within an array for the location of values matching a particular criterion (useful when you have a giant dataset!), you can use numpy.where()."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(array([0, 0, 1, 2]), array([3, 4, 5, 5]))\n"
     ]
    }
   ],
   "source": [
    "# Search for all places within the array with negative values.\n",
    "print(np.where(a < 0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once you have the locations where the criterion is met, you can then modify only those values!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[]\n"
     ]
    }
   ],
   "source": [
    "# This code will look for all the negative values in the array and change them to positive.\n",
    "c = np.where(a<0)\n",
    "a[c] = np.abs(a[c])\n",
    "print(a[c])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also create a list of all values within an array matching a particular criterion.\n",
    "\n",
    "(We'll see more on array testing soon...)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[]\n"
     ]
    }
   ],
   "source": [
    "print(a[a<0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2>7.2 Array Manipulation</h2>\n",
    "\n",
    "Rather than just finding things out about arrays, NumPy also allows you to manipulate arrays. Some of these come from matrix algebra, but others are just ways to shift the data around into useable formats. Here are a few examples:\n",
    "<ul>\n",
    "    <li><b>numpy.reshape()</b>: reshape an array to the desired dimensions.</li>\n",
    "    <li><b>numpy.transpose()</b>: transpose an array (flip rows and columns, for example).</li>\n",
    "    <li><b>numpy.ravel()</b>: \"flatten\" an array back into a 1D vector.</li>\n",
    "    <li><b>numpy.concatenate()</b>: concatenate arrays.</li>\n",
    "    <li><b>numpy.repeat()</b>: repeat array elements.</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 2 3 4 5]\n",
      "[0 1 2 3 4 5 6 7]\n",
      "[[ 2.   3.2  5.5 -6.4 -2.2  2.4]\n",
      " [ 1.  22.   4.   0.1  5.3 -9. ]\n",
      " [ 3.   1.   2.1 21.   1.1 -2. ]]\n"
     ]
    }
   ],
   "source": [
    "# Let's create some arrays to play with.\n",
    "import numpy as np\n",
    "z = np.arange(6)\n",
    "x = np.arange(8)\n",
    "a = np.array([[2,3.2,5.5,-6.4,-2.2,2.4],\n",
    "              [1, 22,  4, 0.1, 5.3, -9],\n",
    "              [3,  1,2.1,  21, 1.1, -2]])\n",
    "\n",
    "print(z)\n",
    "print(x)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0 1 2]\n",
      " [3 4 5]]\n"
     ]
    }
   ],
   "source": [
    "# First, reshape a from a 1x6 to a 2x3 array:\n",
    "print(np.reshape(z,(2,3)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 2.   1.   3. ]\n",
      " [ 3.2 22.   1. ]\n",
      " [ 5.5  4.   2.1]\n",
      " [-6.4  0.1 21. ]\n",
      " [-2.2  5.3  1.1]\n",
      " [ 2.4 -9.  -2. ]]\n"
     ]
    }
   ],
   "source": [
    "# Next, transpose c from a 3x6 to a 6x3 array:\n",
    "print(np.transpose(a))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 2.   3.2  5.5 -6.4 -2.2  2.4  1.  22.   4.   0.1  5.3 -9.   3.   1.\n",
      "  2.1 21.   1.1 -2. ]\n"
     ]
    }
   ],
   "source": [
    "# Try \"flattening\" c into a 1-D array:\n",
    "print(np.ravel(a))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 2 3 4 5 0 1 2 3 4 5 6 7]\n"
     ]
    }
   ],
   "source": [
    "# Now try concatenating (combining) the two 1-D arrays:\n",
    " # This is another function that needs a double-parentheses\n",
    "                             # because there are other options that can be changed in the function.\n",
    "                             \n",
    "                             \n",
    "print(np.concatenate((z,x)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5]\n"
     ]
    }
   ],
   "source": [
    "# Print array a and repeat each value 3 times:\n",
    "\n",
    "print(np.repeat(z,3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2>7.3 Array Operations</h2>\n",
    "\n",
    "<h3>7.3.1 Looping (slow and steady)</h3>\n",
    "\n",
    "As an example of what's going on \"under the hood\", one way you can perform operations on an array is simply to loop through every single value within that array and perform the operation on each element.\n",
    "\n",
    "As you might imagine, this can get <i>tedious</i>."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2, 8)\n"
     ]
    }
   ],
   "source": [
    "# Let's create two arrays and multiply them together, element by element.\n",
    "import numpy as np\n",
    "\n",
    "a = np.array([[2,3,2,5,6,7,45,34],\n",
    "    [123,345,634,3234,234,2,432,4]])\n",
    "b = np.array ([[123,534,234,423,3,2,23,23],[234,543,645,234,6,23,54,9]])\n",
    "             \n",
    "\n",
    "\n",
    "# Let's now get the shape of the arrays (they have the same shape, so we'll just get a's shape).\n",
    "print(np.shape(a))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2, 8)\n"
     ]
    }
   ],
   "source": [
    "# Next, let's create a matrix of zeros in the shape of the element-by-element product we eventually want.\n",
    "ashape = np.shape(a)\n",
    "print(ashape)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Finally, we loop over all x values and all y values using arange.\n",
    "for i in np.arange(ashape[0]):\n",
    "    for j in np.arange(ashape[1]):\n",
    "        pr\n",
    "# ok whatever\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3>7.3.2 Array Syntax (quick and efficient)</h3>\n",
    "\n",
    "...alternatively, we could do this using built-in syntax that operates on the entire array at once."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[   246   1602    468   2115     18     14   1035    782]\n",
      " [ 28782 187335 408930 756756   1404     46  23328     36]]\n"
     ]
    }
   ],
   "source": [
    "productab = a*b\n",
    "print(a*b)\n",
    "\n",
    " # Note that this method assumes the arrays are the same size.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Well, okay. That does seem a little easier."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 2 3 4 5 6 7 8 9]\n"
     ]
    }
   ],
   "source": [
    "# Let's try another example. Start with a simple 1-D array \n",
    "# that's just the integers from 0 to 9. \n",
    "u = np.arange(10)\n",
    "print(u)\n",
    "\n",
    "\n",
    "\n",
    "# Now try some operations on it!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 0  2  4  6  8 10 12 14 16 18]\n"
     ]
    }
   ],
   "source": [
    "print(u*2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2>7.4 Testing Inside an Array</h2>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's return to logical testing within an array. NumPy has several alternative ways to do logical tests: for instance, we can use <b>\\></b> as 'greater than', or we can use <b>numpy.greater()</b> for the same result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 2 3 4 5]\n",
      "[False False False False  True  True]\n",
      "[False False False False  True  True]\n"
     ]
    }
   ],
   "source": [
    "import numpy\n",
    "t = np.arange(6)\n",
    "print(t)\n",
    "print(t > 3)\n",
    "\n",
    "          # This uses a more traditional logical format.\n",
    " # This uses NumPy's built-in function.\n",
    "\n",
    "print(np.greater(t,3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Unfortunately, the same doesn't apply to Python's built-in <b>and</b> and <b>or</b> functions; in those cases, you have to use NumPy's numpy.logical_and() and numpy.logical_or() functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m/Users/nik/data/python/notebooks/Week 03 F.ipynb Cell 37\u001b[0m line \u001b[0;36m3\n\u001b[1;32m      <a href='vscode-notebook-cell:/Users/nik/data/python/notebooks/Week%2003%20F.ipynb#X51sZmlsZQ%3D%3D?line=0'>1</a>\u001b[0m \u001b[39m# Let's write code that outputs 'True' only when a > 1 and a <= 3.\u001b[39;00m\n\u001b[1;32m      <a href='vscode-notebook-cell:/Users/nik/data/python/notebooks/Week%2003%20F.ipynb#X51sZmlsZQ%3D%3D?line=1'>2</a>\u001b[0m w \u001b[39m=\u001b[39m np\u001b[39m.\u001b[39marange(\u001b[39m6\u001b[39m)\n\u001b[0;32m----> <a href='vscode-notebook-cell:/Users/nik/data/python/notebooks/Week%2003%20F.ipynb#X51sZmlsZQ%3D%3D?line=2'>3</a>\u001b[0m \u001b[39mprint\u001b[39m((w\u001b[39m>\u001b[39m\u001b[39m1\u001b[39m) \u001b[39mand\u001b[39;00m (a\u001b[39m<\u001b[39m\u001b[39m=\u001b[39m\u001b[39m3\u001b[39m))\n",
      "\u001b[0;31mValueError\u001b[0m: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"
     ]
    }
   ],
   "source": [
    "# Let's write code that outputs 'True' only when a > 1 and a <= 3.\n",
    "w = np.arange(6)\n",
    "print((w>1) and (a<=3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[False False  True  True False False]\n"
     ]
    }
   ],
   "source": [
    "print(np.logical_and(t>1,t<=3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2>7.5 Take-Home Points</h2>\n",
    "<ul>\n",
    "    <li>You can use functions within NumPy to find the shape, number of dimensions, and number of elements in any array.</li>\n",
    "    <li><b>numpy.where()</b> will let you find the locations of elements within an array that meet a specified criterion.</li>\n",
    "    <li>Some functions within NumPy allow you to manipulate arrays, such as reshaping, transposing, \"unraveling\", concatenation, and repeating individual array elements.</li>\n",
    "    <li>When you're in a situation with multiple nested loops, often you can instead perform all operations a lot more easily by using array syntax.</li>\n",
    "    <li>Mathematical operations act on arrays elementwise.</li>\n",
    "    <li>When testing within an array, <b>and</b> and <b>or</b> cannot be used; instead, use NumPy's built-in functions.</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
