2D Array Slicing
Problem Statement
Extract portions of a 2D NumPy array using slicing.
Background
For 2D arrays:
- arr[i, j] - element at row i, column j
- arr[i, :] or arr[i] - entire row i
- arr[:, j] - entire column j
- arr[r1:r2, c1:c2] - submatrix
Your Task
Write a function slice_2d(arr) that returns a dictionary with:
- "top_left": Top-left 2×2 submatrix (as list)
- "bottom_row": Last row (as list)
- "right_col": Rightmost column (as list)
- "center": Center element (as int, for odd dimensions)
Example:
3×3 array [[1,2,3],[4,5,6],[7,8,9]]
{'top_left': [[1, 2], [4, 5]], 'bottom_row': [7, 8, 9], 'right_col': [3, 6, 9], 'center': 5}[:2,:2] for top-left, [-1,:] for bottom, [:,-1] for right, center at (1,1)
Constraints:
- Array will be at least 3×3
- For center, use shape//2 for both dimensions
More from NumPy Foundations
Background Knowledge
NumPy arrays are the foundational data structure for numerical computing in Python, enabling efficient vectorized operations on multi-dimensional data without explicit loops. In 2D arrays (matrices), indexing accesses specific elements using arr[i, j] for row i and column j, while slicing extracts contiguous subarrays using colon notation (:). For example, arr[i, :] selects an entire row, arr[:, j] a column, and arr[r1:r2, c1:c2] a rectangular submatrix—returning views (not copies) for memory efficiency unless explicitly converted.
Slicing follows Python's sequence rules: start:stop:step, where defaults are start=0, stop=None (array end), and step=1. Negative indices count from the end (-1 is last element). For 2D slicing, row and column slices are separated by a comma, preserving the array's structure. Array shapes are accessed via arr.shape (tuple of dimensions), crucial for dynamic operations like selecting the "center" element at indices (n//2, m//2) for an n×m array with odd dimensions.
Algorithm/Approach
Use NumPy's advanced indexing and slicing to extract required subarrays as views, then convert to lists/ints for the output dictionary. Determine subarray bounds dynamically using arr.shape to handle varying array sizes:
- Fixed-size slices (e.g., top-left 2×2) use literal indices.
- Dynamic slices (e.g., last row, rightmost column) use shape attributes like -1 or [-1].
- Single-element extraction uses integer indexing.
Return a dictionary mapping string keys to processed results, ensuring type compatibility (lists for arrays, int for scalar).
Step-by-Step Strategy
- Get array dimensions: Use rows, cols = arr.shape to enable dynamic slicing.
- Extract top-left 2×2: Slice arr[:2, :2] and convert to nested list via .tolist().
- Get bottom row: Slice arr[-1, :] (or arr[rows-1, :]) and convert to list.
- Get rightmost column: Slice arr[:, -1] (or arr[:, cols-1]) and convert to list.
- Find center element: For odd dimensions, compute center_idx = rows // 2, cols // 2; extract arr[center_idx] as int.
- Build dictionary: Map keys to these values and return.
Test with sample input to verify shapes and types match expected output.
Common Pitfalls
- Assuming fixed shape: Sample is 3×3, but code must use shape for generality (e.g., larger arrays).
- Type mismatches: Slices return NumPy arrays; use .tolist() for lists, .item() or for scalar int—.tolist() on 0D fails.
- Even vs. odd dimensions: Center assumes odd sizes; problem implies valid input, but check rows % 2 == 1 if needed.
- Views vs. copies: Slicing creates views (efficient), but .tolist()/.item() materializes data safely.
- Off-by-one errors: -1 for last index; [:2] takes first two (indices 0,1).
Time & Space Complexity
Time: O(n) where n is total elements extracted—slicing is O(1) view creation (no data copy), but .tolist() scans subarray elements. Dominant for small fixed slices (top-left is constant O(1)).
Space: O(k) for output lists where k is extracted elements (e.g., O(min(rows,cols)) worst-case); dictionary overhead is negligible. Views minimize memory until conversion.