Python for Quantum Mechanics: Numpy Array Creation & Manipulation#
from IPython.display import YouTubeVideo
YouTubeVideo('JkAW8I7GVnw',width=700, height=400)
import numpy as np
Methods To Create Numpy Arrays#
There are various methods we can use to create arrays of different shapes with different data entries.
np.arange(start,stop,step,dtype)
Gives elements between start and stop, including start, in intervals of size step
arr = np.arange(5,21,2)
print(arr)
[ 5 7 9 11 13 15 17 19]
np.linspace(start,stop,num,dtype)
This gives num amount of evenly spaced elements including, and inbetween, start and stop.
arr = np.linspace(5,19,8)
print(arr)
[ 5. 7. 9. 11. 13. 15. 17. 19.]
np.zeros((r,c),dtype)
Gives a matrix of zeros that is of shape (r,c)
arr = np.zeros((3,4),float)
print(arr)
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
np.ones((r,c),dtype)
Gives a matrix of ones that is of shape (r,c)
arr = np.ones((3,4),float)
print(arr)
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
np.diag(v,k=0)
Gives a matrix of zeros with elements of v(array-like) on the diagonal
k specifies which diagonal
k=0 is the main diagonal, and is set if k is unspecified
arr = np.diag([1,2,3],k=0)
print(arr)
[[1 0 0]
[0 2 0]
[0 0 3]]
arr = np.diag([1,2,3],k=3)
print(arr)
[[0 0 0 1 0 0]
[0 0 0 0 2 0]
[0 0 0 0 0 3]
[0 0 0 0 0 0]
[0 0 0 0 0 0]
[0 0 0 0 0 0]]
np.identity(n,dtype)
Gives an nxn identity matrix
arr = np.identity(3,float)
print(arr)
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
np.eye(N,M,k,dtype)
Gives an NxN identity matrix if M is unspecified
Otherwise gives and NxM matrix with ones on a diagonal
k specifies which diagonal
arr = np.eye(4,5,k=0)
print(arr)
[[1. 0. 0. 0. 0.]
[0. 1. 0. 0. 0.]
[0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0.]]
arr = np.eye(4,5,k=1, dtype=int)
print(arr)
[[0 1 0 0 0]
[0 0 1 0 0]
[0 0 0 1 0]
[0 0 0 0 1]]
x,y = np.mgrid(array-like,array-like)
x,y = np.mgrid[0:4,0:6]
print(x,'\n')
print(y)
[[0 0 0 0 0 0]
[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[0 1 2 3 4 5]
[0 1 2 3 4 5]
[0 1 2 3 4 5]
[0 1 2 3 4 5]]
We can also create arrays filled with random numbers, in [0,1), using \(np.random.rand(shape)\).
np.random.rand(rows,columns)
arr = np.random.rand(3,4)
print(arr)
[[0.55948793 0.41369894 0.00174296 0.04175902]
[0.02480888 0.65165339 0.69159216 0.06557134]
[0.75554221 0.04371706 0.76314129 0.76970817]]
If we multiply it by an integer(or float) \(n\), we’ll obtain a random distribution over the interval [0,n).
arr = 100*np.random.rand(3,4)
print(arr, '\n')
[[ 4.21198209 29.54014986 72.9926029 90.17257111]
[97.47574048 98.94689434 89.08261852 35.08825765]
[40.21912357 19.12354169 40.30703767 76.32807857]]
To obtain a random array of integers we can then round the array, then cast the elements as integers, as follows
arr = np.round(arr).astype(int)
print(arr)
[[ 4 30 73 90]
[97 99 89 35]
[40 19 40 76]]
Non-Numerical Data Types#
Numpy also supports non-numerical data:
arr = np.array(['a','bcd'])
print(arr)
print(arr.dtype)
['a' 'bcd']
<U3
We see above that the data type is the minimum data type required to store the largest element in the array. In the above case, a string with three elements, ‘bcd’. This is again demonstrated below:
arr = np.array(['a','bcd','efghijk'])
print(arr)
print(arr.dtype)
['a' 'bcd' 'efghijk']
<U7
Instead of strings we can also cast the data as characters
arr = np.array(['efghijk'], dtype='c')
print(arr)
[[b'e' b'f' b'g' b'h' b'i' b'j' b'k']]
Similarly you might also want to add multiple strings, which can be done as follows.
stringy = 'a' + 'bcd' + 'efghijk'
arr = np.array(stringy, dtype='c')
print(arr)
[b'a' b'b' b'c' b'd' b'e' b'f' b'g' b'h' b'i' b'j' b'k']
Indexing Numpy Arrays#
We can find elements in Numpy arrays in the following way
arr = np.array([1,2])
print(arr)
print(arr[0])
[1 2]
1
This is similar to lists. However, things change when we add dimensions!!
L = [[1,2],[3,4]]
arr = np.array(L)
To access data in the LIST!
print(L[1][1])
4
In the array, it is different and found as follows
print(arr,'\n')
print(arr[0,0])
print(arr[0,1])
print(arr[1,0])
print(arr[1,1])
[[1 2]
[3 4]]
1
2
3
4
Slicing Numpy Arrays#
arr[lower:upper:step]
Slicing is a dynamical way to extract certain parts of an array. Lets create a list and slice it up
arr = np.array([x**2 for x in range(21)]) #No reason why list comprehensions can't be used
print(arr)
[ 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289
324 361 400]
The first thing to notice is that slicing gives views of the array but doesn’t actually remove parts of it from memory.
slic = arr[5:15:3]
print(slic)
print(arr)
[ 25 64 121 196]
[ 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289
324 361 400]
If we leave out lower, it is automatically set to the first element. If we also leave out step, it is set to 1.
print(arr[:5])
[ 0 1 4 9 16]
If we leave out upper, it is set to the last element
print(arr[5:])
[ 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400]
print(arr[::2])
[ 0 4 16 36 64 100 144 196 256 324 400]
print(arr[:10:2])
[ 0 4 16 36 64]
Lets try to input negative indexes
print(arr[-3:])
[324 361 400]
We see that a negative number just means to count indexes from right to left.
print(arr[:-3])
[ 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289]
print(arr[::-1])
[400 361 324 289 256 225 196 169 144 121 100 81 64 49 36 25 16 9
4 1 0]
The above example is a nice trick to reverse an array!
Lets look at slicing 2-d arrays. It works in the same way:
arr = np.random.rand(4,5)
arr = np.round(100*arr).astype(int)
print(arr)
[[27 5 21 21 79]
[89 5 33 25 10]
[81 21 37 5 32]
[58 22 10 54 6]]
print(arr[0:3,0:3])
[[27 5 21]
[89 5 33]
[81 21 37]]
print(arr[1:4,1:4])
[[ 5 33 25]
[21 37 5]
[22 10 54]]
print(arr[::2,::2])
[[27 21 79]
[81 37 32]]
Array Manipulation#
We can change arrays in various ways.
Concatenation is accomplished with \(np.concatenate(array1,array2)\)
np.concatenate((a1,a2,...),axis=0,dtype=None)
arr1 = np.array([[1,2,3],[4,5,6]])
arr2 = np.array([[7,8,9],[10,11,12]])
print(arr1,'\n')
print(arr2,'\n')
conc = np.concatenate((arr1,arr2))
print(conc)
[[1 2 3]
[4 5 6]]
[[ 7 8 9]
[10 11 12]]
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
conc = np.concatenate((arr1,arr2),axis=1)
print(conc)
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]]
We can concatenate more than one array
arr3 = np.array([[13,14,15],[16,17,18]])
conc = np.concatenate((arr1,arr2,arr3),axis=1)
print(conc)
[[ 1 2 3 7 8 9 13 14 15]
[ 4 5 6 10 11 12 16 17 18]]
arr.reshape(n,m)
We can reshape an array so it has different dimensions.
arr = np.array([[1,2,3],[4,5,6]])
print(arr,'\n')
arr_reshaped = arr.reshape(3,2)
print(arr_reshaped,'\n')
[[1 2 3]
[4 5 6]]
[[1 2]
[3 4]
[5 6]]
Note that if we modify the new array, the original is also modified, in other words: \(arr\_reshaped\) is just a view of \(arr\) and not a distinct array.
arr_reshaped[0][0:] = 700
print(arr_reshaped,'\n')
print(arr,'\n')
[[700 700]
[ 3 4]
[ 5 6]]
[[700 700 3]
[ 4 5 6]]
arr.flatten()
We can convert an array into a one-dimensional vecotr/array using \(flatten()\)
arr = np.array([[1,2,3],[4,5,6]])
print(arr,'\n')
arr = arr.flatten()
print(arr,'\n')
[[1 2 3]
[4 5 6]]
[1 2 3 4 5 6]
arr.ravel()
We can similary use \(ravel()\)
arr = np.array([[1,2,3],[4,5,6]])
print(arr,'\n')
arr = arr.ravel()
print(arr,'\n')
[[1 2 3]
[4 5 6]]
[1 2 3 4 5 6]
np.hsplit(array,n)
We can split arrays into sub-arrays as follows
arr = np.array([[1,2,3],[4,5,6]])
print(arr,'\n')
arr = np.hsplit(arr,3)
print(arr,'\n')
print(arr[0],'\n')
[[1 2 3]
[4 5 6]]
[array([[1],
[4]]), array([[2],
[5]]), array([[3],
[6]])]
[[1]
[4]]