Learn NumPy, the foundational Python library for numerical computing. Master arrays, array operations, broadcasting, indexing, reshaping, linear algebra, and random number generation with practical examples.
NumPy (Numerical Python) is the fundamental library for scientific computing in Python. It provides high-performance multi-dimensional arrays and tools for working with them. Every major data science library (Pandas, Matplotlib, Scikit-learn) is built on NumPy.
Installation
import numpy as np
print(np.__version__) # 1.26.x or 2.x
Why NumPy?
| Feature | Python Lists | NumPy Arrays |
|---|
| Speed | Slow (Python loops) | 50-100x faster |
| Memory | More memory | Less memory |
| Operations | Manual loops | Vectorized |
| Data type | Mixed types | Homogeneous |
| Math | Limited | Comprehensive |
import numpy as np
import time
# Speed comparison
size = 1_000_000
# Python list
start = time.time()
py_list = list(range(size))
result = [x * 2 for x in py_list]
print(f"Python list: {time.time() - start:.4f}s")
# NumPy array
start = time.time()
np_array = np.arange(size)
result = np_array * 2
print(f"NumPy array: {time.time() - start:.4f}s")
# NumPy is typically 50-200x faster!
Creating Arrays
import numpy as np
# From Python lists
arr1d = np.array([1, 2, 3, 4, 5])
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print(arr1d) # [1 2 3 4 5]
print(arr2d)
# [[1 2 3]
# [4 5 6]
# [7 8 9]]
# Array properties
print(arr2d.shape) # (3, 3) — rows, columns
print(arr2d.ndim) # 2 — number of dimensions
print(arr2d.size) # 9 — total elements
print(arr2d.dtype) # int64 — data type
print(arr2d.itemsize) # 8 — bytes per element
# Specifying data type
float_arr = np.array([1, 2, 3], dtype=np.float64)
int_arr = np.array([1.5, 2.7, 3.9], dtype=np.int32) # Truncates decimals
bool_arr = np.array([0, 1, 0, 1], dtype=bool)
complex_arr = np.array([1+2j, 3+4j], dtype=complex)
Array Creation Functions
import numpy as np
# Zeros and ones
zeros = np.zeros((3, 4)) # 3x4 matrix of zeros
ones = np.ones((2, 3)) # 2x3 matrix of ones
full = np.full((3, 3), 7) # 3x3 matrix filled with 7
empty = np.empty((2, 2)) # 2x2 uninitialized array
# Identity matrix
eye = np.eye(4) # 4x4 identity matrix
print(eye)
# [[1. 0. 0. 0.]
# [0. 1. 0. 0.]
# [0. 0. 1. 0.]
# [0. 0. 0. 1.]]
# Range functions
arange = np.arange(0, 10, 2) # [0 2 4 6 8] (start, stop, step)
arange2 = np.arange(1, 11) # [1 2 3 4 5 6 7 8 9 10]
# Evenly spaced values
linspace = np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1. ]
logspace = np.logspace(0, 3, 4) # [1. 10. 100. 1000.]
# Like another array (same shape)
like_zeros = np.zeros_like(arr2d) # Same shape, filled with zeros
like_ones = np.ones_like(arr2d)
like_full = np.full_like(arr2d, 99)
print(f"arange: {arange}")
print(f"linspace: {linspace}")
Array Indexing and Slicing
import numpy as np
arr = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90])
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 1D indexing
print(arr[0]) # 10 — first element
print(arr[-1]) # 90 — last element
print(arr[2:5]) # [30 40 50] — slice
print(arr[::2]) # [10 30 50 70 90] — every other
print(arr[::-1]) # [90 80 70 60 50 40 30 20 10] — reversed
# 2D indexing
print(matrix[0]) # [1 2 3] — first row
print(matrix[1, 2]) # 6 — row 1, col 2
print(matrix[:, 1]) # [2 5 8] — all rows, column 1
print(matrix[0:2, 1:3]) # [[2 3] [5 6]] — submatrix
print(matrix[::2, ::2]) # [[1 3] [7 9]] — every other row and col
# Boolean indexing
arr = np.array([15, 3, 8, 20, 5, 12, 7])
mask = arr > 10
print(mask) # [True False False True False True False]
print(arr[mask]) # [15 20 12] — elements greater than 10
print(arr[arr > 10]) # Same — shorthand
# Filter and modify
arr[arr < 5] = 0 # Set elements less than 5 to 0
# Fancy indexing (select by index list)
indices = [0, 2, 4]
print(arr[indices]) # Elements at index 0, 2, 4
matrix_rows = matrix[[0, 2]] # Select rows 0 and 2
matrix_specific = matrix[[0, 1, 2], [0, 1, 2]] # Diagonal elements
Array Operations
import numpy as np
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
# Arithmetic — element-wise
print(a + b) # [ 6 8 10 12]
print(a - b) # [-4 -4 -4 -4]
print(a * b) # [ 5 12 21 32]
print(a / b) # [0.2 0.33 0.43 0.5]
print(a ** 2) # [ 1 4 9 16]
print(a // b) # [0 0 0 0] — floor division
print(a % b) # [1 2 3 4] — modulus
# Scalar operations
print(a + 10) # [11 12 13 14]
print(a * 3) # [ 3 6 9 12]
print(a ** 0.5) # [1. 1.41 1.73 2. ] — square root
# Comparison — element-wise
print(a > 2) # [False False True True]
print(a == b) # [False False False False]
# Mathematical functions
print(np.sqrt(a)) # Square root
print(np.abs(a)) # Absolute value
print(np.log(a)) # Natural log
print(np.log10(a)) # Log base 10
print(np.exp(a)) # e^x
print(np.sin(a)) # Sine
print(np.cos(a)) # Cosine
# Rounding
arr = np.array([1.234, 2.567, 3.891])
print(np.round(arr, 1)) # [1.2 2.6 3.9]
print(np.floor(arr)) # [1. 2. 3.]
print(np.ceil(arr)) # [2. 3. 4.]
Statistical Operations
import numpy as np
data = np.array([[2, 4, 6], [1, 3, 5], [7, 8, 9]])
# Basic statistics
print(np.sum(data)) # 45 — sum of all elements
print(np.sum(data, axis=0)) # [10 15 20] — column sums
print(np.sum(data, axis=1)) # [12 9 24] — row sums
print(np.mean(data)) # 5.0
print(np.mean(data, axis=0)) # Column means
print(np.mean(data, axis=1)) # Row means
print(np.median(data)) # Median
print(np.std(data)) # Standard deviation
print(np.var(data)) # Variance
print(np.min(data)) # Minimum value
print(np.max(data)) # Maximum value
print(np.argmin(data)) # Index of minimum (flattened)
print(np.argmax(data)) # Index of maximum (flattened)
print(np.argmin(data, axis=0)) # Index of min in each column
print(np.argmax(data, axis=1)) # Index of max in each row
# Cumulative operations
arr = np.array([1, 2, 3, 4, 5])
print(np.cumsum(arr)) # [ 1 3 6 10 15]
print(np.cumprod(arr)) # [ 1 2 6 24 120]
# Percentile / quantile
print(np.percentile(data, 25)) # 25th percentile (Q1)
print(np.percentile(data, 75)) # 75th percentile (Q3)
print(np.quantile(data, 0.5)) # Median (50th percentile)
# Correlation and covariance
x = np.array([1, 2, 3, 4, 5])
y = np.array([2, 4, 5, 4, 5])
print(np.corrcoef(x, y)) # Correlation matrix
print(np.cov(x, y)) # Covariance matrix
Broadcasting
Broadcasting allows operations on arrays of different shapes:
import numpy as np
# Scalar broadcasting
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr + 10) # Adds 10 to every element
# 1D array broadcasting with 2D
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # shape (3, 3)
b = np.array([10, 20, 30]) # shape (3,)
print(a + b)
# [[11 22 33]
# [14 25 36]
# [17 28 39]]
# b is broadcast to each row
# Column broadcasting
c = np.array([[100], [200], [300]]) # shape (3, 1)
print(a + c)
# [[101 102 103]
# [204 205 206]
# [307 308 309]]
# c is broadcast to each column
# Broadcasting rules:
# 1. Arrays are compared from trailing dimensions
# 2. Dimensions are compatible if equal or one is 1
# 3. Arrays with fewer dims are padded with 1s on the left
# Normalize each row to 0-1 range
data = np.array([[1, 2, 3], [4, 8, 12], [10, 20, 30]])
min_vals = data.min(axis=1, keepdims=True)
max_vals = data.max(axis=1, keepdims=True)
normalized = (data - min_vals) / (max_vals - min_vals)
print(normalized)
Reshaping and Transposing
import numpy as np
arr = np.arange(24) # [0, 1, 2, ..., 23]
# Reshape
r1 = arr.reshape(4, 6) # 4 rows, 6 columns
r2 = arr.reshape(2, 3, 4) # 3D array
r3 = arr.reshape(6, -1) # -1 means "figure it out" → (6, 4)
# Flatten / Ravel
matrix = np.array([[1, 2], [3, 4], [5, 6]])
flat1 = matrix.flatten() # Returns a copy
flat2 = matrix.ravel() # Returns a view (if possible)
print(flat1) # [1 2 3 4 5 6]
# Transpose
print(matrix.T)
# [[1 3 5]
# [2 4 6]]
print(np.transpose(matrix)) # Same as .T
# Expand dimensions (add axis)
arr = np.array([1, 2, 3])
print(arr.shape) # (3,)
expanded = np.expand_dims(arr, axis=0)
print(expanded.shape) # (1, 3) — row vector
expanded2 = np.expand_dims(arr, axis=1)
print(expanded2.shape) # (3, 1) — column vector
# Squeeze (remove dimensions of size 1)
arr_squeezed = np.squeeze(expanded) # (3,)
Concatenation and Stacking
import numpy as np
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
# Concatenate along existing axis
h_concat = np.concatenate([a, b], axis=1) # Horizontal: [[1 2 5 6] [3 4 7 8]]
v_concat = np.concatenate([a, b], axis=0) # Vertical: stacks rows
# Stack creates new axis
stacked = np.stack([a, b], axis=0) # shape (2, 2, 2)
hstack = np.hstack([a, b]) # Same as concatenate axis=1
vstack = np.vstack([a, b]) # Same as concatenate axis=0
dstack = np.dstack([a, b]) # Stack depth-wise
# Split
arr = np.arange(12).reshape(4, 3)
parts = np.split(arr, 2, axis=0) # Split into 2 along axis 0
h_parts = np.hsplit(arr, 3) # Split into 3 horizontal pieces
v_parts = np.vsplit(arr, 2) # Split into 2 vertical pieces
Linear Algebra
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# Matrix multiplication
product = np.dot(A, B)
product2 = A @ B # Python 3.5+ matrix multiply operator
# Element-wise vs matrix multiply
print(A * B) # Element-wise: [[5 12] [21 32]]
print(A @ B) # Matrix mult: [[19 22] [43 50]]
# Determinant, inverse, eigenvalues
det = np.linalg.det(A)
inv = np.linalg.inv(A)
eigenvalues, eigenvectors = np.linalg.eig(A)
# Solve linear equations: Ax = b
A = np.array([[2, 1], [1, 3]])
b = np.array([5, 10])
x = np.linalg.solve(A, b)
print(x) # Solution to the system of equations
# Norms
v = np.array([3, 4])
print(np.linalg.norm(v)) # 5.0 (Euclidean/L2 norm)
print(np.linalg.norm(v, ord=1)) # 7.0 (L1 norm)
# SVD (Singular Value Decomposition)
U, S, Vt = np.linalg.svd(A)
Random Number Generation
import numpy as np
rng = np.random.default_rng(seed=42) # Modern API (reproducible)
# Uniform distribution [0, 1)
uniform = rng.random((3, 3))
# Normal (Gaussian) distribution
normal = rng.normal(loc=0, scale=1, size=(1000,)) # mean=0, std=1
normal_matrix = rng.normal(0, 1, (5, 5))
# Integer random
integers = rng.integers(low=1, high=10, size=10)
# Random choice
colors = ['red', 'green', 'blue', 'yellow']
choice = rng.choice(colors, size=5, replace=True)
# Shuffle
arr = np.arange(10)
rng.shuffle(arr)
print(arr) # Randomly shuffled
# Other distributions
poisson = rng.poisson(lam=3.0, size=1000)
binomial = rng.binomial(n=10, p=0.5, size=1000)
exponential = rng.exponential(scale=2.0, size=1000)
# Statistics check
print(f"Mean: {np.mean(normal):.4f}") # Should be ~0
print(f"Std: {np.std(normal):.4f}") # Should be ~1
Practical Example: Data Analysis
import numpy as np
# Generate sample student grade data
np.random.seed(42)
num_students = 100
num_subjects = 5
grades = np.random.randint(50, 101, size=(num_students, num_subjects))
subjects = ['Math', 'Science', 'English', 'History', 'Art']
print(f"Grade matrix shape: {grades.shape}")
# Per-student averages
student_avg = np.mean(grades, axis=1)
print(f"\nTop 5 students: {np.sort(student_avg)[::-1][:5]}")
print(f"Class average: {np.mean(student_avg):.1f}")
# Per-subject statistics
for i, subject in enumerate(subjects):
subject_grades = grades[:, i]
print(f"\n{subject}:")
print(f" Mean: {np.mean(subject_grades):.1f}")
print(f" Std: {np.std(subject_grades):.1f}")
print(f" Min: {np.min(subject_grades)}")
print(f" Max: {np.max(subject_grades)}")
# Grading: A=90+, B=80-89, C=70-79, D=60-69, F<60
overall = student_avg
grade_dist = {
'A': np.sum(overall >= 90),
'B': np.sum((overall >= 80) & (overall < 90)),
'C': np.sum((overall >= 70) & (overall < 80)),
'D': np.sum((overall >= 60) & (overall < 70)),
'F': np.sum(overall < 60),
}
print("\nGrade Distribution:", grade_dist)
# Find students who need improvement (average < 65)
struggling = np.where(overall < 65)[0]
print(f"\nStudents needing help: {len(struggling)} students")
Summary
In this lesson, you learned:
- ✅ Installing NumPy and creating arrays
- ✅ Array properties (shape, ndim, dtype)
- ✅ Array creation functions (zeros, ones, arange, linspace)
- ✅ Indexing, slicing, and boolean indexing
- ✅ Element-wise arithmetic and mathematical functions
- ✅ Statistical operations (mean, std, percentile, correlation)
- ✅ Broadcasting — operations on arrays of different shapes
- ✅ Reshaping, transposing, concatenating arrays
- ✅ Linear algebra operations
- ✅ Random number generation
📤 Output Examples
Here, the expected output for every code example is provided so you can verify that your code is running correctly. 👇
Installation Check Output
import numpy as np
print(np.__version__)
Speed Comparison Output
import numpy as np
import time
size = 1_000_000
start = time.time()
py_list = list(range(size))
result = [x * 2 for x in py_list]
print(f"Python list: {time.time() - start:.4f}s")
start = time.time()
np_array = np.arange(size)
result = np_array * 2
print(f"NumPy array: {time.time() - start:.4f}s")
Python list: 0.1285s
NumPy array: 0.0021s
Array Creation Output
import numpy as np
arr1d = np.array([1, 2, 3, 4, 5])
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr1d)
print(arr2d)
print(arr2d.shape)
print(arr2d.ndim)
print(arr2d.size)
print(arr2d.dtype)
print(arr2d.itemsize)
[1 2 3 4 5]
[[1 2 3]
[4 5 6]
[7 8 9]]
(3, 3)
2
9
int64
8
Array Creation Functions Output
import numpy as np
zeros = np.zeros((3, 4))
arange = np.arange(0, 10, 2)
linspace = np.linspace(0, 1, 5)
eye = np.eye(4)
print(f"zeros shape: {zeros.shape}")
print(f"arange: {arange}")
print(f"linspace: {linspace}")
print(eye)
zeros shape: (3, 4)
arange: [0 2 4 6 8]
linspace: [0. 0.25 0.5 0.75 1. ]
[[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
Indexing and Slicing Output
import numpy as np
arr = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90])
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr[0])
print(arr[-1])
print(arr[2:5])
print(arr[::2])
print(matrix[1, 2])
print(matrix[:, 1])
print(matrix[0:2, 1:3])
10
90
[30 40 50]
[10 30 50 70 90]
6
[2 5 8]
[[2 3]
[5 6]]
Boolean Indexing Output
import numpy as np
arr = np.array([15, 3, 8, 20, 5, 12, 7])
mask = arr > 10
print(mask)
print(arr[mask])
[ True False False True False True False]
[15 20 12]
Array Operations Output
import numpy as np
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
print(a + b)
print(a * b)
print(a ** 2)
print(a + 10)
print(np.sqrt(a))
[ 6 8 10 12]
[ 5 12 21 32]
[ 1 4 9 16]
[11 12 13 14]
[1. 1.41421356 1.73205081 2. ]
Statistical Operations Output
import numpy as np
data = np.array([[2, 4, 6], [1, 3, 5], [7, 8, 9]])
print(np.sum(data))
print(np.sum(data, axis=0))
print(np.sum(data, axis=1))
print(np.mean(data))
print(np.std(data))
45
[10 15 20]
[12 9 24]
5.0
2.581988897471611
Broadcasting Output
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = np.array([10, 20, 30])
print(a + b)
[[11 22 33]
[14 25 36]
[17 28 39]]
Reshape and Transpose Output
import numpy as np
matrix = np.array([[1, 2], [3, 4], [5, 6]])
print(matrix.flatten())
print(matrix.T)
[1 2 3 4 5 6]
[[1 3 5]
[2 4 6]]
Linear Algebra Output
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A * B)
print(A @ B)
A2 = np.array([[2, 1], [1, 3]])
b = np.array([5, 10])
x = np.linalg.solve(A2, b)
print(x)
v = np.array([3, 4])
print(np.linalg.norm(v))
[[ 5 12]
[21 32]]
[[19 22]
[43 50]]
[1. 3.]
5.0
Practical Example Output
import numpy as np
np.random.seed(42)
grades = np.random.randint(50, 101, size=(100, 5))
student_avg = np.mean(grades, axis=1)
print(f"Grade matrix shape: {grades.shape}")
print(f"Top 5 students: {np.sort(student_avg)[::-1][:5]}")
print(f"Class average: {np.mean(student_avg):.1f}")
Grade matrix shape: (100, 5)
Top 5 students: [93.4 91.4 90.6 89.4 88.8]
Class average: 74.8
⚠️ Common Mistakes
NumPy use करते time beginners ये common mistakes करते हैं। इन्हें avoid करें! 🚫
1. ❌ List और Array को same समझना
# ❌ Wrong thinking - list multiplication
py_list = [1, 2, 3]
print(py_list * 3) # [1, 2, 3, 1, 2, 3, 1, 2, 3] - repeats!
# ✅ NumPy array multiplication
np_arr = np.array([1, 2, 3])
print(np_arr * 3) # [3, 6, 9] - element-wise multiplication!
List में * repetition करता है, लेकिन NumPy array में element-wise multiplication होता है।
2. ❌ View vs Copy confusion
# ❌ Dangerous - slicing returns a view, not a copy!
original = np.array([1, 2, 3, 4, 5])
sliced = original[1:4] # This is a VIEW
sliced[0] = 99
print(original) # [1, 99, 3, 4, 5] - Original भी change हो गया! 😱
# ✅ Safe - use .copy() for independent copy
original = np.array([1, 2, 3, 4, 5])
sliced = original[1:4].copy()
sliced[0] = 99
print(original) # [1, 2, 3, 4, 5] - Original safe! ✅
3. ❌ Shape mismatch in operations
# ❌ This will throw an error
a = np.array([1, 2, 3]) # shape (3,)
b = np.array([1, 2, 3, 4]) # shape (4,)
# a + b → ValueError: shapes not aligned!
# ✅ Ensure compatible shapes for broadcasting
a = np.array([[1, 2, 3]]) # shape (1, 3)
b = np.array([[10], [20]]) # shape (2, 1)
print(a + b) # Broadcasting works! shape (2, 3)
4. ❌ axis=0 और axis=1 में confusion
data = np.array([[1, 2, 3], [4, 5, 6]])
# axis=0 means "along rows" (column-wise operation) - ऊपर से नीचे
print(np.sum(data, axis=0)) # [5 7 9] - column sums
# axis=1 means "along columns" (row-wise operation) - बाएं से दाएं
print(np.sum(data, axis=1)) # [6 15] - row sums
# याद रखो: axis=0 collapses rows, axis=1 collapses columns
5. ❌ Integer division surprise
# ❌ Integer arrays give integer results
arr = np.array([1, 2, 3, 4, 5])
print(arr / 2) # [0.5 1. 1.5 2. 2.5] ✅ (Python 3 true division)
print(arr // 2) # [0 1 1 2 2] - Floor division!
# ❌ But be careful with dtype
arr_int = np.array([1, 2, 3], dtype=np.int32)
result = arr_int / 2
print(result.dtype) # float64 - type changed automatically!
6. ❌ Forgetting that np.array is NOT immutable
# ❌ Functions can modify array in-place
arr = np.array([3, 1, 4, 1, 5])
np.sort(arr) # Returns sorted copy, original unchanged
print(arr) # [3 1 4 1 5] - unchanged!
arr.sort() # Sorts IN-PLACE
print(arr) # [1 1 3 4 5] - modified! 😱
# ✅ Use np.sort() when you want original preserved
7. ❌ Using == for array comparison
# ❌ Wrong - element-wise comparison
a = np.array([1, 2, 3])
b = np.array([1, 2, 3])
print(a == b) # [True True True] - NOT a single True!
# ✅ Use np.array_equal() for full array comparison
print(np.array_equal(a, b)) # True
# ✅ Or use .all()
print((a == b).all()) # True
✅ Key Takeaways
यहाँ इस lesson की सबसे important points हैं जो आपको याद रखनी चाहिए: 🎯
- 🚀 NumPy Python lists से 50-200x faster है — vectorized operations loops की ज़रूरत eliminate करते हैं
- 📐
ndarray NumPy का core object है — shape, ndim, dtype, size ये properties हमेशा check करें - 🔢 Array creation functions याद रखें —
zeros(), ones(), arange(), linspace(), eye() commonly use होते हैं - 🎯 Boolean indexing powerful filtering है —
arr[arr > 10] जैसे one-liner complex filtering करते हैं - 📊 axis parameter समझना crucial है — axis=0 columns के along (ऊपर→नीचे), axis=1 rows के along (बाएं→दाएं)
- 📡 Broadcasting automatic shape matching है — different shapes वाले arrays पर operations allow करता है बिना manual reshaping के
- 🔄 View vs Copy समझें — slicing view देता है (shared memory),
.copy() independent copy बनाता है - 🧮 Linear algebra built-in है — matrix multiplication (
@), linalg.solve(), linalg.inv(), SVD सब available हैं - 🎲 Modern random API use करें —
np.random.default_rng(seed) preferred है reproducibility के लिए - 📈 NumPy = Foundation — Pandas, Matplotlib, Scikit-learn, TensorFlow सब NumPy पर built हैं, इसे master करना essential है
❓ FAQ
Q1: NumPy और Python list में main difference क्या है? 🤔
Answer: NumPy arrays homogeneous हैं (एक ही data type), contiguous memory में stored हैं, और vectorized C operations use करते हैं। Python lists heterogeneous हो सकती हैं और pointers through indirect access करती हैं। Result: NumPy 50-200x faster है numerical operations के लिए।
Q2: reshape(-1) में -1 का मतलब क्या है? 🔄
Answer: -1 का मतलब है "automatically calculate this dimension"। NumPy total elements count करके missing dimension figure out कर लेता है। Example: arr.reshape(3, -1) — अगर 12 elements हैं तो -1 = 4 हो जाएगा (3×4=12)। एक ही dimension में -1 use कर सकते हैं।
Q3: axis=0 और axis=1 में difference क्या है? 📐
Answer: Think of it as "which axis gets collapsed":
axis=0: Collapse rows → result is per-column (ऊपर से नीचे operate)axis=1: Collapse columns → result is per-row (बाएं से दाएं operate)
Example: 3×4 matrix पर sum(axis=0) → shape (4,), sum(axis=1) → shape (3,)
Q4: View और Copy में difference क्या है? और कब क्या use करें? 👀
Answer:
- View = same memory, changes reflect in original (slicing gives view)
- Copy = independent memory, changes don't affect original (
.copy())
View use करें जब: memory save करनी हो और original change acceptable हो। Copy use करें जब: original data protect करना हो।
Q5: Broadcasting कब fail होता है? ⚠️
Answer: Broadcasting fail होता है जब shapes incompatible हों। Rules:
- Trailing dimensions compare होती हैं
- Dimensions match होनी चाहिए OR one should be 1
- Example: (3,4) + (3,) → Error! But (3,4) + (4,) → ✅ Works!
Q6: np.dot() और @ operator में difference क्या है? ✖️
Answer: 2D arrays (matrices) के लिए दोनों same हैं — matrix multiplication। लेकिन:
- 1D arrays:
np.dot() = dot product (scalar), @ = same - Higher dimensions: behavior slightly different
- Recommendation: Readability के लिए
@ prefer करें, यह Python 3.5+ feature है।
Q7: NumPy arrays को Pandas DataFrames में कैसे convert करें? 🔄
Answer:
import pandas as pd
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
df = pd.DataFrame(arr, columns=['A', 'B', 'C'])
# Reverse: DataFrame to NumPy
back_to_numpy = df.to_numpy() # or df.values
Q8: np.random.seed() vs default_rng() — कौन सा use करें? 🎲
Answer: default_rng(seed=42) modern और recommended है। Reasons:
- Thread-safe है
- Better statistical properties
- Independent random streams create कर सकते हैं
np.random.seed() legacy/global state है — avoid करें new code में।
🎯 Interview Questions
Q1: NumPy array और Python list में fundamental differences बताएं?
Answer:
| Feature | Python List | NumPy Array |
|---|
| Memory | Non-contiguous (pointers) | Contiguous block |
| Data type | Heterogeneous | Homogeneous |
| Speed | Slow (interpreted loops) | Fast (C-level vectorized) |
| Size | Dynamic | Fixed after creation |
| Operations | No element-wise | Built-in element-wise |
| Memory usage | ~28 bytes per int | ~8 bytes per int64 |
NumPy internal में C loops use करता है, SIMD instructions leverage करता है, और cache-friendly memory layout provide करता है।
Q2: Broadcasting rules explain करें with examples।
Answer: Broadcasting rules (trailing dimensions से compare):
- Equal dimensions → element-wise operation
- One dimension is 1 → stretch to match
- Missing dimensions → prepend 1s on left
# Example 1: (3,3) + (3,) → (3,3) + (1,3) → works!
A = np.ones((3, 3)) # shape (3, 3)
b = np.array([1, 2, 3]) # shape (3,) → treated as (1, 3)
print((A + b).shape) # (3, 3)
# Example 2: (4,1) + (1,3) → (4,3)
a = np.ones((4, 1))
b = np.ones((1, 3))
print((a + b).shape) # (4, 3)
# Example 3: FAILS - (3,) + (4,) → incompatible!
Q3: np.dot(), @, और * में difference explain करें।
Answer:
* → Element-wise multiplication (Hadamard product)@ / np.dot() → Matrix multiplication (dot product)
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A * B) # [[5, 12], [21, 32]] — element-wise
print(A @ B) # [[19, 22], [43, 50]] — matrix multiplication
# A@B: (1*5+2*7, 1*6+2*8), (3*5+4*7, 3*6+4*8)
Q4: View और Copy का concept explain करें। Memory implications क्या हैं?
Answer:
- View: Same memory buffer को reference करता है। Changes bidirectional होते हैं। Operations: slicing, reshape, transpose।
- Copy: New memory allocate करता है। Independent object।
a = np.array([1, 2, 3, 4, 5])
# View - shares memory
view = a[1:4]
print(np.shares_memory(a, view)) # True
# Copy - separate memory
copy = a[1:4].copy()
print(np.shares_memory(a, copy)) # False
Memory implication: Views memory-efficient हैं (no duplication), but accidental modification risk है। Large datasets में views prefer करें लेकिन carefully।
Q5: NumPy में axis parameter कैसे काम करता है?
Answer: axis parameter specify करता है कि कौन सी dimension collapse/reduce होगी:
arr = np.array([[1, 2, 3],
[4, 5, 6]]) # shape (2, 3)
np.sum(arr, axis=0) # [5, 7, 9] — shape (3,) — rows collapsed
np.sum(arr, axis=1) # [6, 15] — shape (2,) — columns collapsed
np.sum(arr) # 21 — all elements
Trick: axis=N means "operate ALONG axis N" → that dimension disappears from result shape.
Q6: np.where() function explain करें with use cases।
Answer: np.where(condition, x, y) — condition True हो तो x, False हो तो y:
arr = np.array([1, -2, 3, -4, 5])
# Replace negatives with 0
result = np.where(arr > 0, arr, 0)
print(result) # [1 0 3 0 5]
# Find indices where condition is True
indices = np.where(arr > 0)
print(indices) # (array([0, 2, 4]),)
# Categorize values
scores = np.array([85, 42, 93, 67, 55])
grades = np.where(scores >= 60, 'Pass', 'Fail')
print(grades) # ['Pass' 'Fail' 'Pass' 'Pass' 'Fail']
Q7: NumPy arrays को efficiently save और load कैसे करें?
Answer:
arr = np.random.rand(1000, 1000)
# Single array - .npy format (fastest)
np.save('data.npy', arr)
loaded = np.load('data.npy')
# Multiple arrays - .npz format
np.savez('multi.npz', x=arr, y=arr*2)
data = np.load('multi.npz')
print(data['x'], data['y'])
# Compressed - smaller file size
np.savez_compressed('compressed.npz', x=arr)
# Text format (human readable, slower)
np.savetxt('data.csv', arr, delimiter=',')
loaded_txt = np.loadtxt('data.csv', delimiter=',')
Binary formats (.npy/.npz) text formats से 10-100x faster हैं large arrays के लिए।
Q8: Vectorization क्या है और loops से better क्यों है?
Answer: Vectorization means operating on entire arrays at once instead of element-by-element loops:
import time
arr = np.random.rand(1_000_000)
# ❌ Slow: Python loop
start = time.time()
result = np.zeros(len(arr))
for i in range(len(arr)):
result[i] = arr[i] ** 2 + 2 * arr[i] + 1
loop_time = time.time() - start
# ✅ Fast: Vectorized
start = time.time()
result = arr ** 2 + 2 * arr + 1
vec_time = time.time() - start
print(f"Loop: {loop_time:.4f}s, Vectorized: {vec_time:.4f}s")
# Vectorized is ~100x faster!
Why faster: C-level loops, CPU cache optimization, SIMD instructions, no Python object overhead.
Q9: np.concatenate(), np.stack(), np.vstack(), np.hstack() में differences बताएं।
Answer:
a = np.array([[1, 2], [3, 4]]) # shape (2, 2)
b = np.array([[5, 6], [7, 8]]) # shape (2, 2)
# concatenate - joins along EXISTING axis
np.concatenate([a, b], axis=0) # shape (4, 2) — vertical
np.concatenate([a, b], axis=1) # shape (2, 4) — horizontal
# stack - creates NEW axis
np.stack([a, b], axis=0) # shape (2, 2, 2) — new dimension added
# vstack = concatenate(axis=0) — vertical
np.vstack([a, b]) # shape (4, 2)
# hstack = concatenate(axis=1) — horizontal
np.hstack([a, b]) # shape (2, 4)
Key difference: concatenate existing axis use करता है, stack new axis create करता है।
Q10: NumPy की memory layout (C-order vs F-order) explain करें।
Answer:
- C-order (Row-major): Elements row-by-row stored —
[[1,2,3],[4,5,6]] → memory: [1,2,3,4,5,6] - F-order (Column-major): Elements column-by-column stored — same array → memory:
[1,4,2,5,3,6]
arr = np.array([[1, 2, 3], [4, 5, 6]])
# Check order
c_arr = np.array(arr, order='C') # Default
f_arr = np.array(arr, order='F') # Fortran-style
print(c_arr.flags['C_CONTIGUOUS']) # True
print(f_arr.flags['F_CONTIGUOUS']) # True
# Performance impact:
# Row operations faster in C-order (default)
# Column operations faster in F-order
# Always use C-order unless interfacing with Fortran libraries
Interview tip: C-order default है Python/C++ में, F-order MATLAB/Fortran में use होता है। Memory layout cache performance affect करती है।