**NumPy** (short for Numerical Python) is a powerful open-source library in Python, primarily used for numerical computing and data manipulation. It provides support for large, multi-dimensional arrays and matrices, along with a vast collection of mathematical functions to operate on these arrays efficiently.

### Key Features of NumPy:

**Array-Oriented Computing:**NumPy introduces the concept of n-dimensional arrays (ndarray), which are more efficient and flexible compared to Python lists. These arrays allow for fast element-wise operations and broadcasting, where operations are applied to arrays of different shapes automatically.**Mathematical Functions:**NumPy provides a comprehensive set of mathematical tools such as linear algebra, Fourier transforms, statistical functions, and random number generation.**Efficiency:**NumPy is highly optimized for performance, allowing it to handle large datasets with minimal memory overhead. It’s implemented in C, and operations on NumPy arrays are performed at C-level speed, making it much faster than standard Python loops and list-based operations.**Broadcasting:**Broadcasting in NumPy allows for arithmetic operations between arrays of different shapes. For example, you can add a scalar to an array or add arrays with different dimensions, and NumPy will automatically handle the dimension mismatches.**Integration with Other Libraries:**NumPy is foundational for many other scientific libraries in Python like Pandas (for data analysis), Matplotlib (for data visualization), TensorFlow, and PyTorch (for machine learning), making it a core tool in data science and machine learning.

**Installing NumPy**

If you don’t have NumPy installed yet, you can install it using:

`pip install numpy`

### 2. **Importing NumPy**

`import numpy as np`

### 3. **Creating Arrays**

NumPy arrays are the foundation of NumPy, enabling fast and efficient data storage and manipulation.

#### Creating a 1D array:

`arr1 = np.array([1, 2, 3])`

print(arr1)

# Output: [1 2 3]

#### Creating a 2D array (matrix):

`arr2 = np.array([[1, 2, 3], [4, 5, 6]])`

print(arr2)

# Output:

# [[1 2 3]

# [4 5 6]]

#### Creating an array of zeros:

`zeros = np.zeros((2, 3)) # 2x3 matrix filled with zeros`

print(zeros)

# Output:

# [[0. 0. 0.]

# [0. 0. 0.]]

#### Creating an array of ones:

`ones = np.ones((3, 2)) # 3x2 matrix filled with ones`

print(ones)

# Output:

# [[1. 1.]

# [1. 1.]

# [1. 1.]]

#### Creating an empty array:

`empty = np.empty((2, 2)) # Uninitialized array with random values`

print(empty)

# Output: Random values, shape (2,2)

### 4. **Array Properties**

NumPy arrays have some useful attributes.

`print(arr2.ndim) # Number of dimensions`

# Output: 2

print(arr2.shape) # Shape of the array

# Output: (2, 3)

print(arr2.size) # Total number of elements

# Output: 6

print(arr2.dtype) # Data type of the elements

# Output: int64 (or int32 depending on your system)

### 5. **Array Creation: Special Methods**

`arange()`

: Array with a range of values

`arr = np.arange(0, 10, 2) # From 0 to 10, step by 2`

print(arr)

# Output: [0 2 4 6 8]

`linspace()`

: Array with evenly spaced values

`lin = np.linspace(0, 10, 5) # 5 equally spaced numbers between 0 and 10`

print(lin)

# Output: [ 0. 2.5 5. 7.5 10. ]

### 6. **Reshaping Arrays**

#### Reshaping the array into different dimensions:

`reshaped = arr2.reshape((3, 2)) # Reshape 2x3 to 3x2`

print(reshaped)

# Output:

# [[1 2]

# [3 4]

# [5 6]]

### 7. **Basic Operations**

#### Element-wise operations:

`arr = np.array([1, 2, 3])`

arr2 = np.array([4, 5, 6])

print(arr + arr2) # Add element-wise

# Output: [5 7 9]

print(arr * arr2) # Multiply element-wise

# Output: [ 4 10 18]

print(arr ** 2) # Square each element

# Output: [1 4 9]

#### Broadcasting: Operations between arrays of different shapes

`arr = np.array([1, 2, 3])`

scalar = 3

print(arr * scalar)

# Output: [3 6 9]

### 8. **Statistical Methods**

#### Basic statistical functions:

`arr = np.array([1, 2, 3, 4, 5])`

print(arr.mean()) # Mean of the array

# Output: 3.0

print(arr.sum()) # Sum of elements

# Output: 15

print(arr.min()) # Minimum value

# Output: 1

print(arr.max()) # Maximum value

# Output: 5

print(arr.std()) # Standard deviation

# Output: 1.4142135623730951

### 9. **Indexing and Slicing**

#### 1D array indexing:

`arr = np.array([10, 20, 30, 40, 50])`

print(arr[0]) # First element

# Output: 10

print(arr[-1]) # Last element

# Output: 50

#### 2D array indexing:

`arr2 = np.array([[1, 2, 3], [4, 5, 6]])`

print(arr2[0, 1]) # Element in the first row, second column

# Output: 2

print(arr2[:, 1]) # All rows, second column

# Output: [2 5]

#### Slicing arrays:

`arr = np.array([1, 2, 3, 4, 5])`

print(arr[1:4]) # Elements from index 1 to 3 (4 excluded)

# Output: [2 3 4]

### 10. **Array Copying**

#### Simple assignment (creates a reference, not a copy):

`arr_copy = arr`

arr_copy[0] = 100

print(arr) # arr is also modified

# Output: [100 2 3 4 5]

#### Deep copy:

`arr_copy = arr.copy()`

arr_copy[0] = 1

print(arr) # arr is not modified

# Output: [100 2 3 4 5]

### 11. **Array Operations**

#### Transposing an array:

`arr2 = np.array([[1, 2], [3, 4], [5, 6]])`

print(arr2.T) # Transpose

# Output:

# [[1 3 5]

# [2 4 6]]

#### Dot product of two arrays:

`a = np.array([[1, 2], [3, 4]])`

b = np.array([[5, 6], [7, 8]])

dot_product = np.dot(a, b)

print(dot_product)

# Output:

# [[19 22]

# [43 50]]

### 12. **Concatenation and Stacking**

#### Horizontal stacking:

`arr1 = np.array([1, 2, 3])`

arr2 = np.array([4, 5, 6])

hstack = np.hstack((arr1, arr2))

print(hstack)

# Output: [1 2 3 4 5 6]

#### Vertical stacking:

`vstack = np.vstack((arr1, arr2))`

print(vstack)

# Output:

# [[1 2 3]

# [4 5 6]]

#### Concatenate along an axis:

`arr1 = np.array([[1, 2], [3, 4]])`

arr2 = np.array([[5, 6]])

concat = np.concatenate((arr1, arr2), axis=0)

print(concat)

# Output:

# [[1 2]

# [3 4]

# [5 6]]

### 13. **Random Numbers**

#### Generate random values:

`rand_arr = np.random.rand(3, 3) # Random values in a 3x3 array`

print(rand_arr)

# Output: Random 3x3 matrix

#### Generate random integers:

`rand_int = np.random.randint(0, 10, size=(3, 3)) # Random integers between 0 and 10`

print(rand_int)

# Output: Random 3x3 matrix of integers

### 14. **Boolean Indexing**

`arr = np.array([1, 2, 3, 4, 5])`

bool_idx = arr > 3 # Condition to filter elements

print(bool_idx)

# Output: [False False False True True]

filtered_arr = arr[bool_idx] # Apply the condition to filter the array

print(filtered_arr)

# Output: [4 5]

### 15. **Sorting Arrays**

`arr = np.array([3, 1, 5, 2, 4])`

sorted_arr = np.sort(arr)

print(sorted_arr)

# Output: [1 2 3 4 5]