Module 1 · Lesson 3

Array Attributes & Dtypes

Every array carries metadata describing its layout:

NumPy infers a dtype from your data, but mixing an int with a float upcasts the whole array to float64 — there is no per-element typing.

arr = np.array([[1, 2, 3], [4, 5, 6]])
print("shape:   ", arr.shape)
print("ndim:    ", arr.ndim)
print("size:    ", arr.size)
print("dtype:   ", arr.dtype)
print("itemsize:", arr.itemsize, "bytes")

mixed = np.array([1, 2, 3.5])   # one float -> whole array becomes float64
print(mixed.dtype)

Output:

shape:    (2, 3)
ndim:     2
size:     6
dtype:    int64
itemsize: 8 bytes
float64
3.1

Describe an array

Write describe(arr) that returns a dict with keys "shape", "ndim", "size", and "dtype_name" (the dtype as a string, e.g. "int64") for the given array.

💡 Hint

str(arr.dtype) gives you the dtype name as a string.

✅ Solution
def describe(arr):
    return {
        "shape": arr.shape,
        "ndim": arr.ndim,
        "size": arr.size,
        "dtype_name": str(arr.dtype),
    }

One more pair of tools before the next exercise. np.floor(x) rounds each element down to the nearest integer (elementwise) — np.ceil and np.round work the same way for rounding up / to the nearest. And np.all(bool_array) collapses a whole boolean array down to a single True/False: True only if every element is True. np.any is the mirror image — True if at least one element is True.

print(np.floor(np.array([1.2, 2.9, -0.5])))     # [ 1.  2. -1.]
print(np.all(np.array([True, True, False])))     # False — not all True
print(np.any(np.array([True, True, False])))     # True — at least one True

Output:

[ 1.  2. -1.]
False
True
3.2

Safe integer casting

Write to_int_safe(arr) that casts a float array to int64 using .astype, but raises a ValueError (with any message) if doing so would silently truncate a non-integer value (e.g. casting 2.5 to 2).

💡 Hint

Compare arr to np.floor(arr) elementwise with ==, then wrap that in np.all(...) to get a single True/False before deciding whether to cast.

✅ Solution
def to_int_safe(arr):
    if not np.all(arr == np.floor(arr)):
        raise ValueError("array contains non-integer values")
    return arr.astype(np.int64)