<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 a Python library that provides ndarray (a multi-dimensional array structure) and a wide range of functions to perform operations on ndarray and mathematical operations. <a href="https
Step1:  The ndarray structure
The ndarray data structure is essential in NumPy. It is an n-dimensional array that can contain dtype objects.
Step2:  Array creation methods
Step3:  Aggregate methods (min and max)
Step4:  Summations
Step5:  Transform a 1D array into a 2D array
Step6:  Identity Matrix
Step7:  Random Numbers
Step8:  Sample from the normal distribution
Step9:  Indexing and slicing
Step10:  Tiling
Tiling is a common and useful operation when we need to extend an array so that its shape matches another NumPy array. By doing so, we can use the 'tiled' array in applying some element-wise operation between them.
Step11:  Broadcasting
Broadcasting is Numpy's terminology for performing mathematical operations between arrays of different shapes. If certain assumptions hold, the smaller of the two arrays is said to be broadcast to the size of the larger array in order to make the two arrays compatible so  element-to-element operations can be performed. <a href="http
Step12:  Arithmetic
Step13:  Universal functions
A universal function (ufunc) is a mathematical function that operates on arrays in an element-by-element fashion supporting array broadcasting, type casting, and several other standard features. For more information read the <a href="https
<ASSISTANT_TASK:>
Python Code:
import numpy as np
Explanation: NumPy
NumPy is a Python library that provides ndarray (a multi-dimensional array structure) and a wide range of functions to perform operations on ndarray and mathematical operations. <a href="https://docs.scipy.org/doc/numpy/reference/">NumPy Documentation</a>.
End of explanation
my_vector = np.array([1, 2, 3, 4])
my_vector
my_vector.shape
my_vector.dtype
my_matrix = np.array([[1, 2], [3, 4]])
my_matrix
my_matrix.shape
# Find the length of each element in bytes
my_matrix.itemsize
my_matrix2 = np.array([[1, 2], [3, 4]], dtype=np.int8)
my_matrix2.itemsize
Explanation: The ndarray structure
The ndarray data structure is essential in NumPy. It is an n-dimensional array that can contain dtype objects.
End of explanation
# Create an uninitialised array of specified shape and dtype
np.empty(shape=(4,4),dtype=np.int8)
np.zeros(4)
np.zeros((4,4))
np.zeros((4,4)) + 42
# Create a new zero matrix of the same shape as another matrix.
np.zeros_like(my_matrix)
np.ones(4)
np.ones((4,4))
# Similar to Python's built-in range() function
np.arange(start=0, stop=10, step=2) 
# Like arange() but instead of a step size, we specify the 
# number of values that we need. It generates lineary-spaced
# numbers in the given interval
np.linspace(start=10, stop=20, num=5)
# Generate numbers that are evenly spaced on a logarithmic scale
np.logspace(start=1, stop=2, num=10)
Explanation: Array creation methods
End of explanation
arr1 = np.array([10, 87, 86,  5,  4, 38, 94, 76, 12, 17])
arr1
arr1.max(), arr1.argmax()
arr1.min(), arr1.argmin()
arr1_copy = arr1.copy()
arr1_copy
Explanation: Aggregate methods (min and max)
End of explanation
matrix1 = np.arange(1,26).reshape(5,5)
matrix1
# Sum values in the matrix
matrix1.sum()
# Sum values by column
matrix1.sum(0) 
Explanation: Summations
End of explanation
prime_numbers = np.array([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97])
prime_numbers
prime_numbers.reshape(5, 5)
Explanation: Transform a 1D array into a 2D array
End of explanation
np.eye(4)
Explanation: Identity Matrix
End of explanation
# Use NumPy to generate four random numbers between 0 and 1
np.random.rand(4)
np.random.rand(4, 4)
np.random.randint(1, 101)
# Generates 10 random integers between 1 and 100
np.random.randint(1, 101, 10) 
Explanation: Random Numbers
End of explanation
# Generate four numbers from the normal distribution centred around zero
np.random.randn(4)
np.random.randn(4, 4)
Explanation: Sample from the normal distribution
End of explanation
arr2 = np.arange(0, 101, 10)
arr2
arr2[2]
# Use Python's slice notation to fetch elements from the array
arr2[3:6] 
arr2[3:]
arr2[:4]
# Boolean indexing 
arr2[arr2 > 5]
arr2 > 5
arr_with_nans = np.array([np.nan, 1,2,np.nan,3,4,5])
arr_with_nans
# Get an array where NaN elements are omitted
arr_with_nans[~np.isnan(arr_with_nans)]
matrix2 = np.arange(1, 26).reshape(5,5)
matrix2
matrix2[1]
matrix2[1,2] # same as matrix[1][2]
matrix2[1:4,1:4]
# Use ellipsis to get elements from the third column
matrix2[...,2]
# Fetch elements placed at corners of the 5x5 array
rows = np.array([[0,0],[4,4]])
cols = np.array([[0,4],[0,4]])
matrix2[rows, cols]
Explanation: Indexing and slicing
End of explanation
arr3 = np.array([9, 4, 4])
arr3
np.tile(arr3, (4, 1))
np.tile(arr3, (5, 2))
Explanation: Tiling
Tiling is a common and useful operation when we need to extend an array so that its shape matches another NumPy array. By doing so, we can use the 'tiled' array in applying some element-wise operation between them.
End of explanation
macro_nutrients = np.array([[0.3, 2.5, 3.5],
                            [2.9, 27.5, 0],
                            [0.4, 1.3, 23.9],
                            [14.4, 6, 2.3]])
calories_per_macro = np.array([9, 4, 4])
macro_nutrients * calories_per_macro
arr4 = np.arange(0, 10)
arr4
arr4[0:4] = 10
arr4
Explanation: Broadcasting
Broadcasting is Numpy's terminology for performing mathematical operations between arrays of different shapes. If certain assumptions hold, the smaller of the two arrays is said to be broadcast to the size of the larger array in order to make the two arrays compatible so  element-to-element operations can be performed. <a href="http://eli.thegreenplace.net/2015/broadcasting-arrays-in-numpy/">Read more</a>.
End of explanation
arr5 = np.arange(0, 10)
arr6 = np.arange(10, 20)
arr5
arr6
arr5 + arr6
arr6 - arr5
arr5 * arr6
arr5 + 10 # broadcasting
arr6 - 10
arr5 ** 2
# NumPy generates a warning if we attempt to divide by zero.
arr5 / arr5
1 / arr5
Explanation: Arithmetic
End of explanation
arr7 = np.array([2, 6, 7, 10, 45, 200])
arr7
# Computes the square root of each element of the array
np.sqrt(arr7)
np.exp(arr7)
np.log(arr7)
Explanation: Universal functions
A universal function (ufunc) is a mathematical function that operates on arrays in an element-by-element fashion supporting array broadcasting, type casting, and several other standard features. For more information read the <a href="https://docs.scipy.org/doc/numpy/reference/ufuncs.html">ufunc documentation</a>.
End of explanation
<END_TASK>