<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Operations on NumPy Arrays
The learning objectives of this section are
Step1:  array.T returns the transpose of an array.
Step2:  Stacking and Splitting Arrays
Stacking
Step3:  Similarly, two arrays having the same number of rows can be horizontally stacked using np.hstack((a, b)).
Perform Operations on Arrays
Performing mathematical operations on arrays is extremely simple. Let's see some common operations.
Basic Mathematical Operations
NumPy provides almost all the basic math functions - exp, sin, cos, log, sqrt etc. The function is applied to each element of the array.
Step4:  Apply User Defined Functions
You can also apply your own functions on arrays. For e.g. applying the function x/(x+1) to each element of an array.
One way to do that is by looping through the array, which is the non-numpy way. You would rather want to write vectorised code. 
The simplest way to do that is to vectorise the function you want, and then apply it on the array. Numpy provides the np.vectorize() method to vectorise functions.
Let's look at both the ways to do it.
Step5:  This also has the advantage that you can vectorize the function once, and then apply it as many times as needed. 
Apply Basic Linear Algebra Operations
NumPy provides the np.linalg package to apply common linear algebra operations, such as
<ASSISTANT_TASK:>
Python Code:
import numpy as np
# Reshape a 1-D array to a 3 x 4 array
some_array = np.arange(0, 12).reshape(3, 4)
print(some_array)
# Can reshape it further 
some_array.reshape(2, 6)
# If you specify -1 as a dimension, the dimensions are automatically calculated
# -1 means "whatever dimension is needed" 
some_array.reshape(4, -1)
Explanation: Operations on NumPy Arrays
The learning objectives of this section are:
Manipulate arrays
Reshape arrays
Stack arrays
Perform operations on arrays
Perform basic mathematical operations
Apply built-in functions 
Apply your own functions 
Apply basic linear algebra operations 
Manipulating Arrays
Let's look at some ways to manipulate arrays, i.e. changing the shape, combining and splitting arrays, etc.   
Reshaping Arrays
Reshaping is done using the reshape() function.
End of explanation
# Transposing an array
some_array.T
Explanation: array.T returns the transpose of an array.
End of explanation
# Creating two arrays
array_1 = np.arange(12).reshape(3, 4)
array_2 = np.arange(20).reshape(5, 4)
print(array_1)
print("\n")
print(array_2)
# vstack
# Note that np.vstack(a, b) throws an error - you need to pass the arrays as a list
np.vstack((array_1, array_2))
Explanation: Stacking and Splitting Arrays
Stacking: np.hstack() and n.vstack()
Stacking is done using the np.hstack() and np.vstack() methods. For horizontal stacking, the number of rows should be the same, while for vertical stacking, the number of columns should be the same.
End of explanation
# Basic mathematical operations
a = np.arange(1, 20)
# sin, cos, exp, log
print(np.sin(a))
print(np.cos(a))
print(np.exp(a))
print(np.log(a))
Explanation: Similarly, two arrays having the same number of rows can be horizontally stacked using np.hstack((a, b)).
Perform Operations on Arrays
Performing mathematical operations on arrays is extremely simple. Let's see some common operations.
Basic Mathematical Operations
NumPy provides almost all the basic math functions - exp, sin, cos, log, sqrt etc. The function is applied to each element of the array.
End of explanation
print(a)
# The non-numpy way, not recommended
a_list = [x/(x+1) for x in a]
print(a_list)
# The numpy way: vectorize the function, then apply it
f = np.vectorize(lambda x: x/(x+1))
f(a)
# Apply function on a 2-d array: Applied to each element 
b = np.linspace(1, 100, 10)
f(b)
Explanation: Apply User Defined Functions
You can also apply your own functions on arrays. For e.g. applying the function x/(x+1) to each element of an array.
One way to do that is by looping through the array, which is the non-numpy way. You would rather want to write vectorised code. 
The simplest way to do that is to vectorise the function you want, and then apply it on the array. Numpy provides the np.vectorize() method to vectorise functions.
Let's look at both the ways to do it.
End of explanation
# np.linalg documentation
help(np.linalg)
# Creating arrays
a = np.arange(1, 10).reshape(3, 3)
b= np.arange(1, 13).reshape(3, 4)
print(a)
print(b)
# Inverse
np.linalg.inv(a)
# Determinant
np.linalg.det(a)
# Eigenvalues and eigenvectors
np.linalg.eig(a)
# Multiply matrices
np.dot(a, b)
Explanation: This also has the advantage that you can vectorize the function once, and then apply it as many times as needed. 
Apply Basic Linear Algebra Operations
NumPy provides the np.linalg package to apply common linear algebra operations, such as:
* np.linalg.inv: Inverse of a matrix
* np.linalg.det: Determinant of a matrix
* np.linalg.eig: Eigenvalues and eigenvectors of a matrix
Also, you can multiple matrices using np.dot(a, b).
End of explanation
<END_TASK>