88 NumPy Interview Questions and Answers (2026)

NumPy shows up in almost every numerical, data, and scientific-computing stack, and interviewers know it. As datasets get bigger and interview bars rise, they're testing real fluency—broadcasting, views vs. copies, the memory model—not whether you can call a function you Googled.
This guide has 88 questions with concise, interview-ready answers and code where it helps. It's built Junior to Mid to Senior, so you start with fundamentals and push into the deep memory and performance topics that separate strong candidates from the rest.
Q1.What is NumPy, and what are its core advantages that make it a preferred library for numerical computing in Python?
NumPy, and what are its core advantages that make it a preferred library for numerical computing in Python?NumPy (Numerical Python) is the foundational library for numerical computing in Python, providing a fast, memory-efficient N-dimensional array object (ndarray) plus a rich set of vectorized operations, linear algebra, and random number tools.
Core object: the ndarray: A homogeneous, fixed-size, N-dimensional grid stored in a contiguous memory block.
Key advantages:
Speed: operations run in optimized C loops rather than interpreted Python loops.
Memory efficiency: raw numeric values packed together, no per-element Python object overhead.
Vectorization: element-wise math without explicit loops via +, *, and universal functions.
Broadcasting: operate on arrays of different shapes without copying data.
Ecosystem foundation: Pandas, SciPy, scikit-learn, TensorFlow, and others build on the ndarray.
Q2.What are the key differences between NumPy arrays and Python lists, particularly regarding homogeneity, memory efficiency, and performance?
NumPy arrays and Python lists, particularly regarding homogeneity, memory efficiency, and performance?NumPy arrays are homogeneous, contiguously stored numeric buffers, while Python lists are heterogeneous collections of pointers to objects: this makes arrays far more memory-efficient and much faster for numerical work.
Homogeneity:
Arrays hold one fixed dtype for all elements; lists can mix ints, strings, objects.
Uniform typing is what enables compact storage and typed C loops.
Memory efficiency:
An array stores raw values back-to-back (e.g. 8 bytes per float64).
A list stores pointers to full Python objects, each carrying type and refcount overhead.
Performance:
Array operations are vectorized in C; list operations require slow per-element Python iteration.
Arrays are typically 10x to 100x faster for large numeric computations.
Flexibility trade-off: Lists are dynamic and can hold anything; arrays are fixed-size and typed but purpose-built for math.
Q3.What are the key features and advantages of NumPy?
NumPy?NumPy's central feature is the ndarray combined with vectorized operations, broadcasting, and a broad library of numerical routines that together deliver fast, concise numerical code.
N-dimensional arrays: Efficient, typed containers for vectors, matrices, and higher-dimensional tensors.
Vectorization and ufuncs: Element-wise math (np.sqrt, np.exp) runs in C without Python loops.
Broadcasting: Combine differently shaped arrays without explicit replication or copies.
Rich numerical toolkit: Linear algebra (np.linalg), random sampling (np.random), FFT, and statistical reductions.
Interoperability: Zero-copy interfaces to C/Fortran and the whole scientific Python stack.
Q4.How does NumPy achieve its performance advantages compared to standard Python lists?
NumPy achieve its performance advantages compared to standard Python lists?NumPy is fast because it moves work out of the Python interpreter: data is stored in contiguous typed buffers and operations execute as compiled, vectorized C loops over that buffer.
Contiguous, typed storage: Raw values sit together in memory, giving cache locality and avoiding per-element object overhead.
Vectorized C execution: A single call like a + b loops in C, avoiding Python bytecode dispatch per element.
No boxing/unboxing: Values are used directly as machine numbers, not wrapped Python objects.
Optimized backends and SIMD: Linear algebra routes to BLAS/LAPACK, and compilers use vector instructions.
Caveat: Writing Python for loops over an array throws away these gains: stay vectorized.
Q5.How does NumPy store data in memory, and why is this efficient compared to Python lists?
NumPy store data in memory, and why is this efficient compared to Python lists?NumPy stores an array as one contiguous block of raw, same-typed values, described by shape, dtype, and strides. A Python list, by contrast, stores an array of pointers to separate heap objects, which is why NumPy is far leaner.
The data buffer: A single flat C array of values, e.g. 1000 float64 packed as 8000 contiguous bytes.
The metadata:
shape gives dimensions; strides give byte steps per axis so any element is a simple offset computation.
This lets slices and reshapes be views sharing the same buffer, no copy.
Why it beats a list:
A list holds pointers, and each element is a full Python object with type tag and refcount overhead.
Contiguous values give cache locality and enable SIMD/vectorized C loops; scattered objects do not.
Q6.Describe the different data types supported by NumPy.
NumPy.NumPy supports a rich set of fixed-size numeric and non-numeric types, all described by a dtype object, so each element occupies a known number of bytes.
Boolean: bool_: a single byte True/False.
Integer types:
Signed: int8, int16, int32, int64.
Unsigned: uint8 through uint64 (the number is the bit width).
Floating point: float16, float32, float64 (default float), and platform float128.
Complex: complex64 and complex128 (two floats for real/imaginary parts).
Strings and bytes: Fixed-width byte strings S and Unicode U (e.g. 'U10' holds 10 characters).
Datetime: datetime64 and timedelta64 with unit resolution (year, day, ns, etc.).
Object and structured:
object: stores arbitrary Python objects as pointers.
Structured/record dtypes: named fields with mixed types, like a lightweight table row.
Q7.What is dtype in NumPy, and what is its purpose?
dtype in NumPy, and what is its purpose?A dtype (data type) is a NumPy object that describes how each element of an array is interpreted: its kind, size in bytes, and byte order. It is the metadata that turns a raw block of memory into meaningful typed values.
What it describes:
The type kind (int, float, bool, etc.), the item size (itemsize in bytes), and endianness.
For structured dtypes, the named fields and their offsets.
Its purpose:
Lets NumPy compute element offsets and run typed C operations without inspecting each value.
Controls memory footprint and numeric range/precision.
How you set it: Pass dtype= at creation, or convert later with astype().
Q8.How do you inspect the data type (dtype) of elements in a NumPy array, and what are some common dtypes?
dtype) of elements in a NumPy array, and what are some common dtypes?You inspect an array's element type through its .dtype attribute, and related attributes tell you the byte size and item count; NumPy exposes a small set of dtypes you meet constantly.
How to inspect:
arr.dtype gives the dtype object (e.g. dtype('int64')).
arr.dtype.name gives a readable string; arr.itemsize gives bytes per element.
Test kinds with np.issubdtype(arr.dtype, np.integer).
Common dtypes:
int64 (default integer on most platforms) and int32.
float64 (default float) and float32.
bool_, complex128, and object.
Q9.What are the main attributes of a NumPy ndarray (e.g., shape, ndim, size, dtype)?
ndarray (e.g., shape, ndim, size, dtype)?An ndarray is a fixed-size, homogeneous N-dimensional container, and its metadata attributes describe its layout and element type without copying data.
shape: Tuple giving the size along each dimension, e.g. (3, 4) for 3 rows and 4 columns.
ndim: Number of dimensions (axes); equals len(arr.shape).
size: Total number of elements, the product of all shape entries.
dtype: The element type (e.g. int64, float64); homogeneous across the whole array.
itemsize and nbytes: Bytes per element and total bytes (itemsize * size), useful for memory profiling.
strides: Bytes to step in each dimension to reach the next element; reveals memory layout and how views work.
Q10.How do you determine the shape and size of a NumPy array, and what do these attributes represent?
Use the shape and size attributes: shape tells you how the elements are arranged across axes, and size tells you how many elements there are in total.
arr.shape: A tuple where each entry is the length along one axis; its length is the number of dimensions.
arr.size: A single integer equal to the product of the shape entries (total elements).
Related helpers:
len(arr) gives only the first-axis length, not the total, so prefer size for element counts.
np.shape(arr) and np.size(arr) work on list-like inputs too.
Q11.How can you programmatically determine if a NumPy array is empty or contains zero elements?
Check arr.size == 0: that is the reliable, unambiguous test for an array with zero elements regardless of its shape.
Prefer arr.size == 0: Works for any dimensionality, including shapes like (0, 5) that hold no elements but still have axes.
Avoid truthiness on the array: if arr: raises ValueError for multi-element arrays (ambiguous truth value), so it is not a safe emptiness test.
Distinguish "empty" from "all zeros": size == 0 means no elements; use np.all(arr == 0) or not np.any(arr) to test whether existing elements are zero.
Q12.Describe different ways to create NumPy arrays (e.g., np.array, zeros, ones, arange, linspace), and when you might choose one over another.
np.array, zeros, ones, arange, linspace), and when you might choose one over another.NumPy offers constructors for different needs: wrap existing data with np.array, pre-allocate placeholder arrays with zeros/ones, and generate numeric ranges with arange or linspace.
np.array(seq): Converts existing Python lists/tuples into an array; use when you already have the values.
np.zeros(shape) / np.ones(shape): Pre-allocate an array of a known size to fill later, or as a starting accumulator.
np.empty(shape) and np.full(shape, v): empty allocates without initializing (fastest, values are garbage); full fills with a constant.
np.arange(start, stop, step): Like range with a step; use when you care about the step size (stop is exclusive).
np.linspace(start, stop, num): Fixed count of evenly spaced points; use when you care about how many points and want the endpoint included.
Choosing: Known data -> np.array; known size to fill -> zeros/ones; sequence by step -> arange; sequence by count -> linspace.
Q13.How can you generate random numbers and arrays using NumPy?
Use a random Generator from np.random.default_rng() and call its methods to draw scalars or whole arrays from various distributions by passing a size argument.
Create a generator: rng = np.random.default_rng(seed) is the recommended modern entry point.
Uniform floats: rng.random(size) gives values in [0, 1); rng.uniform(low, high, size) for an arbitrary range.
Integers: rng.integers(low, high, size) for random integers.
Distributions: rng.normal(loc, scale, size) for Gaussian; many others (poisson, binomial).
Sampling and shuffling: rng.choice(a, size, replace, p) to sample; rng.shuffle(a) and rng.permutation(a) to reorder.
Shape control: size can be a tuple to produce multi-dimensional arrays in one call.
Q14.What is the difference between np.arange, np.linspace, and np.logspace, and when would you use each?
np.arange, np.linspace, and np.logspace, and when would you use each?All three build 1-D numeric sequences, but arange is defined by a step size, linspace by a fixed count of evenly spaced points, and logspace by evenly spaced points on a logarithmic scale.
np.arange(start, stop, step):
You specify the step; stop is exclusive.
With float steps, floating-point rounding can make the element count unpredictable, so prefer linspace for floats.
np.linspace(start, stop, num):
You specify how many points; stop is included by default (endpoint=True).
Best when you need an exact number of evenly spaced samples.
np.logspace(start, stop, num):
Returns points evenly spaced in log space: values are base ** linspace(start, stop, num) (default base=10).
Use for log-scaled axes or hyperparameter sweeps spanning orders of magnitude.
Q15.What is the difference between np.zeros, np.ones, and np.empty, and why is np.empty faster?
np.zeros, np.ones, and np.empty, and why is np.empty faster?All three allocate an array of a given shape and dtype, but they differ in initialization: np.zeros fills with 0, np.ones fills with 1, and np.empty leaves memory uninitialized (contains whatever garbage was already there).
np.zeros(shape): Allocates memory and writes 0 into every element.
np.ones(shape): Allocates memory and writes 1 into every element.
np.empty(shape): Allocates memory only, with no fill step, so contents are arbitrary leftover bytes.
Why np.empty is faster:
It skips the O(n) pass that writes an initial value to every element.
The speedup is only meaningful for large arrays you will fully overwrite anyway.
Caution: never read from an np.empty array before writing it, values are undefined.
Q16.How can you reshape a NumPy array, and what is the conceptual process and constraints like total number of elements?
Reshaping reinterprets the same data under a new shape using .reshape(): the memory buffer is unchanged, only the metadata describing dimensions and strides changes. The one hard constraint is that the total number of elements must stay the same.
Element-count constraint: The product of the new shape must equal the product of the old shape (e.g. a 12-element array can become (3,4) or (2,6) but not (5,3)).
Using -1 to infer a dimension: Pass -1 for one axis and NumPy computes it: arr.reshape(3, -1).
View vs copy:
reshape() returns a view when possible (no data copied), sharing memory with the original.
If the layout can't be expressed with strides (e.g. certain non-contiguous cases), it silently returns a copy.
Order matters: Default order='C' fills row-major; order='F' is column-major, affecting how elements map into the new shape.
Q17.How would you flatten a multi-dimensional array in NumPy?
Flattening collapses a multi-dimensional array into 1-D. The main options are .ravel(), .flatten(), and .reshape(-1), differing mainly in whether they return a view or a copy.
arr.ravel(): Returns a view when possible (no copy), so it's memory-cheap.
arr.flatten(): Always returns an independent copy, safe to modify without touching the original.
arr.reshape(-1): Equivalent to ravel in behavior: view when the layout allows.
Order parameter: order='C' (default) reads row-major; order='F' reads column-major.
Q18.How do you transpose a matrix in NumPy using .T or np.transpose()?
.T or np.transpose()?Transposing swaps an array's axes; for a 2-D matrix it turns rows into columns. Both .T and np.transpose() return a view that reinterprets the same data via reordered strides, so no data is copied.
.T attribute: Shorthand for a full reversal of axes; ideal for 2-D matrices.
np.transpose(arr, axes): Lets you specify an explicit axis permutation for higher-dimensional arrays.
It's a view:
The result shares memory; modifying it changes the original.
The result is often non-contiguous; use np.ascontiguousarray() if a contiguous copy is needed.
Note: transpose is a no-op on 1-D arrays (shape unchanged).
Q19.What is the difference between indexing and slicing in NumPy?
Indexing selects individual elements at specific positions, while slicing selects a range (subarray) using start:stop:step: in NumPy, basic slices return views whereas single-element indexing returns a scalar.
Indexing:
a[2] or a[1, 3] pulls out a single element (a scalar) by position.
Negative indices count from the end: a[-1].
Slicing:
a[1:5] or a[::2] returns a subarray spanning a range.
Reduces one dimension per integer index but preserves dimensionality per slice.
Returns a view that shares memory with the source.
Key contrast: Indexing narrows to a point; slicing extracts a contiguous/strided block and stays connected to the original data.
Q20.What is the purpose of the np.where() function?
np.where() function?np.where() is a vectorized conditional selector: it returns elements chosen from two sources depending on a boolean condition, or, in its single-argument form, the indices where a condition is True.
Three-argument form: element-wise choose:
np.where(cond, a, b) picks from a where cond is True, else from b; all three broadcast together.
Acts like a vectorized ternary, replacing an if/else loop.
One-argument form: find indices: np.where(cond) returns a tuple of index arrays (one per dimension) marking True positions.
Common uses: Clamping/replacing values (e.g. set negatives to 0), building masks, or locating matches.
Q21.How do np.any and np.all work, and how are they used with boolean arrays?
np.any and np.all work, and how are they used with boolean arrays?np.any and np.all reduce a boolean array to a single truth value (or per-axis values): np.any tests whether at least one element is True, np.all whether every element is True.
How they evaluate:
np.any is a logical OR across elements; np.all is a logical AND.
Non-zero/non-empty values count as True (works on numeric arrays too).
The axis argument: Without axis they reduce the whole array to one bool; with axis=0/1 they reduce along that dimension.
Typical usage:
Combine with a comparison to ask questions of the data: np.any(a < 0) ("are there any negatives?"), np.all(a > 0) ("are all positive?").
Avoids the ambiguous truth-value error you get from if array: on a multi-element array.
Q22.How do you perform element-wise arithmetic operations on NumPy arrays?
NumPy arrays?You perform element-wise arithmetic by applying standard operators directly to arrays: each operator maps to a ufunc that pairs up corresponding elements. Arrays should be shape-compatible (identical shapes, or broadcastable).
Use ordinary operators:
+, -, *, /, **, % all act element-by-element.
Equivalent ufuncs exist: np.add, np.subtract, np.multiply, np.divide.
Array-with-scalar: The scalar is broadcast to every element, e.g. a * 2.
In-place variants: +=, *= modify the array without allocating a new one (must be compatible dtype).
Shape rules: Same shape maps element-to-element; different shapes must satisfy broadcasting or raise a ValueError.
Q23.Explain the axis argument in NumPy aggregations and operations. What do axis=0, axis=1, and axis=None mean, and how do they affect the result?
axis argument in NumPy aggregations and operations. What do axis=0, axis=1, and axis=None mean, and how do they affect the result?The axis argument selects which dimension an operation collapses (reduces) along. The rule: the axis you name is the one that disappears from the result. axis=0 reduces down the rows (per column), axis=1 reduces across the columns (per row), and axis=None reduces over the entire array to a scalar.
axis=0: Operates down each column (collapses the row dimension); for a (m,n) array the result has shape (n,).
axis=1: Operates across each row (collapses the column dimension); result shape (m,).
axis=None (default for many aggregations): Flattens everything and returns a single scalar.
Mnemonic and options:
"The named axis is consumed." Negative axes count from the end (axis=-1 is the last).
Pass keepdims=True to retain the reduced axis as length 1, which helps later broadcasting.
Q24.Explain how to calculate common descriptive statistics like mean, median, standard deviation, and variance using NumPy, specifically addressing the axis parameter.
NumPy, specifically addressing the axis parameter.NumPy provides direct functions for descriptive statistics: np.mean, np.median, np.std, and np.var. Each accepts an axis argument that controls whether you get one number for the whole array or one per row/column.
The functions:
np.mean: arithmetic average.
np.median: middle value (robust to outliers).
np.std and np.var: spread and its square.
The axis parameter:
axis=None (default): statistic over all elements, returns a scalar.
axis=0: one value per column; axis=1: one value per row.
Key gotcha: sample vs population: np.std/np.var default to ddof=0 (population, divide by N). Use ddof=1 for the sample estimate (divide by N-1).
NaN-safe variants: np.nanmean, np.nanstd, etc. ignore NaN values.
Q25.How do you calculate the cumulative sum or product of a NumPy array?
NumPy array?Use np.cumsum for a running total and np.cumprod for a running product. Unlike sum or prod, these return an array the same length as the input, holding every intermediate result, and they also accept an axis argument.
np.cumsum: Each element is the sum of all preceding elements plus itself.
np.cumprod: Each element is the product of all preceding elements times itself.
Axis behavior on 2D arrays:
No axis (default None): the array is flattened first, giving a 1D cumulative result.
axis=0 accumulates down columns; axis=1 accumulates across rows.
Related: They are the accumulate methods of np.add and np.multiply; NaN-safe versions are np.nancumsum/np.nancumprod.
Q26.What is the role of the axis parameter in NumPy's sorting functions?
axis parameter in NumPy's sorting functions?The axis parameter tells a sort function which dimension to sort along, so each 1-D slice taken along that axis is sorted independently.
axis=-1 (default): Sorts along the last axis: for a 2-D array this sorts each row.
axis=0: Sorts down each column (across rows).
axis=None: Flattens the array first, then sorts the whole thing into 1-D.
Shape is preserved: Unlike aggregations, sorting along an axis keeps the array's shape; only the ordering within each slice changes.
Q27.What do np.argmin and np.argmax return, and how do they differ from np.min and np.max?
np.argmin and np.argmax return, and how do they differ from np.min and np.max?np.argmin and np.argmax return the index (position) of the smallest/largest element, whereas np.min and np.max return the value itself.
arg* return positions: Useful when you need to locate the extreme or index into another aligned array.
min/max return values: arr[arr.argmax()] == arr.max() always holds.
Ties: arg* return only the first occurrence of the extreme value.
With axis: On a flattened array the index is a single int; use np.unravel_index to convert it to multi-dimensional coordinates.
Q28.What does np.clip do, and when would you reach for it?
np.clip do, and when would you reach for it?np.clip limits (clamps) array values to a range: anything below the minimum becomes the minimum, anything above the maximum becomes the maximum, and values in between are unchanged.
Signature: np.clip(a, a_min, a_max); pass None for one bound to clip only from one side.
Vectorized and fast: Replaces manual masking like a[a < lo] = lo in a single call.
When to reach for it: Avoid divide-by-zero or log(0) by clamping to a small epsilon; keep pixel values in [0,255]; bound probabilities to [0,1]; cap outliers.
Bounds can broadcast: a_min / a_max may be arrays that broadcast against a for per-element limits.
Q29.When can you add two NumPy arrays together, and what conditions enable broadcasting?
You can add two arrays whenever their shapes are broadcast-compatible: they are identical, or for each trailing dimension the sizes are equal or one of them is 1 (including a scalar).
Identical shapes: Straightforward element-wise addition; the common case.
Broadcast-compatible shapes: Compared right to left, each dimension pair must be equal or contain a 1; missing leading dims count as 1.
Enabling conditions: A scalar adds to any array; a (3,) row adds to a (5,3) matrix; a (5,1) column plus a (1,3) row makes a (5,3) result.
When it fails: A mismatch where neither dimension is 1 (e.g. (3,) + (4,)) raises a ValueError about non-broadcastable shapes.
Q30.What is a NumPy ndarray and what makes it efficient?
NumPy ndarray and what makes it efficient?An ndarray is NumPy's N-dimensional array: a single contiguous block of same-typed data plus lightweight metadata describing how to interpret that block. Its efficiency comes from storing raw values compactly and operating on them in compiled C.
What it is:
A homogeneous, fixed-size grid of elements all sharing one dtype.
Metadata includes shape, dtype, and strides (byte steps per axis).
Why it is efficient:
Contiguous memory gives cache-friendly access and no pointer chasing.
Strides let views (slices, reshapes, transposes) reinterpret the same buffer without copying.
Uniform typing lets NumPy run tight compiled loops instead of interpreting per element.
Q31.Explain the trade-offs between using NumPy arrays and other Python data structures like lists and dictionaries for numerical computations.
NumPy arrays and other Python data structures like lists and dictionaries for numerical computations.For bulk numerical computation NumPy arrays win decisively on speed and memory, but lists and dictionaries remain better for heterogeneous, dynamic, or key-based data: choose based on data shape and access pattern.
NumPy arrays:
Pro: fast vectorized math, compact memory, broadcasting, rich numeric API.
Con: fixed type, fixed size (append/resize is expensive), overkill for tiny or mixed data.
Lists:
Pro: heterogeneous, dynamically resizable, cheap append.
Con: slow and memory-heavy for numeric loops (pointers to boxed objects).
Dictionaries:
Pro: O(1) key lookup, ideal for sparse or labeled/associative data.
Con: no vectorized arithmetic, high per-entry overhead, poor cache locality.
Rule of thumb: Dense homogeneous numbers, use arrays; growing/mixed sequences, use lists; keyed lookups, use dicts (or a labeled layer like Pandas).
Q32.What are the advantages of NumPy over other numerical computing environments like Matlab/Octave for Python developers?
NumPy over other numerical computing environments like Matlab/Octave for Python developers?NumPy gives Python developers Matlab-like array computing while keeping the advantages of a general-purpose language, an open ecosystem, and free licensing.
General-purpose host language: Numerical code lives alongside web services, I/O, databases, and automation, not a math-only environment.
Open source and free: No per-seat license cost, unlike Matlab; easy to deploy anywhere.
Ecosystem depth: Integrates seamlessly with Pandas, SciPy, scikit-learn, and ML frameworks.
Explicit, readable semantics: 0-based indexing and clear broadcasting rules; arrays are references, so copies are explicit.
Trade-off to acknowledge: Matlab ships polished built-in toolboxes; in Python you assemble packages, but they are freely available.
Q33.Explain the concept of data types in NumPy arrays. Why are NumPy arrays homogeneous, and what are the implications for performance and memory?
NumPy arrays. Why are NumPy arrays homogeneous, and what are the implications for performance and memory?A NumPy array stores every element as the same fixed-size type described by its dtype, which is what makes arrays homogeneous. This uniformity lets NumPy pack values in one contiguous memory block and process them with fast, vectorized C loops.
Homogeneous by design: Every element has identical size and layout, so element N lives at a predictable byte offset (base + N * itemsize).
Memory efficiency: No per-element Python object overhead: a million int64 values take ~8 MB, versus a Python list of int objects that is many times larger.
Performance: Contiguous, typed data enables vectorized C loops, SIMD, and cache-friendly access with no boxing/unboxing.
Implications and trade-offs:
You must pick a dtype wide enough to avoid overflow or precision loss.
Mixing types forces promotion to a common dtype, or falls back to slow object arrays that lose these benefits.
Q34.How do you handle arrays with different data types in NumPy given its homogeneous nature, and what is the exception with object dtype?
NumPy given its homogeneous nature, and what is the exception with object dtype?Because arrays are homogeneous, NumPy stores mixed inputs by promoting them to one common dtype; when no meaningful numeric common type exists, it falls back to object dtype, which can hold arbitrary Python objects at the cost of NumPy's speed advantages.
Default behavior: promotion: np.array([1, 2.5]) becomes all float64; np.array([1, 'a']) becomes a string dtype.
Structured dtypes for genuinely mixed columns: Define named fields (e.g. an int and a string per record) to keep each column typed within one array.
The object dtype escape hatch:
With dtype=object each slot is a pointer to a real Python object, so you can mix ints, strings, lists, anything.
Cost: you lose vectorized C loops and compact storage: operations run at Python speed.
Rule of thumb: For heterogeneous tabular data prefer pandas or structured arrays; reserve object dtype for truly irregular data.
Q35.Explain the difference between np.copy() and np.asarray() when converting data types or creating arrays.
np.copy() and np.asarray() when converting data types or creating arrays.np.copy() always creates a new, independent array, whereas np.asarray() avoids copying when possible: it returns the input unchanged if it is already an array of the requested dtype.
np.copy():
Guarantees a fresh buffer; mutating the copy never affects the original.
Use when you need isolation from the source data.
np.asarray():
Returns the same object (no copy) if it is already an ndarray with a matching dtype; otherwise it converts.
Use to cheaply ensure something is an array at a function boundary.
Key implication:
After asarray the result may share memory with the input, so writes can propagate; after copy they never do.
For a dtype change, both convert: but asarray still skips the copy if the dtype already matches.
Q36.How does astype work for casting arrays, and what are the safe/same_kind/unsafe casting rules?
astype work for casting arrays, and what are the safe/same_kind/unsafe casting rules?astype() returns a new array converted to a target dtype (copying by default), and its casting argument controls how strictly the conversion is validated, from fully safe to entirely unchecked.
How astype works:
arr.astype(np.float32) allocates a new array with the new dtype and copies converted values.
copy=False may skip the copy if the dtype already matches.
By default it uses casting='unsafe', so it will happily truncate floats to ints.
Casting rules (strict to loose):
safe: only conversions that preserve all values (e.g. int32 -> float64).
same_kind: safe casts plus casts within the same category (e.g. float64 -> float32).
unsafe: any conversion allowed, even lossy ones like float -> int.
Practical tip: Pass casting='safe' to force a TypeError on risky conversions instead of silent data loss; verify with np.can_cast().
Q37.What is a 0-dimensional array in NumPy, and how do you extract a Python scalar from an array using .item() or .tolist()?
.item() or .tolist()?A 0-dimensional array is a scalar wrapped in an ndarray: it has ndim == 0 and an empty shape (), and you extract a native Python scalar from it with .item().
0-d array vs scalar: np.array(5) has shape () and is still an array with a dtype, not a plain Python int.
.item(): Returns a single native Python scalar (int, float, etc.); on a larger array it requires exactly one element or an index.
.tolist(): Converts any array to nested Python lists/scalars; on a 0-d array it returns the bare scalar too.
Why it matters: Native scalars serialize cleanly (JSON) and avoid surprising NumPy type behavior downstream.
Q38.What is the purpose of np.random.seed() or the modern default_rng with a seed, and why is it important for reproducibility?
np.random.seed() or the modern default_rng with a seed, and why is it important for reproducibility?Seeding fixes the starting state of the pseudo-random generator so the same code produces the same "random" sequence every run, which is essential for reproducible experiments, tests, and debugging.
Pseudo-random, not truly random: Numbers come from a deterministic algorithm; the seed selects which deterministic sequence you get.
Legacy np.random.seed(): Sets a single global state used by np.random.* functions; convenient but shared across the whole program, so it is easy to disturb.
Modern default_rng(seed):
Creates an independent Generator object with its own state, so different parts of a program don't interfere.
Preferred in new code: better statistical properties and explicit, isolated state.
Why reproducibility matters: Lets others rerun your results, makes bugs deterministic, and keeps ML train/test splits and initializations consistent.
Q39.Explain the difference between np.vstack(), np.hstack(), and np.concatenate() when combining multiple arrays.
np.vstack(), np.hstack(), and np.concatenate() when combining multiple arrays.All three join arrays, but np.concatenate() is the general tool that joins along an existing axis, while np.vstack() and np.hstack() are convenience wrappers that stack vertically (rows) and horizontally (columns).
np.concatenate((a, b), axis=...):
Joins along a chosen existing axis; arrays must match on all other axes.
Never adds a new dimension.
np.vstack():
Stacks along axis 0 (rows on top of each other), roughly concatenate on axis=0.
Promotes 1-D inputs to rows first, so 1-D arrays become a 2-D result.
np.hstack(): Stacks along axis 1 for 2-D (columns side by side); for 1-D it joins along axis 0.
Rule of thumb: Use concatenate for explicit axis control; use vstack/hstack for readable common cases.
Q40.Explain the different methods for combining (concatenate, stack, vstack, hstack) and splitting (split, vsplit, hsplit) NumPy arrays.
concatenate, stack, vstack, hstack) and splitting (split, vsplit, hsplit) NumPy arrays.Combining functions join arrays into one; splitting functions divide one array into several. The key distinction is whether an operation works along an existing axis or introduces a new one.
Combining:
np.concatenate: joins along an existing axis (no new dimension).
np.stack: joins along a new axis, increasing dimensionality by one.
np.vstack: stacks row-wise (axis 0), promoting 1-D to 2-D.
np.hstack: stacks column-wise (axis 1 for 2-D).
Splitting:
np.split(arr, n, axis): splits into n equal parts (raises if not divisible); can also take explicit split indices.
np.vsplit: splits along axis 0 (row-wise).
np.hsplit: splits along axis 1 (column-wise).
np.array_split allows uneven splits without error.
Mental model: stack adds an axis, concatenate/vstack/hstack extend an axis; split is the inverse of concatenate.
Q41.Explain the difference between ravel() and flatten() in NumPy regarding views and copies.
ravel() and flatten() in NumPy regarding views and copies.Both return a flattened 1-D array, but ravel() returns a view whenever possible (sharing memory), while flatten() always returns a fresh, independent copy.
ravel():
Returns a view for contiguous data, so it's faster and uses no extra memory.
Modifying the result can modify the original array.
Falls back to a copy when a view isn't possible (e.g. non-contiguous layout).
flatten():
Always allocates and copies, so changes never affect the original.
Slightly slower and uses more memory.
Choosing: Use ravel() for performance when you won't mutate, flatten() when you need a guaranteed independent copy.
Q42.When would you use np.pad() and what are its key parameters for array padding?
np.pad() and what are its key parameters for array padding?np.pad() adds elements around the edges of an array, useful for tasks like preparing images for convolution, adding borders, or aligning arrays to a required size.
array: The input array to be padded.
pad_width: How many elements to add before and after along each axis, e.g. ((top, bottom), (left, right)) for 2-D.
mode: How to fill new elements: 'constant' (default), 'edge', 'reflect', 'symmetric', 'wrap', etc.
constant_values: The value(s) used when mode='constant' (defaults to 0).
Typical uses: Zero-padding feature maps, extending signal edges before filtering, or making arrays broadcast-compatible.
Q43.What do np.squeeze and np.expand_dims do, and when would you use them?
np.squeeze and np.expand_dims do, and when would you use them?They adjust an array's dimensionality: np.squeeze removes axes of length 1, while np.expand_dims inserts a new length-1 axis: both are cheap reshapes that return views.
np.squeeze(a, axis=None):
Drops all size-1 dimensions, e.g. shape (1, 3, 1) becomes (3,).
Pass axis to remove only a specific unit axis (errors if that axis isn't size 1).
Useful to collapse trailing dims from operations like keepdims=True reductions or single-channel results.
np.expand_dims(a, axis):
Adds a new axis of length 1 at the given position, e.g. (3,) becomes (1, 3) or (3, 1).
Equivalent to indexing with np.newaxis / None.
When to use:
Align shapes for broadcasting (add an axis so arrays line up).
Add or strip a batch/channel dimension when feeding ML models.
Q44.What does np.unique do, and how do parameters like return_counts and return_index work?
np.unique do, and how do parameters like return_counts and return_index work?np.unique returns the sorted distinct values of an array, and its optional flags return extra bookkeeping arrays: counts, first indices, and inverse mappings.
Default behavior: Flattens the input and returns unique values in ascending order.
return_counts=True: Also returns how many times each unique value occurs: great for frequency tables/histograms of discrete data.
return_index=True: Returns the index in the original array where each unique value first appears.
return_inverse=True: Returns indices that reconstruct the original array from the unique values: unique[inverse] equals the input.
axis: Set axis=0 to find unique rows instead of unique scalars.
Q45.What is the difference between np.tile and np.repeat?
np.tile and np.repeat?Both replicate data, but np.tile repeats the whole array as a block, while np.repeat repeats each element in place.
np.tile(a, reps):
Stacks copies of the entire array, like laying tiles.
[1,2,3] tiled twice becomes [1,2,3,1,2,3].
reps can be a tuple to tile along multiple axes.
np.repeat(a, repeats, axis):
Repeats each element consecutively.
[1,2,3] repeated twice becomes [1,1,2,2,3,3].
repeats can be a per-element list, and axis controls which dimension expands.
Mnemonic: tile = repeat the pattern; repeat = repeat each element.
Q46.Explain how boolean masking works in NumPy and why it is important for filtering or modifying elements based on conditions.
Boolean masking uses a boolean array of the same shape as the data to select or modify only the elements where the mask is True: a vectorized, loop-free way to filter by condition.
How the mask is built:
A comparison like a > 5 produces a boolean array elementwise.
Combine conditions with &, |, ~ (parenthesize each: (a>2) & (a<8)): Python's and/or don't work elementwise.
Filtering: a[mask] returns a 1D array of the matching elements (a copy).
Modifying in place: a[a < 0] = 0 assigns only to matching positions: concise conditional updates.
Why it matters: Runs in fast C loops, avoiding slow Python iteration, and expresses intent clearly.
Q47.Explain basic slicing, advanced indexing, and boolean mask indexing in NumPy, and how they differ in terms of returning views vs. copies.
NumPy has three indexing modes: basic slicing (start:stop:step) returns views, while advanced indexing (integer or boolean arrays) always returns copies.
Basic slicing:
Uses slices, integers, and np.newaxis, e.g. a[1:5, ::2].
Returns a view: shares memory with the original, so writes propagate back.
Advanced (fancy) indexing:
Indexing with an integer array/list, e.g. a[[0, 2, 3]].
Returns a copy: the result gathers scattered elements, so it can't be a view.
Can pick arbitrary elements and reorder/repeat them.
Boolean mask indexing:
A form of advanced indexing using a boolean array, e.g. a[a > 0].
Also returns a copy.
View vs copy caveat: Assignment like a[mask] = 0 still modifies the original (the copy rule applies to the read result, not to in-place assignment).
Q48.How can you replace values in a NumPy array based on a condition using boolean indexing?
Build a boolean mask from your condition and assign to the masked positions, or use np.where to produce a new array with values chosen by condition.
In-place assignment: a[a > 100] = 100 clips large values; the right side can be a scalar or a matching-length array.
np.where(cond, x, y): Returns a new array: takes from x where cond is True, else from y. Non-destructive.
Multiple conditions: Combine with &/| and parentheses, e.g. a[(a<0) | (a>10)] = 0.
Q49.How do you perform advanced indexing to select elements from a multidimensional array based on a condition or an array of indices?
Advanced indexing passes integer arrays or a boolean mask to gather arbitrary elements: for multidimensional arrays you supply one index array per axis, and they are broadcast together to pick coordinate-wise.
Integer-array (fancy) indexing:
a[[0, 2], [1, 3]] selects elements at (0,1) and (2,3): the index arrays pair up elementwise.
Index arrays broadcast, so shapes need not match exactly.
Always returns a copy, and result shape follows the broadcasted index shape.
Boolean-condition indexing:
a[a > 0] flattens matching elements into a 1D array.
Use np.nonzero(mask) or np.argwhere(mask) to recover the coordinates of matches.
Selecting whole rows/columns: a[[0, 2], :] picks rows 0 and 2; mix a slice with an index array to grab structured subsets.
Q50.Explain the use of slicing and indexing with NumPy arrays, including basic vs. advanced indexing.
Slicing and indexing extract subsets of an array. The key distinction: basic indexing (slices, integers) returns a view sharing memory, while advanced indexing (integer or boolean arrays) returns a copy.
Basic indexing:
Uses integers and slices like arr[1:4, ::2]; multi-dimensional via a comma-separated tuple.
Returns a view: modifying the result modifies the original.
Advanced (fancy) indexing:
Integer arrays: arr[[0, 2, 4]] selects arbitrary elements in any order.
Boolean masks: arr[arr > 0] selects elements where the mask is True.
Always returns a copy, so assignment back (arr[mask] = 0) still works but the returned array is independent.
Why the view vs copy distinction matters: Views avoid copying data (fast, memory-efficient) but mutations propagate; use .copy() when you need isolation.
Q51.Why must you use &, |, and ~ instead of the Python and/or/not keywords when combining boolean array conditions in NumPy?
&, |, and ~ instead of the Python and/or/not keywords when combining boolean array conditions in NumPy?Because Python's and, or, and not operate on a single truth value, they can't be applied element-wise to arrays and raise an ambiguity error; the bitwise operators &, |, ~ are overloaded by NumPy to combine boolean arrays element by element.
Why the keywords fail:
and/or call __bool__ on the whole array, which raises ValueError: The truth value of an array ... is ambiguous.
They cannot return an array, only a single object.
What the bitwise operators do: &, |, ~ map to np.logical_and/or/not element-wise, producing a boolean array.
Watch operator precedence: Bitwise operators bind tighter than comparisons, so parenthesize each condition: (a > 0) & (a < 5).
Q52.What is the difference between indexing an array with arr[i, j] versus arr[i][j] in NumPy?
arr[i, j] versus arr[i][j] in NumPy?arr[i, j] indexes with a single tuple in one operation, while arr[i][j] is two sequential operations: index by i, then index the result by j. For basic access they give the same value, but they differ in efficiency and in behavior with advanced indexing.
arr[i, j] is one indexing call: NumPy sees the tuple (i, j) and resolves both dimensions together: single, efficient operation.
arr[i][j] is two calls: arr[i] builds an intermediate array, which is then indexed by j; slower and allocates a temporary.
They diverge with fancy indexing: With integer arrays, arr[i, j] pairs elements of i and j (broadcasting together), whereas arr[i][j] applies them in stages and can yield a different shape or result.
Rule of thumb: Prefer the comma form arr[i, j] for multi-dimensional access.
Q53.Explain the concept of a NumPy Universal Function (ufunc), with examples, and discuss their significance.
ufunc), with examples, and discuss their significance.A universal function (ufunc) is a function that operates element-wise on ndarrays, implemented in compiled C with support for broadcasting, type casting, and reductions. They are the core mechanism behind NumPy's fast vectorized math.
What makes it a ufunc:
Applies a scalar operation across every element without a Python loop.
Supports broadcasting so arrays of different but compatible shapes combine.
Examples:
Unary: np.sqrt, np.exp, np.sin, np.abs.
Binary: np.add, np.multiply, np.maximum (operators like + dispatch to these).
Extra methods on ufuncs: .reduce (e.g. np.add.reduce sums), .accumulate (cumulative), and .outer (all pairs).
Significance: They push looping into optimized C, giving concise syntax and large speedups over pure Python.
Q54.Explain np.vectorize and clarify whether it provides a performance speedup or is primarily for convenience.
np.vectorize and clarify whether it provides a performance speedup or is primarily for convenience.np.vectorize wraps a scalar Python function so it can be called on arrays with broadcasting semantics. It is a convenience/readability tool, not a performance tool: internally it still loops in Python, so it is essentially as slow as a manual loop.
What it provides:
Broadcasting and clean array-in/array-out syntax for a function written for scalars.
Handles output typing and shape so you don't write the loop yourself.
What it does NOT provide: No C-level speedup: the docs explicitly state it is "essentially a for loop" for convenience.
When to use it:
When no native vectorized/ufunc expression exists and readability matters more than speed.
Prefer true ufuncs, boolean masks, or np.where when a vectorized form is available.
Q55.Why are vectorized operations and ufuncs preferred over explicit Python loops for numerical computations, and what are the performance advantages?
Vectorized operations and ufuncs push the per-element work into optimized, compiled C code operating on contiguous memory, avoiding Python's per-iteration interpreter overhead. The result is code that is both faster (often 10x to 100x) and more concise than an explicit loop.
Why loops are slow: Each iteration incurs interpreter overhead: bytecode dispatch, boxing/unboxing of Python objects, and dynamic type checks.
Why vectorization is fast:
The loop runs once in C over a typed, contiguous buffer, so there is no per-element Python overhead.
Better cache locality and use of CPU SIMD instructions; some ufuncs are multithreaded or delegate to BLAS.
Secondary benefits: Less memory churn (no millions of Python int/float objects) and clearer, more declarative code.
Caveat: Vectorization can create large temporary arrays; watch memory and consider in-place operations (out=) for very large data.
Q56.How are element-wise operations performed efficiently in NumPy, and what are Universal Functions (ufuncs)?
NumPy, and what are Universal Functions (ufuncs)?NumPy performs element-wise operations efficiently by pushing the loop down into precompiled C code that runs over contiguous memory, avoiding the Python interpreter per element. The functions that do this are called ufuncs (universal functions): vectorized functions that operate element-by-element on arrays with broadcasting, type casting, and optional output support.
What a ufunc is:
A function that applies a scalar operation to each element of an array (or arrays) in a fast C loop, e.g. np.add, np.sqrt, np.exp.
Operators like +, *, ** are just syntactic sugar for ufuncs.
Why it is efficient:
The iteration happens in C, not Python, so there is no per-element interpreter overhead.
Homogeneous, contiguous data enables CPU cache efficiency and sometimes SIMD vectorization.
Built-in features of ufuncs:
Broadcasting: mismatched but compatible shapes align automatically.
Type casting and an optional out= argument to write results in place.
Q57.How is vectorization related to broadcasting in NumPy? Explain what vectorized operations are and why they are faster than Python loops.
NumPy? Explain what vectorized operations are and why they are faster than Python loops.Vectorization means expressing an operation over whole arrays at once so the looping runs in optimized C rather than Python; broadcasting is the rule set that lets vectorized operations work on arrays of different but compatible shapes without copying data. They are complementary: vectorization is the execution model, broadcasting is how shapes are aligned within it.
What vectorized operations are: A single array-level expression (e.g. a + b) that applies to all elements in one C-level pass.
Why they are faster than Python loops:
No per-element interpreter overhead: the loop lives in compiled C.
Contiguous typed memory gives cache locality and enables SIMD; no per-element Python object boxing.
How broadcasting fits in:
It virtually stretches smaller arrays to match larger ones (without materializing copies), so you can vectorize across mismatched shapes.
Example: adding a shape (3,) row to a (4,3) matrix applies it to each row.
Q58.Explain the difference between element-wise multiplication and matrix multiplication in NumPy.
NumPy.Element-wise multiplication (*) multiplies corresponding entries and returns an array of the same shape; matrix multiplication (@ or np.matmul) computes dot products across rows and columns following linear-algebra rules. They are different operations that only coincide for special cases.
Element-wise (*, np.multiply): Requires equal or broadcastable shapes; result[i,j] = a[i,j] * b[i,j].
Matrix multiplication (@, np.matmul, np.dot):
Inner dimensions must match: (m,n) @ (n,p) -> (m,p).
Each output entry is a sum of products (dot product), not a single product.
Common pitfall: On a np.matrix object * means matrix multiply, but on the standard ndarray it is element-wise. Prefer ndarray with explicit @.
Q59.How can you leverage NumPy's argsort() function, and in what scenarios is it particularly useful?
argsort() function, and in what scenarios is it particularly useful?np.argsort() returns the indices that would sort an array, rather than the sorted values themselves: it gives you the permutation to reorder data indirectly.
Returns indices, not values: arr[np.argsort(arr)] reproduces the sorted array; the indices tell you where each sorted element came from.
Indirect / parallel sorting: Sort one array and apply the same ordering to related arrays (e.g. reorder labels by score).
Top-k selection: Slice the result to get largest/smallest: np.argsort(arr)[-k:] for the k largest indices.
Ranking: Applying argsort twice yields the rank of each element.
Controls: kind (algorithm, e.g. 'stable') and axis for row/column-wise sorting.
Q60.What is the keepdims parameter in NumPy aggregations, and why is it useful?
keepdims parameter in NumPy aggregations, and why is it useful?keepdims=True makes an aggregation retain the reduced axes as size-1 dimensions instead of dropping them, keeping the result's rank equal to the input so it broadcasts cleanly back against the original.
Default drops the axis: a.sum(axis=1) on a (3,4) array gives shape (3,); with keepdims=True it gives (3,1).
Why it matters: broadcasting: The (3,1) result lines up with the (3,4) original, so operations like normalization work without manual reshaping.
Common uses: Row/column means for centering, softmax denominators, per-axis normalization.
Q61.What is broadcasting in NumPy? Explain the broadcasting rules and how it facilitates operations between arrays of different shapes without copying data.
Broadcasting is NumPy's set of rules for performing element-wise operations on arrays of different shapes by virtually stretching the smaller array across the larger one, without ever copying the data.
The rules (compared right to left):
Align shapes by their trailing dimensions.
Two dimensions are compatible if they are equal, or one of them is 1.
A size-1 (or missing) dimension is stretched to match the other; otherwise it's an error.
No data copy: The stretched array is not materialized; NumPy uses a stride of 0 along the broadcast axis to reread the same values, saving memory.
Common patterns: Scalar with array; a (3,1) column with a (1,4) row producing a (3,4) grid; subtracting a per-column mean of shape (4,) from a (100,4) matrix.
Q62.How do ufuncs integrate seamlessly with NumPy's broadcasting rules?
Ufuncs are element-wise functions that call broadcasting internally: before applying the operation they align the shapes of their inputs using the same broadcasting rules, so a scalar, a row, and a matrix can all combine without explicit loops.
Ufuncs operate element by element: Functions like np.add, np.multiply, np.exp apply the same scalar operation across every aligned element.
They broadcast inputs first:
Shapes are compared trailing-dimension first; each dimension must be equal or one of them must be 1.
Size-1 and missing dimensions are virtually stretched (via stride 0) to the common shape, no data copied.
Result shape is the broadcasted shape: The ufunc allocates an output of the common shape and fills it element-wise.
Extra ufunc controls: The out= argument reuses a buffer to avoid allocation; where= applies conditionally.
Q63.How do you add a new axis to a NumPy array using np.newaxis or np.expand_dims, and why is this useful for broadcasting?
np.newaxis or np.expand_dims, and why is this useful for broadcasting?Both insert a new length-1 axis, raising the array's dimensionality so it lines up for broadcasting: np.newaxis is an indexing trick, and np.expand_dims is the function form that takes an explicit axis position.
np.newaxis inside indexing: It is an alias for None; place it where you want the new axis: a[:, np.newaxis].
np.expand_dims(a, axis): Explicit and readable: np.expand_dims(a, 1) is the same as a[:, np.newaxis].
Why it helps broadcasting: Turning a (3,) vector into (3,1) lets it broadcast against a (1,3) row to form a (3,3) outer combination.
Both return views: No data is copied; only the shape and strides metadata change.
Q64.What is the difference between a view and a copy in NumPy? How can you tell if an operation returns a view or a copy, and why is this distinction important for aliasing bugs?
A view shares the same underlying data buffer as the original array (only its metadata differs), while a copy owns a separate buffer. This matters because writing to a view also mutates the source, causing aliasing bugs that are hard to trace.
Views (no data copied):
Basic slicing (a[1:4]), reshape, .T, and np.newaxis typically return views.
Mutating a view propagates to the parent array.
Copies (independent data): Fancy indexing (integer or boolean arrays) and explicit .copy() return copies.
How to tell them apart: Check arr.base: it is None for a copy/owner and points to the source for a view.
Why it matters: An in-place edit through a view silently changes data elsewhere; call .copy() when you need isolation.
Q65.Explain the difference between a shallow copy and a deep copy in NumPy.
A shallow copy shares the underlying data buffer with the original (a view), so edits to the elements are seen by both; a deep copy duplicates the data into an independent buffer, so the two arrays no longer affect each other.
Shallow copy (view):
Created by arr.view(), basic slicing, or copy.copy(arr).
New array object and metadata, but .base points to the shared data.
Deep copy (independent):
Created by arr.copy() or copy.deepcopy(arr).
Owns its buffer; .base is None and mutations stay local.
Object-dtype nuance: For dtype=object arrays, .copy() copies the references, not the referenced Python objects; use copy.deepcopy for full independence.
Q66.What are some common pitfalls or gotchas when working with NumPy arrays, especially concerning views vs. copies and integer overflow?
The two classic traps are silently mutating shared data through a view and silently wrapping around fixed-width integers: both produce wrong results with no error.
Views vs. copies:
Basic slicing (e.g. a[1:5]) returns a view sharing memory, so writing to it mutates the original.
Fancy indexing (integer/boolean arrays) and most reshapes that can't be strided return copies.
Check with arr.base (a view's base points at the owner); use .copy() when you need independence.
Integer overflow:
Fixed-width dtypes (int32, int64) wrap silently: np.int8(127)+1 == -128.
Sums/products of large arrays can overflow the accumulator; pass dtype= to widen, or use a floating type.
Other common gotchas:
Broadcasting mismatches that silently expand instead of erroring as expected.
In-place ops with mismatched dtype (e.g. int_arr += 0.5) truncate rather than upcast.
Comparing floats or np.nan with == (nan != nan); use np.isclose/np.isnan.
Q67.How does NumPy handle large datasets efficiently in terms of memory management and performance optimizations?
Q68.How do you save and load NumPy arrays to disk using .npy and .npz files, and how does that differ from text formats like loadtxt/genfromtxt?
.npy and .npz files, and how does that differ from text formats like loadtxt/genfromtxt?Q69.How can you handle and manipulate arrays with missing or infinite values in NumPy, such as np.nan, np.inf, nan-aware functions, and masked arrays?
np.nan, np.inf, nan-aware functions, and masked arrays?Q70.How does NumPy represent missing (np.nan) and infinite (np.inf) values, and what are the implications for operations and aggregations?
np.nan) and infinite (np.inf) values, and what are the implications for operations and aggregations?Q71.What happens when a fixed-width integer array overflows, and why does NumPy wrap around silently?
Q72.Why does NaN != NaN evaluate to True, and what are the implications for comparing or checking floating-point arrays?
NaN != NaN evaluate to True, and what are the implications for comparing or checking floating-point arrays?Q73.Explain the relationship between NumPy arrays and Pandas DataFrames/Series, when you would use one over the other, and how they interoperate.
Q74.How do you compute the dot product of two NumPy arrays, and how does the @ operator differ from element-wise multiplication with *?
@ operator differ from element-wise multiplication with *?Q75.What is the purpose of the np.linalg module in NumPy?
np.linalg module in NumPy?Q76.What are structured arrays in NumPy, and when would you use them?
Q77.Explain the importance of choosing the correct dtype and how type promotion and casting rules work in NumPy.
dtype and how type promotion and casting rules work in NumPy.Q78.What are the ufunc methods like reduce, accumulate, and outer, and what do they do?
ufunc methods like reduce, accumulate, and outer, and what do they do?Q79.Explain the internal rules of broadcasting in NumPy and when it might lead to unexpected results or shape mismatch errors.
Q80.What are NumPy strides and how do they enable efficient array manipulations like reshaping and transposing without copying data?
Q81.Discuss NumPy's memory model, including contiguous memory, C-order (row-major) vs. Fortran-order (column-major), and how strides implement views, reshape, and transpose without copying.
Q82.How can you programmatically check if an array is a view or a copy, for example using .base or __array_interface__?
.base or __array_interface__?Q83.How does NumPy's memory model, such as contiguous memory and homogeneity, contribute to its performance, and what strategies minimize memory copies?
Q84.How does NumPy interoperate with other libraries at zero copy through the array interface or buffer protocol?
Q85.What strategies would you employ to reduce the memory footprint when working with large NumPy arrays while preserving data accuracy?
Q86.How do you handle memory-mapped files in NumPy for large datasets, and what are the benefits?
Q87.What strategies can you use to optimize NumPy code for better performance, including avoiding hidden copies, using in-place operations, and the out= parameter?
out= parameter?Q88.Explain the concept and use of masked arrays (np.ma) in NumPy.
np.ma) in NumPy.