NumPy for Data Engineering: Arrays, Slicing, Broadcasting, and Real Practice

After completing my 50-question SQL project on the Olist Brazilian E-Commerce dataset, I stepped into Stage 2: Python for Data Engineering.

Before touching pandas or building ingestion scripts, I spent the day writing code in JupyterLab to master NumPy (numpy as np). NumPy is the foundational C-optimized engine beneath Pandas, PySpark, scikit-learn, and machine learning tensors.

Here are my complete, unfiltered student notes and code breakdowns from my notebook session.


1. What is NumPy and Why Does Data Engineering Need It?

In standard Python, lists are flexible: they can store integers, strings, objects, and dictionaries all inside the same list. But this flexibility comes with high memory overhead and slow computation because Python has to check each element’s data type at runtime.

A NumPy ndarray (N-dimensional array) solves this:

  • Homogeneous: Every element in the array must have the exact same data type (int64, float64, etc.).
  • Contiguous memory: Stored as continuous blocks of memory in RAM, allowing C-level speed and vectorization.
  • Fixed item size: Fast indexing without pointer lookups.

2. Creating Arrays from Scratch

Here is how I practiced initializing arrays across 1D, 2D, and identity matrices:

import numpy as np

# 1. 1D Array from Python List
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1)
# Output: [1 2 3 4 5]

# 2. 2D Matrix (List of Lists)
arr2 = np.array([[1, 3], [1, 4]])
print(arr2)
# Output:
# [[1 3]
#  [1 4]]

# 3. Zeros and Ones Matrices (Crucial for matrix initialization & masks)
arr3 = np.zeros((3, 3))    # 3x3 matrix of float zeros
arr4 = np.ones((2, 3))     # 2x3 matrix of float ones

# 4. Identity Matrix (np.identity or np.eye)
arr5 = np.identity(5)      # 5x5 matrix with 1.0 on diagonal

Ranges: np.arange vs np.linspace

Understanding the difference between step-size and sample-count is essential:

# np.arange(start, stop, step) - exclusive stop
arr6 = np.arange(5, 15, 2)
# Output: [ 5  7  9 11 13]

# np.linspace(start, stop, num_samples) - inclusive stop
arr7 = np.linspace(10, 20, 10)
# Output: 10 evenly spaced floats from 10.0 to 20.0

[!TIP] Copying Arrays (copy() vs Views): If you assign arr8 = arr7, Python only creates a reference (a view). Modifying arr8 modifies arr7! Always use arr8 = arr7.copy() when you need an isolated, independent array.


3. Array Inspection and Properties

When receiving raw data in a pipeline, inspect its metadata first:

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

print("Shape:", arr9.shape)       # (3, 2, 2) -> 3 blocks, 2 rows, 2 columns
print("Dimensions:", arr9.ndim)    # 3 dimensions
print("Total elements:", arr9.size)# 12 elements
print("Data type:", arr9.dtype)    # int64
print("Item size in bytes:", arr9.itemsize) # 8 bytes per element

# Type Casting with astype()
arr10 = arr9.astype('float32')
print("New Item Size:", arr10.itemsize) # 4 bytes per element

4. Reshaping, Slicing, and Multi-Dimensional Iteration

In data processing, you constantly reshape 1D sensor streams or CSV columns into 2D tables or 3D batches.

# Create a 24-element 1D array and reshape into 6 rows x 4 columns
arr11 = np.arange(24).reshape(6, 4)
# [[ 0,  1,  2,  3],
#  [ 4,  5,  6,  7],
#  [ 8,  9, 10, 11],
#  [12, 13, 14, 15],
#  [16, 17, 18, 19],
#  [20, 21, 22, 23]]

2D Slicing: [row_start:row_stop, col_start:col_stop]

Unlike standard Python lists which require nested list comprehensions, NumPy allows direct multi-axis slicing:

# Extract rows 2 to 4 (exclusive) and columns 2 to 4 (exclusive)
sub_matrix = arr11[2:4, 2:4]
# Output:
# [[10, 11],
#  [14, 15]]

Fast Iteration with np.nditer

Iterating over high-dimensional arrays using standard nested for loops is slow. np.nditer flattens the iteration in C:

for item in np.nditer(arr11):
    # Iterates smoothly element-by-element from 0 to 23
    pass

5. Operations, Matrix Multiplication, and Vectorization

One of NumPy’s biggest superpowers is vectorization: applying mathematical operations element-wise without manual loops.

# Element-wise operations
a = np.arange(6).reshape(2, 3)
b = np.arange(6, 12).reshape(2, 3)

print(a + b)  # Element-wise addition
print(a * b)  # Element-wise multiplication
print(a > 2)  # Boolean condition matrix

# Matrix Multiplication (Dot Product)
# Rules of linear algebra: columns of first must equal rows of second
matrix_a = np.arange(6).reshape(2, 3)    # shape (2, 3)
matrix_b = np.arange(6, 12).reshape(3, 2)# shape (3, 2)
result = matrix_a.dot(matrix_b)          # shape (2, 2)

Aggregations Along Axes

In data engineering and SQL, you often aggregate across columns or rows (GROUP BY equivalent):

  • axis=0: Operation performed vertically downwards across columns.
  • axis=1: Operation performed horizontally across rows.
print(matrix_a.max())        # Overall maximum
print(matrix_a.max(axis=0))  # Max per column
print(matrix_a.max(axis=1))  # Max per row
print(matrix_a.mean())       # Mean of all elements
print(np.median(matrix_a))   # Median
print(matrix_a.std())        # Standard deviation

6. Boolean Masking, Fancy Indexing, and Conditional Filtering

This is where NumPy directly mirrors SQL filtering (WHERE clauses).

Fancy Indexing

You can pull specific rows or items by passing a list of desired coordinate indices:

arr = np.linspace(10, 20, 10)
# Select indices 0, 3, and 4 in one call
subset = arr[[0, 3, 4]]
# Output: array([10., 13.33333333, 14.44444444])

Boolean Filtering (The NumPy WHERE Clause)

# Generate random test dataset
data = np.random.randint(low=1, high=100, size=20).reshape(4, 5)

# Filter: find all numbers greater than 50 that are odd
mask = (data > 50) & (data % 2 != 0)
filtered_values = data[mask]
print(filtered_values)

# Masked Replacement: Set all matching items to 0 (Data Cleaning)
data[(data > 50) & (data % 2 != 0)] = 0

SQL CASE Equivalent: np.where(condition, if_true, if_false)

Just like SQL’s CASE WHEN condition THEN A ELSE B END:

a3 = np.random.randint(10, 20, 10)
# Replace even numbers with -1, leave odd numbers unchanged
cleaned = np.where(a3 % 2 == 0, -1, a3)

7. Understanding Broadcasting

Broadcasting explains how NumPy treats arrays with different shapes during arithmetic operations. Subject to certain constraints, the smaller array is “broadcast” across the larger array so that they have compatible shapes without copying memory.

a1 = np.arange(3).reshape(1, 3)   # shape (1, 3) -> [[0, 1, 2]]
a2 = np.arange(12).reshape(4, 3)  # shape (4, 3) -> 4 rows of 3 columns

# NumPy stretches a1 vertically across the 4 rows of a2 automatically:
result = a1 + a2

The Broadcasting Rule:

Two dimensions are compatible when:

  1. They are equal, or
  2. One of them is 1.

8. Plotting Mathematical Curves with Matplotlib

To visually confirm vectorization and trigonometric arrays, I paired np.linspace with matplotlib.pyplot:

import matplotlib.pyplot as plt

# 1. Sine wave generation
x = np.linspace(-40, 40, 100)
y = np.sin(x)

plt.plot(x, y)
plt.title("Sine Wave with NumPy Vectorization")
plt.show()

Sine Wave generated with NumPy vectorization and Matplotlib

# 2. Parabolic quadratic curve: y = x^2 + 2x + 6
y2 = x**2 + 2*x + 6

plt.plot(x, y2)
plt.title("Quadratic Curve: y = x^2 + 2x + 6")
plt.show()

Quadratic Curve generated with NumPy vectorization and Matplotlib


9. Key Statistical and Utility Functions

Function Purpose in Data Processing
np.random.randint(low, high, size) Generate mock datasets with realistic bounds
np.random.uniform(low, high, size) Continuous random floating numbers
np.argmax(a) / np.argmin(a) Returns the index of the highest / lowest element
np.percentile(a, 25) Calculate quartiles and detect statistical outliers
np.sort(a) In-place or copy-based array sorting

Summary and Next Steps

Practicing NumPy hands-on in JupyterLab made the transition from SQL to Python clear:

  • SQL Tables $\rightarrow$ NumPy 2D ndarrays (and soon Pandas DataFrames)
  • WHERE filtering $\rightarrow$ Boolean masks & np.where()
  • GROUP BY & Aggregations $\rightarrow$ axis=0 / axis=1 operations (sum, mean, max)

With NumPy’s array mechanics under my belt, my next post will dive into Pandas: creating DataFrames, reading CSV/JSON files, and handling missing data.

Want my posts to show up more often on Google?

One click and Google will surface this site in your Top Stories.

Add as preferred source
Niraj Basnet
Written by

Niraj Basnet

Computer Science student at the University of South Florida, exploring web development, mobile app development, and AI. Aspiring full-stack developer.