# - compatibility with Python 3
from __future__ import print_function # print('me') instead of print 'me'
from __future__ import division # 1/2 == 0.5, not 0
# - show figures inside the notebook
%matplotlib inline
# - import common modules
import numpy as np # the Python array package
import matplotlib.pyplot as plt # the Python plotting package
np.tile¶np.tile can be used to replicate data within an array.
For example, let's say I have a row vector R:
R = np.array([[1, 2]])
R
I might want to make a new array, where I repeat the row 4 times, to make a 4 by 2 array. I specify the number of repeats on each axis. In this case I want to repeat the first axis 4 times, and the second axis I don't want to repeat - so I repeat 1 time:
np.tile(R, [4, 1])
In fact, we don't often use np.tile because we can usually get the same effect with broadcasting.
Numpy has a very useful but somewhat advanced feature called broadcasting. Broadcasting is a system that numpy uses when trying to match the shapes of two arrays, that do not have the same shape.
Let's say you have two arrays, A and B:
A = np.array([[2, 3], [4, 5], [6, 7]])
A
B = np.array([1, 3])
B
A has shape (3, 2) and B has shape (2,). What will happen if I try and add the arrays A and B?
One thing that could happen, is that numpy raises an error. Another thing that might happen, is that numpy might realize that you meant to match the only axis of B to the last axis of A, and make enough copies of the only axis of B to match the 3 columns of A. Something like this:
new_B = np.tile(B, (3, 1))
new_B
Then the subtraction would be:
A - new_B
In fact, that is exactly what numpy does by default, using broadcasting:
A - B
A simplified version of the rules that numpy uses is the following:
B has fewer dimensions than A, prepend as many new 1-length dimensions to B as necessary (shape of B goes from (2,) to (1, 2) in this case;B shape, and the corresponding dimension in A is not 1, make as many repeats of the data along this axis as needed to fill out the 1-length dimension to the length of the matching dimension in A.A and the modified B.So, in our case, that would look like:
# Prepend as many 1-length dimensions as necessary
# to give B same number of dimensions as A
B_1 = B.reshape(1, 2)
B_1
# For each 1-length dimension of B, where matching dimension of A has >1 length:
# Replicate data to extend 1-length dimension to same length as A dimension
B_2 = np.tile(B_1, [3, 1])
B_2
# Do the operation on A and new B
A - B_2
The dictionary is a very useful fundamental type in Python.
The dictionary is made up of key, value pairs.
The key is the thing that allows you to look up a value. Like this:
some_scores = {'matthew': 11, 'jb': 21}
some_scores
Here jb and matthew are keys. If I want the jb value, I can look it up, using the jb key:
some_scores['jb']
Same for the matthew value:
some_scores['matthew']
I can add a key, value pair, by assigning like this:
some_scores['stefan'] = 2 # oh dear
some_scores
The keys are often strings, as here, but the keys can be lots of different things, such as numbers or tuples:
some_scores[99] = 7 # 0 is just a number, doesn't refer to position
some_scores
As you may see from the output above, the order of the keys is unpredictable, because Python arranges them as it likes. Only the relationship between the keys and the values is guaranteed.
We'll see dictionaries coming up often as we use Python more.
We often want to display images with a gray colormap, and with nearest neighbor interpolation:
import nibabel as nib
img = nib.load('ds114_sub009_t2r1.nii')
data = img.get_data()
# Not like this:
plt.imshow(data[:, :, 15, 0])
# But like this:
plt.imshow(data[:, :, 15, 0], cmap='gray', interpolation='nearest')
Notice this second image is - grayscale - but also - less blurry - showing us the individual pixels without interpolating between them.
We can tell matplotlib to use these settings by default, by using the rcParams dictionary:
# Set gray colormap and nearest neighbor interpolation by default
plt.rcParams['image.cmap'] = 'gray'
plt.rcParams['image.interpolation'] = 'nearest'
Now we don't need to set these setting specifically:
plt.imshow(data[:, :, 15, 0])
arr = np.arange(10)
arr
We can make another variable point to this, but that's probably not a good idea:
arr2 = arr
arr2[0] = 42
arr
Oops, we have modified the original array.
To avoid stepping on the toes of another array during processing, it is often a good idea to make an explicit copy:
arr3 = arr.copy() # The copy method
We can modify the new copy without modifying the original:
arr3[0] = 99
arr3
arr