= [4,5,7]
myList
= 2 * myList
newList print(newList)
[4, 5, 7, 4, 5, 7]
numpy
moduleNumpy (short for “numerical python” and pronounced “num”-“pie”) is a popular Python library that is heavily used in the scientific/mathematical community. So much so that numpy is typically included as part of the standard bundle of libraries that comes with your Python installation. The functions inside numpy will allow you to solve problems with less effort and will produce faster-executing code.
You are already familiar with Python lists but may not have noticed that they are not suitable for mathematical calculations. For example, attempting to multiply a list by a scalar or evaluating a mathematical function like \(\sin()\) on a list will not produce a mathematical result or may produce an error. For example, consider the following code.
= [4,5,7]
myList
= 2 * myList
newList print(newList)
[4, 5, 7, 4, 5, 7]
You probably expected newList
to be [8,10,14]
but multiplying a list by a number doesn’t do that. Instead it repeats the list and concatenates it to itself. To multiply each element of a list by a number you must use a for
loop.
= [4,5,7]
myList
= []
newList for i in myList:
* 2)
newList.append(i
print(newList)
[8, 10, 14]
but this seems overly cumbersome for such a simple task. Numpy ndarrays (short for n-dimensional arrays) or just arrays make this task much simpler. Arrays are similar to lists or nested lists except that mathematical operations and numpy
functions (but not math
functions) automatically propagate to each element instead of requiring a for
loop to iterate over it. Because of their power and convenience, arrays are the default object type for any operation performed with NumPy.
You can create an array from a list using numpy’s array
function. The list that is to be converted is the argument to the array
function. Mathematical operations can then be performed on the array and that operation will propagate through to all of the elements.
from numpy import array
= array([4,5,7])
myArray
= 2 * myArray
newArray
print(newArray)
[ 8 10 14]
Nested lists, or lists that contain lists as their elements, can be converted to multi-dimensional arrays using the array
function.
from numpy import array
= array([[1,2,3],[4,5,6]])
myArray
print(myArray)
[[1 2 3]
[4 5 6]]
arange
and linspace
FunctionsNumpy has some sequence-generating functions that generate arrays by specifying start, stop, and step size values similar to the way range
generates a list. The two most common ones are arange
and linspace
. The arange
function behaves very similar to the native Python range
function with a few notable exceptions:
arange
produces an array whereas range
produces a list.arange
does not need to be an integer.range
produces an iterator and arange
generates a sequence of values immediately.The arguments to arange
are similar to range
arange(start,stop,step)
The linspace
function is related to the arange
function except that instead of specifying the step size of the sequence, the sequence is generated based on the number of equally-spaced points in the given span of numbers. Additionally, arange
excludes the stop value while linspace
includes it. The difference between these two functions is subtle and the use of one over the other often comes down to user preference or convenience.
linspace(start,stop,number of points)
Below is an example that shows the usage of linspace
and arange
.
from numpy import linspace,arange
= linspace(0,10,20)
myArray = arange(0,10,0.5)
myArray2 print(myArray)
print(myArray2)
[ 0. 0.52631579 1.05263158 1.57894737 2.10526316 2.63157895
3.15789474 3.68421053 4.21052632 4.73684211 5.26315789 5.78947368
6.31578947 6.84210526 7.36842105 7.89473684 8.42105263 8.94736842
9.47368421 10. ]
[0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5 5. 5.5 6. 6.5 7. 7.5 8. 8.5
9. 9.5]
When using linspace
you may still want to know what the sequence spacing is. You can request that linspace
provide this information by adding the optional argument retstep = True
to the argument list. With this addition, linspace not only returns the sequence to you, but also the stepsize.
from numpy import linspace,arange
= linspace(0,10,20,retstep= True)
myArray,mydx print(mydx)
print(myArray)
0.5263157894736842
[ 0. 0.52631579 1.05263158 1.57894737 2.10526316 2.63157895
3.15789474 3.68421053 4.21052632 4.73684211 5.26315789 5.78947368
6.31578947 6.84210526 7.36842105 7.89473684 8.42105263 8.94736842
9.47368421 10. ]
Two other useful functions for generating arrays are zeros
and ones
which generate arrays populated with exclusively ones or zeros. The functions require shape arguments as a tuple or list to specify the shape of the array.
zeros((rows,columns))
If the array to be created is only one dimensional, the argument can be a single number instead of a tuple.
zeros(n)
from numpy import zeros,ones
= zeros([3,4])
myArray = ones(5)
myArray2 print(myArray)
print(myArray2)
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
[1. 1. 1. 1. 1.]
Arrays of any constant (not just one or zero) can then be easily generated by performing the needed math on the original array.
from numpy import zeros,ones
= zeros([3,4]) + 5
myArray = ones(5) * 12
myArray2 print(myArray)
print(myArray2)
[[5. 5. 5. 5.]
[5. 5. 5. 5.]
[5. 5. 5. 5.]]
[12. 12. 12. 12. 12.]
A third approach is to generate an array from a function using the fromfunction
function which generates an array of values using the array indices as the inputs. Ths function requires two arguments: the name of the function being used and the shape of the array being generated.
fromfunction(function, shape)
Let’s make a 3 x 3 array where each element is the product of the row and column indices:
from numpy import fromfunction
def prod(x,y):
return x * y
= fromfunction(prod,(3,3))
myArray print(myArray)
[[0. 0. 0.]
[0. 1. 2.]
[0. 2. 4.]]
The table below gives a summary of useful functions for creating numpy arrays. The required arguments are also described.
Method | Description |
---|---|
linspace(start,stop,n) |
Returns an array of n evenly-spaced points begining at start and ending at stop . |
arange(start,stop,dx) |
Returns an array beginning at start ,ending at stop with a step size of dx . |
empty(dims) |
Returns an empty array with dimensions dim . |
zeros(dims) |
Returns an array of zeros with dimensions dim . |
ones(dims) |
Returns an array of ones with dimensions dim . |
zeros_like(arr) |
Returns an array of zeros with dimensions that match the dimensions of arr . |
fromfunction(function,dims) |
Returns an array of numbers generated by evaluating function on the indices of an array with dimensions dims . |
copy(arr) |
Creates a copy of array arr . |
genfromtext(file) |
Reads file and loads the text into an array (file must only contain numbers). |
Accessing and slicing arrays can be done in exactly the same way as is done with lists. However, there is some additional functionality for accessing and slicing arrays that do not apply to lists.
Elements from a one-dimensional array can be extracted using square brackets ([]
) just like we have done with lists.
from numpy import array
= array([3,4,7,8])
myArray print(myArray[2])
7
Multi-dimensional array can be indexed in a similar fashion to nested lists, but because we often encounter multi-dimensional arrays there is a shortcut that makes the syntax simpler and more convenient. Let’s consider a two-dimensional array as an example. To access the entire second row of the array, provide the row index in square brackets just as with one-dimensional arrays.
from numpy import array
= array([[1,2,3],[4,5,6], [7,8,9]])
myArray
print(myArray[1])
[4 5 6]
To access the second element in the second row, we can add another set of square brackets with the appropriate index inside, just as we did with nested lists. However, for convenience the second set of square brackets can be omitted and the row and column indices can be placed next to each other and separated by a comma.
array_name[row,column]
from numpy import array
= array([[1,2,3],[4,5,6], [7,8,9]])
myArray
print(myArray[1][1]) # This works, but is a bit hard on the eyes
print(myArray[1,1]) # This works also and is easier to look at.
5
5
Multiple elements of an array can be accessed using a list for the index instead of a single number.
from numpy import array
= array([1,2,3,4,5,6,7,8,9,10])
myArray
print(myArray[2]) # Extract element 2
print(myArray[ [3,6,9] ]) # Extract elements 3, 6, and 9.
3
[ 4 7 10]
This can even be done with multi-dimensional arrays. If the index is a single list, the corresponding rows will be extract. If the corresponding list of columns is added to the index list, individual elements will be extracted.
array_name[[rows]] # Access set of rows
array_name[[rows], [columns]] # Access set of elements
from numpy import array
= array([[1,2,3],[4,5,6], [7,8,9]])
myArray
print(myArray[[0,1]]) # Extract rows 0 and 1.
print(myArray[[1,2,0],[0,2,2]]) # Extract elements (1,0), (2,2), and (0,2)
[[1 2 3]
[4 5 6]]
[4 9 3]
We’ve already shown you how to slice a list using the : operator. The same can be done with arrays. However, for 2D (and higher) arrays the slicing is more powerful (intuitive). It can be helpful to visualize an array as a matrix, even if it is not being treated that way Mathematically. For example, let’s say that you define the following array:
from numpy import array
= array([[1,2,3],[4,5,6], [7,8,9]]) myArray
which can be visualized as the following matrix:
\[ \begin{pmatrix} 1&2&3\\ 4&5&6\\ 7&8&9\\ \end{pmatrix} \]
To slice out the following \(2\) x \(2\) sub matrix:
\[ \begin{pmatrix} 5&6\\ 8&9\\ \end{pmatrix} \]
we could do
from numpy import array
= array([[1,2,3],[4,5,6], [7,8,9]])
myArray
print(myArray[1:3,1:3])
[[5 6]
[8 9]]
To include all of the elements in a given dimension, use the :
alone with no numbers surrounding it.
from numpy import array
= array([[1,2,3],[4,5,6], [7,8,9]])
myArray
print(myArray[:,1:3]) # Extract all rows with columns 1 and 2
[[2 3]
[5 6]
[8 9]]
Boolean operations can be evaluated on arrays to produce corresponding arrays of booleans. The boolean array can then be used to index the original array and extract elements that meet some criteria.
from numpy import array
= array([1,2,3,4,5,6])
a
= a > 2
boolArray
print(boolArray)
print(a[boolArray])
[False False True True True True]
[3 4 5 6]
This also works on multi-dimensional arrays although the result is always one-dimensional regardless of the shape of the original array.
from numpy import array
= array([[1,2,3],[4,5,6], [7,8,9]])
myArray
print(myArray[myArray>2]) # Extract elements that are greater than 2.
[3 4 5 6 7 8 9]
A major advantage of numpy arrays over lists is that operations vectorize across the arrays. This means that mathematical operations propagate through the array instead of requiring a for
loop. This speeds up the calculation and makes code easier to write and read. Simple mathematical operations like adding, subtracting, etc can be performed on arrays as you would expect and the operation propagates through to all elements.
from numpy import array
= array([1,2,3])
a = array([4,5,6])
b
= a + b
c = a**2
d = 2 * b
e = 2/b
f = a * b
g
print(c,d,e,f,g)
[5 7 9] [1 4 9] [ 8 10 12] [0.5 0.4 0.33333333] [ 4 10 18]
All of the common mathematical operations that you learned for numbers now apply to arrays. Cool!
The numpy library has a massive collection of vectorized mathematical functions and these functions should be used instead of similar functions from other libraries that are not vectorized (like math
).
from numpy import array
from numpy import sqrt as nsqrt
from math import sqrt as mathsqrt
= array([1,4,9,16,25])
squares
print(nsqrt(squares))
#print(mathsqrt(squares)) #This will fail because it wasn't a numpy function.
[1. 2. 3. 4. 5.]
If a mathematical operation is performed between two arrays of the same dimensions, the mathematical operation is performed between corresponding elements in the two arrays. For example, if two \(2\) x \(2\) arrays are added together, element (0,0) of the first array gets added to the corresponding element in the second and so forth for all elements:
\[ \begin{pmatrix} 1&2\\ 3&4\\ \end{pmatrix}+ \begin{pmatrix} 5&6\\ 7&8\\ \end{pmatrix}= \begin{pmatrix} 6&8\\ 10&12\\ \end{pmatrix} \]
from numpy import array
= array([[1,2],[3,4]])
a = array([[5,6],[7,8]])
b
= a + b # Add the elements of the arrays together.
c = a * b # Multiply the elements of the arrays together.
d
print(c)
print(d)
[[ 6 8]
[10 12]]
[[ 5 12]
[21 32]]
When a mathematical operation between two arrays of different dimensions is attempted, Python has to figure out how to make them have the same shape before performing the operation. Broadcasting refers to the set of rules used for operations like this. To handle arrays with different dimensions, NumPy pads or clones the array with fewer dimensions to make it have the same dimensions as the larger array. For example, what would happen if you attempted this operation:
\[ \begin{pmatrix} 1&2\\ 3&4\\ \end{pmatrix}+ \begin{pmatrix} 2&2\\ \end{pmatrix} \]
One array is \(2\) x \(2\) and the other is \(1\) x \(2\). Before the addition can take place, NumPy clones the smaller array and repeats it until it has the same size as the bigger array.
\[ \begin{pmatrix} 1&2\\ 3&4\\ \end{pmatrix}+ \begin{pmatrix} 2&2\\ 2&2\\ \end{pmatrix} \]
from numpy import array
= array([[1,2],[3,4]])
a = array([2,2])
b = a + b
c
print(c)
[[3 4]
[5 6]]
There are some cases where NumPy simply cannot figure out how to broadcast one of the arrays appropriately and an error results. When broadcasting, NumPy must verify that all dimensions are compatible with each other. Two dimensions are compatible when i) they are equal or ii) one of the dimensions is 1. For example, if we tried to perform the following mathematical operation, broadcasting would fail because the first dimension of the first array is 2 and the first dimension of the second array is 3.
\[ \begin{pmatrix} 1&2&3\\ 3&4&5\\ \end{pmatrix}+ \begin{pmatrix} 1&1&1\\ 2&2&2\\ 3&3&3\\ \end{pmatrix} \]
from numpy import array
= array([[1,2,3],[4,5,6]])
a = array([[1,1,1],[2,2,2],[3,3,3]])
b = a + b c
but if we attempted
\[ \begin{pmatrix} 1&2&3\\ \end{pmatrix}+ \begin{pmatrix} 1&1&1\\ 2&2&2\\ 3&3&3\\ \end{pmatrix} \]
the operation would succeed because the first dimension of the first array is 1 and the second dimension of both arrays are 3.
Standard Python functions are often designed to perform a single calculation rather than iterate over a list to perform many calculations. For example, here is a function to calculate the average acceleration of an object given its final velocity and time of travel.
def accel(velocity, time):
return velocity / time
print(accel(52.6,5.6))
9.392857142857144
Now what if I have a list of many times that I’d like to feed into this function and get an acceleration value for each one.
def accel(velocity, time):
return velocity / time
= [4.6,7.9,3.2,8.5,9.2,4.7]
times print(accel(52.6,times)) # Produces an error because you can't divide by a list
An error results here because Python does not know how to divide by a list. We can NumPy-ify this function using a function called vectorize
. The resulting function will behave just like the other functions from the NumPy library, vectorizing across the list of times.
from numpy import vectorize
def accel(velocity, time):
return velocity / time
= [4.6,7.9,3.2,8.5,9.2,4.7]
times = vectorize(accel) # Vectorize the function!
vaccel print(vaccel(52.6,times)) # Succeeds because NumPy knows how to vectorize.
[11.43478261 6.65822785 16.4375 6.18823529 5.7173913 11.19148936]
Of course, we also could have just converted our times list into an array and used the original function.
from numpy import array
def accel(velocity, time):
return velocity / time
= array([4.6,7.9,3.2,8.5,9.2,4.7])
times print(accel(52.6,times)) # Succeeds because times is an array not a list.
[11.43478261 6.65822785 16.4375 6.18823529 5.7173913 11.19148936]
A wealth of functions exist to perform routine manipulation tasks on arrays once they are created. Often these tasks will involve changing the number of rows or columns or merging two arrays into one. The size and shape of an array are the number of elements and dimensions, respectively. These can be determined using the shape
and size
methods.
from numpy import array
= array([[1,2,3],[4,5,6]])
a print(a.size)
print(a.shape)
6
(2, 3)
The dimensions of an array can be modified using the reshape
function. This methods maintains the number of elements and the order of elements but repacks them into a different number of rows and columns. Because the number of elements is maintained, the size of the new array has to be the same as the original. Let’s see an example.
from numpy import array, reshape
= array([[1,2,3],[4,5,6]])
a
= reshape(a,[3,2])
b
print(a)
print(b)
[[1 2 3]
[4 5 6]]
[[1 2]
[3 4]
[5 6]]
The original array (a
) was a \(2\) x \(3\) and had \(6\) elements and the reshaped array also has \(6\) elements but is a \(3\) x \(2\). You can start with a one-dimensional array and reshape it to a higher dimensional array.
from numpy import linspace, reshape
= linspace(0,10,12)
a
= reshape(a,[3,4])
b
print(a)
print(b)
[ 0. 0.90909091 1.81818182 2.72727273 3.63636364 4.54545455
5.45454545 6.36363636 7.27272727 8.18181818 9.09090909 10. ]
[[ 0. 0.90909091 1.81818182 2.72727273]
[ 3.63636364 4.54545455 5.45454545 6.36363636]
[ 7.27272727 8.18181818 9.09090909 10. ]]
Flattening an array takes a higher-dimensional array and squishes it into a one-dimensional array. You can “flatten” an array with the flatten
method, but note that flatten
doesn’t actually modify the original array.
from numpy import array
= array([[1,2,3],[4,5,6]])
a
a.flatten()
print(a) # 'a' remains unchanged
= a.flatten() # If you want to change the definition of a, redifine it.
a print(a)
[[1 2 3]
[4 5 6]]
[1 2 3 4 5 6]
Transposing an array rotates it across the diagonal and can be accomplished with the transpose
function. There is also a shortcut method for this of array.T
to accomplish the same thing but just as with flatten
it does not modify the original array. (neither does transpose
)
from numpy import array,transpose
= array([[1,2,3],[4,5,6]])
a
transpose(a)
print(a) # 'a' remains unchanged
= a.T # If you want to change the definition of a, redefine it.
a print(a)
[[1 2 3]
[4 5 6]]
[[1 4]
[2 5]
[3 6]]
Several function exist for combining multiple arrays into a single array. We’ll give examples for a couple and mention the others in reference tables. The most commonly used functions for this task are vstack
(vertical stacking) and hstack
(horizontal stacking). vstack
will stack the original arrays vertically to create the new array and hstack
will stack them horizontally. Here are some examples.
from numpy import linspace, hstack, vstack
= linspace(0,10,10)
a = linspace(0,5,10)
b
= vstack((a,b))
c print(c) # 'a' remains unchanged
= hstack((a,b))
d print(a.T)
print(d)
[[ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556
6.66666667 7.77777778 8.88888889 10. ]
[ 0. 0.55555556 1.11111111 1.66666667 2.22222222 2.77777778
3.33333333 3.88888889 4.44444444 5. ]]
[ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556
6.66666667 7.77777778 8.88888889 10. ]
[ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556
6.66666667 7.77777778 8.88888889 10. 0. 0.55555556
1.11111111 1.66666667 2.22222222 2.77777778 3.33333333 3.88888889
4.44444444 5. ]
NumPy contains an extensive listing of array methods and functions and it would be impractical to list them all here. However, below you will find some tables of some of the commonly used ones that can serve as a reference.
Method | Description |
---|---|
shape(array) or array.shape |
Returns the dimensions of the array. |
ndim(array) or array.ndim |
Returns the number of dimensions (i.e. a 2D array is \(2\)). |
size(array) or array.size |
Returns the number of elements in an array. |
Method | Description |
---|---|
array.flatten() |
Returns a flattened view of array . |
reshape(array,dims) or array.reshape(dims) |
Returns a view of array reshaped into an array with dimensions given by dims . |
array.resize(dims) |
Modifies array to be a resized array with dimensions dims . |
transpose(array) or array.T |
Returns a view of the transpose of array . |
sort(array) |
Returns a view of a sorted version of array . |
array.sort() |
Modifies array to be sorted. |
argsort(array) |
Returns index values that will sort array . |
array.fill(x) |
Modifies array so that all elements are equal to x . |
vstack(a,b) |
Vertically stack arrays a and b to form the new array. |
hstack(a,b) |
Horizontally stack arrays a and b to form the new array. |
vsplit(array,n) |
Splits array vertically into n equal parts. |
hsplit(array,n) |
Splits array horizontally into n equal parts. |
append(array,x) |
Returns a view of array with x added to the end of the array. |
insert(array,n,x) |
Returns a view of array with x inserted at location n . |
delete(array,n) |
Returns a view of array with element at location n removed. |
unique(array) |
Returns a view of the unique elements of array . |
Method | Description |
---|---|
min(array) or array.min() |
Returns the minimum value in an array. |
max(array) or array.max() |
Returns the maximum value in an array. |
argmin(array) or array.argmin() |
Returns the location of the minimum value in an array. |
argmax(array) or array.argmax() |
Returns the location of the maximum value in an array. |
fmin(array1,array2) |
Returns the minimum between two arrays of the same size. (Arrays are compared element wise.) |
fmax(array1,array2) |
Returns the maximum between two arrays of the same size. (Arrays are compared element wise.) |
mean(array) or array.mean() |
Returns the mean of array . |
median(array) or array.median() |
Returns the median of array . |
std(array) or array.std() |
Returns the standard deviation of array . |
cumprod(array) or array.cumprod() |
Returns the cumulative product of array . |
cumsum(array) or array.cumsum() |
Returns the cumulative sum of array . |
sum(array) or array.sum() |
Returns the sum of all elements in array . |
prod(array) or array.prod() |
Returns the product of all elements in array . |
floor(array) |
Returns the floor (i.e., rounds down) of all elements in array . |
ceil(array) |
Returns the ceiling (i.e., rounds up) of all elements in array . |
linspace
function.arange
function.zeros_like
function.reshape
function do? Give an example to show the usage.flatten
function do? Give an example to show the usage.transpose
function do? Give an example to show the usage.vstack
function do? Give an example to show the usage.hstack
function do? Give an example to show the usage.argmax
and argmin
functions do?cumsum
function do?arange
function to generate all of the angles (in degrees) on the unit circle displayed in blue or black text.linspace
function to generate all of the angles (in degrees) on the unit circle displayed in red or black text.# Python code here
In the cell below you will find three arrays containing the masses, lengths, and radii for a collection of cylinders. The moment of inertia for each cylinder can be calculated as \[ I = {1\over 4} M R^2 + {1\over 12} M L^2\].
from numpy.random import uniform
= uniform(3,8,1000)
mass = uniform(0.5,1.2,1000)
radius = uniform(0.8,3,1000) length
Construct a two-dimensional array with the following entries: \[\begin{bmatrix} 1& 5 & 7 & 2\\ 3& 9 & 1 & 4\\ 4& 2 & 2 & 8\\ 9& 1 & 6 & 3\\ \end{bmatrix}\]
The following temperatures are prominent on the Fahrenheit scale: [0,32,100,212,451]
.
# Python code here.
The equation below defines the relationship between energy (\(E\)) in Joules of a photon and its wavelength (\(\lambda\)) in meters. The \(h\) is Plank’s constant (\(6.626 \times 10^{-34}\) J \(\cdot\) s) and \(c\) is the speed of light in a vacuum (\(2.998 \times 10^8\) m/s). \[E = {h c \over \lambda}\]
# Python code here
A boat is out at sea with the following location and velocity vectors: \[x_i = \begin{bmatrix} 5 \\ 2 \end{bmatrix} \text{ km}\] \[v_i = \begin{bmatrix} -13 \\ 25 \end{bmatrix} \text{ m/s}\] when a gust of wind causes the boat to accelerate for approximately \(3\) minutes with the following acceleration vector:\[a = \begin{bmatrix} -8 \\ -5 \end{bmatrix} \text{ m/s}^2\] We can find the position and velocity vectors of the boat after the wind has died back down with the following equation: \[ \vec{x_f} = \vec{x_i} + \vec{v_i} \Delta t + {1\over 2} \vec{a} \Delta t^2\] \[ \vec{v_f} = \vec{v_i} + \vec{a} \Delta t\]
Predict the outcome of the following operations between two arrays. Then test your prediction.
\[\begin{bmatrix} 1&8&9\\ 8&1&9\\ 1&8&1\\ \end{bmatrix}+ \begin{bmatrix} 1&1\\ 1&1\\ \end{bmatrix} \] \[ \begin{bmatrix} 1&1\\ 2&2\\ \end{bmatrix} + \begin{bmatrix} 1\\ \end{bmatrix}\] \[ \begin{bmatrix} 1&8&9\\ 8&1&9\\ 1&8&1\\ \end{bmatrix} + \begin{bmatrix} 1\\ 2\\ 3\\ \end{bmatrix}\] \[ \begin{bmatrix} 1&8&9\\ 8&1&9\\ 1&8&1\\ \end{bmatrix} + \begin{bmatrix} 1&2&3\\ \end{bmatrix}\]
# Python code here
In quantum mechanics you will learn that the allowed energy levels for the harmonic oscillator are given by:\[E_n = \hbar \sqrt{k \over m} (n + {1\over 2})\] where \(k = 4 \pi^2\) and \(m = 1\) gram.
# Python code here.
scipy.special
has a factorial
function that will work on arrays.cumsum
to evaluate the sum.