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›NumPy›Quiz

NumPy quiz

Ten questions at a time, drawn from 130. Every answer is explained. Nothing is saved and no account is needed.

Question 1 of 10Score 0

If you have a 2D array 'arr' with shape (3, 4), what happens when you perform 'arr + 10'?

Practice quiz for NumPy. Scores are not saved.

Study first?

Every question comes from a lesson in the NumPy course.

Read the course →

Interview prep

Written questions with full answers.

NumPy interview questions →

All NumPy quiz questions and answers

  1. If you have a 2D array 'arr' with shape (3, 4), what happens when you perform 'arr + 10'?

    • It raises a ValueError because you cannot add a scalar to an array
    • It adds 10 to every element in the array via broadcasting
    • It adds 10 only to the elements in the first row
    • It appends the value 10 to the end of each row

    Answer: It adds 10 to every element in the array via broadcasting. Broadcasting allows scalars to be applied to every element in an array. The other options are incorrect because NumPy supports scalar-array arithmetic, and it applies to the entire structure, not just specific rows or appending.

    From lesson: Introduction to NumPy and ndarray

  2. Which of the following describes the memory behavior of a basic NumPy slice, such as 'sub_arr = arr[0:2]'?

    • It creates a complete, independent copy of the original array segment
    • It creates a view that points to the same underlying data buffer as the original array
    • It raises an error because slices are not allowed on ndarrays
    • It returns a generator object that must be cast to an array

    Answer: It creates a view that points to the same underlying data buffer as the original array. NumPy slices are views, meaning they share the same memory. Option 1 is wrong because it requires .copy(), and options 3 and 4 are incorrect as slicing is a fundamental NumPy feature.

    From lesson: Introduction to NumPy and ndarray

  3. An array is created with 'np.array([1, 2, 3], dtype=float)'. What is the result of attempting to assign the string 'four' to the first element?

    • The array will store the string 'four' in the first position
    • The array will convert 'four' to a float and store it
    • It will raise a ValueError or TypeError
    • The entire array will automatically be converted to an object type

    Answer: It will raise a ValueError or TypeError. ndarrays are homogeneous and strictly typed. Since the array is float, it cannot store non-numeric strings, resulting in an error. Options 1, 2, and 4 are incorrect as they ignore the strict data type constraint.

    From lesson: Introduction to NumPy and ndarray

  4. What is the result of applying 'np.sum(arr, axis=0)' on a (2, 3) matrix?

    • A single sum of all elements in the matrix
    • A 1D array of length 2 containing row sums
    • A 1D array of length 3 containing column sums
    • A (2, 3) matrix with all elements summed

    Answer: A 1D array of length 3 containing column sums. Axis 0 operation collapses rows, resulting in a sum for each column. Option 1 is for sum() without axis, option 2 is for axis 1, and option 4 describes a scalar broadcast, not an aggregation.

    From lesson: Introduction to NumPy and ndarray

  5. Why is 'ndarray' generally faster than a Python list for numerical computations?

    • Because ndarrays are written in highly optimized C code and use contiguous memory blocks
    • Because Python lists do not support indexing
    • Because ndarrays automatically use GPU acceleration by default
    • Because ndarrays allow for storing mixed data types

    Answer: Because ndarrays are written in highly optimized C code and use contiguous memory blocks. Contiguous memory and C-level optimizations make ndarrays fast. Python lists are dynamic collections of pointers. Options 2, 3, and 4 are factually incorrect or irrelevant to the performance difference.

    From lesson: Introduction to NumPy and ndarray

  6. If you need an array with 10 elements starting at 0 and ending exactly at 1, which function is the most appropriate?

    • np.arange(0, 1, 10)
    • np.linspace(0, 1, 10)
    • np.ones((10))
    • np.zeros(10)

    Answer: np.linspace(0, 1, 10). np.linspace(0, 1, 10) generates 10 evenly spaced values including both 0 and 1. np.arange would fail to include 1 and requires a step size, while ones and zeros do not produce the requested range.

    From lesson: Creating Arrays — array, zeros, ones, arange, linspace

  7. What is the result of np.zeros(3, dtype=int)?

    • An array of three zeros of type integer
    • An error, because the shape must be a tuple
    • A 3x3 matrix of zeros
    • An array of three ones

    Answer: An array of three zeros of type integer. For a 1D array, a single integer is accepted for the shape parameter. By setting dtype=int, the array stores integers instead of the default floats. The other options are incorrect because the shape is valid and the values remain zero.

    From lesson: Creating Arrays — array, zeros, ones, arange, linspace

  8. Which of the following expressions creates a 2x3 matrix of ones?

    • np.ones(2, 3)
    • np.ones((2, 3))
    • np.ones(6)
    • np.array([1, 1, 1], [1, 1, 1])

    Answer: np.ones((2, 3)). np.ones requires the shape to be passed as a single tuple argument. np.ones(2, 3) is wrong because it interprets the second argument as the 'dtype'. np.array syntax in option 4 is invalid.

    From lesson: Creating Arrays — array, zeros, ones, arange, linspace

  9. What does np.arange(2, 10, 3) return?

    • array([2, 5, 8])
    • array([2, 5, 8, 11])
    • array([2, 4, 6, 8])
    • array([3, 6, 9])

    Answer: array([2, 5, 8]). np.arange(start, stop, step) starts at 2, increments by 3 until it reaches 10 (exclusive). 2, 5, 8 are generated; the next would be 11, which exceeds the limit. The other options either use the wrong step or miscalculate the stop boundary.

    From lesson: Creating Arrays — array, zeros, ones, arange, linspace

  10. Why might you choose np.linspace(0, 10, 5) over np.arange(0, 10, 2)?

    • To ensure the final value 10 is always included
    • To ensure the step size is always an integer
    • Because linspace is faster for large arrays
    • To explicitly define the step size rather than the count

    Answer: To ensure the final value 10 is always included. linspace guarantees the inclusion of the endpoint (10) and determines the number of samples, whereas arange relies on a step size that may not reach the endpoint exactly. The other options do not accurately describe the functional difference.

    From lesson: Creating Arrays — array, zeros, ones, arange, linspace

  11. An array is created with the shape (3, 4). What will calling .size return?

    • 3
    • 4
    • 7
    • 12

    Answer: 12. .size returns the total number of elements, which is the product of the shape dimensions (3 * 4 = 12). The other options represent individual dimensions, their sum, or are incorrect calculations.

    From lesson: Array Attributes — shape, dtype, ndim, size

  12. If an array has .ndim equal to 2, which of the following is true regarding its .shape?

    • The shape tuple will contain exactly two integers.
    • The shape tuple will contain only one integer.
    • The shape tuple will contain three integers.
    • The shape tuple will be empty.

    Answer: The shape tuple will contain exactly two integers.. .ndim represents the number of dimensions. A 2D array must have a shape tuple with exactly two values (rows, columns). A 1D array has 1 value, and 3D has 3.

    From lesson: Array Attributes — shape, dtype, ndim, size

  13. What is the primary purpose of the .dtype attribute?

    • To define the number of dimensions in the array.
    • To describe the type of elements stored in the array.
    • To indicate the memory address of the array.
    • To identify the layout order (row-major vs column-major).

    Answer: To describe the type of elements stored in the array.. .dtype specifies the data type of the elements (e.g., int32, float64). The other attributes deal with structure, memory, or storage order, not the element type.

    From lesson: Array Attributes — shape, dtype, ndim, size

  14. Consider an array 'arr' defined as np.array([10, 20, 30]). What is the result of arr.ndim?

    • 0
    • 1
    • 3
    • 30

    Answer: 1. A 1D array (vector) has exactly 1 axis, so .ndim is 1. .size would be 3, and 0 would imply a scalar, while 30 is the content of the last element.

    From lesson: Array Attributes — shape, dtype, ndim, size

  15. How does .shape change when you transpose a (2, 5) array?

    • It becomes (2, 5).
    • It becomes (5, 2).
    • It becomes (10, 1).
    • It becomes (1, 10).

    Answer: It becomes (5, 2).. Transposing an array swaps its dimensions. A shape of (rows, columns) becomes (columns, rows), thus (2, 5) becomes (5, 2). The other options ignore the logic of transposition.

    From lesson: Array Attributes — shape, dtype, ndim, size

  16. If you perform 'view = arr[0:2]', and then modify 'view[0] = 99', what happens to the original 'arr'?

    • The original array remains unchanged because slicing creates a copy.
    • The original array is modified because slicing creates a view.
    • NumPy raises an error because you cannot modify a slice.
    • Only the first two elements of the array are duplicated and modified.

    Answer: The original array is modified because slicing creates a view.. Slicing in NumPy creates a view, meaning both variables point to the same memory. Thus, modification affects the original. Option 0 is wrong because slicing doesn't copy; Option 2 is wrong as views are mutable; Option 3 is wrong as no duplication occurs.

    From lesson: Array Indexing and Slicing

  17. What is the shape of the result if you extract a single row from a 2D array using 'arr[0, :]' compared to 'arr[0:1, :]'?

    • Both return a 1D array.
    • Both return a 2D array.
    • The first returns a 1D array, the second returns a 2D array.
    • The first returns a 2D array, the second returns a 1D array.

    Answer: The first returns a 1D array, the second returns a 2D array.. Integer indexing reduces dimensions (dropping the axis), while slicing preserves dimensions. Option 0 is wrong because slicing maintains rank. Option 1 is wrong because integer indexing reduces rank. Option 3 reverses the correct behavior.

    From lesson: Array Indexing and Slicing

  18. What does the expression 'arr[::-1]' achieve when applied to a NumPy array?

    • It raises an error because a step of -1 is invalid.
    • It sorts the array in descending order.
    • It returns a view of the array with elements in reverse order.
    • It deletes the last element of the array.

    Answer: It returns a view of the array with elements in reverse order.. The step parameter -1 traverses the array backwards. Option 0 is wrong as negative steps are valid. Option 1 is wrong because it only reverses order, not sorts by value. Option 3 is wrong as it does not modify the structure.

    From lesson: Array Indexing and Slicing

  19. Given a 2D array 'arr', what does 'arr[[0, 1], [0, 1]]' return?

    • A 2x2 subarray containing the intersection of the first two rows and columns.
    • The elements at positions (0,0) and (1,1) as a 1D array.
    • The entire first two rows and first two columns.
    • An error because you cannot provide lists as indices.

    Answer: The elements at positions (0,0) and (1,1) as a 1D array.. This is fancy indexing; it maps pairs of indices (0,0) and (1,1). Option 0 is wrong because slicing would be required for a subarray. Option 2 describes the result. Option 3 is wrong as fancy indexing is a standard NumPy feature.

    From lesson: Array Indexing and Slicing

  20. If you have a 3D array of shape (4, 4, 4), what does the slice 'arr[:, :, 0]' return?

    • A 2D array of shape (4, 4).
    • A 3D array of shape (4, 4, 1).
    • A 1D array of length 4.
    • An error because you cannot index the third dimension.

    Answer: A 2D array of shape (4, 4).. Slicing the first two dimensions fully and selecting index 0 in the third dimension reduces the rank to 2. Option 1 is wrong because integer indexing removes the dimension. Option 2 is wrong as the shape is (4,4). Option 3 is wrong as two full dimensions remain.

    From lesson: Array Indexing and Slicing

  21. What is the primary difference in how NumPy handles the bitwise '&' operator compared to the Python 'and' keyword when applied to boolean arrays?

    • The 'and' keyword is faster for large arrays.
    • The '&' operator performs element-wise operations, whereas 'and' evaluates the truth value of the entire array object.
    • The '&' operator only works on integers.
    • The 'and' keyword automatically handles broadcasting between arrays of different shapes.

    Answer: The '&' operator performs element-wise operations, whereas 'and' evaluates the truth value of the entire array object.. The bitwise operator '&' is overloaded in NumPy to perform element-wise AND logic. The Python keyword 'and' is used for boolean logic between single objects and will raise a ValueError when used on non-empty arrays, or evaluate the truthiness of the whole structure, which is not what is intended for masking.

    From lesson: Boolean Masking and Fancy Indexing

  22. If you have an array 'arr' and perform 'result = arr[[0, 2, 4]]', what is the nature of the returned object 'result'?

    • A view of the original array, meaning changes to 'result' affect 'arr'.
    • A copy of the original array, meaning changes to 'result' do not affect 'arr'.
    • A generator that calculates indices on the fly.
    • A pointer to the original memory block.

    Answer: A copy of the original array, meaning changes to 'result' do not affect 'arr'.. Fancy indexing (using a list of indices) always returns a copy of the data. Changes to this result do not propagate back to the original array, unlike slices which return views.

    From lesson: Boolean Masking and Fancy Indexing

  23. Given a 2D array 'data' of shape (5, 5), what does 'data[data > 10] = 0' do?

    • It sets the first row and column elements to 0 if they are greater than 10.
    • It sets all elements in the entire array to 0.
    • It sets all elements in the array that meet the condition to 0, returning a 1D array.
    • It sets all elements in the array that meet the condition to 0, maintaining the original (5, 5) shape.

    Answer: It sets all elements in the array that meet the condition to 0, maintaining the original (5, 5) shape.. Boolean masking with an assignment operation modifies the original array in-place. The mask selects only the elements satisfying the condition; the structural integrity (shape) of the array is preserved.

    From lesson: Boolean Masking and Fancy Indexing

  24. If 'arr' is a 1D array, what is the output shape of 'arr[arr > 5]' if 3 elements satisfy the condition?

    • (3,)
    • (5,)
    • (1, 3)
    • The shape is undefined.

    Answer: (3,). Boolean indexing always flattens the result into a 1D array containing only the elements where the mask is True. Here, it will be a 1D array of length 3.

    From lesson: Boolean Masking and Fancy Indexing

  25. To select elements from a 2D array using fancy indexing to get specific coordinates (e.g., (0,0) and (1,2)), what is the correct syntax?

    • arr[[0, 1], [0, 2]]
    • arr[[0, 1], [0, 2], index='fancy']
    • arr[0, 1] and arr[0, 2]
    • arr[0:1, 0:2]

    Answer: arr[[0, 1], [0, 2]]. Passing a list of row indices and a list of column indices allows you to pick out specific coordinates. Option 0 correctly pairs 0 with 0 and 1 with 2. Other options are syntax errors or perform different slicing operations.

    From lesson: Boolean Masking and Fancy Indexing

  26. What is the result of adding two 1D NumPy arrays of different lengths using the '+' operator?

    • The arrays are padded with zeros to match the longest array
    • The operation fails and raises a ValueError
    • The shorter array is truncated to the length of the longer one
    • NumPy performs an element-wise addition based on the first index

    Answer: The operation fails and raises a ValueError. Option 2 is correct because element-wise operations require compatible shapes; NumPy cannot guess how to match them. Option 1 describes a different operation (like a custom pad), not default behavior. Option 3 and 4 are incorrect because NumPy refuses to lose data or assume alignment without explicit broadcasting rules.

    From lesson: Element-wise Arithmetic

  27. Given a = np.array([1, 2, 3]) and b = 2, what is the result of a * b?

    • 2
    • [1, 2, 3, 1, 2, 3]
    • [2, 4, 6]
    • Error: operands have different dimensions

    Answer: [2, 4, 6]. Option 3 is correct because NumPy uses broadcasting to apply the scalar 2 to every element in array 'a'. Option 1 is incorrect as NumPy returns an array, not a scalar. Option 2 is incorrect because '*' on a list would repeat it, but in NumPy, it performs multiplication. Option 4 is incorrect because scalars are broadcastable to any shape.

    From lesson: Element-wise Arithmetic

  28. Which of the following describes the behavior of np.add(a, b) compared to a + b?

    • np.add() is significantly faster and uses less memory
    • They are functionally identical for standard ndarrays
    • np.add() requires explicitly defined output buffers
    • a + b only works for 1D arrays, while np.add() works for multidimensional arrays

    Answer: They are functionally identical for standard ndarrays. Option 2 is correct because the '+' operator is overloaded to call the underlying np.add() ufunc. Option 1 is false as there is no performance difference. Option 3 is incorrect because the 'out' parameter is optional. Option 4 is false as both handle multidimensional arrays identically.

    From lesson: Element-wise Arithmetic

  29. If array A has shape (3, 1) and array B has shape (1, 3), what is the shape of A + B?

    • (3, 3)
    • (1, 1)
    • (3, 1)
    • The operation is invalid

    Answer: (3, 3). Option 1 is correct due to broadcasting rules: dimensions of size 1 are stretched to match the larger dimension. Option 2 and 3 are incorrect shapes. Option 4 is incorrect because the arrays are fully compatible under broadcasting rules.

    From lesson: Element-wise Arithmetic

  30. What is the outcome of performing division on an array containing a zero?

    • An exception is raised and the program crashes
    • The code continues with the zero value replaced by a random number
    • The operation succeeds, resulting in 'inf' or 'nan' values in the result
    • The entire array is set to zero

    Answer: The operation succeeds, resulting in 'inf' or 'nan' values in the result. Option 3 is correct because NumPy handles floating-point division by zero by issuing a warning and producing 'inf' or 'nan'. Option 1 is incorrect because it is a warning, not a hard crash. Option 2 and 4 are incorrect as they do not reflect the standard IEEE 754 handling used by NumPy.

    From lesson: Element-wise Arithmetic

  31. Which of the following array shapes can be added together without an error?

    • (5, 3) and (3, 5)
    • (4, 1) and (1, 4)
    • (2, 2) and (3, 3)
    • (5, 2) and (5,)

    Answer: (4, 1) and (1, 4). Option 1 is correct because trailing dimensions are 4 and 1, and the leading dimensions are 1 and 4, which are compatible via broadcasting. Option 0 fails because 5 != 3. Option 2 fails because neither dimension is 1. Option 3 fails because the trailing dimension 2 is not equal to 5 and neither is 1.

    From lesson: Broadcasting Rules

  32. If you have an array A of shape (10, 5) and array B of shape (5,), what is the result of A + B?

    • A ValueError
    • An array of shape (10, 5)
    • An array of shape (10,)
    • An array of shape (5, 5)

    Answer: An array of shape (10, 5). Correct. NumPy aligns (10, 5) and (5,) by prepending 1s to the smaller array to make it (1, 5). The 1 broadcasts against the 10, resulting in (10, 5). Others are wrong as they violate shape matching rules.

    From lesson: Broadcasting Rules

  33. How does NumPy resolve broadcasting for dimensions that do not match?

    • It stretches the smaller array to match the larger array.
    • It only broadcasts if the dimensions are multiples of each other.
    • It requires that one of the dimensions in the pair is exactly 1.
    • It truncates the larger array to match the smaller one.

    Answer: It requires that one of the dimensions in the pair is exactly 1.. NumPy requires that for every dimension, they are equal or one is 1. Stretching or multiples don't satisfy the strict mathematical requirements of broadcasting.

    From lesson: Broadcasting Rules

  34. Given array X with shape (3, 1) and Y with shape (1, 3), what is the shape of X + Y?

    • (1, 1)
    • (3, 1)
    • (1, 3)
    • (3, 3)

    Answer: (3, 3). The trailing dimensions are 1 and 3 (compatible), and the leading dimensions are 3 and 1 (compatible). The resulting shape takes the maximum of each dimension: (max(3,1), max(1,3)) which is (3, 3).

    From lesson: Broadcasting Rules

  35. What is the purpose of using np.newaxis in a broadcasting operation?

    • To flatten a multi-dimensional array into 1D.
    • To increase the rank of an array by inserting a new axis of size 1.
    • To delete a dimension that is causing a broadcasting error.
    • To change the data type of the array elements.

    Answer: To increase the rank of an array by inserting a new axis of size 1.. np.newaxis increases the dimension count, allowing you to explicitly position the '1' in the shape, which is essential for forcing broadcasting in specific alignments. It does not flatten, delete, or change data types.

    From lesson: Broadcasting Rules

  36. What is the primary advantage of using a NumPy universal function (ufunc) over a standard Python for-loop when performing element-wise operations?

    • Ufuncs automatically generate visualization plots
    • Ufuncs perform operations in pre-compiled C loops, avoiding Python overhead
    • Ufuncs always consume less total system RAM
    • Ufuncs support higher-order functions like recursion

    Answer: Ufuncs perform operations in pre-compiled C loops, avoiding Python overhead. Ufuncs are implemented in C, allowing them to execute operations in bulk at near-machine speeds. Option 0 is false as ufuncs don't plot, 2 is false as intermediate arrays may increase memory usage, and 3 is false as they are not inherently recursive.

    From lesson: Universal Functions (ufuncs)

  37. When calling a ufunc with the 'out' argument, what happens to the output array?

    • A new array is created and the result is copied into it
    • The input array is modified in place to save memory
    • The result is stored directly in the provided array, overwriting its previous contents
    • The operation is aborted because 'out' is reserved for file exports

    Answer: The result is stored directly in the provided array, overwriting its previous contents. The 'out' parameter specifies an existing array to hold the calculation result. Option 0 is wrong because no new array is allocated. Option 1 is wrong because it stores it in 'out', not the input. Option 3 is a misunderstanding of the parameter's purpose.

    From lesson: Universal Functions (ufuncs)

  38. What happens when you apply a ufunc to two arrays with different shapes?

    • NumPy raises a ShapeMismatchError
    • NumPy broadcasts the arrays to a compatible shape if possible
    • NumPy ignores the smaller array and only processes the larger one
    • NumPy automatically flattens both arrays into 1D vectors

    Answer: NumPy broadcasts the arrays to a compatible shape if possible. Broadcasting is a core feature of ufuncs that allows them to handle inputs of different shapes if they are dimensionally compatible. Options 0, 2, and 3 are incorrect as they do not describe the standard NumPy broadcasting behavior.

    From lesson: Universal Functions (ufuncs)

  39. Which of the following describes the 'where' parameter in a ufunc?

    • It defines the memory address where the result is stored
    • It accepts a boolean array to specify which elements should be processed
    • It is used to handle file paths for array storage
    • It specifies which axis the operation should be performed along

    Answer: It accepts a boolean array to specify which elements should be processed. The 'where' parameter acts as a mask, limiting the operation to specific indices. Option 0 confuses this with memory addresses, 2 is for I/O, and 3 describes the 'axis' parameter.

    From lesson: Universal Functions (ufuncs)

  40. If you want to perform a cumulative sum (not element-wise) across an array, why would you use 'ufunc.accumulate' instead of a standard ufunc?

    • Because standard ufuncs cannot be used for reduction
    • Because 'accumulate' is faster for 3D arrays only
    • Because 'accumulate' calculates results based on previous values, which is not an element-wise operation
    • Because 'accumulate' is the only way to convert an array to a list

    Answer: Because 'accumulate' calculates results based on previous values, which is not an element-wise operation. Standard ufuncs apply operations to independent elements, whereas 'accumulate' tracks state, making it a reduction-related operation. Option 0 is false, 1 is arbitrary, and 3 is incorrect as this isn't the purpose of accumulation.

    From lesson: Universal Functions (ufuncs)

  41. Given an array 'arr' of shape (3, 4), what happens if you call 'arr.mean(axis=0)'?

    • It returns a scalar representing the mean of all 12 elements.
    • It returns an array of shape (4,), representing the mean of each column.
    • It returns an array of shape (3,), representing the mean of each row.
    • It raises a ValueError because the array is not square.

    Answer: It returns an array of shape (4,), representing the mean of each column.. Axis=0 operates down the rows, collapsing them into a single summary per column. Option 0 describes the default behavior with no axis; option 2 describes axis=1; option 3 is incorrect as aggregation functions handle non-square arrays perfectly.

    From lesson: Aggregation — sum, mean, min, max, std

  42. If you need to find the standard deviation of a dataset representing a sample (not a population), which parameter is required?

    • axis=1
    • keepdims=True
    • ddof=1
    • dtype=np.float64

    Answer: ddof=1. ddof (Delta Degrees of Freedom) must be set to 1 to divide by N-1 instead of N. Axis controls direction, keepdims preserves dimensions, and dtype changes the precision of calculation, none of which affect the population/sample correction.

    From lesson: Aggregation — sum, mean, min, max, std

  43. Which function is best to use when you want to compute the sum of an array while ignoring any missing (NaN) values?

    • np.sum(arr, skipna=True)
    • np.nansum(arr)
    • np.sum(arr, nan=0)
    • np.sum(arr, ignore_nan=True)

    Answer: np.nansum(arr). np.nansum is the standard NumPy function designed to treat NaNs as zero. The other options are incorrect function names or parameters that do not exist in the NumPy library.

    From lesson: Aggregation — sum, mean, min, max, std

  44. When computing 'arr.max(axis=1, keepdims=True)' on a (5, 3) array, what is the resulting shape?

    • (5,)
    • (3,)
    • (5, 1)
    • (1, 3)

    Answer: (5, 1). keepdims=True prevents the reduced axis from disappearing, maintaining it as a size of 1. Since we collapsed axis 1 (the columns), the result becomes (5, 1). Option 0 is the result without keepdims; others are incorrect shapes.

    From lesson: Aggregation — sum, mean, min, max, std

  45. What is the result of calling 'min()' on a boolean array in NumPy?

    • The minimum value of the underlying integers (0 for False, 1 for True).
    • An error, as min() cannot be called on boolean types.
    • The count of False values.
    • The boolean value True if all elements are True.

    Answer: The minimum value of the underlying integers (0 for False, 1 for True).. NumPy treats True as 1 and False as 0. Therefore, the minimum of a boolean array is 0 if at least one False exists, and 1 if all elements are True. The other options describe incorrect or non-existent behaviors.

    From lesson: Aggregation — sum, mean, min, max, std

  46. What is the correct way to filter an array 'arr' for elements that are both greater than 5 and less than 10?

    • arr > 5 and arr < 10
    • arr[arr > 5 & arr < 10]
    • arr[(arr > 5) & (arr < 10)]
    • arr[np.and(arr > 5, arr < 10)]

    Answer: arr[(arr > 5) & (arr < 10)]. Bitwise operators require parentheses due to operator precedence, which is why option 3 is correct. Option 1 fails because 'and' is for scalars. Option 2 fails due to precedence issues. Option 4 uses a non-existent function name.

    From lesson: Comparison and Logical Operations

  47. Given 'a = np.array([1, 2, 3])' and 'b = np.array([1, 2, 4])', what does 'a == b' return?

    • False
    • True
    • array([True, True, False])
    • array([False, False, True])

    Answer: array([True, True, False]). NumPy comparison operators perform element-wise evaluation. '1==1' is True, '2==2' is True, and '3==4' is False, resulting in an array of booleans. Options 1 and 2 are incorrect because they expect a scalar output, and option 4 is logically wrong.

    From lesson: Comparison and Logical Operations

  48. Which method is the most appropriate to check if any element in a boolean array is True?

    • if arr:
    • arr.any()
    • arr.all()
    • bool(arr)

    Answer: arr.any(). The .any() method returns True if at least one element is True. .all() requires all elements to be True. 'if arr' and 'bool(arr)' raise a ValueError because NumPy does not know if you mean the whole array or specific elements.

    From lesson: Comparison and Logical Operations

  49. What is the result of 'np.where(arr > 0, arr, 0)'?

    • An array with values from 'arr' where they are positive, and 0 otherwise.
    • An array containing only the indices where elements are positive.
    • An array of booleans indicating where 'arr > 0'.
    • A tuple containing the elements themselves.

    Answer: An array with values from 'arr' where they are positive, and 0 otherwise.. np.where(condition, x, y) returns elements from x where the condition is True, and from y otherwise. Option 1 describes this exact behavior. Other options describe different functions like np.argwhere or boolean indexing.

    From lesson: Comparison and Logical Operations

  50. Why does 'a = np.array([np.nan]); a == np.nan' return '[False]'?

    • Because the array contains only one element.
    • Because NumPy treats NaN as an integer.
    • Because NaN is defined as not equal to any value, including itself.
    • Because floating point precision issues occur.

    Answer: Because NaN is defined as not equal to any value, including itself.. Floating-point standards define NaN (Not a Number) as unequal to every value, which is why a equality check fails. Option 1 is irrelevant to the logic, option 2 is false, and while precision matters in some cases, it is not the reason for this specific standard behavior.

    From lesson: Comparison and Logical Operations

  51. You have a 1D array of shape (12,). Which operation effectively transforms it into a 3x4 matrix?

    • arr.reshape(3, 4)
    • arr.transpose(3, 4)
    • arr.resize(3, 4)
    • arr.swapaxes(3, 4)

    Answer: arr.reshape(3, 4). reshape(3, 4) creates a new view with the requested dimensions. Transpose only swaps existing axes, resize is for in-place modification without returning a new view, and swapaxes expects axis indices, not new dimensions.

    From lesson: Reshaping and Transposing

  52. What is the result of using -1 in a reshape command: arr.reshape(-1, 2)?

    • It throws an error because -1 is an invalid dimension.
    • It sets the first dimension to 1 by default.
    • It automatically calculates the first dimension based on the total array size and the specified second dimension.
    • It removes the first dimension entirely.

    Answer: It automatically calculates the first dimension based on the total array size and the specified second dimension.. -1 tells NumPy to infer the size of that dimension based on the remaining elements. The other options are incorrect as NumPy is designed to handle this inference for convenience, and -1 is a valid placeholder, not an error.

    From lesson: Reshaping and Transposing

  53. If 'a' is a 3D array of shape (2, 3, 4), what is the shape of 'a.T'?

    • (4, 3, 2)
    • (2, 3, 4)
    • (3, 4, 2)
    • (4, 2, 3)

    Answer: (4, 3, 2). The .T attribute reverses the order of dimensions for an array. For (2, 3, 4), reversing yields (4, 3, 2). The other options represent incorrect permutations.

    From lesson: Reshaping and Transposing

  54. Which method is specifically used to swap two axes of an array?

    • reshape()
    • flatten()
    • swapaxes()
    • ravel()

    Answer: swapaxes(). swapaxes() is designed to interchange two specified axes. reshape and ravel change the shape/dimensionality, while flatten returns a copy of the array collapsed into one dimension.

    From lesson: Reshaping and Transposing

  55. How does ravel() differ from flatten()?

    • ravel() returns a copy, while flatten() returns a view.
    • ravel() returns a view whenever possible, while flatten() always returns a copy.
    • ravel() only works on 1D arrays, while flatten() works on nD arrays.
    • There is no difference; they are aliases for the same function.

    Answer: ravel() returns a view whenever possible, while flatten() always returns a copy.. ravel() returns a view of the original array if possible, making it more memory-efficient. flatten() guarantees a new copy. The other statements incorrectly describe their return types or dimensionality constraints.

    From lesson: Reshaping and Transposing

  56. You have two 1D arrays of shape (3,). Which function call will result in an array of shape (2, 3)?

    • np.vstack([a, b])
    • np.hstack([a, b])
    • np.concatenate([a, b])
    • np.stack([a, b], axis=1)

    Answer: np.vstack([a, b]). vstack stacks rows vertically, turning two (3,) arrays into a (2, 3) matrix. hstack and concatenate on 1D arrays would result in shape (6,). stack with axis=1 would produce (3, 2).

    From lesson: Stacking — vstack, hstack, concatenate

  57. What is the primary difference between np.concatenate and np.stack?

    • concatenate can only join two arrays, while stack can join many
    • stack adds a new dimension to the output, whereas concatenate joins along an existing axis
    • concatenate only works on 2D arrays, stack works on any dimension
    • stack is faster than concatenate for large datasets

    Answer: stack adds a new dimension to the output, whereas concatenate joins along an existing axis. stack creates a new axis (e.g., stacking two 2D arrays creates a 3D array), while concatenate joins along an existing axis without changing the rank of the arrays.

    From lesson: Stacking — vstack, hstack, concatenate

  58. Given two arrays 'a' and 'b' of shape (4, 4), what is the shape of the result of np.hstack([a, b])?

    • (8, 4)
    • (4, 8)
    • (2, 4, 4)
    • (4, 4, 2)

    Answer: (4, 8). hstack stacks arrays horizontally (along the second axis), doubling the number of columns. Thus, (4, 4) + (4, 4) becomes (4, 8). (8, 4) is vstack, and the others represent different stacking behaviors.

    From lesson: Stacking — vstack, hstack, concatenate

  59. If you have two arrays of shape (3, 2) and (3, 4), which operation is valid?

    • np.vstack([a, b])
    • np.hstack([a, b])
    • np.concatenate([a, b], axis=1)
    • None of these are valid

    Answer: np.hstack([a, b]). hstack works because the number of rows (dimension 0) matches (3 and 3). vstack requires matching column counts (2 and 4), and concatenate requires matching dimensions along the chosen axis, which is not true here for axis=1.

    From lesson: Stacking — vstack, hstack, concatenate

  60. How do you join a list of ten (2, 2) arrays into a single (10, 2, 2) array?

    • np.concatenate(list_of_arrays, axis=0)
    • np.vstack(list_of_arrays)
    • np.stack(list_of_arrays, axis=0)
    • np.hstack(list_of_arrays)

    Answer: np.stack(list_of_arrays, axis=0). np.stack adds a new dimension at the specified axis. By default (axis=0), it places the ten arrays along the first dimension to create a (10, 2, 2) structure. The others would either attempt to join along existing axes or flatten the result.

    From lesson: Stacking — vstack, hstack, concatenate

  61. If you have a 1D array of length 10 and call np.array_split(arr, 3), what will be the shape of the sub-arrays?

    • Three arrays of length 3, 3, and 4
    • Three arrays of length 4, 3, and 3
    • An error because 10 is not divisible by 3
    • Two arrays of length 5 and one of length 0

    Answer: Three arrays of length 4, 3, and 3. array_split handles non-divisible lengths by distributing the extra elements to the first sub-arrays. Option 1 is wrong because it puts the extra on the end, and Option 3 is wrong because array_split is designed to handle this case.

    From lesson: Splitting Arrays

  62. What is the primary difference between np.split and np.array_split when a split does not result in equal parts?

    • np.split returns a tuple, np.array_split returns a list
    • np.split raises a ValueError, while np.array_split creates unequal chunks
    • np.split ignores the remainder, while np.array_split raises an error
    • There is no difference in functionality

    Answer: np.split raises a ValueError, while np.array_split creates unequal chunks. np.split strictly requires the array to be divisible by the integer provided, whereas np.array_split allows for unequal segments, preventing the ValueError.

    From lesson: Splitting Arrays

  63. Given a 2D array of shape (4, 4), what happens if you run np.hsplit(arr, 2)?

    • It splits the array into two (4, 2) arrays along columns
    • It splits the array into two (2, 4) arrays along rows
    • It raises an error because 4 cannot be split into 2
    • It flattens the array then splits it

    Answer: It splits the array into two (4, 2) arrays along columns. hsplit performs horizontal splitting, which acts on columns (axis 1). A 4x4 array split into 2 horizontal parts results in two 4x2 arrays. Row splitting is done via vsplit.

    From lesson: Splitting Arrays

  64. You want to split an array at specific indices [2, 5]. Which function is appropriate to use?

    • np.split(arr, 2)
    • np.array_split(arr, [2, 5])
    • np.hsplit(arr, 2)
    • np.vsplit(arr, [2, 5])

    Answer: np.array_split(arr, [2, 5]). Passing a list of integers to split or array_split tells NumPy to slice the array at those specific indices. Other options either use integers (dividing into equal parts) or specific axis-based splitting.

    From lesson: Splitting Arrays

  65. What is the result of splitting an array using an index that is exactly equal to the array length?

    • An empty array at the end
    • An error
    • The original array remains unchanged
    • The split creates a copy of the array

    Answer: An empty array at the end. Splitting at the array length results in the full array being placed in the first segment and an empty array in the second segment, as indices are treated as boundaries.

    From lesson: Splitting Arrays

  66. If you have a 3x3 array 'arr' and you execute 'b = arr.flatten()', what is the result of 'b[0] = 99'?

    • The original 'arr' is updated at index (0,0).
    • The original 'arr' remains unchanged.
    • An error is raised because flatten() returns an immutable object.
    • Only the last row of 'arr' is updated.

    Answer: The original 'arr' remains unchanged.. flatten() returns a copy of the array. Modifying the copy does not affect the original. Option 0 describes a view behavior (like ravel), Option 2 is incorrect because NumPy arrays are mutable, and Option 3 makes no sense.

    From lesson: Flattening and Raveling

  67. Which of the following scenarios describes the primary benefit of using ravel() over flatten()?

    • ravel() always consumes less memory than flatten().
    • ravel() can perform in-place operations that flatten() cannot.
    • ravel() avoids memory copying whenever the array's memory layout allows.
    • ravel() supports more dimensions than flatten().

    Answer: ravel() avoids memory copying whenever the array's memory layout allows.. ravel() is preferred for performance because it returns a view of the original array if the data is contiguous, whereas flatten() always creates a copy. The other options are either false or not the primary benefit.

    From lesson: Flattening and Raveling

  68. Given a 2x2 array created via 'np.arange(4).reshape(2, 2).T', why does calling .ravel() behave differently than on a standard array?

    • It triggers an automatic transpose back to the original order.
    • It produces a copy because the transposed array is not C-contiguous.
    • It forces the array to become 3D.
    • It requires the order='F' parameter to function.

    Answer: It produces a copy because the transposed array is not C-contiguous.. Transposing an array breaks C-contiguity. Since ravel() tries to return a view, but cannot do so on non-contiguous memory, it must create a copy. The other answers do not accurately describe NumPy's memory management.

    From lesson: Flattening and Raveling

  69. How does setting 'order="F"' in the ravel() function change the output for a 2x2 matrix [[1, 2], [3, 4]]?

    • The output becomes [1, 2, 3, 4].
    • The output becomes [1, 3, 2, 4].
    • The operation fails because 'F' is not a valid parameter.
    • It sorts the elements in descending order.

    Answer: The output becomes [1, 3, 2, 4].. 'F' stands for Fortran order, which traverses the array column by column. Reading the columns of [[1, 2], [3, 4]] gives 1, 3, then 2, 4. 'C' order would yield [1, 2, 3, 4].

    From lesson: Flattening and Raveling

  70. You have a 4D array and want to convert it into a 1D array while ensuring you do not consume extra memory. Which approach is most robust?

    • arr.flatten()
    • arr.reshape(arr.size)
    • arr.ravel()
    • np.concatenate(arr)

    Answer: arr.ravel(). ravel() is the specific tool for flattening while prioritizing a view over a copy. flatten() always copies, reshape() may fail on non-contiguous memory without explicit handling, and concatenate is inefficient for dimensionality reduction.

    From lesson: Flattening and Raveling

  71. Given an array 'a = np.array([1, 10, 100])', what is the result of np.log10(a)?

    • [0, 1, 2]
    • [2.71, 23.02, 230.25]
    • [1, 10, 100]
    • [0.69, 2.30, 4.60]

    Answer: [0, 1, 2]. np.log10 computes the base-10 logarithm. log10(1)=0, log10(10)=1, and log10(100)=2. The other options represent natural logs, the original array, or incorrect scalar applications.

    From lesson: Mathematical Functions — sqrt, exp, log

  72. What is the primary difference between np.exp(x) and np.power(e, x) where e is np.e?

    • They return different data types
    • np.exp is specifically optimized for efficiency and precision with base e
    • np.power(e, x) only works for integers
    • np.exp(x) only works for scalar inputs

    Answer: np.exp is specifically optimized for efficiency and precision with base e. np.exp is a specialized universal function optimized for floating-point calculations with the mathematical constant e. While np.power(np.e, x) yields the same result, np.exp is faster and handles floating-point errors more gracefully.

    From lesson: Mathematical Functions — sqrt, exp, log

  73. If you need to calculate ln(1 + x) for very small values of x, why is np.log1p(x) preferred over np.log(1 + x)?

    • It is faster by a constant factor
    • It avoids precision loss due to floating-point arithmetic when x is near 0
    • It forces the output to be a float
    • It handles negative values of x automatically

    Answer: It avoids precision loss due to floating-point arithmetic when x is near 0. When x is tiny, 1+x rounded to floating point precision loses the information about x. np.log1p(x) uses a specific Taylor expansion or algorithm to preserve the precision of x, whereas np.log(1+x) may return 0 erroneously.

    From lesson: Mathematical Functions — sqrt, exp, log

  74. What happens when you execute np.sqrt(np.array([-1, 0, 1]))?

    • An error is raised
    • It returns [0, 0, 1]
    • It returns [nan, 0, 1] with a runtime warning
    • It returns [0, 0, 1] after discarding negative values

    Answer: It returns [nan, 0, 1] with a runtime warning. NumPy produces a RuntimeWarning for the square root of a negative number and returns 'nan' (Not a Number) for that specific index. It does not crash the program (error) nor does it silently ignore or change the input value.

    From lesson: Mathematical Functions — sqrt, exp, log

  75. You have an array x. Which operation correctly computes the value of e^(x^2)?

    • np.exp(x**2)
    • np.exp(x)**2
    • np.exp(x * 2)
    • np.power(e, x)**2

    Answer: np.exp(x**2). The expression e^(x^2) requires squaring x first, then passing it to the exponential function. np.exp(x)**2 is equivalent to (e^x)^2 = e^(2x), which is mathematically different.

    From lesson: Mathematical Functions — sqrt, exp, log

  76. If A and B are 2D arrays of shape (3, 2) and (2, 3) respectively, what is the shape of the result of A @ B?

    • (3, 3)
    • (2, 2)
    • (3, 2)
    • Error: shapes do not match

    Answer: (3, 3). Matrix multiplication (A @ B) results in a matrix with rows from A and columns from B. (3, 2) @ (2, 3) yields (3, 3). Options 1 and 2 are dimensionally impossible, and the shapes match perfectly, so no error occurs.

    From lesson: Linear Algebra — dot, matmul, inv, eig

  77. Which function is best suited to find the solution 'x' to the linear system Ax = b in a numerically stable way?

    • np.linalg.inv(A) * b
    • np.linalg.dot(np.linalg.inv(A), b)
    • np.linalg.solve(A, b)
    • A.T / b

    Answer: np.linalg.solve(A, b). np.linalg.solve() uses LU decomposition which is much more numerically stable than computing an explicit inverse. Options 0 and 1 are unstable and prone to floating-point errors, and option 3 uses division which is not defined for matrix-vector systems.

    From lesson: Linear Algebra — dot, matmul, inv, eig

  78. Given a square matrix M, what does the 'v' output of 'w, v = np.linalg.eig(M)' represent?

    • The inverse of M
    • A matrix where each column is an eigenvector of M
    • A diagonal matrix of eigenvalues
    • The transpose of the original matrix

    Answer: A matrix where each column is an eigenvector of M. np.linalg.eig returns eigenvalues in array 'w' and eigenvectors as columns in matrix 'v'. Option 0 is np.linalg.inv, option 2 describes a separate operation, and option 3 is M.T.

    From lesson: Linear Algebra — dot, matmul, inv, eig

  79. What happens if you use the '*' operator between two 2D NumPy arrays of the same shape?

    • Standard matrix multiplication occurs
    • A LinAlgError is raised
    • The dot product of the two matrices is computed
    • Each element of the first matrix is multiplied by the corresponding element of the second

    Answer: Each element of the first matrix is multiplied by the corresponding element of the second. In NumPy, '*' is the element-wise multiplication operator (Hadamard product). It does not perform matrix multiplication (which uses @), nor does it raise an error for identical shapes. It is not equivalent to the dot product.

    From lesson: Linear Algebra — dot, matmul, inv, eig

  80. In the context of np.linalg.eig, how are the eigenvalues and eigenvectors related to the matrix A?

    • A @ v[:, i] == w[i] * v[:, i]
    • A @ v[:, i] == w[i] + v[:, i]
    • A / v[:, i] == w[i]
    • A * v[i, :] == w[i]

    Answer: A @ v[:, i] == w[i] * v[:, i]. The definition of an eigenvalue/eigenvector pair (w, v) is Av = λv. In NumPy, this translates to A @ v[:, i] (the transformed vector) being equal to the scalar eigenvalue times the vector itself. The other options involve incorrect mathematical operations like addition or matrix division.

    From lesson: Linear Algebra — dot, matmul, inv, eig

  81. Given a square matrix A and vector b, which method is the most numerically stable way to solve Ax = b in NumPy?

    • np.linalg.inv(A) @ b
    • np.linalg.solve(A, b)
    • np.linalg.inv(b) @ A
    • A * b

    Answer: np.linalg.solve(A, b). np.linalg.solve uses LU decomposition, which is faster and more stable than computing the inverse. Option 0 is unstable, option 2 is mathematically invalid, and option 3 performs element-wise multiplication.

    From lesson: Solving Linear Systems

  82. What is the expected behavior when calling np.linalg.solve(A, b) if matrix A is singular?

    • It returns a zero vector.
    • It returns a random solution.
    • It raises a LinAlgError.
    • It prints a warning but continues.

    Answer: It raises a LinAlgError.. A singular matrix lacks an inverse, and NumPy is designed to raise a LinAlgError to prevent the propagation of mathematically invalid results. The other options are incorrect as they imply silent failure or incorrect behavior.

    From lesson: Solving Linear Systems

  83. You have an overdetermined system (more equations than variables) and want to minimize the squared error. Which function should you use?

    • np.linalg.solve
    • np.linalg.det
    • np.linalg.lstsq
    • np.linalg.inv

    Answer: np.linalg.lstsq. np.linalg.lstsq performs a least-squares solution, which is the standard approach for overdetermined systems. np.linalg.solve requires a square, full-rank matrix, and the others do not compute solutions for linear systems.

    From lesson: Solving Linear Systems

  84. What does the condition number returned by np.linalg.cond(A) tell you about the system Ax = b?

    • How many solutions exist.
    • The sensitivity of the solution to errors in input data.
    • The exact execution time of the solve function.
    • The number of variables in the system.

    Answer: The sensitivity of the solution to errors in input data.. The condition number measures how much the output value of the function can change for a small change in the input argument. High values indicate the system is poorly conditioned. It does not measure count, time, or complexity.

    From lesson: Solving Linear Systems

  85. If matrix A is (3, 3) and vector b is (3,), what is the shape of the result returned by np.linalg.solve(A, b)?

    • (3, 3)
    • (1, 3)
    • (3,)
    • (3, 1)

    Answer: (3,). NumPy broadcasting and linear algebra functions typically return a 1D array of shape (N,) when solving for a vector b of shape (N,). Option 0 is a matrix, and the others represent incompatible or different array dimensions.

    From lesson: Solving Linear Systems

  86. Given an array 'arr' with shape (3, 4), what is the result of np.var(arr, axis=1)?

    • A single scalar value representing the variance of the whole array.
    • A 1D array of length 3 containing the variance of each row.
    • A 1D array of length 4 containing the variance of each column.
    • A 2D array of shape (3, 4) with variance applied element-wise.

    Answer: A 1D array of length 3 containing the variance of each row.. axis=1 reduces the array across columns, resulting in a value for each row. Scalar is for default axis=None. Length 4 would be axis=0. Variance is not an element-wise mapping.

    From lesson: NumPy for Statistics

  87. If you need to calculate the sample standard deviation (unbiased estimator) of an array, which parameter must be adjusted in np.std()?

    • set axis=1
    • set ddof=1
    • set dtype=float64
    • set keepdims=True

    Answer: set ddof=1. ddof (delta degrees of freedom) defaults to 0 (population). Setting it to 1 changes the divisor to N-1, providing the sample standard deviation. Axis, dtype, and keepdims do not influence the estimator bias.

    From lesson: NumPy for Statistics

  88. What is the primary difference between np.mean() and np.nanmean()?

    • np.nanmean() is faster for large arrays.
    • np.mean() includes NaN in the count, whereas np.nanmean() ignores it.
    • np.mean() only works on integers while np.nanmean() works on floats.
    • There is no difference; they are aliases for the same function.

    Answer: np.mean() includes NaN in the count, whereas np.nanmean() ignores it.. np.mean() treats NaNs as arithmetic poisons, returning NaN for the whole operation. np.nanmean() specifically excludes these values during computation. Speed and data types are not the distinguishing factors.

    From lesson: NumPy for Statistics

  89. You have a dataset where each column represents a variable. How should you pass this to np.cov() to get the correct covariance matrix?

    • Pass it directly, as np.cov() treats columns as variables by default.
    • Pass the transpose of the array using .T.
    • Pass the array flattened using .flatten().
    • Pass it as a list of independent arrays.

    Answer: Pass the transpose of the array using .T.. np.cov() expects rows to be variables. Since the prompt specifies columns are variables, transposing (.T) is necessary. Flattening loses structure, and passing columns directly treats them as observations, which is wrong.

    From lesson: NumPy for Statistics

  90. What does setting keepdims=True do in a statistical reduction function like np.sum() or np.mean()?

    • It preserves the original data array without reducing it.
    • It maintains the original number of dimensions, making the result broadcastable against the input.
    • It forces the output to be a 64-bit float regardless of input type.
    • It ensures that NaN values are kept in the final output.

    Answer: It maintains the original number of dimensions, making the result broadcastable against the input.. keepdims=True ensures the reduced axis remains in the shape as a dimension of size 1, allowing for easy broadcasting back to the original array shape. It does not prevent reduction, nor does it affect type or NaN handling.

    From lesson: NumPy for Statistics

  91. Which approach is considered the best practice for thread-safe random number generation in NumPy?

    • Use np.random.seed() before each parallel call
    • Use the global np.random state functions
    • Instantiate a unique Generator object for each thread
    • Use np.random.default_rng() inside the thread function

    Answer: Instantiate a unique Generator object for each thread. Instantiating a Generator per thread avoids contention and ensures state isolation. Global functions rely on a shared state that is not thread-safe. Re-creating the generator inside a thread function would result in identical sequences if called simultaneously.

    From lesson: Random Number Generation

  92. What is the primary difference between np.random.random() and np.random.standard_normal()?

    • One returns a single float and the other returns an array
    • One follows a uniform distribution and the other follows a normal distribution
    • One is restricted to [0, 1) and the other is restricted to (-1, 1)
    • One requires a seed and the other does not

    Answer: One follows a uniform distribution and the other follows a normal distribution. np.random.random() provides values from a uniform distribution [0.0, 1.0), whereas standard_normal() produces samples from a normal distribution with mean 0 and variance 1. Both can return arrays, and both are influenced by the global state.

    From lesson: Random Number Generation

  93. If you need an array of 1000 integers between 1 and 10 (inclusive), which call is most correct?

    • np.random.randint(1, 11, 1000)
    • np.random.integers(1, 10, 1000)
    • np.random.randint(1, 10, 1000)
    • np.random.default_rng().integers(1, 11, 1000)

    Answer: np.random.default_rng().integers(1, 11, 1000). np.random.default_rng().integers() is the modern API. With low=1 and high=11, it is inclusive of 1 and exclusive of 11, correctly covering 1-10. Option 1 uses legacy methods. Option 2 excludes the value 10. Option 3 is deprecated.

    From lesson: Random Number Generation

  94. How does using a fixed seed with a Generator instance impact array generation?

    • It locks the CPU to a single core for determinism
    • It forces the random sequence to be identical across runs with the same seed
    • It changes the distribution type to a deterministic one
    • It increases the memory usage of the resulting array

    Answer: It forces the random sequence to be identical across runs with the same seed. A fixed seed ensures that the underlying bit generator produces the same sequence of bits, resulting in reproducible output. It does not affect CPU usage, memory, or the type of distribution (e.g., normal vs uniform).

    From lesson: Random Number Generation

  95. Why should you prefer Generator objects over the legacy np.random.* functions?

    • Generators are always faster regardless of the algorithm
    • Generators allow for easier serialization of the internal state
    • Generators remove the need to use shape arguments
    • Legacy functions were removed in the most recent version of NumPy

    Answer: Generators allow for easier serialization of the internal state. Generator objects provide improved statistical properties and allow you to capture/save their state, which is crucial for reproducible simulations. They are not necessarily faster in all cases, they still require shape arguments, and legacy functions remain for backward compatibility.

    From lesson: Random Number Generation

  96. You need to generate a 2x3 matrix of floats sampled from a normal distribution with a mean of 0 and a variance of 4. Which code snippet is correct?

    • np.random.normal(0, 4, size=(2, 3))
    • np.random.normal(0, 2, size=(2, 3))
    • np.random.normal(0, 4, shape=(2, 3))
    • np.random.normal(loc=0, scale=4, size=6)

    Answer: np.random.normal(0, 2, size=(2, 3)). Option 1 is correct because scale is standard deviation, which is the square root of 4 (i.e., 2). Option 0 is wrong because it uses variance instead of standard deviation. Option 2 is wrong because the parameter is 'size', not 'shape'. Option 3 is wrong because it creates a 1D array instead of a 2x3 matrix.

    From lesson: Distributions — normal, uniform, binomial

  97. What is the primary difference between np.random.uniform(0, 10, 5) and np.random.randint(0, 10, 5)?

    • One generates floats, the other integers.
    • One is inclusive of the upper bound, the other is exclusive.
    • They sample from different statistical distributions.
    • The first generates 5 values, the second generates 10.

    Answer: One generates floats, the other integers.. Option 0 is correct: uniform generates continuous floats, while randint generates discrete integers. Option 1 is incorrect because both functions have specific rules for bounds, but that isn't the primary distinction. Option 2 is wrong as both are types of uniform distributions. Option 3 is wrong as the last argument defines sample count for both.

    From lesson: Distributions — normal, uniform, binomial

  98. If you want to simulate flipping a fair coin 10 times, 1000 separate times, which parameters should you pass to np.random.binomial?

    • n=1, p=0.5, size=1000
    • n=10, p=0.5, size=1000
    • n=10, p=0.5, size=10
    • n=1000, p=0.5, size=1

    Answer: n=10, p=0.5, size=1000. Option 1 represents 10 trials per experiment (n=10) performed 1000 times (size=1000). Option 0 is wrong as it only flips the coin once. Option 2 is wrong because the size should be the number of experiments. Option 3 is wrong as it represents a single experiment with 1000 trials.

    From lesson: Distributions — normal, uniform, binomial

  99. How does setting np.random.seed(42) affect subsequent calls to np.random.normal?

    • It forces the mean to be exactly 42.
    • It makes the random stream deterministic and reproducible.
    • It increases the standard deviation by 42.
    • It shifts the distribution range by 42.

    Answer: It makes the random stream deterministic and reproducible.. Option 1 is correct: setting the seed initializes the pseudo-random number generator to a specific state, ensuring the same 'random' numbers are produced every time the code runs. Options 0, 2, and 3 are incorrect because the seed only controls the sequence, not the statistical properties or range of the distribution.

    From lesson: Distributions — normal, uniform, binomial

  100. You generate data using np.random.uniform(low=0.0, high=1.0, size=1000). What will be the approximate result of calling .mean() on this array?

    • 0.0
    • 1.0
    • 0.5
    • It will change every time the code is run.

    Answer: 0.5. Option 2 is correct because the mean of a uniform distribution over [0, 1] is (0+1)/2 = 0.5. With a large sample size of 1000, the sample mean will converge to the population mean. Option 0 and 1 are incorrect as they are outside the expected center. Option 3 is incorrect because while the values change, the Law of Large Numbers ensures the mean remains stable near 0.5.

    From lesson: Distributions — normal, uniform, binomial

  101. When using np.random.default_rng(seed=42), what happens if you call the generator's random method multiple times in the same script?

    • It generates the same number every time because the seed is fixed.
    • It generates a deterministic sequence of numbers based on the initial seed.
    • It resets the seed to 42 automatically after every call.
    • It generates a new random number that is completely independent of previous calls.

    Answer: It generates a deterministic sequence of numbers based on the initial seed.. Option 2 is correct; a Generator instance creates a deterministic sequence from a seed. Option 1 is wrong because the internal state updates. Option 3 is wrong because the state persists. Option 4 is wrong because the sequence is reproducible, not independent.

    From lesson: Setting Seeds for Reproducibility

  102. If you have two separate code blocks using the same seed with np.random.default_rng(), what is the relationship between the numbers they produce?

    • The first number generated in both blocks will be identical.
    • The second number generated in the first block will match the first number of the second block.
    • They will be completely different sequences regardless of the seed.
    • The sequences will only match if the hardware architecture is identical.

    Answer: The first number generated in both blocks will be identical.. Option 1 is correct because each generator starts at the same state defined by the seed. Option 2 is incorrect because the sequences are identical from the start. Option 3 is false as seeds control the sequence. Option 4 is incorrect because NumPy's algorithms are designed for cross-platform consistency.

    From lesson: Setting Seeds for Reproducibility

  103. Why is the legacy np.random.seed() function considered less ideal for complex simulations compared to np.random.Generator?

    • It is slower at generating integers.
    • It consumes more memory during initialization.
    • It relies on a global state that can be inadvertently altered by other code.
    • It cannot generate floating-point numbers.

    Answer: It relies on a global state that can be inadvertently altered by other code.. Option 3 is correct; global state is prone to interference. Option 1 and 4 are false as both systems handle various types, and Option 2 is not the primary reason for the shift to the Generator API.

    From lesson: Setting Seeds for Reproducibility

  104. To ensure your results are reproducible across different runs of a NumPy script, where should you place the seed initialization?

    • Immediately before the specific array that requires randomness.
    • At the very beginning of the main execution block.
    • Inside the function that performs the calculations.
    • It doesn't matter where it is placed.

    Answer: At the very beginning of the main execution block.. Option 2 is correct because placing it early ensures all subsequent operations use the same predictable state. Placing it later (Option 1 and 3) risks accidental state consumption, and Option 4 is false as timing and order are critical.

    From lesson: Setting Seeds for Reproducibility

  105. If you pass an existing Generator instance to a function that requires a seed, what is the best practice for reproducibility?

    • Extract the seed from the generator and pass the integer.
    • Pass the generator instance directly to ensure state continuity.
    • Create a new generator inside the function with the same seed.
    • Ignore the seed and rely on default settings.

    Answer: Pass the generator instance directly to ensure state continuity.. Option 2 is correct as it preserves the state. Option 1 is ineffective because re-seeding resets the state. Option 3 is incorrect as it creates a duplicate sequence. Option 4 would lead to non-reproducible results.

    From lesson: Setting Seeds for Reproducibility

  106. Why does a NumPy array generally provide faster computation than a Python list for numerical tasks?

    • NumPy uses parallel processing by default for all operations.
    • NumPy arrays are stored in contiguous memory blocks, allowing for CPU cache optimization.
    • NumPy uses a dynamically typed engine that optimizes instructions at runtime.
    • NumPy arrays are compressed in memory to reduce the total size.

    Answer: NumPy arrays are stored in contiguous memory blocks, allowing for CPU cache optimization.. NumPy arrays are stored in contiguous memory, which significantly improves cache locality compared to Python lists, which are arrays of pointers. Option 0 is false as parallelization requires specific routines; option 2 is false as NumPy is statically typed; option 3 is false as arrays are typically larger due to strict data types.

    From lesson: NumPy vs Python Lists — Speed Comparison

  107. Which of the following scenarios best demonstrates the 'vectorization' performance advantage of NumPy?

    • Iterating through a list to calculate the square root of each element.
    • Summing a large array using a custom for-loop.
    • Calculating the square root of every element in an array using np.sqrt().
    • Appending elements to an array inside a while-loop.

    Answer: Calculating the square root of every element in an array using np.sqrt().. Vectorization via np.sqrt() pushes the loop into compiled C code, bypassing Python overhead. Options 0 and 1 force Python-level iteration, which is slow. Option 3 is an anti-pattern that causes frequent re-allocations.

    From lesson: NumPy vs Python Lists — Speed Comparison

  108. What is the primary reason for avoiding the use of Python's 'append' equivalent for NumPy arrays during a loop?

    • It consumes too much CPU power to check array bounds.
    • It forces a full re-allocation and memory copy of the array every time an element is added.
    • It loses the memory alignment of the array, causing crashes.
    • It forces the array to change its data type to object, losing performance.

    Answer: It forces a full re-allocation and memory copy of the array every time an element is added.. NumPy arrays are fixed-size; np.append() returns a brand new array after copying all existing data, leading to quadratic time complexity. The other options either describe different behaviors or are not the primary cause of the performance degradation.

    From lesson: NumPy vs Python Lists — Speed Comparison

  109. In a performance-critical application, why should one specify a precise dtype during array initialization?

    • To allow NumPy to optimize memory bandwidth and cache usage.
    • To prevent NumPy from automatically casting to a Python integer type.
    • To automatically trigger vectorized SIMD instructions.
    • To decrease the computational complexity of the mathematical operations.

    Answer: To allow NumPy to optimize memory bandwidth and cache usage.. Smaller, precise dtypes like int8 or float32 occupy less memory, allowing more data to fit into the CPU cache, reducing cache misses. Option 1 is incorrect as NumPy doesn't cast to Python types; option 2 is not the direct purpose of dtypes; option 3 is independent of the math logic.

    From lesson: NumPy vs Python Lists — Speed Comparison

  110. If you are processing a small dataset (e.g., 5 elements), why might using a Python list be faster than a NumPy array?

    • NumPy has high overhead for initializing metadata and checking array structures.
    • Python lists can store multiple data types, which is faster for small numbers.
    • Python lists do not need to check for memory contiguity.
    • NumPy arrays require a global lock that slows down small operations.

    Answer: NumPy has high overhead for initializing metadata and checking array structures.. NumPy incurs 'setup' overhead to handle indices, strides, and memory metadata, which is expensive for tiny arrays but negligible for large ones. Python lists are simple pointers that have very low overhead for small collections. The other options are either incorrect or unrelated to speed.

    From lesson: NumPy vs Python Lists — Speed Comparison

  111. You have a large 2D array and need to perform operations that involve constant row-wise access. Which memory layout strategy will likely minimize cache misses?

    • Store the data in Fortran order
    • Store the data in C order
    • Force the array into a non-contiguous buffer
    • Use an object-dtype array for flexibility

    Answer: Store the data in C order. C-order stores rows contiguously, making row-wise access efficient. Fortran-order stores columns contiguously, making column-wise access efficient. Non-contiguous buffers and object arrays significantly degrade performance due to pointer chasing and memory fragmentation.

    From lesson: Memory Layout — C vs Fortran order

  112. What happens to the memory layout when you perform 'arr.T' on a C-contiguous NumPy array?

    • The data is reordered in memory to become F-contiguous
    • The data is reordered to become C-contiguous
    • A view is returned that is F-contiguous but does not copy data
    • The array remains a view and its memory layout status changes to F-contiguous

    Answer: The array remains a view and its memory layout status changes to F-contiguous. Transposing a C-contiguous array creates a view where the strides are inverted. While the underlying memory buffer remains in the same physical order, the view is now F-contiguous in its access pattern. It does not reorder the physical data.

    From lesson: Memory Layout — C vs Fortran order

  113. If you are passing a NumPy array to a legacy library that strictly requires Fortran-style memory layout, which function ensures your array is formatted correctly without unnecessary work?

    • np.asfortranarray(arr)
    • arr.reshape(-1, order='C')
    • arr.flatten(order='C')
    • np.copy(arr)

    Answer: np.asfortranarray(arr). np.asfortranarray() checks if the array is already Fortran-contiguous. If it is, it returns the original array; otherwise, it creates a copy in the correct layout. Other options either create C-order data or ignore the requirement entirely.

    From lesson: Memory Layout — C vs Fortran order

  114. How does the 'order' parameter affect the output of 'np.ravel()'?

    • It determines if the output is a view or a copy
    • It dictates whether the array is flattened by rows (C) or columns (F)
    • It has no effect because ravel always uses C-order
    • It triggers an immediate memory reallocation regardless of current layout

    Answer: It dictates whether the array is flattened by rows (C) or columns (F). The 'order' parameter in 'ravel()' defines how the elements are traversed: 'C' traverses row by row, while 'F' traverses column by column. The other options are incorrect as they misdescribe the primary function of the order argument.

    From lesson: Memory Layout — C vs Fortran order

  115. A function is running significantly slower than expected. You observe the input array is Fortran-contiguous, but the function iterates over it using 'for i in range(rows): for j in range(cols):'. Why is this slow?

    • The array is too large for the cache
    • The code uses a double loop instead of vectorization
    • The inner loop is iterating over the first index, which jumps across columns in F-order
    • Fortran-order arrays are inherently slower for all loops

    Answer: The inner loop is iterating over the first index, which jumps across columns in F-order. In Fortran order, elements of the first column are adjacent in memory. Iterating by row (the first index) in a nested loop forces the computer to jump to distant memory addresses for every step, causing cache misses. Options 1 and 4 are false generalizations, and while vectorization is preferred, the layout mismatch is the specific performance bottleneck here.

    From lesson: Memory Layout — C vs Fortran order

  116. Which of the following is the most efficient way to compute the square root of every element in a large NumPy array 'arr'?

    • Use a list comprehension: [math.sqrt(x) for x in arr]
    • Use a for loop: for i in range(len(arr)): arr[i] = np.sqrt(arr[i])
    • Use the ufunc: np.sqrt(arr)
    • Use map(np.sqrt, arr)

    Answer: Use the ufunc: np.sqrt(arr). np.sqrt is a vectorized universal function. It executes in compiled C code. List comprehensions and loops involve Python object overhead, making them significantly slower. map() also fails to leverage NumPy's internal optimization.

    From lesson: Vectorization Best Practices

  117. You have a 1D array 'x' and a 2D array 'y'. You want to add 'x' to each row of 'y'. What allows this without explicit loops?

    • Explicitly calling np.repeat to match the shape of y
    • Broadcasting rules that align the dimensions of x and y
    • Flattening y and using np.add
    • NumPy automatically assumes you want to add the mean of x

    Answer: Broadcasting rules that align the dimensions of x and y. Broadcasting allows arrays with different shapes to be combined if the trailing dimensions are compatible. The other options involve either unnecessary memory overhead or logic that doesn't exist in NumPy.

    From lesson: Vectorization Best Practices

  118. What is the primary reason to avoid np.append inside a loop?

    • It generates a syntax error in recent NumPy versions
    • It forces an array to be cast to a Python list
    • It triggers a complete memory reallocation and copy of the array on every call
    • It creates a shallow copy that doesn't update the original data

    Answer: It triggers a complete memory reallocation and copy of the array on every call. NumPy arrays have fixed sizes. np.append creates a new array in memory every time. Doing this in a loop results in quadratic time complexity. Pre-allocation is the standard practice.

    From lesson: Vectorization Best Practices

  119. Consider an array 'data' of shape (100, 100). How do you efficiently sum all elements in every row?

    • data.sum(axis=0)
    • data.sum(axis=1)
    • data.sum(axis=None)
    • A for loop summing each row individually

    Answer: data.sum(axis=1). Axis 1 represents the column index, so summing across axis 1 collapses the columns, resulting in the sum of each row. Axis 0 would sum the columns, None sums everything, and a for loop is inefficient.

    From lesson: Vectorization Best Practices

  120. How should you initialize an array of 1,000,000 zeros to be filled later?

    • arr = np.array([0] * 1000000)
    • arr = np.zeros(1000000)
    • arr = np.array([]) followed by 1,000,000 appends
    • arr = [0 for i in range(1000000)]

    Answer: arr = np.zeros(1000000). np.zeros creates a contiguous block of memory allocated for the correct size instantly. Using a list comprehension or repeated appends creates a list first, then converts it, which is memory-intensive and slow.

    From lesson: Vectorization Best Practices

  121. What happens when you perform arr1 + arr2 where arr1 is shape (3, 1) and arr2 is shape (3,)?

    • A ValueError is raised
    • The arrays are broadcast to shape (3, 3)
    • The arrays are concatenated into a (6,) shape
    • The result is a (3, 1) array

    Answer: The arrays are broadcast to shape (3, 3). NumPy broadcasts trailing dimensions; the (3, 1) and (3,) arrays result in a (3, 3) shape. A ValueError isn't raised because they are compatible, and concatenation or static size results are incorrect.

    From lesson: NumPy Interview Questions

  122. If you create a slice 'sub = arr[0:2, :]', how does modifying 'sub' affect 'arr'?

    • It has no effect on the original array
    • It creates a deep copy of the data
    • It modifies the original array because slices return views
    • It raises an error because slices are read-only

    Answer: It modifies the original array because slices return views. Slicing an array creates a view, meaning both variables point to the same memory. Deep copies are not default behavior; the other options ignore the view mechanics of NumPy.

    From lesson: NumPy Interview Questions

  123. Which function is best for replacing values in an array based on a condition?

    • np.map()
    • np.where()
    • np.replace()
    • np.filter()

    Answer: np.where(). np.where(condition, x, y) is specifically designed to choose elements based on a boolean mask. The other functions are either not part of the NumPy API or intended for different data structures.

    From lesson: NumPy Interview Questions

  124. What is the primary benefit of using vectorized operations in NumPy?

    • It allows for multi-threading without a global interpreter lock
    • It reduces the memory footprint of the array
    • It shifts the computation loop from the Python interpreter to optimized C code
    • It allows arrays to hold multiple data types

    Answer: It shifts the computation loop from the Python interpreter to optimized C code. Vectorization moves loops into compiled C/Fortran code, drastically increasing speed. It does not resolve Python's interpreter lock, nor does it inherently change memory usage or allow mixed types (arrays are homogeneous).

    From lesson: NumPy Interview Questions

  125. Given two arrays 'a' and 'b' of the same shape, what does 'a * b' compute?

    • The dot product of the two arrays
    • The element-wise product
    • The cross product
    • The outer product

    Answer: The element-wise product. In NumPy, the * operator is strictly for element-wise multiplication. Dot product requires @ or .dot(), cross product uses np.cross(), and the outer product uses np.outer().

    From lesson: NumPy Interview Questions

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

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

    Answer: 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.

    From lesson: NumPy Coding Challenges

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

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

    Answer: 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.

    From lesson: NumPy Coding Challenges

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

    • 1
    • 9
    • Error
    • 2

    Answer: 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.

    From lesson: NumPy Coding Challenges

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

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

    Answer: 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.

    From lesson: NumPy Coding Challenges

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

    • (3, 3)
    • (1, 1)
    • (3, 1)
    • An error is raised

    Answer: (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.

    From lesson: NumPy Coding Challenges