``for`` loops
=============
Sources
-------
This lesson is based on the `Software Carpentry group's `__ lessons on `Programming with Python `__.
Basics of ``for`` loops
-----------------------
1. Loops allow parts of code to be repeated over some number of times.
One of the simple loop options in Python is the ``for`` loop.
.. code:: python
>>> word = 'rock'
>>> for char in word:
... print(char)
...
r
o
c
k
2. ``for`` loops in Python have the general form below.
.. code:: python
for variable in collection:
do things with variable
The ``variable`` can be any name you like, and the statement of the ``for`` loop must end with a ``:``.
The code that should be executed as part of the loop must be indented beneath the ``for`` loop, and the typical indentation is 4 spaces.
There is not additional special word needed to end the loop, just change the indentation back to normal.
3. Let's consider another example.
.. code:: python
>>> length = 0
>>> for letter in 'earthquake':
... length = length + 1
...
>>> print('There are', length, 'letters')
There are 10 letters
Note that the variable used in the loop, ``letter`` is just a normal variable and still exists after the loop has completed with the final value given to letter.
.. code:: python
>>> print('After the loop, letter is', letter)
e
4. A loop can be used to iterate over any list of values in Python.
So far we have considered only character strings, but we could also write a loop that performs a calculation a specified number of times.
.. code:: python
>>> for number in range(5):
... print(number)
...
0
1
2
3
4
What happens here?
Well, in this case, we use a special function called ``range()`` to give us a list of 5 numbers ``[0, 1, 2, 3, 4]`` and then print each number in the list to the screen.
When given an integer (whole number) as an argument, ``range()`` will produce a list of numbers with a length equal to the specified number.
The list starts at zero and ends with number-1.
5. Often when you use ``for`` loops, you are looping over the values in
a list and either calculating a new value or modifying the existing
values. Let's consider an example.
.. code:: python
>>> mylist = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
>>> print(mylist)
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
>>> for i in range(6):
... mylist[i] = mylist[i] + i
...
>>> print(mylist)
[0.0, 2.0, 4.0, 6.0, 8.0, 10.0]
So, what happened?
We first create a list of 6 numbers.
Then, we loop over 6 values using the ``range()`` function and add each value to the existing location in ``mylist``.
6. One of the drawbacks in the example above is that we need to know the length of the list before running that ``for`` loop example.
However, we already know how to find the length of a list using the ``len()`` function, and we can take advantage of this knowledge to make our ``for`` loop more flexible.
.. code:: python
>>> for i in range(len(mylist)):
... mylist[i] = mylist[i] + i
...
>>> print(mylist)
[0.0, 3.0, 6.0, 9.0, 12.0, 15.0]
Using the ``len()`` function with ``range()`` to perform calcluations
using list or array values is an *extremely* common operation in
Python.
.. topic:: Exercise - Putting it together
- Create a new NumPy array called ``numbers`` that starts at 1 and goes to 100 in increments of 1
- Create a new NumPy array of zeros called ``squared`` that is the same size as ``numbers``
- Using a ``for`` loop, calculate the square of each value in ``numbers`` and store it in the corresponding location in ``squared``
.. topic:: Exercise - Let's get functional
- Take your code above and use it to create a new Python function ``square()`` that accepts a NumPy array and returns an array of squared values
- Do you get the expected results when using your function?
- Can you break your function (get it to give an error message)? If so, how?
.. topic:: Exercise - Drag race
IPython has a magic function called ``%timeit`` that you can use to calculate how long it takes a line of code (or program) to execute.
.. code:: python
>>> %timeit np.ones(100000000).mean()
loop, best of 3: 427 ms per loop
We can use this now to compare the performance of your new ``square()`` function with calculating the square of values directly in NumPy
- Create a new NumPy array called ``input`` that goes from 1 to 10 in increments of 0.0000001
- Use ``%timeit`` with your function above to calculate the square of ``input``, storing the output in an array called ``out1``
- Compare the performance of your function to simply squaring the ``input`` array directly and storing its output as ``out2``
- Can you see any benefits to using NumPy?