<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 NumPy
NumPy is the fundamental package for scientific computing with Python. The main additions to the standard Python are
New datatype, NumPy array
static, multidimensional
Fast processing of arrays
Tools for linear algebra, random numbers, ...
Numpy array
The NumPy array is static, which means that
All elements have the same type, i.e. contrary to Python lists one cannot have both e.g. integer numbers and strings as elements
The size of the array is fixed at the time of creation, so elements cannot be added or removed
Array can have arbitrary number of dimensions, and even though the size of the array is fixed, the shape can be changed, i.e 2x2 matrix can be changed into 4 element vector. It is possible to combine, split, and resize arrays, but a new array is then always created.
The picture below illustrates the differences between NumPy arrays and Python list. As the NumPy array is (normally) contiguous in memory, the processing is much faster, and the dynamic nature of list adds also lots of overhead.
First thing when starting to work with NumPy, is to import the package. The package is commonly imported as np
Step1:  Creating NumPy arrays
From a list (or tuple)
Step2:  Multidimensional lists (or tuples) produce multidimensional arrays
Step3:  Evenly spaced values
Step4:  Specific sized arrays
Step5:  Indexing and slicing arrays
Indexing is similar to lists, with different dimensions separated by commas
Step6:  Contrary to lists, slicing is possible over all dimensions
Step7:  Views and copies of arrays
As with all mutable Python objects, simple assignment creates a reference to array. If a and b are references to same array, changing contents of b changes also contents of a. NumPy arrays have a copy method for actual copying of array
Step8:  Slicing creates a view to the array, and modifying the view changes corresponding original contents
Step9:  Array operations
Most arithmetic operations for NumPy arrays are done elementwise. Note for Matlab users! Multiplication is done elementwise.
Step10:  NumPy has special functions which can work with the array arguments (sin, cos, exp, sqrt, log, ...)
Step11:  Vectorized operations
For loops and indexing in Python are slow. If the corresponding operation can be written in terms of full (or partial) arrays, operation can be speeded up significantly.
Example
Step12:  Linear algebra
NumPy contains linear algebra operations for matrix and vector products, eigenproblems and linear systems. Typically, NumPy is built against optimized BLAS libraries which means that these operations are very efficient (much faster than naive implementation e.g. with C or Fortran)
Step13:  Simple plotting with matplotlib
matplotlib is powerful 2D plotting library for Python. matplotlib can produce publication quality figures in various hardcopy formats. matplotlib can be used in scripts and in interactive shells, as well as in notebooks. matplotlib tries to make easy things easy and hard things possible. You can generate plots, histograms, power spectra, bar charts, errorcharts, scatterplots, etc, with just a few lines of code. Good way to learn about matplotlib's possibilities is to check the screenshots and gallery which provide also the code to produce the figures.
For simple plotting, one commonly imports matplotlib.pyplot package
Step14:  For showing fictures in the notebook, one can invoke the following magic
Step15:  Simple line plots of NumPy arrays
Step16:  Multiple subplots
<ASSISTANT_TASK:>
Python Code:
import numpy as np
Explanation: NumPy
NumPy is the fundamental package for scientific computing with Python. The main additions to the standard Python are
New datatype, NumPy array
static, multidimensional
Fast processing of arrays
Tools for linear algebra, random numbers, ...
Numpy array
The NumPy array is static, which means that
All elements have the same type, i.e. contrary to Python lists one cannot have both e.g. integer numbers and strings as elements
The size of the array is fixed at the time of creation, so elements cannot be added or removed
Array can have arbitrary number of dimensions, and even though the size of the array is fixed, the shape can be changed, i.e 2x2 matrix can be changed into 4 element vector. It is possible to combine, split, and resize arrays, but a new array is then always created.
The picture below illustrates the differences between NumPy arrays and Python list. As the NumPy array is (normally) contiguous in memory, the processing is much faster, and the dynamic nature of list adds also lots of overhead.
First thing when starting to work with NumPy, is to import the package. The package is commonly imported as np
End of explanation
a = np.array((1, 2, 3, 4))
print(a)
print(a.dtype)
print(a.size)
a = np.array((1,2,3,4), dtype=float) # Type can be explicitly specified
print(a)
print(a.dtype)
print(a.size)
Explanation: Creating NumPy arrays
From a list (or tuple):
End of explanation
my_list = [[1,2,3], [4,5,6]]
a = np.array(my_list)
print(a)
print(a.size)
print(a.shape)
Explanation: Multidimensional lists (or tuples) produce multidimensional arrays
End of explanation
a = np.arange(6)  # half open interval up to 6
print(a)
a = np.arange(0.1, 1, 0.2) # half open interval with start, stop, step
print(a)
b = np.linspace(-4.5, 4.5, 5) # specified number of samples within closed interval
print(b)
Explanation: Evenly spaced values
End of explanation
mat = np.empty((2, 2, 2), float)  # uninitialized 2x2x2 array
mat = np.zeros((3,3), int)  # initialized to zeros
mat = np.ones((2,3), complex) #initialized to ones
Explanation: Specific sized arrays
End of explanation
a = np.arange(6)
print(a[2])
print(a[-2])
mat = np.array([[1, 2, 3], [4, 5, 6]])
print(mat)
print(mat[0,2])
print(mat[-1,-2])
Explanation: Indexing and slicing arrays
Indexing is similar to lists, with different dimensions separated by commas
End of explanation
mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
print(mat[1, 1:3])
mat = np.zeros((4,4))
mat[1:-1,1:-1] = 2
print(mat)
Explanation: Contrary to lists, slicing is possible over all dimensions
End of explanation
a = np.arange(6)
print(a)
b = a  # b is a reference, changing values in b changes also a
b[2] = -3
print(a)
b = a.copy() # b is copy, changing b does not affect a
b[0] = 66
print(b)
print(a)
Explanation: Views and copies of arrays
As with all mutable Python objects, simple assignment creates a reference to array. If a and b are references to same array, changing contents of b changes also contents of a. NumPy arrays have a copy method for actual copying of array
End of explanation
c = a[1:4]
print(c)
c[-1] = 47
print(a)
Explanation: Slicing creates a view to the array, and modifying the view changes corresponding original contents
End of explanation
a = np.array([1.0, 2.0, 3.0])
b = 2.0
print(b * a)
print(b + a)
print(a + a)
print(a * a)
Explanation: Array operations
Most arithmetic operations for NumPy arrays are done elementwise. Note for Matlab users! Multiplication is done elementwise.
End of explanation
x = np.linspace(-np.pi, np.pi, 5)
y = np.sin(x)
Explanation: NumPy has special functions which can work with the array arguments (sin, cos, exp, sqrt, log, ...)
End of explanation
N = 1000
a = np.arange(N)
dif = np.zeros(N-1, a.dtype)
%%timeit   #timeit magic allows easy timing for the execution of an cell
# brute force with for loop
for i in range(1, N):
    dif[i-1] = a[i] - a[i-1]
%%timeit
# vectorized operation
dif = a[1:] - a[:-1]
Explanation: Vectorized operations
For loops and indexing in Python are slow. If the corresponding operation can be written in terms of full (or partial) arrays, operation can be speeded up significantly.
Example: calculating the difference between successive array elements
End of explanation
A = np.array(((2, 1), (1, 3)))
B = np.array(((-2, 4.2), (4.2, 6)))
C = np.dot(A, B) # matrix-matrix product
w, v = np.linalg.eig(A) # eigenvalues in w, eigenvectors in v
b = np.array((1, 2))
x = np.linalg.solve(C, b) # Solve Cx = b
print(np.dot(C, x)) # np.dot calculates also matrix-vector and vector-vector products
Explanation: Linear algebra
NumPy contains linear algebra operations for matrix and vector products, eigenproblems and linear systems. Typically, NumPy is built against optimized BLAS libraries which means that these operations are very efficient (much faster than naive implementation e.g. with C or Fortran)
End of explanation
import matplotlib.pyplot as plt
Explanation: Simple plotting with matplotlib
matplotlib is powerful 2D plotting library for Python. matplotlib can produce publication quality figures in various hardcopy formats. matplotlib can be used in scripts and in interactive shells, as well as in notebooks. matplotlib tries to make easy things easy and hard things possible. You can generate plots, histograms, power spectra, bar charts, errorcharts, scatterplots, etc, with just a few lines of code. Good way to learn about matplotlib's possibilities is to check the screenshots and gallery which provide also the code to produce the figures.
For simple plotting, one commonly imports matplotlib.pyplot package
End of explanation
%matplotlib inline
Explanation: For showing fictures in the notebook, one can invoke the following magic
End of explanation
x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
plt.plot(x, y)
plt.title('A simple plot')
plt.xlabel('time (s)')
Explanation: Simple line plots of NumPy arrays
End of explanation
x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.cos(x)
plt.subplot(211) #create 2x1 plot, use 1st
plt.plot(x, y1, linewidth=2)
plt.ylabel('sin')
plt.subplot(212) #use 2nd
plt.plot(x, y2, '--or') # use dashed line, 'o' markers and red color
plt.ylabel('cos', fontsize=16)
plt.xlabel(r'$\theta$') # when using Latex, string has to be so called raw string (r'my string')
Explanation: Multiple subplots
End of explanation
<END_TASK>