SkillAgentSearch skills...

Numpy

A structured NumPy practice repository showcasing hands-on learning through Jupyter notebooks. It covers array creation, indexing, slicing, reshaping, broadcasting, and numerical operations. Built to strengthen core foundations required for Data Science, Machine Learning, and efficient scientific computing in Python.

Install / Use

/learn @mdzaheerjk/Numpy
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

NumPy Complete Reference Notes

What is NumPy?

NumPy (Numerical Python) is the foundational library for numerical computing in Python. It provides:

  • ndarray — a fast, memory-efficient N-dimensional array
  • Vectorised math operations (no Python loops needed)
  • Broadcasting — operating on arrays of different shapes
  • Linear algebra, FFT, random number generation
  • The backbone of pandas, SciPy, TensorFlow, scikit-learn, and PyTorch
pip install numpy
import numpy as np

The ndarray — Core Data Structure

Every NumPy array is an ndarray: a typed, fixed-size, contiguous block of memory.

Key attributes

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

a.ndim        # 2          — number of dimensions (axes)
a.shape       # (2, 3)     — tuple of sizes per axis
a.size        # 6          — total number of elements
a.dtype       # int64      — element data type
a.itemsize    # 8          — bytes per element
a.nbytes      # 48         — total bytes (size × itemsize)
a.strides     # (24, 8)    — bytes to step along each axis

Creating Arrays

From Python sequences

np.array([1, 2, 3])                  # 1-D array
np.array([[1, 2], [3, 4]])           # 2-D array (matrix)
np.array([1.0, 2, 3])                # dtype inferred as float64
np.array([1, 2, 3], dtype=np.int32)  # explicit dtype

Constant arrays

np.zeros((3, 4))              # 3×4 array of 0.0 (float64)
np.ones((2, 3))               # 2×3 array of 1.0
np.full((3, 3), 7)            # 3×3 array filled with 7
np.empty((2, 2))              # uninitialised (fast, garbage values)
np.eye(4)                     # 4×4 identity matrix
np.identity(4)                # same as eye(4)
np.zeros_like(a)              # zeros with same shape/dtype as a
np.ones_like(a)
np.full_like(a, fill_value=0)

Range & sequence arrays

np.arange(10)                 # [0 1 2 ... 9]  (like range())
np.arange(1, 10, 2)           # [1 3 5 7 9]    start, stop, step
np.linspace(0, 1, 5)          # [0.   0.25 0.5  0.75 1.]  — n evenly spaced
np.linspace(0, 1, 5, endpoint=False)  # exclude endpoint
np.logspace(0, 3, 4)          # [1. 10. 100. 1000.]  — log spacing
np.geomspace(1, 1000, 4)      # [1. 10. 100. 1000.]  — geometric spacing

Grid arrays

np.meshgrid([1, 2, 3], [4, 5])       # two 2-D grids (broadcasting helper)
np.mgrid[0:3, 0:4]                   # open mesh grid via slice notation
np.ogrid[0:3, 0:4]                   # open (sparse) mesh grid
np.indices((3, 4))                   # array of indices

Special arrays

np.diag([1, 2, 3])                   # diagonal matrix from 1-D
np.diag(A)                           # extract diagonal from 2-D
np.tri(3, 4, k=0)                    # lower-triangular matrix
np.triu(A)                           # upper triangle of A (in-place zero)
np.tril(A)                           # lower triangle of A
np.vander([1, 2, 3], 4)              # Vandermonde matrix

Data Types (dtype)

| Type | Alias | Bytes | Notes | |------|-------|-------|-------| | np.int8 | i1 | 1 | | | np.int16 | i2 | 2 | | | np.int32 | i4 | 4 | | | np.int64 | i8 | 8 | Default int on 64-bit | | np.uint8 | u1 | 1 | Unsigned (images!) | | np.uint16uint64 | u2u8 | 2–8 | | | np.float16 | f2 | 2 | Half precision | | np.float32 | f4 | 4 | GPU-friendly | | np.float64 | f8 | 8 | Default float | | np.complex64 | c8 | 8 | | | np.complex128 | c16 | 16 | Default complex | | np.bool_ | ? | 1 | | | np.str_ | U | varies | Fixed-length Unicode | | np.object_ | O | ptr | Python object |

a = np.array([1, 2, 3], dtype=np.float32)
b = a.astype(np.int16)          # cast to new dtype (copies data)
np.can_cast(np.int32, np.float64)  # True — safe cast check
np.result_type(np.int32, np.float64)  # float64 — common type

Indexing & Slicing

Basic indexing

a = np.array([[10, 20, 30],
              [40, 50, 60],
              [70, 80, 90]])

a[0]          # [10 20 30]        — first row
a[0, 1]       # 20                — row 0, col 1
a[-1]         # [70 80 90]        — last row
a[1, -1]      # 60                — row 1, last col

Slicing [start:stop:step]

a[0:2]        # rows 0 and 1
a[:, 1]       # all rows, col 1 → [20 50 80]
a[::2, ::2]   # every other row and col
a[1:, :2]     # rows 1+, cols 0-1
a[::-1]       # reversed rows

Slices return views (no copy). Modifying a slice modifies the original.

b = a[0:2, 0:2]
b[0, 0] = 999          # a[0, 0] is now 999
b = a[0:2, 0:2].copy() # force a copy

Boolean (mask) indexing

a = np.array([1, 5, 3, 8, 2])
mask = a > 3             # [False True False True False]
a[mask]                  # [5 8]
a[a > 3]                 # same, inline

# Multi-condition
a[(a > 2) & (a < 8)]     # & | ~ for and/or/not  (not Python and/or)
a[np.logical_and(a > 2, a < 8)]

Fancy (integer) indexing

a = np.array([10, 20, 30, 40, 50])
a[[0, 2, 4]]             # [10 30 50]   — select by index list
a[[3, 3, 1]]             # [40 40 20]   — repetition allowed

# 2-D fancy indexing
M = np.arange(12).reshape(3, 4)
M[[0, 2], [1, 3]]        # [M[0,1], M[2,3]] = [1, 11]

# np.ix_ for selecting submatrices
M[np.ix_([0, 2], [1, 3])]  # 2×2 submatrix at rows {0,2} × cols {1,3}

Fancy indexing returns copies, not views.

np.where

np.where(a > 3)                      # indices where condition is True
np.where(a > 3, a, 0)                # elementwise ternary: a if a>3 else 0
np.where(a > 3, "big", "small")

Reshaping & Manipulation

a = np.arange(12)

a.reshape(3, 4)          # 3×4 — returns a view when possible
a.reshape(3, -1)         # -1 infers the missing dimension (= 4)
a.reshape(2, 2, 3)       # 3-D

a.ravel()                # flatten to 1-D (view when possible)
a.flatten()              # flatten to 1-D (always a copy)

a.T                      # transpose (view)
a.transpose(1, 0, 2)     # reorder axes explicitly

np.expand_dims(a, axis=0)  # insert axis: (12,) → (1, 12)
np.squeeze(a)              # remove size-1 axes
a[:, np.newaxis]           # insert axis with np.newaxis
a[np.newaxis, :]           # shape (1, 12)

Stacking & splitting

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

np.concatenate([a, b])              # [1 2 3 4 5 6]
np.concatenate([A, B], axis=0)      # stack rows (vstack)
np.concatenate([A, B], axis=1)      # stack cols (hstack)

np.vstack([a, b])                   # vertical stack → 2-D
np.hstack([a, b])                   # horizontal stack
np.dstack([a, b])                   # depth stack (3rd axis)
np.stack([a, b], axis=0)            # new axis; shape (2, 3)
np.column_stack([a, b])             # 1-D arrays become columns

np.split(a, 3)                      # split into 3 equal parts
np.split(a, [2, 5])                 # split at indices 2 and 5
np.vsplit(A, 2)
np.hsplit(A, 2)
np.array_split(a, 4)                # unequal split (no error)

Repeating & tiling

np.repeat([1, 2], 3)               # [1 1 1 2 2 2]
np.repeat(A, 2, axis=0)            # repeat each row twice
np.tile([1, 2], 3)                 # [1 2 1 2 1 2]
np.tile(A, (2, 3))                 # tile the whole array 2×3 times

Broadcasting

Broadcasting lets NumPy operate on arrays of compatible but different shapes without copying data.

Rules (applied left-to-right on shape tuples, padded with 1s on the left)

  1. Shapes are equal, OR
  2. One of them is 1 (that dimension is "stretched")
# Shape (3,) + (3, 3) → (3, 3)
row = np.array([1, 2, 3])
M = np.ones((3, 3))
M + row                  # adds row to every row of M

# Shape (3, 1) + (1, 4) → (3, 4)
col = np.arange(3).reshape(3, 1)
row = np.arange(4).reshape(1, 4)
col + row                # outer-sum: 3×4 matrix
# Practical: normalise each row to zero mean
X = np.random.randn(100, 5)
X -= X.mean(axis=0)      # X.mean shape (5,) broadcasts over rows
X /= X.std(axis=0)

Universal Functions (ufuncs)

Ufuncs are vectorised C-level functions. They operate element-wise without Python loops and support broadcasting, reduceat, accumulate, and out= arguments.

Math ufuncs

np.add(a, b)          # a + b
np.subtract(a, b)     # a - b
np.multiply(a, b)     # a * b
np.divide(a, b)       # a / b  (true divide)
np.floor_divide(a, b) # a // b
np.mod(a, b)          # a % b
np.power(a, b)        # a ** b
np.negative(a)        # -a
np.absolute(a)        # |a|  — also np.abs
np.sign(a)            # -1, 0, or 1
np.sqrt(a)
np.square(a)
np.cbrt(a)            # cube root
np.reciprocal(a)      # 1/a

Exponential & logarithmic

np.exp(a)             # e^a
np.exp2(a)            # 2^a
np.log(a)             # natural log
np.log2(a)
np.log10(a)
np.log1p(a)           # log(1 + a) — more accurate near zero
np.expm1(a)           # e^a - 1    — more accurate near zero

Trigonometric

np.sin(a);  np.cos(a);  np.tan(a)
np.arcsin(a);  np.arccos(a);  np.arctan(a)
np.arctan2(y, x)      # angle of (x,y) respecting quadrant
np.hypot(a, b)        # sqrt(a² + b²)
np.deg2rad(a);  np.rad2deg(a)
np.sinh(a);  np.cosh(a);  np.tanh(a)
np.arcsinh(a);  np.arccosh(a);  np.arctanh(a)

Comparison & logical ufuncs

np.equal(a, b)           # a == b
np.not_equal(a, b)       # a != b
np.greater(a, b)         # a > b
np.less_equal(a, b)      # a <= b
np.logical_and(a, b)
np.logical_or(a, b)
np.logical_not(a)
np.logical_xor(a, b)

Ufunc methods

np.add.reduce(a)          # sum along axis 0 (same as np.sum)
np.add.accumulate(a)      # running cumulative sum
np.multiply.reduce(a)     # product
np.add.outer(a, b)        # outer product: a[:, None] + b[None, :]
np.add.reduceat(a, [0,3]) # reduce over slices defined by indices

Aggregation & Reduction

View on GitHub
GitHub Stars8
CategoryEducation
Updated1d ago
Forks0

Languages

Jupyter Notebook

Security Score

90/100

Audited on Apr 5, 2026

No findings