Skip to content

Instantly share code, notes, and snippets.

@jiffyclub
Created November 4, 2012 17:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jiffyclub/4012734 to your computer and use it in GitHub Desktop.
Save jiffyclub/4012734 to your computer and use it in GitHub Desktop.
Examples of NumPy array arithmetic with binary operators.
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"name": "ArrayMath"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Introduction to Array Math with NumPy Arrays"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from IPython.display import HTML"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"HTML('<img src=\"http://teaching.software-carpentry.org/wp-content/uploads/2012/11/array-math-cmap.png\" height=\"500px\">')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<img src=\"http://teaching.software-carpentry.org/wp-content/uploads/2012/11/array-math-cmap.png\" height=\"500px\">"
],
"output_type": "pyout",
"prompt_number": 2,
"text": [
"<IPython.core.display.HTML at 0x1055a5890>"
]
}
],
"prompt_number": 2
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import numpy as np"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 3
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We'll need a couple of arrays for demo purposes..."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"a = np.arange(5)\n",
"b = np.arange(5, 10)\n",
"print a\n",
"print b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"[0 1 2 3 4]\n",
"[5 6 7 8 9]\n"
]
}
],
"prompt_number": 4
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Array Math with Scalars\n",
"\n",
"Now we can use binary operators like `+`, `-`, `*`, `/`, and `**` on these arrays, which will return new arrays. We'll start with combining arrays and scalars and then look at what happens with arrays on both sides of the operator."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"a + 6"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "pyout",
"prompt_number": 5,
"text": [
"array([ 6, 7, 8, 9, 10])"
]
}
],
"prompt_number": 5
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You could also do this with a loop, [list comprehension](http://www.python.org/dev/peps/pep-0202/), or the [map function](http://docs.python.org/2/library/functions.html#map), but I think you'll agree `a + 6` is much easier to read and write:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"new_a = []\n",
"for i in xrange(a.size):\n",
" new_a.append(a[i] + 6)\n",
"np.array(new_a)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "pyout",
"prompt_number": 6,
"text": [
"array([ 6, 7, 8, 9, 10])"
]
}
],
"prompt_number": 6
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Array Math with Two Arrays\n",
"\n",
"You can also do math with two or more arrays using the binary operators:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"a * b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "pyout",
"prompt_number": 7,
"text": [
"array([ 0, 6, 14, 24, 36])"
]
}
],
"prompt_number": 7
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The new arrays was constructed by multiplying the first element of `a` by the first element of `b`, and so on.\n",
"\n",
"What if the arrays aren't the same size?"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"a = np.arange(5)\n",
"b = np.arange(6)\n",
"a * b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"ename": "ValueError",
"evalue": "operands could not be broadcast together with shapes (5) (6) ",
"output_type": "pyerr",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-8-8c541a828af2>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0ma\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;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mb\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;36m6\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0ma\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mb\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 (5) (6) "
]
}
],
"prompt_number": 8
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Well that didn't work. What if the arrays are the same in at least one dimension?"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"a = np.ones((3, 2)) # 3 x 2\n",
"b = np.arange(4, 6) # 1 x 2\n",
"a * b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "pyout",
"prompt_number": 9,
"text": [
"array([[ 4., 5.],\n",
" [ 4., 5.],\n",
" [ 4., 5.]])"
]
}
],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"NumPy saw that `b` could be repeated to match `a`'s shape and did that automatically. It will also work if the number of rows matches:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"b = np.array([[4], [5], [6]]) # 3 x 1\n",
"a * b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "pyout",
"prompt_number": 10,
"text": [
"array([[ 4., 4.],\n",
" [ 5., 5.],\n",
" [ 6., 6.]])"
]
}
],
"prompt_number": 10
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Will two 2d arrays work if one can be repeated to match the other?"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"a = np.ones((4, 2)) # 4 rows, 2 columns\n",
"b = np.array([[4, 5], [6, 7]]) # 2 rows, 2 columns\n",
"a * b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"ename": "ValueError",
"evalue": "operands could not be broadcast together with shapes (4,2) (2,2) ",
"output_type": "pyerr",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-11-14ab02e48779>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mones\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 4 rows, 2 columns\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m7\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 2 rows, 2 columns\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0ma\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mb\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 (4,2) (2,2) "
]
}
],
"prompt_number": 11
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this case NumPy couldn't figure out what to do and raised an exception. But let's look at what happens when one array matches the dimensionality of a subarray in the other:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"a = np.ones((4, 3, 2))\n",
"b = np.array([[4, 5], [6, 7], [8, 9]]) # 3 x 2\n",
"a * b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "pyout",
"prompt_number": 12,
"text": [
"array([[[ 4., 5.],\n",
" [ 6., 7.],\n",
" [ 8., 9.]],\n",
"\n",
" [[ 4., 5.],\n",
" [ 6., 7.],\n",
" [ 8., 9.]],\n",
"\n",
" [[ 4., 5.],\n",
" [ 6., 7.],\n",
" [ 8., 9.]],\n",
"\n",
" [[ 4., 5.],\n",
" [ 6., 7.],\n",
" [ 8., 9.]]])"
]
}
],
"prompt_number": 12
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here were are getting into arrays of higher dimensionality so it becomes harder to visualize.\n",
"\n",
"When combining two arrays the operation must fit into one these categories:\n",
"\n",
"1. The arrays are the same shape.\n",
"2. The array with smaller number of dimensions matches the last dimensions of the other array.\n",
" * For example, an array with dimensions (4, 3, 2) and an array with dimensions (3, 2) can be combined."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Teaching Discussion\n",
"\n",
"1. I prefer to do live coding when working in front of a class so I would print this out and use it as lecture notes, \n",
" but the class would see me working in an empty notebook.\n",
"2. On their own learners can step through this, read my annotations, and change things to see what happens.\n",
"3. Having a small amount of code followed by the result of that code makes it easy to see the result of changes, as shown in Bret Victor's demo.\n",
" Thinking of the book, I think demonstrating situations that work *and* situations that error helps reinforce underlying mechanics for students."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment