ndarrays

A homogeneous block of numbers — shape, dtype, and why lists multiply differently.

An ndarray is one block of numbers with a shape and a type. Every value has the same dtype. That is why array * 2 doubles every entry, while a Python list * 2 repeats the list.

Goal

Build a small array, read .shape / .dtype / .ndim, and see how list arithmetic differs.

From a list

prices = np.array([10.5, 22.0, 31.0])
print(prices)
print("shape:", prices.shape)
print("ndim:", prices.ndim)
print("dtype:", prices.dtype)

A 1-D shape prints as (3,) — the trailing comma is Python’s way to write a one-element tuple.

Two dimensions

Rows are cities (Nairobi, Mombasa, Kisumu). Columns are products A, B, C.

units = np.array(
    [
        [12, 7, 2],
        [9, 6, 0],
        [3, 11, 1],
    ]
)
print(units)
print("shape:", units.shape)
print("ndim:", units.ndim)

shape is (rows, cols) — here (3, 3).

Lists do not do elementwise math

py = [12, 7, 2]
arr = np.array(py)
print("list * 2:", py * 2)
print("array * 2:", arr * 2)

Use arrays when you want every cell multiplied. Use lists when you want a Python sequence.

One memory buffer

units = np.array(
    [
        [12, 7, 2],
        [9, 6, 0],
        [3, 11, 1],
    ],
    dtype=float,
)
print(units.flags["C_CONTIGUOUS"])
print("itemsize:", units.itemsize, "bytes")
print("nbytes:", units.nbytes)

The numbers live in one contiguous block. Later chapters show when a slice still points at that block (a view) and when NumPy copies.

You should see

dtype for integer literals is usually int64 (or int32 on some builds). Passing dtype=float stores float64.