NumPy

Priyesh ShahPriyesh Shah
7 min read

Hello everyone! Today we kick off our deep dive into essential Python libraries—starting with NumPy. Watching this NUMPY TUTORIAL. This continues the same cheat sheet version notes I started in my Basics of Python Lessons!

What Is NumPy?

NumPy is an open‑source library for fast, memory‑efficient n‑dimensional arrays and vectorized math operations, all powered by optimized C code. It also offers built‑in statistical and linear algebra functions.

💡 For mixed‑type data (strings, dates), use Pandas instead.

NumPy Arrays vs. Python List

  • Speed & Efficiency: C‑backed arrays run math on large datasets much faster than lists.

  • Vectorization: Apply operations to entire arrays without explicit loops.

  • Broadcasting: Perform element‑wise math on arrays of different shapes automatically.

NumPy Fundamentals

Creating Arrays: You can create arrays with np. array() and access elements using indexing

import numpy as np
a = np.array([1, 2, 3, 4, 5, 6])
print(a)  
# => [1 2 3 4 5 6]

Indexing and Modification

print(a[2])  # => 3 

a[3] = 13
print(a)     # => [ 1  2  3 13  5  6]

Slicing: Slicing works similarly to Python lists

1. a[3:]     
   OP => array([13,  5,  6])
   a[:2]
   OP => arr([1,2])

2. b = a[4:] 
   OP => arr([5,6]) # you can also assign to other variable & print & play along this way

Array Attributes: NumPy arrays come with built‑in attributes to inspect their structure:

1. # 0-D array(scalar)
   a= np.array(23)
   print(a.ndim,a.shape,a.size,len(a.shape))     
   #=> No of Dimensions: 0  #=> Shape:()  #=> Len of Shape: 0   #=> Size: 1 

- Shape is empty becuase it is non negative number that specify the number of elemnts 
  along each dimension but in this case we have dimension as 0.
- Len of Shape is always equal to no. of dimensions.
- Size is no of elements.(or product of all elements in shape)

2. # 1-D array
   a= np.array([1.2])
   print(a.ndim,a.shape,a.size,len(a.shape)) 
   #=> No of Dimensions: 1  #=> Shape- (2,)  #=> Len of Shape: 1  #=> Size: 2

3.# 2-D array
   a= np.array([[1,2], 
                [1,2]])
   print(a.ndim,a.shape,a.size,len(a.shape)) 
   #=> No of Dimensions: 2  #=> Shape- (2,2)  #=> Len of Shape: 2  #=> Size: 4

   a= np.array([[1, 2, 3],
               [4, 5, 6],
               [7, 8, 9]])
   print(a.ndim,a.shape,a.size,len(a.shape)) 
   #=> No of Dimensions: 2  #=> Shape- (3,3)  #=> Len of Shape: 2  #=> Size: 12

5. # 3-D array
   a= np.array([[[1,2,6,7], 
                [1,2,6,7]],
               [[1,2,6,7], 
                 [1,2,6,7]]])
   print(a.ndim,a.shape,a.size,len(a.shape)) 
   #=> No of Dimensions: 3  #=> Shape- (2,2,4)  #=> Len of Shape: 2  #=> Size: 12

6. # 4-D array
   a= np.array([[[[1,2,6,7], 
                  [1,2,6,7]],
                 [[1,2,6,7], 
                  [1,2,6,7]]],

                [[[1,2,6,7], 
                  [1,2,6,7]],
                 [[1,2,6,7], 
                  [1,2,6,7]]]])
   print(a.ndim,a.shape,a.size,len(a.shape)) 
   #=> No of Dimensions: 4  #=> Shape- (2,2,2,4)  #=> Len of Shape: 2  #=> Size: 12

Data Type: dtype()

Data Type (dtype): You can check the data type of array elements
a = np.array([1, 2, 3], dtype=np.int32)
a.d(type)                            #=> 'int32'

Creating Basic Arrays

np.zeros() and np.ones(): Creating arrays filled with all zeros or ones.

1. a = np.zeros(2)
   array([0., 0.]) 

2. But you can also give shape and define every single thing in it
   a = np.zeros((2,3,7,4))
   # Here we have 2 dimensions with 3 elements each having 7 columns and 4 rows

3. Similarly for ones instead of 0 it will fill the array with ones
   np.ones((2,3,6))        #=> 2×3×4 array of 1.0
   # Here we have 2 dimensions each having 6 columns and 3 rows.

np.empty(): Ceates an array with random initial content, which is faster.

1. np.empty(3)  #=> array([1.05463191e-311, 1.05658269e-311, 0.00000000e+000]) garbage value

2. To print certain shape of garbage values (uninitialized) 
   np.empty((3,8)) 

Why use empty? It’s faster than zeros/ones when you plan to overwrite every element.

np.arange(): Creates arrays with a range of values. Like Python’s range, but returns an array:

Format:(first number, last number, step size) 
1. np.arange(2,9,2)   #=> array([2, 4, 6, 8]) stop is exclusive
2. np.arange(6)       #=> array([0, 1, 2, 3, 4, 5]) takes default start 0 and 1 step

np.linspace(): Generates N evenly spaced points including endpoints:

Format(first number, last number , no of elements you want)
np.linspace(0, 10, num=5)   #=> array([ 0. ,  2.5,  5. ,  7.5, 10. ]) stop is exclusive     
'''Step size = (end – start) / (num – 1)
   Here: (10 – 0) / 4 = 2.5'''

📌 Tip: To convert it into integer we can do:- x = np.ones(2, dtype = np.int64)
Output:- array([1, 1]) Do the same thing everywhere!

Sorting and Concatenating Arrays

np.sort(): Sorts array elements & np.concatenate() : Joins arrays along a specified axis

arr = np.array([2,1,5,3,7,4,6,8])
arr2 = np.array([24,52,43,14,25,56])

1. # Sort
   np.sort(arr)           # ⇒ [1 2 3 4 5 6 7 8]
   np.argsort(arr)        # ⇒ [1 0 3 5 2 6 4 7]  # Sort and return indices

2. # Partition all elements < kth position come first (unordered)
   np.partition(arr, 3)   # ⇒ [1 2 3 4 5 7 6 8]

3. # Concatenate
   np.concatenate((arr, arr2))  # ⇒ [ 2  1  5  3  7  4  6  8 24 52 43 14 25 56]

4. # 2-D concatenation 
   axis=0 adds rows; axis=1 adds columns
   a = np.array([[1,2],[3,4]])
   b = np.array([[5,6]])
   np.concatenate((a, b), axis=0) # Shapes must match, except the axis you’re stacking along
   =>[[1 2]
      [3 4]
      [5 6]]

Reshaping and Adding New Axes

np.reshape(): Changes the shape of an array

1. a= np.arange(1,7)     #=> array([1, 2, 3, 4, 5, 6])
   a.reshape(2,3)        #=> array([[1, 2, 3], [4, 5, 6]]) Reshapes the array

2. a= np.arange(6)  
   np.reshpae(a, (2,3), order='F') #=> array([[0, 2, 4], [1, 3, 5]]) Reshapes column wise
   np.reshape(a, (2,3), order='C') #=> array([[0, 1, 2], [3, 4, 5]]) Reshapes row wise

np.newaxis() or np.expand_dims(): Adds a new dimension to an array

a = np.array([1,2,3,4,5,6])
a.shape                      Shape is #=> (6,)

If we add new axis
1. a2 = a[np.newaxis, :]
   a2.shape                  #=> A new row vector gets added and Shape becomes (1,6) 
2. a2 = a[:, np.newaxis,]
   a2.shape                  #=> A new column vector gets added and Shape becomes #=>(6,1) 

Equivalent using expand_dims
1. b = np.expand_dims(a, axis =0) 
   b.shape                       #=> A new column vector gets added #=>(1,6)
2. c = np.expand_dims(a, axis =1) 
   c.shape                       #=> A new row vector gets added #=>(6,1)

Advanced Indexing and Slicing

Condition based Slicing

arr = np.array([12, 89, 45, 31, 789, 200])
mask = arr < 50           # ⇒ [True False True True False False]
arr[mask]                 # ⇒ [12 45 31]

# 2D mask & nonzero
mat = np.array([[13,62,3,42],
                [75,6,7,84],
                [93,10,2,21]])
mat < 50                     # ⇒ array of True/False
np.nonzero(mat < 50)         # ⇒ (row_indices, col_indices) Gets you indices

Creating Arrays from Existing Data

Stacking: np.vstack() (vertical) and np.hstack() (horizontal) & Splitting: np.hsplit()

a = np.array([1,2,3])
b = np.array([4,5,6])
#Stacking 
1. Vertical stack (np.vstack) 
   np.vstack((a, b))   # => [[1,2,3],
                       #     [4,5,6]]

2. Horizontal stack (np.hstack)
   np.hstack((a, b))     #=> [1,2,3,4,5,6]

# Splitting
1. x = np.arange(8)
   np.hsplit(x, 4)       #=> [array([0,1]), array([2,3]), array([4,5]), array([6,7])]

2. np.hsplit(x, (3,4,7)) #=> [array([0,1,2]), array([3]), array([4,5,6]), array([7])]

Views vs Copies

arr= np.arange(6)
1. View: Shares memory with the original array
   arr = np.arange(6)
   v = arr[1:4]
   v[0] = 99      # also changes arr[1] => array([0,99,2,3,4,5])

2. Copy: An independent array
   c = arr.copy()
   c[0] = –1      # arr unchanged array([0,1,2,3,4,5])

Basic Array Operations

Element‑wise math & Aggregations

1. a + b, a-b, a * b, a / b #both of them should have same shape though! 

2. arr= np.arange(1,10).reshape(3,3)
   arr.sum()                              #return sum of all elements
   # Axis-specific sums
    a.sum(axis=0)  # column sums
    a.sum(axis=1)  # row sums


3. arr.min(), arr.max()                   # Returns Max and Min Element from array
   Return column wise or do the same row wise with:
   arr.max(axis = 0) or arr.max(axis = 1)

4. arr.mean(), arr.std(), arr.prod()  # Returns mean std and product
  -In this also we can find column wise or row wise the same way

Generating Random Numbers

Integers randint() & New Generator(default_rng)

1. np.random.randint(low=0, high=10, size=5) # or you could give shape like(3,2)
   # => e.g. [3,7,1,8,0]

2. rng = np.random.default_rng()
   rng.random(5)    # floats in [0,1)
   rng.integers(5, size=3) # array([0, 1, 4], dtype=int64)

Other Useful Operations

1. Unique items
   np.unique(arr)

2. Transpose
   arr.T    # doesnt change the original array

3. Flip
   np.flip(arr)           # full reverse
   np.flip(arr, axis=0)   # flip rows
   np.flip(arr, axis=1)   # flip columns
   # Choose particular thing to filp
   np.flip(arr[1]) np.flip(arr[:1]  #for 2nd row / for 2nd column

4. Flatten vs. Ravel
   Flatten- # new array This flattens the 2d or 3d array into 1d!
   a1= x.flatten()        
   a1[0]= 99
   print(x)
   print(a1)
   '''[[1 2 3]
       [4 5 6]
       [7 8 9]]
    [99  1  2  3  4  5  6  7]'''
   Ravel- # works like view orignal array also gets changed!
   a2= arr.ravel()
   a2[0]= 98
   print(arr)
   print(a2)
   '''[[98  2  3]
       [4  5  6]
       [7  8  9]]
       [98  2  3  4  5  6  7  8  9]'''

📌 Tip: Use help(np.function_name) in your notebook to see full docs and parameters.

Thanks for sticking with me through this NumPy deep dive! You now have the fundamentals to create, manipulate, and analyze arrays efficiently—essential groundwork for any ML project.

🔜 What’s next?
In our next lesson, we’ll level up to pandas, the library built on NumPy for working with labeled, tabular data—perfect for real‑world data analysis and machine learning preparation.

See you there! 🚀Stay Tuned and Follow if you like :)!

0
Subscribe to my newsletter

Read articles from Priyesh Shah directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Priyesh Shah
Priyesh Shah

Hi there👋 I'm Priyesh Shah 💻 B.Tech Computer Engineering student (2028) 🐍 Python, exploring AI/ML and open-source 🚀 Building projects to contribute to GSoC 2026