Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›NumPy›NumPy Coding Challenges

Interview Prep

NumPy Coding Challenges

This module presents common algorithmic challenges designed to test proficiency with array manipulation, broadcasting, and vectorization techniques. Mastering these patterns is essential for optimizing performance and avoiding the common pitfalls of slow, iterative code in data-intensive tasks. Use these challenges to refine your ability to think in terms of multidimensional transformations rather than element-wise loops.

Vectorized Conditional Logic

A frequent interview challenge involves replacing elements based on a condition without resorting to standard control flow loops. Beginners often attempt to iterate through arrays, which is inefficient and ignores the underlying architecture designed for SIMD operations. Instead, we use boolean indexing or the 'np.where' function. When you use 'np.where(condition, x, y)', NumPy evaluates the condition across the entire structure simultaneously. The internal logic creates a mask, allowing the engine to pick values from x or y efficiently. This approach is superior because it minimizes the overhead associated with Python's interpreter. Understanding this is crucial because it teaches you that the 'how' of computation—specifically, moving the loop into highly optimized C or Fortran kernels—is just as important as the logic itself. By shifting from imperative to declarative programming, you write code that scales with data size.

import numpy as np

# Replace negative values with zero without a loop
data = np.array([-5, 2, -1, 8, 3])
result = np.where(data < 0, 0, data)  # Evaluates vector-wide
print(result)

Efficient Array Normalization

Normalizing data is a fundamental task, yet doing it incorrectly can lead to severe performance bottlenecks. The challenge is to subtract the mean and divide by the standard deviation across specific axes. If you attempt to do this by looping over individual elements, you destroy the advantage of contiguous memory layouts. NumPy handles this through broadcasting, which implicitly expands dimensions of smaller arrays to match larger ones. By specifying the 'axis' parameter, you force the operation to collapse the data along that dimension while maintaining the shape for the broadcast. This works because the underlying memory access pattern remains cache-friendly, allowing the hardware to pre-fetch data effectively. When you compute the mean across an axis, you create a smaller aggregate array; broadcasting this back against the original matrix relies on logical alignment rather than physical copying, saving massive amounts of memory.

import numpy as np

# Standardize columns by subtracting mean and dividing by std
matrix = np.random.rand(5, 3)
mean = matrix.mean(axis=0)  # Collapse rows
std = matrix.std(axis=0)
normalized = (matrix - mean) / std  # Broadcasting in action
print(normalized)

Sliding Window Aggregation

Calculating moving statistics over an array is a classic problem that demonstrates the power of strided views. Instead of copying data to create new arrays for each window, you can use 'np.lib.stride_tricks.sliding_window_view'. This creates a view into the existing memory buffer, effectively creating a higher-dimensional representation without moving the actual data. This is an advanced concept that relies on understanding how NumPy maps indices to memory addresses via strides. By manipulating the strides, we can treat a 1D array as a 2D windowed array. This is mathematically efficient because it avoids O(N*W) copy operations where W is window size, reducing the complexity to O(1) in terms of memory allocation for the view itself. It is a vital technique for time-series analysis where memory overhead is the primary constraint during the preprocessing pipeline.

from numpy.lib.stride_tricks import sliding_window_view

# Compute moving average of size 3
arr = np.array([1, 2, 3, 4, 5, 6])
windows = sliding_window_view(arr, window_shape=3)
moving_avg = windows.mean(axis=1)  # Aggregates across the window
print(moving_avg)

Advanced Array Masking

Interviewers often test your ability to filter multidimensional arrays using complex logical masks. The challenge is often to extract elements that satisfy multiple conditions across different dimensions simultaneously. The key here is using bitwise operators like '&', '|', and '~' on boolean arrays. Because these operators work on the underlying binary representation of the arrays, they are executed at the native speed of the processor. When you chain multiple conditions, NumPy performs element-wise logical evaluation across the entire grid. This is far more robust than attempting to subset the data in stages. By building a master boolean mask, you isolate exactly the memory locations needed in one pass. This pattern is foundational for data cleaning, as it allows you to identify outliers or missing values globally before applying secondary transformations, ensuring high consistency in your statistical results.

import numpy as np

# Filter for values > 0.5 AND < 0.8
grid = np.random.rand(4, 4)
mask = (grid > 0.5) & (grid < 0.8)
subset = grid[mask]  # Boolean indexing flattens result
print(subset)

Matrix Transformation Optimization

The final challenge involves rotating or rearranging matrix data, which often requires a deep understanding of memory layout and transpose operations. A frequent mistake is using 'np.reshape' when the underlying memory order requires 'np.transpose' or 'np.swapaxes'. 'Reshape' changes the view of the array shape without moving memory, but only if the memory is contiguous. Transposition is a powerful tool because it effectively changes the stride of the array. When you transpose, you do not move the data; you swap the interpretation of the strides so that rows become columns. This allows for extremely fast matrix multiplication, as the dot product kernel can access the necessary elements with a different memory stride. Mastering this allows you to perform complex geometric transformations or signal processing operations entirely within the memory space of the original dataset, avoiding expensive memory allocation cycles.

import numpy as np

# Efficiently rotate a 2D matrix by 90 degrees
matrix = np.arange(9).reshape(3, 3)
rotated = np.rot90(matrix)
# rot90 uses transpose and flip internally, avoiding heavy copying
print(rotated)

Key points

  • Vectorization allows operations to execute in optimized C kernels rather than Python loops.
  • Broadcasting aligns dimensions implicitly to perform element-wise arithmetic on mismatched shapes.
  • Boolean masking provides a declarative way to subset arrays based on complex logical criteria.
  • Strided views enable memory-efficient sliding window operations without redundant data copying.
  • The axis parameter is critical for controlling which dimension an aggregation function collapses.
  • Memory-efficient code relies on understanding the difference between views and copies.
  • Bitwise operators are preferred over standard logical operators when working with boolean arrays.
  • Transposition modifies how the interpreter traverses memory addresses without reordering physical data.

Common mistakes

  • Mistake: Using Python lists for element-wise operations. Why it's wrong: Lists interpret the '+' operator as concatenation, leading to extended lists rather than sum of elements. Fix: Always convert inputs to numpy.array objects first.
  • Mistake: Misunderstanding shape vs. size. Why it's wrong: Users often try to reshape an array into a shape that doesn't match the total number of elements. Fix: Ensure the product of dimensions in reshape matches the original array.size.
  • Mistake: Relying on shallow copies for modification. Why it's wrong: Using 'b = a' creates a reference, so changing 'b' modifies 'a'. Fix: Use 'b = a.copy()' if you need an independent array.
  • Mistake: Improper use of axis parameter in reduction functions. Why it's wrong: Confusing axis=0 (down columns) with axis=1 (across rows) leads to logical errors. Fix: Visualize the dimension being collapsed rather than the dimension remaining.
  • Mistake: Using loops for array operations. Why it's wrong: Python loops are extremely slow compared to vectorized NumPy operations. Fix: Leverage NumPy universal functions and slicing to perform operations in C-speed.

Interview questions

How would you create an array of ten zeros, and why is this preferred over a standard Python list for numerical operations?

To create an array of ten zeros, you use 'np.zeros(10)'. This is significantly preferred over a standard Python list because NumPy arrays are stored in contiguous memory blocks, which allows for vectorization. Unlike lists, which store pointers to objects scattered in memory, NumPy arrays enforce a single data type for all elements, enabling the CPU to perform operations like arithmetic much faster through optimized C-level loops and SIMD instructions.

Explain the concept of broadcasting in NumPy and provide an example of how it simplifies arithmetic operations between arrays of different shapes.

Broadcasting is the mechanism NumPy uses to allow element-wise operations on arrays with different shapes. For example, if you add a scalar 5 to a (3, 3) array, NumPy 'broadcasts' the 5 across the entire array, effectively treating it as a (3, 3) array of fives. This eliminates the need for explicit Python loops, reducing overhead and making code cleaner and more performant by avoiding unnecessary data duplication in memory.

How do you perform boolean indexing, and what advantages does it offer over writing traditional for-loops with conditional statements?

Boolean indexing involves passing a boolean array to an indexer, like 'arr[arr > 5]'. This approach is superior to using traditional for-loops with 'if' statements because the boolean selection is executed entirely in optimized C code. It masks out the elements you do not need without the interpretive overhead of the Python interpreter checking each element individually, making the filtering process orders of magnitude faster on large datasets.

Compare using 'np.reshape' versus 'np.ravel' when modifying the layout of an array. When should you prefer one over the other?

Both 'np.reshape' and 'np.ravel' change the view of an array without modifying the underlying data, but their purposes differ. 'np.reshape' allows you to force an array into any specific valid shape, while 'np.ravel' strictly flattens a multi-dimensional array into a 1D array. You should prefer 'np.ravel' when you need to ensure a flat sequence, as it is clearer to read, whereas 'np.reshape' is essential for complex multi-dimensional manipulations.

How can you compute the sum of rows or columns in a 2D array, and how does the 'axis' parameter change the calculation?

To compute sums, you use 'np.sum(array, axis=...)'. Setting 'axis=0' collapses the array along the vertical direction, calculating the sum of each column, while 'axis=1' collapses it along the horizontal direction, calculating the sum of each row. This is efficient because it avoids manual nested loops. By specifying the axis, you instruct NumPy to traverse the memory in a way that minimizes cache misses, leading to faster execution.

Describe the difference between a 'view' and a 'copy' in NumPy, and why it is critical for memory management to understand the distinction.

A 'view' is an array that shares the same data buffer as the original, whereas a 'copy' creates an entirely new block of memory. Understanding this is critical because modifying a view affects the original array, which can lead to hard-to-track bugs. You use views (like slicing) to save memory during large-scale processing, but you must explicitly use '.copy()' if you intend to transform data without corrupting the source array.

All NumPy interview questions →

Check yourself

1. What is the result of performing arr1 * arr2 if both are NumPy arrays of the same shape?

  • A.A list containing elements from both arrays
  • B.A new array containing the dot product of the two arrays
  • C.A new array containing the element-wise product
  • D.A broadcasted array that results in a higher dimension
Show answer

C. A new array containing the element-wise product
NumPy arrays perform element-wise multiplication by default. The dot product requires the dot() method, lists use concatenation, and broadcasting only occurs if shapes are different.

2. Given an array 'a' of shape (3, 4), what happens if you call a.reshape(4, 3)?

  • A.It transposes the matrix, swapping rows and columns
  • B.It creates a new array with the new dimensions without changing the data order
  • C.It raises a ValueError because the dimensions are incompatible
  • D.It changes the original array 'a' in-place to (4, 3)
Show answer

B. It creates a new array with the new dimensions without changing the data order
Reshape reorders the elements into the new shape based on the original data buffer. It does not transpose (which uses the transpose method) and creates a new view, it does not modify the original in-place.

3. If you perform a = np.array([1, 2, 3]); b = a[:2]; b[0] = 9, what is the value of a[0]?

  • A.1
  • B.9
  • C.Error
  • D.2
Show answer

B. 9
Slicing an array returns a view, not a copy. Therefore, modifying the view 'b' directly affects the original array 'a'. The other options are incorrect because they assume a copy was made.

4. What is the primary benefit of using NumPy's Boolean masking?

  • A.To compress the size of the array in memory
  • B.To quickly select or filter elements based on a condition without explicit loops
  • C.To convert non-numeric types into binary representations
  • D.To sort the array in ascending order automatically
Show answer

B. To quickly select or filter elements based on a condition without explicit loops
Boolean masking allows for vectorized filtering, which is the most efficient and readable way to handle conditions in NumPy. It does not sort data or compress memory.

5. When performing an operation between arrays of shape (3, 1) and (1, 3), what is the shape of the result?

  • A.(3, 3)
  • B.(1, 1)
  • C.(3, 1)
  • D.An error is raised
Show answer

A. (3, 3)
NumPy uses broadcasting rules to expand the dimensions of both arrays to (3, 3) to perform the operation. It is not an error because the trailing dimensions are compatible for broadcasting.

Take the full NumPy quiz →

← PreviousNumPy Interview Questions

NumPy

26 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app