NumPy Interview Questions for Data Roles

NumPy questions check whether you understand what an array really is: a block of typed memory with a shape. Ten questions, answered on 1,599 real wine samples with NumPy 2, cover axes, views versus copies, broadcasting, silent integer overflow, NaN, float comparison, ties in top-N, vectorisation and seeds.

GloryTecks Team

Training & Placement Faculty

6 May 2026 9 min read

Last updated: 8 October 2026

Share:

NumPy interview questions are less about function names than about what an array is: one block of memory, one data type, and a shape that says how to read it. Most surprises in NumPy follow from that. A slice shares the memory, an integer type has fixed room, shapes must line up, and NaN spreads through arithmetic. The ten questions below are answered on real data with NumPy 2, so each behaviour is shown rather than asserted.

The data is the red wine part of the UCI Wine Quality set: 1,599 wines with eleven chemical measurements and a quality score from tasters (UCI: Wine Quality). Loaded with np.loadtxt, it is a 1,599 by 12 array of 64-bit floats.

python
import time

import numpy as np

with open("winequality-red.csv") as f:                     # UCI Wine Quality, red wines
    names = [n.strip('"') for n in f.readline().strip().split(";")]
X = np.loadtxt("winequality-red.csv", delimiter=";", skiprows=1)
alcohol, quality = X[:, names.index("alcohol")], X[:, names.index("quality")]
print("N1 shape, dtype, and what axis means:", X.shape, X.dtype)
print("   mean of each column (axis=0), first 3:", X.mean(axis=0)[:3].round(3))
print("   mean of each row (axis=1), first 3:   ", X.mean(axis=1)[:3].round(3))

print("\nN2 view or copy?")
first_rows = X[:5]                          # basic slicing: a view
picked = X[[0, 1, 2, 3, 4]]                 # fancy indexing: a copy
print("   slice shares memory:", np.shares_memory(X, first_rows), "| fancy index shares memory:", np.shares_memory(X, picked))
first_rows[0, 0] = -1.0
print("   after writing -1 into the slice, X[0, 0] =", X[0, 0])
X[0, 0] = 7.4                               # put the original value back

print("\nN3 broadcasting")
z = (X - X.mean(axis=0)) / X.std(axis=0)    # (1599, 12) with (12,) -> per-column z-scores
print("   column z-scores:", z.shape, "| column means now:", np.abs(z.mean(axis=0)).max() < 1e-12)
try:
    X - X.mean(axis=1)                      # (1599, 12) with (1599,) does not line up
except ValueError as error:
    print("   row means without keepdims:", str(error).strip())
print("   with keepdims:", (X - X.mean(axis=1, keepdims=True)).shape)

print("\nN4 masks and np.where")
good = quality >= 7
print(f"   wines rated 7 or more: {good.sum()} ({good.mean():.1%})")
print("   mean alcohol, good vs rest:", alcohol[good].mean().round(2), alcohol[~good].mean().round(2))
label = np.where(good, "good", "other")
print("   labels:", {str(k): int(v) for k, v in zip(*np.unique(label, return_counts=True))})

print("\nN5 integer types overflow silently")
q8 = quality.astype(np.uint8)
best = q8 == 8
print("   a quality-8 wine times 50, as uint8:", (q8[best] * np.uint8(50))[0], "| as int64:", (q8[best].astype(np.int64) * 50)[0])
try:
    q8 + 300                                # NumPy 2: the Python int must fit the array's dtype
except OverflowError as error:
    print("   uint8 + 300:", error)

print("\nN6 comparing floats")
print("   0.1 + 0.2 == 0.3:", 0.1 + 0.2 == 0.3, "| np.isclose:", np.isclose(0.1 + 0.2, 0.3))

print("\nN7 one missing value poisons the mean")
gappy = alcohol.copy()
gappy[10] = np.nan
print("   mean:", gappy.mean(), "| nanmean:", np.nanmean(gappy).round(3))

print("\nN8 top 3 by alcohol, and the ties behind it")
top = np.argsort(alcohol, kind="stable")[::-1][:3]
part = np.argpartition(alcohol, -3)[-3:]
print("   argsort picks rows:", sorted(top.tolist()), "| argpartition picks rows:", sorted(part.tolist()))
print("   values:", alcohol[top], "| wines tied at", alcohol[top[-1]], "->", int((alcohol == alcohol[top[-1]]).sum()))

print("\nN9 loop vs vectorised z-scores")
start = time.perf_counter()
loop = [[(X[i, j] - X[:, j].mean()) / X[:, j].std() for j in range(X.shape[1])] for i in range(200)]
loop_time = (time.perf_counter() - start) / 200 * X.shape[0]
start = time.perf_counter()
vec = (X - X.mean(axis=0)) / X.std(axis=0)
vec_time = time.perf_counter() - start
print("   same answer:", np.allclose(loop, vec[:200]), f"| vectorised was about {loop_time / vec_time:,.0f}x faster (varies)")

print("\nN10 reproducible randomness")
a = np.random.default_rng(42).choice(len(X), size=5, replace=False)
b = np.random.default_rng(42).choice(len(X), size=5, replace=False)
print("   same seed, same sample:", a, (a == b).all())
text
N1 shape, dtype, and what axis means: (1599, 12) float64
   mean of each column (axis=0), first 3: [8.32  0.528 0.271]
   mean of each row (axis=1), first 3:    [ 6.212 10.255  8.308]

N2 view or copy?
   slice shares memory: True | fancy index shares memory: False
   after writing -1 into the slice, X[0, 0] = -1.0

N3 broadcasting
   column z-scores: (1599, 12) | column means now: True
   row means without keepdims: operands could not be broadcast together with shapes (1599,12) (1599,)
   with keepdims: (1599, 12)

N4 masks and np.where
   wines rated 7 or more: 217 (13.6%)
   mean alcohol, good vs rest: 11.52 10.25
   labels: {'good': 217, 'other': 1382}

N5 integer types overflow silently
   a quality-8 wine times 50, as uint8: 144 | as int64: 400
   uint8 + 300: Python integer 300 out of bounds for uint8

N6 comparing floats
   0.1 + 0.2 == 0.3: False | np.isclose: True

N7 one missing value poisons the mean
   mean: nan | nanmean: 10.424

N8 top 3 by alcohol, and the ties behind it
   argsort picks rows: [652, 1269, 1270] | argpartition picks rows: [142, 144, 652]
   values: [14.9 14.  14. ] | wines tied at 14.0 -> 7

N9 loop vs vectorised z-scores
   same answer: True | vectorised was about 1,116x faster (varies)

N10 reproducible randomness
   same seed, same sample: [1235  701 1045  142  692] True

N1. What does axis=0 mean?

The axis you collapse. X.mean(axis=0) averages down the rows and returns one value per column, 12 values here; axis=1 averages across columns and returns one value per row. Saying "axis 0 is the direction the operation runs along, and it disappears from the result" is clearer than "rows" or "columns", which people mix up.

N2. When does indexing give a view, and when a copy?

Basic slicing such as X[:5] returns a view: a new array object over the same memory. Fancy indexing with a list or a boolean mask returns a copy (NumPy: copies and views). np.shares_memory confirmed it, and writing -1 into the slice changed X[0, 0] in the original. That is efficient for large data and dangerous when you meant to work on a copy; call .copy() when you need independence.

N3. How does broadcasting decide whether two shapes fit?

It compares shapes from the last dimension backwards; each pair must be equal or one of them must be 1 (NumPy: broadcasting). A (1599, 12) array minus the (12,) column means works, giving per-column z-scores. Subtracting the (1599,) row means fails, because 12 and 1599 do not match. keepdims=True keeps the row means as (1599, 1), and then it broadcasts. Reading the shapes out loud is the quickest way to answer this kind of question.

N4. How do you filter and label with a condition?

Build a boolean mask once and reuse it. 217 wines, 13.6%, were rated 7 or more, and their mean alcohol was 11.52% against 10.25% for the rest. np.where(mask, a, b) picks element by element, which is the vectorised form of an if-else.

N5. Can integer arithmetic go wrong without an error?

Yes. Fixed-size integers wrap around. Stored as uint8, which holds 0 to 255, a quality score of 8 times 50 gave 144 instead of 400, with no error. Sums of large integer arrays can overflow the same way in narrower types. NumPy 2 did tighten one case: under its NEP 50 promotion rules, adding the Python integer 300 to a uint8 array raises OverflowError, because 300 cannot be represented in that type (NEP 50). Older NumPy versions quietly promoted to a wider type instead, so name your version when you answer.

N6. Why is 0.1 + 0.2 == 0.3 False?

Because neither 0.1 nor 0.2 has an exact binary representation, and their sum differs from the stored 0.3 in the last bits. Compare floats with a tolerance: np.isclose for single values, np.allclose for arrays.

N7. What happens to a mean when one value is missing?

NaN spreads through arithmetic, so one missing reading made the whole mean nan. np.nanmean ignores missing values and returned 10.424. Say which one you want: sometimes a NaN result is the correct alarm that the data is incomplete.

N8. How do you get the top 3 values, and what about ties?

np.argsort sorts everything; np.argpartition only guarantees that the k largest end up at the end, which is faster on large arrays. On this data they disagreed. The strongest wine, row 652 at 14.9%, was picked by both, but seven wines are tied at 14.0%, so the second and third places are arbitrary, and the two methods chose different ones. When an interviewer asks for "the top 3", ask how ties should be handled. It is the same question as RANK versus ROW_NUMBER in SQL.

N9. Why are vectorised operations faster than loops?

A Python loop runs interpreter code for every element, while a vectorised operation runs a compiled loop over contiguous memory. Computing z-scores element by element was more than a thousand times slower than the one-line array version here, with an identical result. The exact factor changes from run to run, so quote the order of magnitude rather than the number.

N10. How do you make random results reproducible?

Create a generator with a seed, np.random.default_rng(42), and pass it around instead of using the global np.random functions (NumPy: random generator). Two generators with the same seed drew the same five rows. Without a seed, fresh entropy from the operating system makes every run different.

How to answer NumPy questions well

  • Say the shapes before and after each operation.
  • Name the data type, especially for integers and anything read from a file.
  • Mention views whenever you slice and then modify.
  • Ask about ties and missing values before writing top-N or summary code.

For the teaching version of these ideas, see the NumPy guide. Broader preparation is in Python interview questions: top 50 with answers, DataFrame questions in pandas interview questions, and algorithm-style tasks in Python coding interview questions and patterns.

What NumPy topics come up in data interviews?

Array creation and shapes, axis arguments, indexing and the difference between views and copies, broadcasting, boolean masks and np.where, data types and overflow, missing values, sorting and top-N, vectorisation, and reproducible random numbers.

What is the difference between a NumPy array and a Python list?

An array holds elements of one fixed type in a contiguous block of memory, which makes vectorised arithmetic fast; a list holds references to arbitrary Python objects and needs a loop for element-wise work.

Does slicing a NumPy array copy the data?

No. Basic slicing returns a view of the same memory, so changing the slice changes the original. Fancy indexing with a list or mask returns a copy.

What changed for interviews in NumPy 2?

The type promotion rules in NEP 50 mean that Python numbers no longer silently widen an array's type, so an out-of-range Python integer with a small integer array raises an error.

NumPy is taught in the Python Programming course.

Sources

GloryTecks Team

Training & Placement Faculty

The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.

Want to master Interview Questions?

Join GloryTecks’ expert-led Python Programming training in Hyderabad with real-time projects and 100% placement support.

Explore course